3
\$\begingroup\$

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.

enter image description here

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
plt.subplots_adjust(bottom=0.25)
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()
asked May 28, 2020 at 4:36
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

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
plt.subplots_adjust(bottom=0.25)
# add a slider widget
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()
```
answered May 28, 2020 at 9:01
\$\endgroup\$
9
  • \$\begingroup\$ 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. \$\endgroup\$ Commented May 28, 2020 at 10:27
  • \$\begingroup\$ Ah, just saw that the same is true in the OP :) \$\endgroup\$ Commented May 28, 2020 at 10:28
  • \$\begingroup\$ Thanks, want to edit the post perhaps? \$\endgroup\$ Commented May 28, 2020 at 10:29
  • \$\begingroup\$ No, that would be a valid point to include in a review! Feel free to edit your post to include it, though. \$\endgroup\$ Commented May 28, 2020 at 10:30
  • 1
    \$\begingroup\$ 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) \$\endgroup\$ Commented May 28, 2020 at 11:08

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.