#python #pandas #scipy #holoviews #panel-pyviz
#python #pandas #scipy #holoviews #панель-pyviz
Вопрос:
Как я могу создать интерактивный график pdf и cdf непрерывного равномерного распределения с помощью python?
Я хотел бы иметь интерактивные ползунки, с помощью которых я мог бы настраивать параметры распределения.
Это удобно для лучшего понимания поведения этого распределения при разных значениях его параметров.
Ответ №1:
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)
)
Ответ №2:
Чтобы основываться на другом ответе, вы также можете использовать классоориентированный подход, который 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()
Комментарии:
1. Спасибо Кэмерон за ваш подробный ответ! Приятно видеть, как вы это создаете.
2. к вашему сведению, я добавил недостающий импорт боке в ваш код