отслеживание нулей в боке с помощью виджета слайдера - PullRequest
0 голосов
/ 15 февраля 2019

Я создаю интерактивный график, который будет отслеживать нули уравнения, и ползунок настраивается.Я столкнулся с проблемой при обновлении исходных данных нулей.

1) Как обновить / пересчитать нули?

2) Есть ли более эффективный способ сделать это?

#@title Interactive Phase Plane Plot
output_notebook()
mu = 0
x = np.linspace(-2*np.pi, 2*np.pi, 2000)
y = mu*np.sin(x)-np.sin(2*x)

Исходная функция до манипулирования ползунком.Следующий раздел приблизительно оценивает нули.

def init_fp(x):
    fp_x = []
    fp_y = []
    i = 0
    while i < len(x):
        if np.abs(y[i]) > 0.005:
            pass
        else:
            fp_x.append(x[i])
            fp_y.append(0)
        i += 1
    return fp_x, fp_y

В этом разделе создается источник данных для манипулирования обратным вызовом на графике Боке.

source = ColumnDataSource(data={
    'x' : x,
    'y' : y
})

fpx, fpy = init_fp(x)
source1 = ColumnDataSource(data={
    'fpx' : fpx,
    'fpy' : fpy
})

Это определяет операцию обратного вызова, и внутри я нахожусьобновление графика новым функционалом и пересчет нулей.

callback_single = CustomJS(args=dict(source=source, source1=source1), code="""
        var data = source.data;
        var mu = cb_obj.value
        var x = data['x']
        var y = data['y']
        var x1 = data['xfp']
        var y1 = data['yfp']
        for (var i = 0; i < x.length; i++) {
            y[i] = mu*Math.sin(x[i])-Math.sin(2*x[i]);
        }
        source.change.emit();
        for (var i=0; i < x.length; i++){
            if (Math.fps(y[i]) < 0.05){
                x1[i] = x[i];
                y1[i] = 0;
            }
        }
        source1.change.emit();
    """)

Здесь определен ползунок и сделаны графики, а также различные эстетики для графика.

mu = Slider(start=-5, end=5, value=0, step=0.01, title="mu", callback=callback_single)
p = figure(plot_width=1000, plot_height=500)
p.line('x', 'y', source=source)
p.circle('fpx', 'fpy', source=source1)
p.xgrid.grid_line_color=None
p.ygrid.grid_line_alpha=0.8
p.xaxis.axis_label = 'Theta'
p.yaxis.axis_label = 'd Theta/dt'

t = Title()
t.text = 'Interactive Phase Plane Plot'
layout = column(p, widgetbox(mu))
p.title = t
show(layout)

1 Ответ

0 голосов
/ 26 февраля 2019

В вашем примере есть несколько вещей, которые можно изменить:

  • Свойства fpx, fpy принадлежат source1, а не source.
  • Math.fps должно быть Math.abs.
  • У вас есть опечатка в именах полей при получении x1 и y1.
  • Второй цикл в CustomJS обратном вызовене работает, если число нулей меняется.

Один из способов переписать обратный вызов заключается в следующем:

callback_single = CustomJS(args=dict(source=source, source1=source1), code="""
        const mu = cb_obj.value
        const { x } = source.data;
        const y = x.map( val => mu * Math.sin(val) - Math.sin(2*val));
        source.data = { x, y };
        const fpx = x.filter((val, ind) => Math.abs(y[ind]) < 0.05);
        const fpy = fpx.map(() => 0);         
        source1.data = { fpx, fpy };
    """)

Также есть способ использовать более идиоматический numyв init_fp:

def init_fp(x, y):
    fp_x = x[np.abs(y) < 0.05]
    fp_y = np.zeros_like(fp_x)
    return fp_x, fp_y

Вот полный код, я оставил математику нулей как есть и также изменил с output_notebook на output_file:

from bokeh.io import show, output_file
from bokeh.layouts import column, widgetbox
from bokeh.models import ColumnDataSource, CustomJS, Slider, Title
from bokeh.plotting import figure
import numpy as np
output_file('zeros.html')
mu = 0
x = np.linspace(-2*np.pi, 2*np.pi, 2000)
y = mu*np.sin(x)-np.sin(2*x)

def init_fp(x, y):
    fp_x = x[np.abs(y) < 0.05]
    fp_y = np.zeros_like(fp_x)
    return fp_x, fp_y

source = ColumnDataSource(data={
    'x' : x,
    'y' : y
})

fpx, fpy = init_fp(x, y)
source1 = ColumnDataSource(data={
    'fpx' : fpx,
    'fpy' : fpy
})

callback_single = CustomJS(args=dict(source=source, source1=source1), code="""
        const mu = cb_obj.value;
        const { x } = source.data;
        const y = x.map( val => mu * Math.sin(val) - Math.sin(2*val));
        source.data = { x, y };
        const fpx = x.filter((val, ind) => Math.abs(y[ind]) < 0.05);
        const fpy = fpx.map(() => 0);
        source1.data = { fpx, fpy };
                               """)
mu = Slider(start=-5, end=5, value=0, step=0.01, title="mu", callback=callback_single)
p = figure(plot_width=1000, plot_height=500)
p.line('x', 'y', source=source)
p.circle('fpx', 'fpy', source=source1)
p.xgrid.grid_line_color=None
p.ygrid.grid_line_alpha=0.8
p.xaxis.axis_label = 'Theta'
p.yaxis.axis_label = 'd Theta/dt'

t = Title()
t.text = 'Interactive Phase Plane Plot'
layout = column(p, widgetbox(mu))
p.title = t
show(layout)
...