使用 ipywidget 具有两个参数和相互依赖的数据的交互式图

interactive plot with two parameters and interdependent data using ipywidget

我想创建一个 jupyter-notebook 单元格,显示带有 matplotlib 的交互式绘图,以说明噪声信号的平滑。在下面的示例中,我使用了 scikit-image 中的高斯滤波器。我希望噪音水平以及平滑程度可以通过滑块进行调节。为此,我使用了 ipywidgets

最初我尝试了以下方法

import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import skimage
%matplotlib inline

def plot_noise_filter(signal,cnts,sigma):
    s = signal/np.sum(signal)*cnts  #normalize the signal to have cnts counts
    noise = np.random.poisson(s)    #randomly generate poissonian noise
    filtered = skimage.filters.gaussian(noise,sigma)   #filter noisy signal with gauss filter
    f,ax = plt.subplots()                              #plot
    ax.plot(noise/np.max(noise))
    ax.plot(filtered/np.max(filtered))

c_slide = widgets.IntSlider(min=100,max=10000,step=10,description='counts')
s_slide = widgets.IntSlider(min=1,max=100,description='smoothing')

sig = np.heaviside(np.linspace(-1,1,100),1)+1
widgets.interact(plot_noise_filter,signal=widgets.fixed(sig),cnts=c_slide,sigma=s_slide)

原则上这给了我想要的情节,但现在每次我使用滑块 s_slide,都会调用该函数并生成一个新的随机信号,即使计数没有改变。我希望图中的噪声信号仅在移动相应的滑块时才会改变。

我能想到的唯一解决方法是预先计算噪声信号并将其存储在一个数组中,然后根据滑块选择该数组的元素,但这不是很优雅并且可能会非常耗费内存。

我目前的安装是使用 conda 和 python 3.7.3

ipywidgets                7.5.1
matplotlib                3.1.1
jupyter                   1.0.0
jupyter_client            5.3.1
jupyter_console           6.0.0
jupyter_core              4.4.0
notebook                  6.0.1
numpy                     1.17.2

欢迎任何帮助。提前致谢!

首先,感谢您提供非常清晰且可运行的示例。

如何使用 lru_cache 将信号和噪声生成线提取到缓存函数?然后,对于给定的 signalcnts 输入,这部分将始终 return 相同的值,移动滑块时只留下平滑变化。

import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import skimage
%matplotlib inline
from functools import lru_cache

@lru_cache(32)
def make_sig_noise(signal, cnts):
    s = signal/np.sum(signal)*cnts  #normalize the signal to have cnts counts
    noise = np.random.poisson(s)    #randomly generate poissonian noise
    return s, noise

def plot_noise_filter(signal,cnts,sigma):
    s, noise = make_sig_noise(tuple(signal), cnts)
    filtered = skimage.filters.gaussian(noise,sigma)   #filter noisy signal with gauss filter
    f,ax = plt.subplots()                              #plot
    ax.plot(noise/np.max(noise))
    ax.plot(filtered/np.max(filtered))

c_slide = widgets.IntSlider(min=100,max=10000,step=10,description='counts')
s_slide = widgets.IntSlider(min=1,max=100,description='smoothing')

sig = np.heaviside(np.linspace(-1,1,100),1)+1
widgets.interact(plot_noise_filter,signal=widgets.fixed(sig),cnts=c_slide,sigma=s_slide)