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

移除图表间隙:合并连续同色时间块的技术问询

问题描述

我用Python生成了一个时间块图表(如下所示),数据来自CSV文件,每行对应一个时间块。现在最后两个绿色时间块之间有微小间隙,想把间隙去掉,让它们变成连续的绿色长条。

时间块图表

示例数据

data = {
    'start': ['2024-08-02 08:00', '2024-08-02 09:00', '2024-08-02 18:50'],
    'ende':  ['2024-08-02 09:00', '2024-08-02 18:50', '2024-08-03 10:00'],
    'color': ['green', 'green','green', 'green', 'green']
}

当前生成图表的代码

def createBarChart(df):
    # 将‘start’和‘ende’列转换为datetime对象
    df['start'] = pd.to_datetime(df['start'])
    df['ende'] = pd.to_datetime(df['ende'])

    # 创建图表
    fig = go.Figure()

    for i, row in df.iterrows():
        fig.add_trace(go.Scatter(
            x=[row['start'], row['ende'], row['ende'], row['start']],
            y=[1, 1, 0, 0],
            fill='toself',
            fillcolor=row['color'],
            mode='none',  
            showlegend=False,

        ))

    # 格式化X轴
    tickvals = pd.date_range(start=df['start'].min(), end=df['ende'].max(), freq='3H')
    ticktext = []
    previous_day = None

    for d in tickvals:
        if previous_day is None or d.day != previous_day:
            ticktext.append(f"<b>{d.strftime('%d.%m')}</b>")
        else:
            ticktext.append(d.strftime('%H:%M'))
        previous_day = d.day

    fig.update_layout(
        yaxis=dict(showticklabels=False),
        xaxis=dict(
            tickformat='%H:%M',
            tickvals=tickvals,
            ticktext=ticktext
        ),
        margin=dict(l=20, r=20, t=10, b=20),
        height=200,

    )

    return fig

解决方案

间隙出现的核心原因是:相邻同色时间块是独立的Scatter填充区域,即使时间上首尾衔接,渲染时也可能因精度问题出现缝隙。以下两种方案可以解决这个问题:

方案1:合并同色连续时间块

先预处理数据,把相邻、同色且首尾衔接的时间块合并成一个,从根源上消除间隙:

def merge_consecutive_blocks(df):
    # 按时间排序,确保块的顺序正确
    df = df.sort_values('start').reset_index(drop=True)
    merged_blocks = []
    if len(df) == 0:
        return pd.DataFrame(merged_blocks)
    
    # 初始化第一个块的参数
    curr_start = df.iloc[0]['start']
    curr_end = df.iloc[0]['ende']
    curr_color = df.iloc[0]['color']
    
    for i in range(1, len(df)):
        row = df.iloc[i]
        # 判断是否和当前块同色且首尾衔接
        if row['color'] == curr_color and row['start'] == curr_end:
            curr_end = row['ende']
        else:
            merged_blocks.append({
                'start': curr_start,
                'ende': curr_end,
                'color': curr_color
            })
            # 更新为当前块的参数
            curr_start = row['start']
            curr_end = row['ende']
            curr_color = row['color']
    # 添加最后一个块
    merged_blocks.append({
        'start': curr_start,
        'ende': curr_end,
        'color': curr_color
    })
    return pd.DataFrame(merged_blocks)

# 修改原图表生成函数,加入合并逻辑
def createBarChart(df):
    df['start'] = pd.to_datetime(df['start'])
    df['ende'] = pd.to_datetime(df['ende'])
    # 合并同色连续块
    df = merge_consecutive_blocks(df)
    
    fig = go.Figure()
    for i, row in df.iterrows():
        fig.add_trace(go.Scatter(
            x=[row['start'], row['ende'], row['ende'], row['start']],
            y=[1, 1, 0, 0],
            fill='toself',
            fillcolor=row['color'],
            mode='none',  
            showlegend=False,
        ))
    
    # 后续X轴格式化代码保持不变
    tickvals = pd.date_range(start=df['start'].min(), end=df['ende'].max(), freq='3H')
    ticktext = []
    previous_day = None

    for d in tickvals:
        if previous_day is None or d.day != previous_day:
            ticktext.append(f"<b>{d.strftime('%d.%m')}</b>")
        else:
            ticktext.append(d.strftime('%H:%M'))
        previous_day = d.day

    fig.update_layout(
        yaxis=dict(showticklabels=False),
        xaxis=dict(
            tickformat='%H:%M',
            tickvals=tickvals,
            ticktext=ticktext
        ),
        margin=dict(l=20, r=20, t=10, b=20),
        height=200,
    )

    return fig

方案2:改用Bar图绘制时间块

go.Bar天生支持连续块的无缝衔接,无需处理间隙问题,代码更简洁:

def createBarChart(df):
    df['start'] = pd.to_datetime(df['start'])
    df['ende'] = pd.to_datetime(df['ende'])
    # 计算每个时间块的持续时长(转换为毫秒,适配Bar图宽度)
    df['duration_ms'] = (df['ende'] - df['start']).dt.total_seconds() * 1000
    
    fig = go.Figure()
    fig.add_trace(go.Bar(
        x=df['start'],
        y=[1] * len(df),  # 固定y值,让所有块在同一水平线上
        width=df['duration_ms'],
        marker_color=df['color'],
        orientation='h',
        showlegend=False
    ))
    
    # 调整X轴格式
    tickvals = pd.date_range(start=df['start'].min(), end=df['ende'].max(), freq='3H')
    ticktext = []
    previous_day = None
    for d in tickvals:
        if previous_day is None or d.day != previous_day:
            ticktext.append(f"<b>{d.strftime('%d.%m')}</b>")
        else:
            ticktext.append(d.strftime('%H:%M'))
        previous_day = d.day
    
    fig.update_layout(
        yaxis=dict(showticklabels=False, range=[0, 2]),  # 调整y轴范围,让Bar显示正常
        xaxis=dict(
            type='date',
            tickformat='%H:%M',
            tickvals=tickvals,
            ticktext=ticktext
        ),
        margin=dict(l=20, r=20, t=10, b=20),
        height=200,
        barmode='stack'
    )
    return fig

两种方案都能彻底消除间隙,方案1适合需要保留原始数据逻辑的场景,方案2更简洁高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 03:05:16