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

Bokeh Server:基于选择切换日期时间轴与分类轴

解决Bokeh中每周数据图表的间隙问题

我明白你遇到的问题了——当切换到每周数据时,年份+周数的浮点数被当成连续数值处理,跨年的周(比如2023.52到2024.01)之间会出现超大间隙,完全不符合实际的时间顺序。这本质是Bokeh对数值型轴和类别/时间轴的处理逻辑不同导致的,下面给你两种靠谱的解决思路:

思路1:将周标识转换为类别字符串

把浮点数格式的周(比如2023.01)转换成标准的周字符串(比如"2023-W01"),让Bokeh把x轴当成类别轴处理,这样每个周会按顺序紧密排列,不会有数值间隙。

预处理数据(用Pandas)

import pandas as pd

# 假设你的每周数据DataFrame是weekly_df,浮点数周列叫year_week
weekly_df["week_label"] = weekly_df["year_week"].apply(lambda x:
    # 拆分年份和周数,周数补零保证排序正确
    f"{int(x)}-W{str(int((x - int(x)) * 100)).zfill(2)}"
)

Bokeh切换逻辑示例

from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, Select, CustomJS
from bokeh.layouts import column

# 初始化两个数据源
daily_source = ColumnDataSource(data=dict(x=daily_df.date_col, y=daily_df.value_col))
# 用转换后的类别字符串作为x值
weekly_source = ColumnDataSource(data=dict(x=weekly_df.week_label, y=weekly_df.value_col))

# 先渲染每日数据(时间轴)
p = figure(x_axis_type="datetime", title="数据视图", x_axis_label="时间", y_axis_label="数值")
line_renderer = p.line(x="x", y="y", source=daily_source)

# 创建切换选择器
view_select = Select(title="查看类型", options=["每日", "每周"], value="每日")

# 编写JS回调切换数据源和轴类型
callback = CustomJS(args=dict(renderer=line_renderer, daily=daily_source, weekly=weekly_source, fig=p), code="""
    const selected = cb_obj.value;
    if (selected === '每日') {
        renderer.data_source = daily;
        fig.x_axis[0].type = 'datetime';
    } else {
        renderer.data_source = weekly;
        fig.x_axis[0].type = 'categorical';
        // 可选:手动设置刻度为所有周标签,避免自动省略
        fig.xaxis.ticker = weekly.data.x;
    }
    // 触发轴更新
    fig.x_axis[0].change.emit();
""")

view_select.js_on_change('value', callback)

show(column(view_select, p))

思路2:将周标识转换为Datetime对象

另一种更贴合时间逻辑的方式是把年份+周数转换成实际的日期(比如每周一的日期),这样Bokeh的时间轴会自动处理间隔,不会出现间隙。

预处理数据(用Pandas)

# 把浮点数周转换成对应的周一日期(%W表示周一为一周起始,%U是周日,按需调整)
weekly_df["week_date"] = pd.to_datetime(
    weekly_df["year_week"].apply(lambda x: f"{int(x)}-W{int((x - int(x)) * 100)}"),
    format="%Y-W%W"
)

Bokeh切换逻辑示例

这种方式下,x轴可以统一用时间轴,不需要切换轴类型,代码更简洁:

# 初始化数据源
daily_source = ColumnDataSource(data=dict(x=daily_df.date_col, y=daily_df.value_col))
weekly_source = ColumnDataSource(data=dict(x=weekly_df.week_date, y=weekly_df.value_col))

# 时间轴图表
p = figure(x_axis_type="datetime", title="数据视图", x_axis_label="时间", y_axis_label="数值")
line_renderer = p.line(x="x", y="y", source=daily_source)

# 选择器和回调
view_select = Select(title="查看类型", options=["每日", "每周"], value="每日")
callback = CustomJS(args=dict(renderer=line_renderer, daily=daily_source, weekly=weekly_source), code="""
    renderer.data_source = cb_obj.value === '每日' ? daily : weekly;
""")

view_select.js_on_change('value', callback)

show(column(view_select, p))

关键注意点

  • 用字符串类别时,一定要给周数补零(比如W01而不是W1),否则字符串排序会乱(W10会排在W2前面)。
  • 用Datetime转换时,注意周数的起始定义(ISO周和本地周的区别),如果你的周数是周日起始,把format="%Y-W%W"改成format="%Y-W%U"。

内容的提问来源于stack exchange,提问作者machump

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:27:24