%matplotlib inline
import numpy as np
import skfuzzy as fuzz
import matplotlib.pyplot as plt
# Generate universe variables
# * Quality and service on subjective ranges [0, 10]
# * Tip has a range of [0, 25] in units of percentage points
x_spike = np.arange(0, 11, 1)
x_clim = np.arange(0, 11, 1)
x_RoC = np.arange(0, 6, 0.5)
x_tip = np.arange(0.0, 1.01, .01)
# Generate fuzzy membership functions
spike_lo = fuzz.trapmf(x_spike, [-1e30, -1e30, 0.07, 0.2])
spike_md = fuzz.trapmf(x_spike, [0.07, 0.2, 2, 6])
spike_hi = fuzz.trapmf(x_spike, [2, 6, 1e30, 1e30])
clim_lo = fuzz.trapmf(x_clim, [-1e30, -1e30, 3, 4])
clim_md = fuzz.trapmf(x_clim, [3, 4, 5, 6])
clim_hi = fuzz.trapmf(x_clim, [5, 6, 1e30, 1e30])
RoC_lo = fuzz.trapmf(x_RoC, [-1e30, -1e30, 0.5, 1.5])
RoC_md = fuzz.trapmf(x_RoC, [0.5, 1.5, 3, 4])
RoC_hi = fuzz.trapmf(x_RoC, [3, 4, 1e30, 1e30])
tip_lo = fuzz.trimf(x_tip, [0.0, 0.225, 0.45])
tip_md = fuzz.trimf(x_tip, [0.275, 0.5, 0.725])
tip_hi = fuzz.trimf(x_tip, [0.55, 0.775, 1.0])
# Visualize these universes and membership functions
fig, (ax0, ax1, ax2, ax3) = plt.subplots(nrows=4, figsize=(8, 9))
ax0.plot(x_spike, spike_lo, 'g', linewidth=1.5, label='Bad')
ax0.plot(x_spike, spike_md, 'y', linewidth=1.5, label='Decent')
ax0.plot(x_spike, spike_hi, 'r', linewidth=1.5, label='Great')
ax0.set_title('Spike')
ax0.legend()
ax1.plot(x_clim, clim_lo, 'g', linewidth=1.5, label='Poor')
ax1.plot(x_clim, clim_md, 'y', linewidth=1.5, label='Acceptable')
ax1.plot(x_clim, clim_hi, 'r', linewidth=1.5, label='Amazing')
ax1.set_title('Climatology')
ax1.legend()
ax2.plot(x_RoC, RoC_lo, 'g', linewidth=1.5, label='Low')
ax2.plot(x_RoC, RoC_md, 'y', linewidth=1.5, label='Medium')
ax2.plot(x_RoC, RoC_hi, 'r', linewidth=1.5, label='High')
ax2.set_title('Rate of Change')
ax2.legend()
ax3.plot(x_tip, tip_lo, 'g', linewidth=1.5, label='Low')
ax3.plot(x_tip, tip_md, 'y', linewidth=1.5, label='Medium')
ax3.plot(x_tip, tip_hi, 'r', linewidth=1.5, label='High')
ax3.set_title('Tip amount')
ax3.legend()
# Turn off top/right axes
for ax in (ax0, ax1, ax2):
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
plt.tight_layout()
spike = 1.0
clim = 5.2
RoC = 0.9
# We need the activation of our fuzzy membership functions at these values.
# The exact values 6.5 and 9.8 do not exist on our universes...
# This is what fuzz.interp_membership exists for!
spike_level_lo = fuzz.interp_membership(x_spike, spike_lo, spike)
spike_level_md = fuzz.interp_membership(x_spike, spike_md, spike)
spike_level_hi = fuzz.interp_membership(x_spike, spike_hi, spike)
print("Qual level: %s" % spike)
print(spike_level_lo, spike_level_md, spike_level_hi)
clim_level_lo = fuzz.interp_membership(x_clim, clim_lo, clim)
clim_level_md = fuzz.interp_membership(x_clim, clim_md, clim)
clim_level_hi = fuzz.interp_membership(x_clim, clim_hi, clim)
print("Serv level: %s" % clim)
print(clim_level_lo, clim_level_md, clim_level_hi)
RoC_level_lo = fuzz.interp_membership(x_RoC, RoC_lo, RoC)
RoC_level_md = fuzz.interp_membership(x_RoC, RoC_md, RoC)
RoC_level_hi = fuzz.interp_membership(x_RoC, RoC_hi, RoC)
print("Rate of Change: %s" % RoC)
print(RoC_level_lo, RoC_level_md, RoC_level_hi)
Qual level: 1.0 (0.0, 1.0, 0.0) Serv level: 5.2 (0.0, 0.79999999999999982, 0.20000000000000018) Rate of Change: 0.9 (0.59999999999999998, 0.40000000000000002, 0.0)
# Now we take our rules and apply them. Rule 1 concerns bad food OR service.
# The OR operator means we take the maximum of these two.
active_rule1 = np.mean((spike_level_lo, clim_level_lo, RoC_level_lo), axis=0)
print("active_rule1: %s, %s -> %s" % (spike_level_lo, clim_level_lo, active_rule1))
# Now we apply this by clipping the top off the corresponding output
# membership function with `np.fmin`
tip_activation_lo = np.fmin(active_rule1, tip_lo) # removed entirely to 0
print("tip_activation_lo: %s" % tip_activation_lo)
active_rule1: 0.0, 0.0 -> 0.2 tip_activation_lo: [ 0. 0.04444444 0.08888889 0.13333333 0.17777778 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.17777778 0.13333333 0.08888889 0.04444444 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. ]
# For rule 2 we connect acceptable service to medium tipping
active_rule2 = np.mean((spike_level_md, clim_level_md, RoC_level_md), axis=0)
print("active_rule1: %s, %s -> %s" % (spike_level_md, clim_level_md, active_rule2))
#tip_activation_md = np.fmin(clim_level_md, tip_md)
tip_activation_md = np.fmin(active_rule2, tip_md)
#print("Medium")
#print("clim_level_md: %s" % clim_level_md)
#print(tip_md)
print("tip_activation_md: %s" % tip_activation_md)
active_rule1: 1.0, 0.8 -> 0.733333333333 tip_activation_md: [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.02222222 0.06666667 0.11111111 0.15555556 0.2 0.24444444 0.28888889 0.33333333 0.37777778 0.42222222 0.46666667 0.51111111 0.55555556 0.6 0.64444444 0.68888889 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.73333333 0.68888889 0.64444444 0.6 0.55555556 0.51111111 0.46666667 0.42222222 0.37777778 0.33333333 0.28888889 0.24444444 0.2 0.15555556 0.11111111 0.06666667 0.02222222 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. ]
# For rule 3 we connect high service OR high food with high tipping
active_rule3 = np.fmax(RoC_level_hi, np.fmax(spike_level_hi, clim_level_hi))
tip_activation_hi = np.fmin(active_rule3, tip_hi)
print("High")
print("active_rule3: %s, %s -> %s" % (spike_level_hi, clim_level_hi, active_rule3))
print("tip_activation_hi: %s" % tip_activation_hi)
tip0 = np.zeros_like(x_tip)
High active_rule3: 0.0, 0.2 -> 0.2 tip_activation_hi: [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.04444444 0.08888889 0.13333333 0.17777778 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.17777778 0.13333333 0.08888889 0.04444444 0. ]
# Visualize this
fig, ax0 = plt.subplots(figsize=(8, 3))
ax0.fill_between(x_tip, tip0, tip_activation_lo, facecolor='g', alpha=0.7)
ax0.plot(x_tip, tip_lo, 'g', linewidth=0.5, linestyle='--', )
ax0.fill_between(x_tip, tip0, tip_activation_md, facecolor='y', alpha=0.7)
ax0.plot(x_tip, tip_md, 'y', linewidth=0.5, linestyle='--')
ax0.fill_between(x_tip, tip0, tip_activation_hi, facecolor='r', alpha=0.7)
ax0.plot(x_tip, tip_hi, 'r', linewidth=0.5, linestyle='--')
ax0.set_title('Output membership activity')
# Turn off top/right axes
for ax in (ax0,):
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
plt.tight_layout()
# Aggregate all three output membership functions together
aggregated = np.fmax(tip_activation_lo,
np.fmax(tip_activation_md, tip_activation_hi))
# Calculate defuzzified result
#tip = fuzz.defuzz(x_tip, aggregated, 'centroid')
tip = fuzz.defuzz(x_tip, aggregated, 'bisector')
tip_activation = fuzz.interp_membership(x_tip, aggregated, tip) # for plot
print tip
0.5
# Visualize this
fig, ax0 = plt.subplots(figsize=(8, 3))
ax0.plot(x_tip, tip_lo, 'b', linewidth=0.5, linestyle='--', )
ax0.plot(x_tip, tip_md, 'g', linewidth=0.5, linestyle='--')
ax0.plot(x_tip, tip_hi, 'r', linewidth=0.5, linestyle='--')
ax0.fill_between(x_tip, tip0, aggregated, facecolor='Orange', alpha=0.7)
ax0.plot([tip, tip], [0, tip_activation], 'k', linewidth=1.5, alpha=0.9)
ax0.set_title('Aggregated membership and result (line)')
# Turn off top/right axes
for ax in (ax0,):
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.get_xaxis().tick_bottom()
ax.get_yaxis().tick_left()
plt.tight_layout()