使用 python 中的 "bokeh" 库更新绘图时拟合曲线

Fitting a curve while updating a plot using the "bokeh" library in python

我已经编写了一段代码来使用 bokeh 库可视化数据。我定义了一个简单的数据框 df 为:

      Y1     Y2  x_val
0   3.01   0.99      0
1   5.10   3.05      1
2   6.99   7.29      2
3   8.02  13.41      3
4  10.81  20.31      4

并使用以下代码,我可以交互式地绘制 x_val 与 Y1 和 Y2 的关系图。

import pandas as pd
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Select
from bokeh.plotting import figure


dc = {'x_val':[0,1,2,3,4], 'Y1':[3.01, 5.10, 6.99, 8.02, 10.81],
     'Y2':[0.99, 3.05, 7.29, 13.41, 20.31]}
df = pd.DataFrame(dc)

source = ColumnDataSource(data={'x': df['x_val'], 'y': df['Y1'], 'y2':df['Y2']})

plot = figure(x_axis_label = 'x', y_axis_label = 'f(x)')
plot.circle('x', 'y', source=source)


def update_plot(attr, old, new):
        y = y_select.value
        new_data = {'x': df['x_val'], 'y': df[y]}
        source.data = new_data


y_select = Select(options=['Y1', 'Y2'],value='Y1',title='y-axis data')
y_select.on_change('value', update_plot)

# Create layout and add to current document
layout = row(widgetbox(y_select), plot)

# Add layout to current document
curdoc().add_root(layout)

现在,每当我更新绘图时,我都想在数据上拟合一条曲线,即,当我选择绘制 x_val 与 Y1 的关系时,也会绘制相关的拟合曲线,对于其他情况,即 x_val 与 Y2。为此,我将上面的代码扩展为如下一段:

import pandas as pd
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Select
from bokeh.plotting import figure
from scipy.optimize import curve_fit


def fit_func(xdata, ydata):

    def func(x,a,c):
        return a*(x**2)+a*x+c

    y = func(xdata, 1, 1)
    popt, pcov = curve_fit(func, xdata, ydata)
    new_x = np.arange(0,10,2)
    new_y = func(new_x,*popt)

    return (new_x, new_y)


dc = {'x_val':[0,1,2,3,4], 'Y1':[3.01, 5.10, 6.99, 8.02, 10.81],
     'Y2':[0.99, 3.05, 7.29, 13.41, 20.31]}
df = pd.DataFrame(dc)

dfit = pd.DataFrame({'x': fit_func(df['x_val'].values, df['Y1'].values)[0],
                     'y': fit_func(df['x_val'].values, df['Y1'].values)[1]})

source = ColumnDataSource(data={'x': df['x_val'], 'y': df['Y1'], 'y2':df['Y2']})
source_f = ColumnDataSource(data={'x': dfit['x'], 'y': dfit['y']})

plot = figure(x_axis_label = 'x', y_axis_label = 'f(x)')
plot.circle('x', 'y', source=source)
plot.line('x', 'y', source=source_f)    

def update_plot(attr, old, new):
        y = y_select.value
        new_data = {'x': df['x_val'], 'y': df[y]}
        source.data = new_data

        answ = fit_func(df['x_val'].values, df[y].values)
        new_fit = pd.DataFrame({'x': fit_func(df['x_val'].values, df['Y1'].values)[0],
                                'y': fit_func(df['x_val'].values, df['Y1'].values)[1]})
        source_f.data = new_fit


y_select = Select(options=['Y1', 'Y2'],value='Y1',title='y-axis data')
y_select.on_change('value', update_plot)

# Create layout and add to current document
layout = row(widgetbox(y_select), plot)

# Add layout to current document
curdoc().add_root(layout)

这次更新图是看不到拟合曲线的 我的问题。 另一个问题是:是否有任何优雅的方法可以仅在我们更新绘图时拟合曲线?

谢谢!

考虑到 Alex 的评论,我把我的问题的解决方案放在这里:

import pandas as pd
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource, Select
from bokeh.plotting import figure
from scipy.optimize import curve_fit

def fit_func(xdata, ydata):

    def func(x,a,c):
        return a*(x**2)+a*x+c

    y = func(xdata, 1, 1)
    popt, pcov = curve_fit(func, xdata, ydata)
    new_x = np.arange(0,10,2)
    new_y = func(new_x,*popt)

    return (new_x, new_y)

dc = {'x_val':[0,1,2,3,4], 'Y1':[3.01, 5.10, 6.99, 8.02, 10.81],
     'Y2':[0.99, 3.05, 7.29, 13.41, 20.31]}
df = pd.DataFrame(dc)

source = ColumnDataSource(data={'x': df['x_val'], 'y': df['Y1'], 'y2':df['Y2']})

xf, yf = fit_func(df['x_val'].values, df['Y1'].values)
source_f = ColumnDataSource(data={'x': xf, 'y': yf})

plot = figure(x_axis_label = 'x', y_axis_label = 'f(x)')
plot.circle('x', 'y', source=source)
plot.line('x', 'y', source=source_f)

def update_plot(attr, old, new):
        y = y_select.value
        new_data = {'x': df['x_val'], 'y': df[y]}
        source.data = new_data

        answ = fit_func(df['x_val'].values, df[y].values)
        new_fit = {'x':answ[0], 'y':answ[1]}
        source_f.data = new_fit

y_select = Select(options=['Y1', 'Y2'],value='Y1',title='y-axis data')
y_select.on_change('value', update_plot)

layout = row(widgetbox(y_select), plot)    
curdoc().add_root(layout)