You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 23:09:59