Создание интерактивного графика непрерывного равномерного распределения с помощью ползунков для значений параметров

#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. к вашему сведению, я добавил недостающий импорт боке в ваш код