LSR/extramf.py

206 lines
6.7 KiB
Python
Raw Normal View History

2020-06-04 19:21:01 +02:00
# -*- coding: utf-8 -*-
"""
Some extra membership functions to augment those in skfuzzy.membership
@author: james.power@mu.ie Created on Fri Jul 27 16:10:03 2018
"""
from collections import OrderedDict
import numpy as np
import skfuzzy
import skfuzzy.membership as skmemb
import scipy.interpolate as interp
def singletonmf(x, xpt):
''' Find which x-val is nearest the given point, and set it to 1'''
mf = np.zeros(len(x))
diffs = np.abs(x - xpt)
idx = np.nonzero(diffs == diffs.min())[0][0]
mf[idx] = 1
return mf
def pointsetmf(x, pointset, method='linear'):
'''Interpolate from a point-set using the chosen interpolation method'''
# Make sure we're in ascending order first:
pointset = sorted(pointset, key=lambda p: p[0])
x_min, x_max = x[0], x[-1]
# Lead on left from y=0, unless otherwise specified:
if pointset[0][0] > x_min:
pointset = [(x_min, 0)] + pointset
# Trail on right from last given y value
if pointset[-1][0] < x_max:
pointset = pointset + [(x_max, pointset[-1][1])]
px, py = [p[0] for p in pointset], [p[1] for p in pointset]
if method == 'linear':
f = interp.interp1d(px, py)
elif method == 'lagrange':
f = interp.lagrange(px, py)
elif method == 'spline':
f = interp.make_interp_spline(px, py)
elif method == 'cubic':
f = interp.CubicSpline(px, py, bc_type='natural')
# Sometimes interpoliation can go outside the bounds:
return np.clip(f(x), 0, 1)
def gaussprod(x, mean1, sigma1, mean2, sigma2):
'''Ensure the means are in correct order before calling gauss2mf'''
if mean1 > mean2:
mean1, sigma1, mean2, sigma2 = mean2, sigma2, mean1, sigma1
return skmemb.gauss2mf(x, mean1, sigma1, mean2, sigma2)
def rectanglemf(x, a, b):
'''Zero before and after given end points, one in between them'''
mf = np.ones(len(x))
mf[np.nonzero(x < a)] = 0
mf[np.nonzero(x > b)] = 0
return mf
def leftlinearmf(x, a, b):
'''One to the left, zero to the right, slope down in-between'''
mf = np.ones(len(x))
midpts = np.nonzero(np.logical_and(a < x, x < b))
mf[midpts] = (((b - x[midpts]) / (b - a)))
mf[np.nonzero(x >= b)] = 0
return mf
def rightlinearmf(x, a, b):
'''Zero to the left, one to the right, slope up in-between'''
mf = np.zeros(len(x))
midpts = np.nonzero(np.logical_and(a < x, x < b))
mf[midpts] = 1 - (((b - x[midpts]) / (b - a)))
mf[np.nonzero(x >= b)] = 1
return mf
def rampmf(x, a, b):
'''A line from a up/down to b (depending on the order of a and b)'''
if a < b:
return rightlinearmf(x, a, b)
elif a > b:
return leftlinearmf(x, b, a)
else: # a == b
return np.zeros(len(x))
def cosinemf(x, center, width):
'''A cosine curve distributed about the center with the given width'''
mf = np.zeros(len(x))
# Only plot the curve within the given width either side the center:
midpts = np.nonzero(np.logical_and(center - 0.5 * width <= x,
x <= center + 0.5 * width))
to_angle = 2.0 * np.pi / width
mf[midpts] = (0.5 * (1.0 + np.cos(to_angle * (x[midpts] - center))))
return mf
def concavemf(x, infl, end):
'''A curve rising/falling to end point, bent according to inflexion pt'''
mf = np.ones(len(x))
if infl <= end: # Concave increasing
incpts = np.nonzero(x < end)
mf[incpts] = (end - infl) / (2.0 * end - infl - x[incpts])
else: # Concave decreasing
decpts = np.nonzero(x > end)
mf[decpts] = (infl - end) / (infl - 2.0 * end + x[decpts])
return mf
def leftgaussmf(x, mean, sigma):
''' Like Gaussian, but always 1 when <= mean (so, slopes down only)'''
mf = skmemb.gaussmf(x, mean, sigma)
mf[np.nonzero(x <= mean)] = 1
return mf
def rightgaussmf(x, mean, sigma):
''' Like Gaussian, but always 1 when >= mean (so, slopes up only)'''
mf = skmemb.gaussmf(x, mean, sigma)
mf[np.nonzero(x >= mean)] = 1
return mf
def spikemf(x, center, width):
'''A symmetrical curved (exp) spike centered at the given location'''
return np.exp(-np.abs(10.0 / width * (x - center)))
def jfl_sigmf(x, gain, center):
'''Like sigmf, but jFuzzyLogic supplies parameters in a different order'''
return skmemb.sigmf(x, center, gain)
def fl_bellmf(x, center, width, slope):
'''Like gbellmf, but fuzzylite supplies parameters in a different order'''
return skmemb.gbellmf(x, width, slope, center)
# ### Sanity check: plot some examples of the membership functions
import matplotlib.pyplot as plt
def visualise_all(x, y_list, titles, ncols=3):
'''
Just display the given plot-data on a grid of separate graphs.
Also show the centroid as a vertical red line.
'''
nrows = np.int(np.ceil(len(y_list) / ncols))
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(8, 9))
fig.tight_layout()
fig.subplots_adjust(bottom=-.25)
for i, p in enumerate(y_list):
r, c = divmod(i, ncols)
axes[r][c].plot(x, p)
cog = skfuzzy.centroid(x, p)
axes[r][c].axvline(x=cog, color='red', linestyle='--')
axes[r][c].set_title(titles[i])
axes[r][c].set_ylim([-0.05, 1.05]) # so all have the same (0,1) y-axis
def _plot_mf_for(x):
'''Given the x-values, plot a series of example membership functions'''
tests = OrderedDict([
# Gaussians:
('gauss', skmemb.gaussmf(x, 50, 10)),
('left gauss', leftgaussmf(x, 50, 10)),
('right gauss', rightgaussmf(x, 50, 10)),
# Triangles:
('triangular', skmemb.trimf(x, [25, 50, 75])),
('left linear', leftlinearmf(x, 25, 75)),
('right linear', rightlinearmf(x, 25, 75)),
# fuzzylite:
('cosine', cosinemf(x, 50, 50)),
('inc concave', concavemf(x, 50, 75)),
('dec concave', concavemf(x, 50, 25)),
('spike', spikemf(x, 50, 50)),
('inc ramp', rampmf(x, 25, 75)),
('dec ramp', rampmf(x, 75, 25)),
# Rectangle-ish
('trapezoid', skmemb.trapmf(x, [20, 40, 60, 80])),
('rectangle', rectanglemf(x, 25, 75)),
('singleton', singletonmf(x, 50)),
])
# Example point sets:
ps_tests = [
[(40, 0.5), (60, 1)],
[(10, 0.5), (25, 0.25), (40, 0.75), (80, .5)],
[(0, 1), (40, 0.25), (50, .5), (99, 0)]
]
# Now try some interpolation methods on these:
for method in ['linear', 'lagrange', 'spline', 'cubic']:
tests.update([('{} ex{}'.format(method, i), pointsetmf(x, ps, method))
for i, ps in enumerate(ps_tests)])
return tests
if __name__ == '__main__':
x = np.arange(0, 100)
plots = _plot_mf_for(x)
visualise_all(x, plots.values(), list(plots.keys()))