如何使用 python创建连续均匀分布的 pdf 和 cdf 的交互式图?
我想有交互式滑块,我可以用它来调整分布的参数。
这有助于更好地了解该分布在不同参数值下的行为。
1. 最简单的方法是使用scipy.stats.uniform()获取分布的 pdf 和 cdf,然后使用pn.interact()获取参数的交互式滑块:
# import libraries
from scipy import stats
import pandas as pd
import hvplot.pandas
import panel as pn
pn.extension()
import panel.widgets as pnw
import holoviews as hv
hv.extension('bokeh')
# define pdf and cdf for cont uniform distr and return plots
def get_interactive_plot_cont_uniform(loc=1, scale=5):
continuous_uniform = stats.uniform(loc=loc, scale=scale)
x_values = np.linspace(0, 10, num=1000)
fx_values = continuous_uniform.pdf(x_values)
Fx_values = continuous_uniform.cdf(x_values)
interactive_plot = (
hv.Curve((x_values, fx_values), label='PDF')
+ hv.Curve((x_values, Fx_values), label='CDF'))
return interactive_plot
# use pn.interact() to get interactive sliders
# and define the sliders yourself for more flexibility
pn.interact(
get_interactive_plot_cont_uniform,
loc=pnw.FloatSlider(
name='Value for loc', value=1.,
start=0, step=0.5, end=10),
scale=pnw.FloatSlider(
name='Value for scale', value=5.,
start=0, step=0.5, end=10),
)
生成的带有滑块的交互式绘图:
2. 更广泛、更灵活的示例,可以更直观地设置参数a和b:
# create sliders to adjust parameter a and b
param_a = pnw.FloatSlider(name='Value for parameter a', value=1., start=0, step=0.5, end=10)
param_b = pnw.FloatSlider(name='Value for parameter b', value=6., start=0, step=0.5, end=10)
# get interactivity by using following decorator
@pn.depends(param_a, param_b)
def get_interactive_cont_uniform(param_a, param_b):
# define the uniform distribution
# scale in scipy.stats.uniform is b - a
loc = param_a
scale = param_b - param_a
continuous_uniform = stats.uniform(loc=loc, scale=scale)
# calculate x and y values for pdf and cdf and put in dataframe
x_values = np.linspace(0, 10, num=1000)
fx_values = continuous_uniform.pdf(x_values)
Fx_values = continuous_uniform.cdf(x_values)
df = pd.DataFrame({
'x': x_values,
'f(x)': fx_values,
'F(x)': Fx_values,
})
# create pdf and cdf plot
pdf_plot = df.hvplot.line(
x='x',
y='f(x)',
ylim=(0, 1.02),
ylabel='f(x) - pdf values', xlabel='',
title=f'pdf where a={param_a} and b={param_b}',
height=225,
).opts(fontscale=1.25)
cdf_plot = df.hvplot.line(
x='x',
y='F(x)',
ylim=(0, 1.02),
ylabel='F(x) - cdf values',
title=f'cdf where a={param_a} and b={param_b}',
height=225,
).opts(fontscale=1.25)
return (pdf_plot + cdf_plot).cols(1)
# use pyviz panel to get a nice view of sliders and the plots
pn.Column(
pn.pane.Markdown("## Continuous Uniform Distribution"),
pn.Row(param_a, param_b),
pn.Row(get_interactive_cont_uniform)
)
要建立在另一个答案的基础上,您还可以使用panel
实现的面向类的方法。这是我最喜欢的方法,因为它使您的代码保持美观和模块化。bokeh
此外,它直接通过而不是通过holoviews
(我个人通常在围栏上)直接绘制。使用bokeh
而不是可以holoviews
让您更好地控制您的情节,但出于这些目的,您选择哪条路线并不重要。
.show()
此代码段在笔记本中工作,但可以通过更改对方法的调用.servable()
并使用以下命令从命令行/终端运行脚本来适应脚本格式:panel serve file.py --show
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
import panel as pn
import param
from scipy import stats
class UniformViewer(param.Parameterized):
a = param.Number(1, bounds=(0, 15), step=.5)
b = param.Number(4, bounds=(0, 15), step=.5, inclusive_bounds=(False, True))
def __init__(self, **params):
super().__init__(**params)
# Initialize 2 plots with common arguments
plot_kwargs = {
"height": 250,
"width": 250,
"y_range": (-.1, 1.1)
}
self.p_pdf = figure(title="PDF", **plot_kwargs)
self.p_cdf = figure(title="CDF", **plot_kwargs)
# Calculate range of data based on parameter bounds
a_min, a_max = self.param.a.bounds
b_min, b_max = self.param.b.bounds
# Create x-values for pdf/cdf calculations
x_min, x_max = min(a_min, b_min), max(a_max, b_max)
self.x = np.linspace(x_min, x_max, 1000)
# store x values in our CDS since they won't change
self.cds = ColumnDataSource({"x": self.x})
self.update_cds() # Populate cdf/pdf values into the CDS before plotting
# Draw our pdf and cdf on their respective figures
self.p_pdf.line(x="x", y="pdf", source=self.cds)
self.p_cdf.line(x="x", y="cdf", source=self.cds)
# Add callback that executes anytime self.a or self.b changes
@param.depends("a", "b", watch=True)
def update_cds(self):
# Calcualte the uniform distribution using
# self.a as the left endpoint and self.b as the right endpoint
uniform_dist = stats.uniform(loc=self.a, scale=self.b-self.a)
self.cds.data.update({
"pdf": uniform_dist.pdf(self.x),
"cdf": uniform_dist.cdf(self.x)
})
def view(self):
return pn.Row(self.p_pdf, self.p_cdf)
interactive_plot = UniformViewer()
pn.Column(interactive_plot, interactive_plot.view()).show()