Ipywidgets: alter slider defaults based on a scenario - python

I'm using ipywidges to create a plot. I'd like to have a dropdown with options (e.g. Scenario A, Scenario B). Each scenario should change the slider position (a=0, b=1), and one should be able to modify the parameters freely afterwards. Any ideas?
Here is my toy example:
import ipywidgets as widgets
def line(a=0,b=1):
#fig, ax = plt.subplots(figsize=(6, 4))
x = np.arange(-10,10)
y = a+xrange*b
plt.xlim((-10,10))
plt.ylim((-10,10))
plt.plot(x, y)
widgets.interact(line, a=(-10,10,0.1), b=(-10,10,0.1))
I was playing with an additional wrapper functions but with no success. In reality of course, I would like to have a few more scenarios and a lot more parameters.

Just as another answer, you could also link different widgets. In this case we are going to link the a and b sliders with a Dropdown menu, such that a change of the latter will call the functions on_change_* and switch the default values of the sliders (depending on the chosen scenario).
import ipywidgets as widgets
import numpy as np
import matplotlib.pyplot as plt
def line(a=0,b=0):
x = np.arange(-10,10)
y = a+x*b
plt.xlim((-10,10))
plt.ylim((-10,10))
plt.plot(x, y)
a_slider = widgets.FloatSlider(min=-10, max=10, step=0.1, value=0)
b_slider = widgets.FloatSlider(min=-10, max=10, step=0.1, value=1)
drop = widgets.Dropdown(options=["a", "b"], value="a", description='Scenario:')
def on_choose_a(d):
if drop.value == "a":
a_slider.value = 2
else:
a_slider.value = 5
return a_slider.value
def on_choose_b(d):
if drop.value == "a":
b_slider.value = 3
else:
b_slider.value = 7
return b_slider.value
widgets.dlink((drop, "value"), (a_slider, "value"), on_choose_a)
widgets.dlink((drop, "value"), (b_slider, "value"), on_choose_b)
display(drop)
widgets.interact(line, a=a_slider, b=b_slider);

All right, I think I've found a very nice workaround:
def line(a=0,b=0):
x = np.arange(-10,10)
y = a+x*b
plt.xlim((-10,10))
plt.ylim((-10,10))
plt.plot(x, y)
sliders = widgets.interact(a=(-10,10,0.1), b=(-10,10,0.1))
def test(chose_defaults):
if chose_defaults=="a":
#sliders
def h(a=5,b=5):
return(line(a,b))
if chose_defaults=="b":
#sliders
def h(a=0,b=1):
return(line(a,b))
widgets.interact(test, chose_defaults=["a","b"])
The above code basically nests two widgets. Firstly a separate widget for chosing a scenario is shown; action for the scenarios are the plots that differ only in the default setup.

Related

`update` function doesn't work correctly for bokeh interactors in python

I have a source code that plots the alphashape of a stock price. There's a slider to update the plot dynamically. But the update function doesn't work as expected.
Here's the source code.
x=[76.84,76.85,76.86,76.87,76.88,76.9,76.91,76.92,76.93,76.94,76.97,76.97,76.98,76.99,77.0,77.03,77.03,77.04,77.05,77.06,77.09,77.09,77.1,77.11,77.12,77.15,77.16,77.16,77.17,77.18,77.21,77.22,77.22,77.23,77.24,77.27,77.28,77.28,77.29,77.3,77.33,77.34,77.35,77.35,77.36,77.39,77.4,77.41,77.41,77.42,77.45,77.46,77.47,77.47,77.48,77.51,77.52,77.53,77.54,77.54,77.57,77.58,77.59,77.6,77.6,77.63,77.64,77.65,77.66,77.66,77.69,77.7,77.71,77.72,77.73,77.75,77.76,77.77,77.78,77.79,77.81,77.82,77.83,77.84,77.85,77.87,77.88,77.89,77.9,77.91,77.93,77.94,77.95,77.96,77.97,77.99,78.0,78.01,78.02,78.03,78.05,78.06,78.07,78.08,78.09,78.13,78.14,78.15,78.17,78.18,78.19,78.2,78.21,78.24,78.24,78.25,78.26,78.27,78.3,78.3,78.31,78.32,78.33,78.36,78.36,78.37,78.38,78.39,78.42,78.43,78.43,78.44,78.45,78.48,78.49,78.49,78.5,78.51,78.54,78.55,78.55,78.56,78.57,78.6,78.61,78.62,78.62,78.63,78.66,78.67,78.68,78.68,78.69,78.72,78.73,78.74,78.74,78.75,78.78,78.79,78.8,78.81,78.81,78.84,78.85,78.86,78.87,78.87,78.91,78.92,78.93,78.94,78.96,78.97,78.98,78.99,79.0,79.02,79.03,79.04,79.05,79.06,79.08,79.09,79.1,79.11,79.12,79.2,79.21,79.22,79.23,79.24,79.26,79.27,79.28,79.29,79.3,79.32,79.33,79.34,79.35,79.36,79.38,79.39,79.4,79.41,79.42,79.44,79.45,79.46,79.47,79.48,79.51,79.51,79.52,79.53,79.54,79.57,79.57,79.58,79.59,79.6,79.63,79.63,79.64,79.65,79.66,79.69,79.7,79.7,79.71,79.72,79.75,79.76,79.76,79.77,79.78,79.81,79.82,79.82,79.83,79.84,79.87,79.88,79.89,79.89,79.9,79.94,79.95,79.95,79.96,79.99,80.0,80.01,80.02,80.02,80.05,80.06,80.07,80.08,80.08,80.11,80.12,80.13,80.14,80.14,80.17,80.18,80.19,80.2,80.21,80.23,80.24,80.25,80.26,80.27,80.29,80.3,80.31,80.32,80.33,80.35,80.36,80.37,80.38,80.39,80.41,80.42,80.43,80.44,80.45,80.47,80.48,80.49,80.5,80.51,80.53,80.54,80.55,80.56,80.57,80.59,80.6,80.61,80.62,80.63,80.65,80.66,80.67,80.68,80.69,80.71,80.72,80.73,80.74,80.75,80.78,80.78,80.79,80.8,80.81,80.84,80.84,80.85,80.86,80.87,80.9,80.9,80.91,80.92,80.93,80.96,80.97,80.97,80.98,80.99,81.02,81.03,81.03,81.04,81.05,81.08,81.09,81.1,81.1,81.11,81.14,81.15,81.16,81.16,81.17,81.2,81.21,81.22,81.22,81.23,81.28,81.29,81.29,81.32,81.33,81.34,81.35,81.35,81.38,81.39,81.4,81.41,81.41,81.44,81.45,81.46,81.47,81.48,81.5,81.51,81.52,81.53,81.54,81.56,81.57,81.58,81.59,81.6,81.62,81.63,81.64,81.65,81.66,81.68,81.69,81.7,81.71,81.72,81.74,81.75,81.76,81.77,81.78,81.8,81.81,81.82,81.83,81.84,81.86,81.87,81.88,81.89,81.9,81.92,81.93,81.94,81.95,81.96,81.98,81.99,82.0,82.01,82.02,82.05,82.06,82.07,82.08,82.11,82.11,82.12,82.13,82.14,82.17,82.18,82.18,82.19,82.2,82.23,82.24,82.24,82.25,82.26,82.29,82.3,82.3,82.31,82.32,82.35,82.36,82.37,82.37,82.38,82.41,82.42,82.43,82.43,82.44,82.59,82.6,82.61,82.62,82.62,82.65,82.66,82.67,82.68,82.68,82.71,82.72,82.73,82.74,82.75,82.77,82.78,82.79,82.8,82.81,82.83,82.84,82.85,82.86,82.87,82.89,82.9,82.91,82.92,82.93,82.95,82.96,82.97,82.98,82.99,83.01,83.02,83.03,83.04,83.05,83.07,83.08,83.1,83.11,83.13,83.14,83.15,83.16,83.17,83.19,83.2,83.21,83.22,83.23,83.26,83.26,83.27,83.28,83.29,83.32,83.32,83.33,83.34,83.35,83.38,83.38,83.39,83.4,83.41,83.44,83.45,83.45,83.46,83.47,83.5,83.51,83.51,83.52,83.53,83.56,83.57,83.57,83.58,83.59,83.62,83.63,83.64,83.64,83.65,83.68,83.69,83.7,83.7,83.71,83.74,83.75,83.76,83.76,83.77,83.8,83.81,83.82,83.83,83.83,83.86,83.87,83.88,83.89,83.89,83.92,83.93,83.94,83.95,83.95,83.98,83.99,84.0,84.01,84.02,84.04,84.05,84.06,84.07,84.08,84.1,84.11,84.12,84.13,84.14,84.16,84.17,84.18,84.19,84.2,84.22,84.23,84.24,84.25,84.26,84.28,84.29,84.3,84.31,84.32,84.34,84.35,84.36,84.37,84.38,84.43,84.44,84.46,84.47,84.48,84.49,84.5,84.53,84.53,84.54,84.55,84.56,84.59,84.59,84.6,84.61,84.62,84.65,84.65,84.66,84.67,84.68,84.71,84.72,84.72,84.73,84.74,84.77,84.78,84.78,84.79,84.8,84.83,84.84,84.84,84.85,84.86,84.89,84.9,84.91,84.91,84.92,84.95,84.96,84.97,84.97,84.98,85.01,85.02,85.03,85.03,85.04,85.07,85.08,85.09,85.1,85.1,85.13,85.14,85.15,85.16,85.16,85.19,85.2,85.22,85.22,85.25,85.26,85.27,85.28,85.29,85.31,85.32,85.33,85.34,85.35,85.37,85.38,85.39,85.4,85.41,85.43,85.44,85.45,85.46,85.47,85.61,85.62,85.63,85.64,85.65,85.67,85.68,85.69,85.7,85.71,85.73,85.74,85.75,85.76,85.77,85.8,85.8,85.81,85.82,85.83,85.86,85.86,85.87,85.88,85.89,85.92,85.92,85.93,85.94,85.95,85.98,85.99,85.99,86.0,86.01,86.04,86.05,86.05,86.06,86.07,86.1,86.11,86.11,86.12,86.13,86.16,86.17,86.18,86.18,86.19,86.22,86.23,86.24,86.28,86.29,86.3,86.3,86.31,86.34,86.35,86.36,86.37,86.37,86.4,86.41,86.42,86.43,86.43,86.46,86.47,86.48,86.49,86.5,86.52,86.53,86.54,86.55,86.56,86.58,86.59,86.6,86.61,86.62,86.64,86.65,86.66,86.67,86.68,86.7,86.71,86.72,86.73,86.74,86.78,86.79,86.8,86.82,86.83,86.84,86.85,86.86,86.88,86.89,86.9,86.91,86.92,86.94,86.95,86.96,86.97,86.98,87.0,87.01,87.02,87.03,87.04,87.07,87.07,87.08,87.09,87.1,87.13,87.13,87.14,87.15,87.16,87.19,87.19,87.2,87.21,87.22,87.25,87.26,87.26,87.27,87.28,87.31,87.32,87.32,87.33,87.34,87.37,87.38,87.38,87.39,87.4,87.43,87.44,87.45,87.45,87.46,87.49,87.5,87.51,87.51,87.52,87.55,87.56,87.61,87.62,87.63,87.64,87.64,87.67,87.68,87.69,87.7,87.7,87.73,87.74,87.75,87.76,87.77,87.79,87.8,87.81,87.82,87.83,87.85,87.86,87.87,87.88,87.89,87.91,87.92,87.93,87.94,87.95,87.97,87.98,87.99,88.0,88.01,88.03,88.04,88.05,88.06,88.07,88.09,88.1,88.11,88.12,88.13,88.15,88.16,88.17,88.18,88.19,88.21,88.22,88.23,88.24,88.25,88.27,88.28,88.29,88.3,88.31,88.34,88.34,88.35,88.4,88.4,88.41,88.42,88.43,88.46,88.46,88.47,88.48,88.49,88.52,88.53,88.53,88.54,88.55,88.7,88.71,88.72,88.72,88.73,88.76,88.77,88.78,88.78,88.79,88.82,88.83,88.84,88.85,88.85,88.88,88.89,88.9,88.91,88.91,88.94,88.95,88.96,88.97,88.97,89.0,89.01,89.02,89.03,89.04,89.06,89.07,89.08,89.09,89.1,89.12,89.13,89.14,89.15,89.16,89.18,89.19,89.2,89.21,89.22,89.24,89.25,89.26,89.27,89.28,89.3,89.31,89.32,89.33,89.34,89.36,89.37,89.38,89.39,89.42,89.43,89.44,89.45,89.46,89.48,89.49,89.5,89.51,89.52,89.54,89.55,89.56,89.57,89.58,89.61]
y=[2.29,2.41,2.4,2.38,2.43,2.42,2.38,2.36,2.4,2.37,2.36,2.37,2.34,2.32,2.31,2.25,2.25,2.21,2.2,2.21,2.21,2.21,2.21,2.19,2.17,2.1,2.08,2.08,2.12,2.15,2.1,2.09,2.1,2.08,2.08,2.01,2.0,1.98,1.98,1.95,1.92,1.92,1.92,1.92,1.92,1.88,1.88,1.91,1.91,1.88,1.89,1.87,1.85,1.84,1.83,1.88,1.93,1.88,1.82,1.82,2.08,2.13,2.35,2.32,2.37,2.34,2.25,2.35,2.33,2.34,2.32,2.34,2.39,2.53,2.49,2.53,2.54,2.55,2.53,2.52,2.52,2.54,2.66,2.71,2.81,2.92,3.09,2.99,3.03,2.98,3.01,2.98,2.93,2.91,2.93,2.91,2.89,2.92,2.9,2.87,2.9,2.9,2.93,2.83,2.78,2.67,2.6,2.66,2.61,2.61,2.61,2.54,2.56,2.51,2.52,2.55,2.6,2.6,2.67,2.63,2.62,2.63,2.61,2.58,2.59,2.59,2.62,2.59,2.58,2.61,2.63,2.6,2.63,2.63,2.61,2.6,2.58,2.58,2.57,2.58,2.58,2.58,2.58,2.57,2.58,2.58,2.58,2.58,2.55,2.52,2.53,2.53,2.51,2.46,2.48,2.45,2.54,2.53,2.49,2.51,2.49,2.48,2.49,2.47,2.48,2.49,2.48,2.5,2.5,2.55,2.53,2.52,2.51,2.49,2.5,2.49,2.49,2.47,2.46,2.48,2.45,2.45,2.43,2.43,2.45,2.45,2.45,2.45,2.45,2.45,2.45,2.45,2.46,2.45,2.44,2.44,2.45,2.45,2.47,2.56,2.52,2.48,2.47,2.5,2.54,2.54,2.58,2.61,2.63,2.63,2.63,2.61,2.59,2.59,2.56,2.57,2.58,2.56,2.57,2.61,2.59,2.6,2.6,2.58,2.6,2.59,2.6,2.61,2.61,2.59,2.6,2.62,2.62,2.6,2.61,2.59,2.59,2.59,2.59,2.61,2.67,2.65,2.63,2.63,2.6,2.56,2.59,2.59,2.59,2.58,2.58,2.57,2.58,2.55,2.55,2.58,2.58,2.57,2.58,2.83,2.88,2.93,2.79,2.82,2.81,2.86,2.86,2.85,2.82,2.82,2.82,2.78,2.78,2.82,2.79,2.8,2.79,2.79,2.78,2.72,2.73,2.71,2.72,2.73,2.73,2.74,2.74,2.72,2.73,2.73,2.71,2.68,2.71,2.75,2.84,2.91,2.89,2.92,2.97,2.96,2.94,2.99,3.04,2.97,2.99,2.97,2.99,2.98,2.99,3.0,3.01,2.99,2.98,2.99,2.99,2.99,3.01,2.96,2.97,3.0,2.98,2.97,2.96,2.96,3.0,3.0,2.99,2.98,2.99,2.99,2.99,2.99,2.99,2.99,2.98,2.98,2.98,2.98,3.02,3.03,3.03,3.05,3.09,3.08,3.1,3.12,3.14,3.13,3.12,3.14,3.15,3.13,3.15,3.14,3.14,3.14,3.14,3.13,3.11,3.08,3.08,3.08,3.08,3.1,3.11,3.11,3.11,3.09,3.13,3.17,3.28,3.43,3.52,3.47,3.45,3.45,3.45,3.44,3.46,3.46,3.45,3.44,3.45,3.45,3.45,3.45,3.45,3.47,3.5,3.54,3.52,3.5,3.5,3.5,3.44,3.45,3.45,3.45,3.43,3.45,3.48,3.48,3.45,3.46,3.43,3.46,3.45,3.43,3.43,3.42,3.42,3.43,3.42,3.41,3.39,3.38,3.38,3.38,3.4,3.39,3.38,3.39,3.37,3.37,3.38,3.38,3.38,3.38,3.38,3.38,3.37,3.36,3.37,3.36,3.36,3.37,3.36,3.41,3.41,3.4,3.39,3.39,3.37,3.37,3.36,3.36,3.36,3.36,3.36,3.37,3.36,3.37,3.39,3.45,3.42,3.39,3.4,3.4,3.39,3.38,3.38,3.38,3.38,3.38,3.38,3.38,3.38,3.38,3.42,3.42,3.41,3.39,3.39,3.39,3.37,3.38,3.4,3.41,3.44,3.43,3.43,3.43,3.43,3.42,3.42,3.42,3.47,3.46,3.47,3.53,3.65,3.59,3.76,3.85,3.77,3.9,3.76,3.75,3.8,3.73,3.7,3.66,3.68,3.66,3.69,3.68,3.69,3.69,3.61,3.61,3.61,3.59,3.59,3.59,3.63,3.61,3.62,3.63,3.62,3.61,3.61,3.62,3.69,3.66,3.69,3.68,3.66,3.65,3.66,3.68,3.78,3.76,3.77,3.74,3.75,3.77,3.75,3.7,3.7,3.73,3.74,3.79,3.83,3.87,3.86,3.8,3.81,3.78,3.8,3.78,3.78,3.84,3.81,3.81,3.82,3.78,3.75,3.76,3.74,3.72,3.71,3.72,3.78,3.78,3.77,3.76,3.74,3.74,3.75,3.75,3.73,3.72,3.71,3.68,3.7,3.67,3.64,3.56,3.57,3.56,3.61,3.62,3.59,3.57,3.59,3.55,3.54,3.53,3.52,3.53,3.53,3.58,3.6,3.57,3.53,3.53,3.54,3.55,3.57,3.57,3.58,3.64,3.63,3.6,3.6,3.6,3.59,3.6,3.6,3.61,3.61,3.62,3.64,3.64,3.64,3.69,3.73,3.71,3.69,3.69,3.69,3.65,3.66,3.66,3.72,3.73,3.7,3.7,3.72,3.74,3.74,3.74,3.79,3.85,3.9,3.88,3.93,3.86,3.94,4.0,4.0,3.97,3.94,3.93,3.91,3.92,3.94,3.94,3.94,3.99,3.98,4.01,3.99,3.92,3.82,3.71,3.81,3.77,3.76,3.81,3.79,3.83,3.83,3.88,3.89,3.84,3.84,3.83,3.79,3.81,3.8,3.81,3.82,3.83,3.8,3.81,3.81,3.83,3.83,3.86,3.92,3.93,3.97,3.97,3.96,3.95,3.94,3.96,3.98,3.88,3.98,4.0,4.02,4.04,4.08,4.09,4.09,4.16,4.22,4.21,4.19,4.19,4.18,4.19,4.2,4.19,4.2,4.21,4.27,4.3,4.29,4.26,4.29,4.29,4.34,4.36,4.35,4.33,4.33,4.36,4.34,4.33,4.34,4.37,4.35,4.36,4.39,4.38,4.41,4.4,4.4,4.39,4.39,4.41,4.42,4.46,4.48,4.53,4.63,4.65,4.71,4.81,4.91,5.0,4.95,5.04,5.01,4.98,4.9,4.95,4.91,4.8,4.9,4.86,4.76,4.77,4.77,4.79,4.8,4.79,4.81,4.89,4.87,4.87,4.87,4.8,4.79,4.75,4.69,4.69,4.71,4.78,4.76,4.74,4.73,4.8,4.81,4.84,4.83,4.83,4.83,4.79,4.75,4.75,4.66,4.69,4.7,4.68,4.7,4.73,4.72,4.75,4.75,4.75,4.71,4.72,4.71,4.69,4.68,4.64,4.65,4.65,4.66,4.66,4.64,4.65,4.64,4.62,4.63,4.6,4.52,4.45,4.53,4.49,4.5,4.48,4.37,4.39,4.4,4.41,4.43,4.47,4.46,4.45,4.42,4.44,4.45,4.45,4.44,4.43,4.41,4.41,4.44,4.41,4.38,4.38,4.37,4.37,4.38,4.32,4.24,4.29,4.31,4.29,4.27,4.28,4.28,4.28,4.32,4.32,4.33,4.33,4.32,4.33,4.39,4.47,4.47,4.53,4.53,4.53,4.52,4.54,4.51,4.53,4.53,4.53,4.54,4.54,4.58,4.56,4.58,4.56,4.55,4.53,4.54,4.54,4.55,4.54,4.53,4.52,4.49,4.45,4.45,4.46,4.46,4.48,4.46,4.47,4.47,4.49,4.47,4.47,4.48,4.51,4.57,4.57,4.59,4.61,4.57,4.57,4.6,4.64,4.64,4.63,4.65,4.65,4.64,4.64,4.66,4.72,4.73,4.76,4.74,4.8,4.78,4.72,4.76,4.86,4.86,4.88,4.86,4.83,4.85,4.85,4.84,4.81,4.82,4.82,4.82,4.81,4.82,4.85,4.85,4.84,4.82,4.81,4.78,4.81,4.79,4.75,4.78,4.8,4.79,4.78,4.76,4.77,4.77,4.77,4.78,4.79,4.79,4.76,4.75,4.74,4.73,4.74,4.75,4.8,4.81,4.84,4.82,4.8,4.81,4.8,4.77,4.81,4.8,4.81,4.84,4.86,4.83,4.82,4.81,4.8,4.78,4.81,4.81,4.82,4.88,4.84,4.84,4.83,4.83,4.85,4.85,4.83,4.81,4.82,4.79,4.8,4.79,4.78,4.8,4.79,4.78,4.77,4.78,4.77,4.76,]
from alphashape import alphashape
from shapely.geometry import mapping
from bokeh.plotting import figure
from ipywidgets import interact
from bokeh.io import output_notebook, show, push_notebook
def alphashape_func(x, y, alpha):
length = range(len(x))
# date count
pnt = [[x[i],y[i]] for i in length]
# return a shapely.polygon/multipolygon
alpha_shape = alphashape(pnt, alpha=alpha)
# convert shapely.polygon/multipolygon to list
map = mapping(alpha_shape)['coordinates']
poly_shp = [i[0] for i in map]
bound_len = len(poly_shp)
# single alpha shape case
if bound_len == 1:
bound_x = [i[0] for i in poly_shp]
bound_y = [i[1] for i in poly_shp]
# multiple alpha shape case
else:
bound_x = [[i[0] for i in poly_shp[j]] for j in range(bound_len)]
bound_y = [[i[1] for i in poly_shp[j]] for j in range(bound_len)]
# return a dict containing 2 lists: x & y.
return {'x':bound_x, 'y':bound_y}
alpha = 5
alpha_high_pnt = alphashape_func(x,y,alpha)
plot = figure(sizing_mode='stretch_width', output_backend="webgl")
# line_pnt(plot, max_processed_xy['x'], max_processed_xy['y'],legend_label ='processed_xy',line_color='yellow', line_width=2)
alpha_shape_plt = plot.multi_line(xs=alpha_high_pnt['x'],ys=alpha_high_pnt['y'], line_color='cyan',legend_label = 'alpha_high_pnt')
# create an update function
def update(alpha=5):
alpha_high_pnt = alphashape_func(x,y,alpha)
alpha_shape_plt.data_source.data['xs'] = alpha_high_pnt['x']
alpha_shape_plt.data_source.data['ys'] = alpha_high_pnt['y']
# push new values to the notebook
push_notebook()
output_notebook()
show(plot)
interact(update, alpha=(0,25,1))
(the dynamic slider only works when you run it in jupyter in a web browser)
When I drag the slider, it shows an error message:
BokehUserWarning: ColumnDataSource's columns must be of the same length. Current lengths: ('xs', 54), ('ys', 99)
I don't see the reason of this error, since when I manually adjust the alpha value, the lengths of xs and ys equal.
Can anyone help?
===================== update ======================
Based on #bigreddot suggestion, I update the code to this, the doesn't match problem is resolved, but the plot doesn't refresh yet.
from alphashape import alphashape
from shapely.geometry import mapping
from bokeh.plotting import figure
from bokeh.io import output_notebook, show, push_notebook
from bokeh.models import ColumnDataSource
from ipywidgets import interact
output_notebook()
def alphashape_func(x, y, alpha):
length = range(len(x))
# date count
pnt = [[x[i],y[i]] for i in length]
# return a shapely.polygon/multipolygon
alpha_shape = alphashape(pnt, alpha=alpha)
# convert shapely.polygon/multipolygon to list
map = mapping(alpha_shape)['coordinates']
poly_shp = [i[0] for i in map]
bound_len = len(poly_shp)
# single alpha shape case
if bound_len == 1:
bound_x = [i[0] for i in poly_shp]
bound_y = [i[1] for i in poly_shp]
# multiple alpha shape case
else:
bound_x = [[i[0] for i in poly_shp[j]] for j in range(bound_len)]
bound_y = [[i[1] for i in poly_shp[j]] for j in range(bound_len)]
# return a dict containing 2 lists: x & y.
return {'x':bound_x, 'y':bound_y}
alpha = 5
plot = figure(sizing_mode='stretch_width', output_backend="webgl")
source = ColumnDataSource(data=alphashape_func(x,y,alpha))
alpha_shape_plt = plot.multi_line(source=source, xs='x',ys='y', line_color='cyan',legend_label = 'alpha_high_pnt')
print
# create an update function
def update(alpha=5):
source.data = alphashape_func(x,y,alpha)
# push new values to the notebook
push_notebook()
interact(update, alpha=(0,25,1))
show(plot)
In between this line:
alpha_shape_plt.data_source.data['xs'] = alpha_high_pnt['x']
and this line:
alpha_shape_plt.data_source.data['ys'] = alpha_high_pnt['y']
the CDS columns are not all the same length. If you need to update with data that has a new length you should collect all the updates up front in a new_data dict and then set
source.data = new_data
to update the CDS "all at once". This is more efficient in any case, as well, since it results in fewer property update change events being sent out.

Options next to graph / plot

This is how it looks now:
current application
This is what I want:
what i want
I am looking for ways to directly implement options next to my graph. When I select an option, the option should be instantly applied into the graph. For example: I want to change the points color to green. Or only show values that are lower than 5.
I can't find a way to create such window next to my graph. The graph takes the whole screen, I believe it's a canvas. But I want the graph to dock to the right side and dock the graph options to the left side.
Here is my code (just an example):
import numpy as np
from matplotlib.widgets import LassoSelector
from matplotlib.path import Path
class SelectFromCollection:
"""
Select indices from a matplotlib collection using `LassoSelector`.
Selected indices are saved in the `ind` attribute. This tool fades out the
points that are not part of the selection (i.e., reduces their alpha
values). If your collection has alpha < 1, this tool will permanently
alter the alpha values.
Note that this tool selects collection objects based on their *origins*
(i.e., `offsets`).
Parameters
----------
ax : `~matplotlib.axes.Axes`
Axes to interact with.
collection : `matplotlib.collections.Collection` subclass
Collection you want to select from.
alpha_other : 0 <= float <= 1
To highlight a selection, this tool sets all selected points to an
alpha value of 1 and non-selected points to *alpha_other*.
"""
def __init__(self, ax, collection, alpha_other=0.3):
self.canvas = ax.figure.canvas
self.canvas.show()
self.collection = collection
self.alpha_other = alpha_other
self.xys = collection.get_offsets()
self.Npts = len(self.xys)
# Ensure that we have separate colors for each object
self.fc = collection.get_facecolors()
if len(self.fc) == 0:
raise ValueError('Collection must have a facecolor')
elif len(self.fc) == 1:
self.fc = np.tile(self.fc, (self.Npts, 1))
self.lasso = LassoSelector(ax, onselect=self.onselect)
self.ind = []
def onselect(self, verts):
path = Path(verts)
self.ind = np.nonzero(path.contains_points(self.xys))[0]
self.fc[:, -1] = self.alpha_other
self.fc[self.ind, -1] = 1
self.collection.set_facecolors(self.fc)
self.canvas.draw_idle()
def disconnect(self):
#self.lasso.disconnect_events()
self.fc[:, -1] = 1
self.collection.set_facecolors(self.fc)
self.canvas.draw_idle()
if __name__ == '__main__':
import matplotlib.pyplot as plt
# Fixing random state for reproducibility
np.random.seed(19680801)
data = np.random.rand(100, 2)
subplot_kw = dict(xlim=(0, 1), ylim=(0, 1), autoscale_on=False)
fig, ax = plt.subplots(subplot_kw=subplot_kw)
pts = ax.scatter(data[:, 0], data[:, 1], s=80)
selector = SelectFromCollection(ax, pts)
def onPress(event):
# print("Selected points:")
# print(selector.xys[selector.ind])
selector.disconnect()
ax.set_title("")
fig.canvas.draw()
def onRelease(event):
print("Selected points:")
print(selector.xys[selector.ind])
fig.canvas.mpl_connect("button_press_event", onPress)
fig.canvas.mpl_connect("button_release_event", onRelease)
ax.set_title("Press enter to accept selected points.")
ax_color = plt.axes([0, 0.10, 0.20, 0.20])
plt.show()
Does anyone know a solution for this? I would greatly appreciate it.

creating functions in for loop to control ToggleButton, problem with late binding (ipywidgets JupyterLab)

I'm trying to make ToggleButtons for my plot and stumble upon a problem. I created the buttons, no problem:
# create buttons
button = []
for i in range(len(expnum)):
button.append(wg.ToggleButton(value=False, description=expnum[i], tooltip='Click to show/hide', layout=wg.Layout(width='60px', height='30px')))
button.append(wg.Output())
The problem is this part:
# control buttons
for i in range(len(expnum)):
def on_value_change(change):
with button[i * 2 + 1]:
plots[i][0].set_visible(change['new'])
button[i * 2].observe(on_value_change, names='value')
The problem with the code is (I guess) the late binding problem. It recognizes only the last value (meaning: only in the state of i == len(expnum)-1) which is not what I expected. I actually succeeded in making it works like what I expected with the following codes below:
# control buttons
def on_value_change(change):
with button[1]:
plots[0][0].set_visible(change['new'])
button[0].observe(on_value_change, names='value')
def on_value_change(change):
with button[3]:
plots[1][0].set_visible(change['new'])
button[2].observe(on_value_change, names='value')
def on_value_change(change):
with button[5]:
plots[2][0].set_visible(change['new'])
button[4].observe(on_value_change, names='value')
and so on. Currently the number of iteration is still manageable to have it manually written like that, but with increasing amount of data, it is impossible to not using for loop.
I am kind of new to python and I'm pretty sure there must be some kind of simple hack or workaround to make this thing works. I have found some thread that explained about forcing early binding with def f(i=i): instead of just def f():. But I have no idea how to implement those to my case.
Does anyone have any idea how to solve my problem with for loop? Otherwise a complete new approach is also welcomed.
--- edit ---
This is the full code:
import pandas as pd
import numpy as np
import ipywidgets as wg
from ipywidgets import HBox, VBox
import matplotlib.pyplot as plt
import matplotlib
from IPython.display import display
%matplotlib widget
expnum = ['data1', 'data2', 'data3']
t_all = [ [1, 2, 3], [1, 3, 4], [1, 2, 5] ]
V_all = [ [100, 200, 100], [100, 120, 130], [150, 105, 100] ]
# create buttons
button = []
for i in range(len(expnum)):
button.append(wg.ToggleButton(value=False, description=expnum[i], tooltip='Click to show/hide', layout=wg.Layout(width='60px', height='30px')))
button.append(wg.Output())
display(HBox(( [button[i] for i in range(len(button))] )) )
# create figure
fig, ax = plt.subplots()
ax.set_xlabel('t [s]')
ax.set_ylabel('V [V]')
plots = []
for i in range(len(t_all)):
plots.append(ax.plot(t_all[i], V_all[i], visible=False, alpha=0.7))
# ---------------- The problem starts here --------------------------
# set visibility of plots
for i in range(len(expnum)):
def on_value_change(change):
with button[i * 2 + 1]:
plots[i][0].set_visible(change['new'])
button[i * 2].observe(on_value_change, names='value')
Why does above code (starting from # set visibility of plots) doesn't work, while below code works:
def on_value_change(change):
with button[1]:
plots[0][0].set_visible(change['new'])
button[0].observe(on_value_change, names='value')
def on_value_change(change):
with button[3]:
plots[1][0].set_visible(change['new'])
button[2].observe(on_value_change, names='value')
def on_value_change(change):
with button[5]:
plots[2][0].set_visible(change['new'])
button[4].observe(on_value_change, names='value')
I see no difference in the code below and the code above other than using for loop instead of manually retype the same code over and over.

Restricting panning range in matplotlib plots

Is there any way to restrict the range that a user can interactively pan a plot in matplotlib?
For example, I'd like to limit the user's panning range to the positive quadrant of the plot, so x > 0 and y > 0, effectively creating floors at x = 0 and y = 0.
There is not built-in way to restrict zooming or panning other than restricting it to either the x or y direction. So you need to implement that yourself.
Connecting to the 'xlim_changed' and 'ylim_changed' events, you may check if the limits are valid or not, and potentially reset them to the valid range.
import matplotlib.pyplot as plt
import numpy as np
fig,ax=plt.subplots()
x = np.sin(np.linspace(0,10, 266))+1
ax.plot(x)
class Restrictor():
def __init__(self, ax, x=lambda x: True,y=lambda x: True):
self.res = [x,y]
self.ax =ax
self.limits = self.get_lim()
self.ax.callbacks.connect('xlim_changed', lambda evt: self.lims_change(axis=0))
self.ax.callbacks.connect('ylim_changed', lambda evt: self.lims_change(axis=1))
def get_lim(self):
return [self.ax.get_xlim(), self.ax.get_ylim()]
def set_lim(self, axis, lim):
if axis==0:
self.ax.set_xlim(lim)
else:
self.ax.set_ylim(lim)
self.limits[axis] = self.get_lim()[axis]
def lims_change(self, event=None, axis=0):
curlim = np.array(self.get_lim()[axis])
if self.limits[axis] != self.get_lim()[axis]:
# avoid recursion
if not np.all(self.res[axis](curlim)):
# if limits are invalid, reset them to previous state
self.set_lim(axis, self.limits[axis])
else:
# if limits are valid, update previous stored limits
self.limits[axis] = self.get_lim()[axis]
res = Restrictor(ax, x=lambda x: x>=0,y=lambda x: x>=0)
plt.show()
The user experience of such limitations are not great, since a certain action (like moving the mouse) is not answered with the expected behaviour (plot does not move); but one has to judge for oneself if this would still make sense.

How to set interact arguments programmatically?

Suppose I have function, that accepts list of arguments. List can be of variable length and function is ok with it. For example:
import math
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
%matplotlib inline
def PlotSuperposition(weights):
def f(x):
y = 0
for i, weight in enumerate(weights):
if i==0:
y+=weight
else:
y += weight*math.sin(x*i)
return y
vf = np.vectorize(f)
xx = np.arange(0,6,0.1)
plt.plot(xx, vf(xx))
plt.gca().set_ylim(-5,5)
PlotSuperposition([1,1,2])
shows
I can hardcode interact for given number of arguments, like here
interact(lambda w0, w1, w2: PlotSuperposition([w0,w1,w2]), w0=(-3,+3,0.1), w1=(-3,+3,0.1), w2=(-3,+3,0.1))
which shows
But how can I make number of sliders defined programmatically?
I tried
n_weights=10
weight_sliders = [widgets.FloatSlider(
value=0,
min=-10.0,
max=10.0,
step=0.1,
description='w%d' % i,
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='.1f',
) for i in range(n_weights)]
interact(PlotSuperposition, weights=weight_sliders)
but got error
TypeError: 'FloatSlider' object is not iterable
inside PlotSuperposition saying that interact doesn't pass a list of values to the function.
How to accomplish?
First, modify your function to take an arbitrary number of keyword arguments instead of a plain list:
def PlotSuperposition(**kwargs):
def f(x):
y = 0
for i, weight in enumerate(kwargs.values()):
if i==0:
y+=weight
else:
y += weight*math.sin(x*i)
return y
vf = np.vectorize(f)
xx = np.arange(0,6,0.1)
plt.plot(xx, vf(xx))
plt.gca().set_ylim(-5,5)
Notice the asterisks in front of kwargs. Then, call interact with a dictionary of key/value arguments:
kwargs = {'w{}'.format(i):slider for i, slider in enumerate(weight_sliders)}
interact(PlotSuperposition, **kwargs)

Categories