Dash Plotly 根据日期值更新卡片

Dash Plotly Update Cards Based on Date Value

我刚刚问了一个关于在 Plotly Dash 中更新卡片的类似问题,但出于某种原因,这种略有不同的情况让我很困惑。我的仪表板上有一些卡片,我想通过日期滑块的移动来更新它们。使用带有字符串类型变量的下拉菜单工作得很好,但现在我们在滑块中有一个日期值,这给我带来了一些麻烦。

这是我的一些代码:

#Import packages
import pandas as pd
import numpy as np
import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
import time


who_data = pd.read_csv("https://covid19.who.int/WHO-COVID-19-global-data.csv")
pops = pd.read_csv("https://gist.githubusercontent.com/curran/0ac4077c7fc6390f5dd33bf5c06cb5ff/raw/605c54080c7a93a417a3cea93fd52e7550e76500/UN_Population_2019.csv")

who_data.rename(columns={'New_cases': 'New Cases', 'Cumulative_cases': 'Cumulative Cases', 'New_deaths': 'New Deaths','Cumulative_deaths': 'Cumulative Deaths'}, inplace=True)



daterange = pd.date_range(start=who_data['Date_reported'].min(),
                          end=who_data['Date_reported'].max(),
                          freq='D')

def unixTimeMillis(dt):
    ''' Convert datetime to unix timestamp '''
    return int(time.mktime(dt.timetuple()))

def unixToDatetime(unix):
    ''' Convert unix timestamp to datetime. '''
    return pd.to_datetime(unix,unit='s')

def getMarks(start, end, Nth=50):
    ''' Returns the marks for labeling. 
        Every Nth value will be used.
    '''

    result = {}
    for i, date in enumerate(daterange):
        if(i%Nth == 1):
            # Append value to dict
            result[unixTimeMillis(date)] = str(date.strftime('%Y-%m-%d'))

    return result




card1_body = dbc.CardBody([html.H4("Card title", className="card-title",id="card_num1"),
                  html.P("Cumulative Cases for Date", className="card-text",id="card_text1")
                 ],
                 style={'display': 'inline-block',
                        'text-align': 'center',
                        'color':'white',
                        'background-color': 'rgba(37, 150, 190)'})

card2_body = dbc.CardBody([html.H4("Card title", className="card-title",id="card_num2"),
                  html.P("Cumulative Deaths for Date", className="card-text",id="card_text2")
                 ],
                 style={'display': 'inline-block',
                        'text-align': 'center',
                        'color':'white',
                        'background-color': 'rgba(37, 150, 190)'})

card1 = dbc.Card(card1_body,outline=True)
card2 = dbc.Card(card2_body,outline=True)



app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = html.Div([
    dcc.Tabs([
        
        dcc.Tab(label='Spread',
                children=[
                    html.Div([
                       
                        dcc.RangeSlider(id='slider',
                                        min = unixTimeMillis(daterange.min()),
                                        max = unixTimeMillis(daterange.max()),
                                        value = [unixTimeMillis(daterange.min())],
                                        marks=getMarks(daterange.min(),daterange.max())

                                       ),
                         dbc.Row(id="card_row",children=[dbc.Col(card1),
                                                         dbc.Col(card2)
                                                        ])
                    ])
        ]),
        dcc.Tab(label="Top 10",
                children=[
             html.P('Some stuff will go here eventually.') 
        ])
    ])
])


@app.callback(
    Output('card_row','children'),
    Input('slider','value')
)

def update_cards(date_select):
    date_df = who_data[(who_data.Date_reported<=date_select)]
    tot_cases = f"{date_df['New Cases'].sum():,.0f}"
    tot_deaths = f"{date_df['New Deaths'].sum():,.0f}"
    
    card1 = dbc.Card([
        dbc.CardBody([
            html.H4(tot_cases, className="card-title"),
            html.P(f"Cumulative Cases on {date_select}")

        ])
    ],
    style={'display': 'inline-block',
           'width': '50%',
           'text-align': 'center',
           'background-color': 'rgba(37, 150, 190)',
           'color':'white',
           'fontWeight': 'bold',
           'fontSize':20},
    outline=True)
    
    card2 = dbc.Card([
        dbc.CardBody([
            html.H4(tot_deaths, className="card-title"),
            html.P(f"Cumulative Deaths on {date_select}")
        ])
    ],
    style={'display': 'inline-block',
           'width': '50%',
           'text-align': 'center',
           'background-color': 'rgba(37, 150, 190)',
           'color':'white',
           'fontWeight': 'bold',
           'fontSize':20},
    outline=True)
    
    return (card1, card2)


app.run_server(host='0.0.0.0',port='8050') 

当我 运行 这样做时,两张卡没有更新,我收到一条错误消息,内容为“ValueError:长度必须匹配才能比较”。这是指我尝试在 update_cards 函数中创建“date_df”的行。在 Whosebug 上做了一些研究,我发现了一些例子表明 date_select 是一系列 1 值,我需要在它的末尾粘贴一个 .values[0] 到 select 一个值- 但这也没有用。不确定如何进行,如有任何帮助,我们将不胜感激!

谢谢!

你的错误是因为你将数据框对象与非标量进行比较,而该非标量与你的数据框对象的长度不同。

这就是我认为你认为正在发生的事情

In [79]: df = pd.DataFrame(np.random.randint(5, 15, (10, 3)), columns=list('abc'))

In [80]: df
Out[80]:
    a   b   c
0   6  11  11
1  14   7   8
2  13   5  11
3  13   7  11
4  13   5   9
5   5  11   9
6   9   8   6
7   5  11  10
8   8  10  14
9   7  14  13

In [81]: df[df.b > 10]
Out[81]:
   a   b   c
0  6  11  11
5  5  11   9
7  5  11  10
9  7  14  13

但实际上你在做这样的事情

df[df.b > [1,2,3,4,5,6]]

所以首先你需要找出 date_select 实际上是。这些只是我的假设,但您的数据格式化方式肯定存在问题。

其次,您真的应该仔细阅读情节回调文档。您正试图在回调函数中重新创建整个 HTML div。回调功能的功能只是更新图形。如果您将图表与 HTML 隔离开来并只更新图表,这会让您的生活变得更轻松。

https://dash.plotly.com/basic-callbacks

https://dash.plotly.com/advanced-callbacks