Графическое обновление карт Dash на основе значения даты

Я только что задал аналогичный вопрос об обновлении карт в 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: для сравнения длины должны совпадать. Это относится к строке, в которой я пытаюсь создать date_df в функции update_cards. Проведя небольшое исследование StackOverflow, я нашел несколько примеров, в которых предполагалось, что date_select представляет собой серию из 1 значения и что мне нужно прикрепить .values ​​[0] в конце, чтобы выбрать одно значение, но это тоже не сработало. . Не знаете, как действовать, любая помощь будет принята с благодарностью!

Спасибо!


person user2813606    schedule 09.03.2021    source источник


Ответы (1)


Что ж, ваша ошибка связана с тем, что вы сравниваете объект фрейма данных с нескалярным, и этот нескалярный объект не такой же длины, как ваш объект фрейма данных.

Это то, что я думаю, ты думаешь, что происходит

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

person Devin Burke    schedule 10.03.2021
comment
Привет, спасибо за отзыв! Я перепрофилировал старое решение, которое, как мне кажется, не было таким эффективным, и не думал об удалении некоторых ненужных вещей. Приятно видеть это еще одной парой глаз! Ценю отзывы! - person user2813606; 11.03.2021