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

Shiny Express应用中Plotly折线图空白问题排查

Shiny Express Plotly折线图空白问题解决办法

问题现象

开发的Shiny Express应用中,过滤器、信息卡片及过滤后的DataFrame均能正常响应,但Plotly折线图显示空白,x轴(tran_date)显示默认的2000年日期。尝试多种日期转换方式后问题仍未解决。

核心原因

  1. 日期类型不匹配:input.date()返回Python datetime.date对象,而原始DataFrame的tran_date是字符串类型,导致fee_df()中的日期过滤逻辑失效,返回空DataFrame,最终图表无数据显示。
  2. 日期转换时机错误:在render_widget的feelp()函数中才转换tran_date类型,但过滤操作已在fee_df()中完成,此时转换无法修正过滤逻辑的问题。

修复方案

步骤1:提前转换原始DataFrame的日期类型

在创建df2后,立即将tran_date转换为与input.date()返回值匹配的date类型:

df2 = pd.DataFrame(data, columns=columns)
df2['tran_date'] = pd.to_datetime(df2['tran_date']).dt.date

步骤2:调整fee_df()的过滤逻辑

确保日期比较时类型一致,同时简化Card_Type的匹配逻辑(避免不必要的模糊匹配):

@reactive.calc
def fee_df():
    drange = input.date()
    start_date, end_date = drange[0], drange[1]
    
    filtered_df = df2[
        (df2['tran_date'] >= start_date) &
        (df2['tran_date'] <= end_date)
    ]
    
    inacct = input.acct()
    filtered_df = filtered_df[filtered_df['Card_Type'] == inacct]
    
    return filtered_df

步骤3:简化feelp()中的日期处理

由于fee_df()返回的DataFrame已包含正确的日期类型,无需再次转换:

@render_widget
def feelp():
    df = fee_df().copy()
    df = df.dropna(subset=['tran_date'])
    
    fig = px.line(
       data_frame = df,
       x = 'tran_date',
       y = 'fee_pct',
       color = 'Entity',
       color_discrete_map = {'1': 'blue', '3': 'red'},
       line_shape = 'spline',
       markers = True,
       labels = {
           "tran_date": "Transaction Date",
           "fee_pct": "Fee Percentage"
            },
       custom_data = 'Entity'
    )
    fig.update_traces(
        line = dict(width = 3),
        hovertemplate = 
            'Entity %{customdata[0]}<br>' +
            '%{y:,.2f}%<br>' +
            '<extra></extra>'
    )
    fig.update_layout(
        xaxis = dict(type='date'),
        hovermode = 'x unified',
        font=dict(family="Calibri Black",size=18),
        hoverlabel=dict(font_size=18, font_weight='bold')
    )
    return fig

完整修复后代码

import plotly.express as px
import pandas as pd
import datetime as dt
from shiny import reactive
from shiny.express import ui, input, render
from shinywidgets import render_widget
import plotly

columns = ["tran_date", "Card_Type", "Entity", "tran_amt", "fee", "fee_pct"]

data = [
["2025-04-10", "Test", "HF", 39.77, -1.39, 3.5],
["2025-04-10", "Test2", "1", 156722.44, -2133.43, 1.36],
["2025-04-10", "Test2", "3", 7026.4, -91.81, 1.31],
["2025-04-10", "Test2", "HF", 19326.39, -257.04, 1.33],
["2025-04-10", "Test3", "1", 537720.57, -17150.55, 3.19],
["2025-04-10", "Test3", "3", 51286.76, -1616.46, 3.15],
["2025-04-10", "Test3", "HF", 3610.66, -121.13, 3.35],
["2025-04-10", "Test4", "1", 769.76, -21.17, 2.75],
["2025-04-10", "Test4", "HF", 398.01,   -5.77, 1.45],
["2025-04-10", "Test5", "1", 456447.03, -5058.61, 1.11],
["2025-04-10", "Test5", "3", 27686.38, -325.47, 1.18],
["2025-04-10", "Test5", "HF", 37173.72, -439.91, 1.18],
["2025-04-10", "Test6", "1", 1005795.05, -23607.7, 2.35],
["2025-04-10", "Test6", "3", 301824.57, -7086.06, 2.35],
["2025-04-10", "Test6", "HF", 24651.23, -579.0, 2.35]
]

df2 = pd.DataFrame(data, columns = columns)
# 提前转换tran_date为date类型,匹配input.date()返回值
df2['tran_date'] = pd.to_datetime(df2['tran_date']).dt.date

# Define filter elements
accts = df2['Card_Type'].unique().tolist()
mindate = df2['tran_date'].min()
maxdate = df2['tran_date'].max()

# Define the UI
ui.page_opts(title='Settlement Fee Tracking', fillable = True)
with ui.sidebar(title='Filter Controls'):
    ui.input_select(
        'acct',
        'Select an Account',
        choices=accts
    )
    ui.input_date_range(
        "date",
        "Date Range",
        min=mindate,
        max=maxdate,
        start=mindate,
        end=maxdate
    )
with ui.navset_pill(id = "tab"):
    with ui.nav_panel("Fee Percentage by Card Type - Entity Split"):
        # Graph
        with ui.layout_columns():
            with ui.card(full_screen = True):
                ui.card_header("Card Fee Percentage Tracking")
                
                @render_widget
                def feelp():
                    df = fee_df().copy()
                    df = df.dropna(subset=['tran_date'])
                    
                    fig = px.line(
                       data_frame = df,
                       x = 'tran_date',
                       y = 'fee_pct',
                       color = 'Entity',
                       color_discrete_map = {'1': 'blue', '3': 'red'},
                       line_shape = 'spline',
                       markers = True,
                       labels = {
                           "tran_date": "Transaction Date",
                           "fee_pct": "Fee Percentage"
                            },
                       custom_data = 'Entity'
                    )
                    fig.update_traces(
                        line = dict(width = 3),
                        hovertemplate = 
                            'Entity %{customdata[0]}<br>' +
                            '%{y:,.2f}%<br>' +
                            '<extra></extra>'
                    )
                    fig.update_layout(
                        xaxis = dict(type='date'),
                        hovermode = 'x unified',
                        font=dict(family="Calibri Black",size=18),
                        hoverlabel=dict(font_size=18, font_weight='bold')
                    )
                    return fig            

@reactive.calc
def fee_df():
    drange = input.date()
    start_date, end_date = drange[0], drange[1]
    
    # 日期过滤:类型一致,逻辑生效
    filtered_df = df2[
        (df2['tran_date'] >= start_date) &
        (df2['tran_date'] <= end_date)
    ]
    
    inacct = input.acct()
    # 直接匹配Card_Type,避免模糊匹配导致的意外过滤
    filtered_df = filtered_df[filtered_df['Card_Type'] == inacct]
    
    return filtered_df

验证说明

修改后,过滤器将正确筛选出符合条件的数据,Plotly折线图将正常显示对应实体的费率趋势,x轴将显示正确的交易日期(如2025-04-10),不再出现空白或默认2000年日期的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 01:35:55