# matplotlib fixing axis scale and alignment

There is few points In the script I don't like I think it can be improved, but I need a second opinion, here is the interactive plot I'm trying to build.

here is the code with thought in comments.

#!/usr/bin/python3

from functools import lru_cache

import numpy as np
import scipy.stats as ss
import matplotlib.pyplot as plt
import matplotlib.widgets as widgets

@lru_cache # beacuse it redraws each time
def get_pdf(mu, sigma=1, offset=4):
o = sigma * offset
x = np.linspace(mu - o, mu + o, 100)
rv = ss.norm(mu, sigma)
return x, rv.pdf(x)

fig, ax = plt.subplots()
# added the subplot for bottom margin and the slider, since its also the ax

ax.fill_between(*get_pdf(0), label='A', alpha=0.7)
ax.set_xlim(-10, 10)
ax.set_ylim(0, 1)

# my guess is this has to be integrated with the update rather than outside
# and using t variable as global which I don't like, and guess it can be improved
t = ax.fill_between(*get_pdf(2), label='B', color='crimson', alpha=0.7)

slider = widgets.Slider(
# ax position are absolute, should be easy without subplot may be
ax      = plt.axes([0.25, 0.1, 0.5, 0.03]),
label   = "shift",
valmin  = -5,
valmax  = 5,
valinit = 2,
valstep = 0.5
)

def update(val):
x, y = get_pdf(val)
global t # really hate to do this
t.remove()
t = ax.fill_between(*get_pdf(val), color='crimson', alpha=0.7)
fig.canvas.draw_idle()

slider.on_changed(update)
ax.legend()
plt.show()


If you want to get rid of the global t you could consider creating a class to manage the data and updating. I quickly whipped something together, although I'm not well versed enough in stats to have a good idea about what names would be more appropriate.

from functools import lru_cache

import numpy as np
import scipy.stats as ss
import matplotlib.pyplot as plt
import matplotlib.widgets as widgets

#probability density function generator: chached
@lru_cache # beacuse it redraws each time
def get_pdf(mu, sigma=1, offset=4):
o = sigma * offset
x = np.linspace(mu - o, mu + o, 100)
rv = ss.norm(mu, sigma)
return x, rv.pdf(x)

# This simple class will hold the reference to t so that it doesn't need to
# be a global
class Data():
'''
A simple class that plots data on an axis and contains a method for updating
the plot.
'''

def __init__(self, ax, **properties):
self.properties = properties
self.t = ax.fill_between(*get_pdf(2), **properties)

def update(self, val):
x, y = get_pdf(val)
self.t.remove()
self.t = ax.fill_between(x, y, **self.properties)
fig.canvas.draw_idle()

# Generate the figure and axis for our widget

fig, ax = plt.subplots()
# added the subplot for bottom margin and the slider, since its also the ax

slider = widgets.Slider(
# ax position are absolute, should be easy without subplot may be
ax      = plt.axes([0.25, 0.1, 0.5, 0.03]),
label   = "shift",
valmin  = -5,
valmax  = 5,
valinit = 2,
valstep = 0.5
)

# add a reference distribution *A*
ax.fill_between(*get_pdf(0), label='A', alpha=0.7)
ax.set_xlim(-10, 10)
ax.set_ylim(0, 1)

# Create a data instance
properties = dict(label='B', color='crimson', alpha=0.7)
data = Data(ax=ax, **properties)

# link data update method to slider.on_changed
slider.on_changed(data.update)
ax.legend()
plt.show()

$$$$

• You are not using the x, y you defined in the update method. You can either remove them or re-use them in the fill_between. – Graipher May 28 at 10:27
• Ah, just saw that the same is true in the OP :) – Graipher May 28 at 10:28
• Thanks, want to edit the post perhaps? – Andrew Micallef May 28 at 10:29
• No, that would be a valid point to include in a review! Feel free to edit your post to include it, though. – Graipher May 28 at 10:30
• actually, on reflection I feel like getting x,y = get_pdf(val) and then ax.fill_between(x,y...) is more pythonic than *get_pdf(val)` – Andrew Micallef May 28 at 11:08