如何在Bokeh curdoc构建的交互图表中添加线性回归线
Bokeh 交叉筛选图表添加线性回归线实现方案
实现逻辑
线性回归线的计算与绘制逻辑直接嵌入现有create_figure()函数即可,完全适配当前图表重绘更新的结构,不需要修改原有控件绑定的update方法。额外增加离散轴判断逻辑:仅当X、Y轴同时选中连续型数值字段时才计算绘制回归线,避免分类字段触发计算报错。
操作步骤
- 补充导入numpy依赖,用于线性回归参数计算,同时替换原有代码中已被新版Pandas弃用的
pd.np调用 - 在散点绘制完成后,增加轴类型判断:若X/Y均为连续字段,提取当前绘图用的x、y数值序列
- 用numpy一次多项式拟合计算线性回归的斜率、截距参数
- 取X轴取值范围的最小、最大值作为回归线端点的x坐标,代入回归公式计算对应y坐标
- 调用
p.line()将回归线绘制到图表上,设置差异化样式和散点做区分
完整修改后代码
import pandas as pd import numpy as np from bokeh.layouts import column, row from bokeh.models import Select from bokeh.palettes import Spectral5 from bokeh.plotting import curdoc, figure from bokeh.sampledata.autompg import autompg_clean as df df = df.copy() SIZES = list(range(6, 22, 3)) COLORS = Spectral5 N_SIZES = len(SIZES) N_COLORS = len(COLORS) # 数据清洗 df.cyl = df.cyl.astype(str) df.yr = df.yr.astype(str) del df['name'] columns = sorted(df.columns) discrete = [x for x in columns if df[x].dtype == object] continuous = [x for x in columns if x not in discrete] def create_figure(): xs = df[x.value].values ys = df[y.value].values x_title = x.value.title() y_title = y.value.title() kw = dict() if x.value in discrete: kw['x_range'] = sorted(set(xs)) if y.value in discrete: kw['y_range'] = sorted(set(ys)) kw['title'] = "%s vs %s" % (x_title, y_title) p = figure(height=600, width=800, tools='pan,box_zoom,hover,reset', **kw) p.xaxis.axis_label = x_title p.yaxis.axis_label = y_title if x.value in discrete: # 替换已弃用的pd.np调用 p.xaxis.major_label_orientation = np.pi / 4 sz = 9 if size.value != 'None': if len(set(df[size.value])) > N_SIZES: groups = pd.qcut(df[size.value].values, N_SIZES, duplicates='drop') else: groups = pd.Categorical(df[size.value]) sz = [SIZES[xx] for xx in groups.codes] c = "#31AADE" if color.value != 'None': if len(set(df[color.value])) > N_COLORS: groups = pd.qcut(df[color.value].values, N_COLORS, duplicates='drop') else: groups = pd.Categorical(df[color.value]) c = [COLORS[xx] for xx in groups.codes] # 绘制原始散点 p.circle(x=xs, y=ys, color=c, size=sz, line_color="white", alpha=0.6, hover_color='white', hover_alpha=0.5) # ========== 新增:线性回归线绘制逻辑 ========== # 仅当XY轴均为连续数值型时绘制回归线 if x.value in continuous and y.value in continuous: # 转换数据类型避免计算报错 x_num = xs.astype(np.float64) y_num = ys.astype(np.float64) # 一次多项式拟合,等价于线性回归,返回斜率、截距 slope, intercept = np.polyfit(x_num, y_num, deg=1) # 取X轴两端点生成回归线坐标 x_line = np.array([x_num.min(), x_num.max()]) y_line = slope * x_line + intercept # 绘制回归线,设置差异化样式 p.line(x=x_line, y=y_line, color="#E63946", line_width=3, line_dash="dashed", legend_label="线性回归线") # 配置图例样式 p.legend.location = "top_right" p.legend.background_fill_alpha = 0.8 # ============================================== return p def update(attr, old, new): layout.children[1] = create_figure() x = Select(title='X-Axis', value='mpg', options=columns) x.on_change('value', update) y = Select(title='Y-Axis', value='hp', options=columns) y.on_change('value', update) size = Select(title='Size', value='None', options=['None'] + continuous) size.on_change('value', update) color = Select(title='Color', value='None', options=['None'] + continuous) color.on_change('value', update) controls = column(x, y, color, size, width=200) layout = row(controls, create_figure()) curdoc().add_root(layout) curdoc().title = "Crossfilter"
效果说明
- 切换X/Y轴字段时,回归线会跟随当前选中的字段自动重新拟合计算,和散点同步更新
- 选中离散分类字段作为X或Y轴时,回归线自动隐藏,不会触发类型错误
- 回归线默认用红色虚线绘制,和原有蓝色散点有明显视觉区分,可自行修改
p.line()的颜色、线宽、线型参数调整样式
内容的提问来源于stack exchange,提问作者Javi Nitro 5
相关产品推荐
相关产品推荐

