从回调 Plotly Dash 设置列名

Set column names from callback Plotly Dash

我正在尝试创建一个数据 table,它显示来自核心 table (df) 的汇总结果。为此,我使用 Pandas、Dash 和 Plotly,如下所示:

import pandas as pd
import plotly.graph_objects as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash_html_components import Div
from dash_table import DataTable
from dash.dependencies import Input, Output

df = pd.DataFrame([['1', '2021-01-31', 'category_1', 20],
                   ['1', '2021-01-31', 'category_3', 12],
                   ['1', '2021-02-28', 'category_1', 35],
                   ['1', '2021-02-28', 'category_2', 17],
                   ['1', '2021-02-28', 'category_3', 35],
                   ['1','2021-03-31', 'category_1', 12],
                   ['1','2021-03-31', 'category_2', 58],
                   ['1','2021-03-31', 'category_3', 23],
                   ['2', '2021-01-31', 'category_1', 29],
                   ['2', '2021-01-31', 'category_2', 66],
                   ['2', '2021-01-31', 'category_3', 22],
                   ['2', '2021-02-28', 'category_1', 53],
                   ['2', '2021-02-28', 'category_2', 71],
                   ['2', '2021-02-28', 'category_3', 32],
                   ['2','2021-03-31', 'category_1', 19],
                   ['2','2021-03-31', 'category_2', 2],
                   ['2','2021-03-31', 'category_3', 99],
                   ['3', '2021-02-28', 'category_1', 53],
                   ['3', '2021-02-28', 'category_2', 71],
                   ['3','2021-03-31', 'category_1', 19],
                   ['3','2021-03-31', 'category_2', 2],
                   ['3','2021-03-31', 'category_3', 99],
                   ['3','2021-03-31', 'category_4', 39]],
                   columns=['Account', 'Date', 'category', 'Amount'])


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
                        dcc.Input(id="account", type="string", placeholder="Enter Account"),
                        DataTable(id='figure_1',
                        style_cell_conditional=[{'if': {'column_id': c}, 'textAlign': 'center'} for c in [0, 1, 2, 3, 4]],
                        style_as_list_view=True,
                        fill_width=True,
                        style_cell={'font-size': '12px'},
                        style_header={'display': 'none'},
                        style_table={'height': '395px', 'overflowY': 'auto'})])
                                       

@app.callback(dash.dependencies.Output('figure_1',  'data'),
              [dash.dependencies.Input('account', 'value')])

def update_figura_2(account):
    df_query = df[df['Account'] == account].copy()
    df_query = df_query.groupby(['Account', 'Date', 'category']).agg({'Amount': 'sum'}).reset_index().pivot(values='Amount', columns='Date')
    df_query.index =  df.loc[df['Account'] == account, 'category'].copy()
    df_query.fillna(0, inplace=True)
    return df_query.to_dict(orient='records')


if __name__ == '__main__':
    app.run_server(debug=False)

所以我想要得到的输出:

但是,当我 运行 代码时,我得到一个空数据框:

我怀疑它必须与回调有关,但我不知道是什么!

我应该在数据表中定义列参数吗?如果是这样,我怎样才能得到回调的列?

如果您查看 DataTable 对象的 documentation,则有 columnsdata 的参数,但我无法成功修改这两个属性使用回调(如果我弄明白了,我会编辑我的答案)。

我尝试使用以下回调来修改 DataTable 对象的列和数据:

@app.callback([Output('datatable', 'columns'), Output('datatable', 'data')],
    [Input('account', 'value')])
def update_table(account):
...
return df_query

...但我无法使其正常工作:即使未引发任何错误,table 也不会显示。我的猜测是一旦创建了 DataTable 对象就无法修改 columns,这就是回调无法按预期工作的原因。

相反,我们可以使用 作为模板从头构建 table,将整个 table 包装在 html.Div 容器中。

由于每个用户输入产生的 DataFrame df_query 是按类别索引的,因此我们需要注意如何使用 html.Trhtml.Th 构建 table dash_html_components 库中的对象。我们可以遍历 DataFrame 的每一行,添加索引值,然后是该行的剩余值。

import pandas as pd
import plotly.graph_objects as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash_html_components import Div
import dash_table_experiments as dt
from dash_table import DataTable
from dash.dependencies import Input, Output

df = pd.DataFrame([['1', '2021-01-31', 'category_1', 20],
                   ['1', '2021-01-31', 'category_3', 12],
                   ['1', '2021-02-28', 'category_1', 35],
                   ['1', '2021-02-28', 'category_2', 17],
                   ['1', '2021-02-28', 'category_3', 35],
                   ['1','2021-03-31', 'category_1', 12],
                   ['1','2021-03-31', 'category_2', 58],
                   ['1','2021-03-31', 'category_3', 23],
                   ['2', '2021-01-31', 'category_1', 29],
                   ['2', '2021-01-31', 'category_2', 66],
                   ['2', '2021-01-31', 'category_3', 22],
                   ['2', '2021-02-28', 'category_1', 53],
                   ['2', '2021-02-28', 'category_2', 71],
                   ['2', '2021-02-28', 'category_3', 32],
                   ['2','2021-03-31', 'category_1', 19],
                   ['2','2021-03-31', 'category_2', 2],
                   ['2','2021-03-31', 'category_3', 99],
                   ['3', '2021-02-28', 'category_1', 53],
                   ['3', '2021-02-28', 'category_2', 71],
                   ['3','2021-03-31', 'category_1', 19],
                   ['3','2021-03-31', 'category_2', 2],
                   ['3','2021-03-31', 'category_3', 99],
                   ['3','2021-03-31', 'category_4', 39]],
                   columns=['Account', 'Date', 'category', 'Amount'])

## construct a table with variable number of columns
def generate_table(df, max_rows=100):
    return html.Table(
        # Header
        [html.Tr([html.Th(df.columns.name)] + [html.Th(col) for col in df.columns])] +

        # Body
        [html.Tr([html.Th(df.index.name)])] + 
        [html.Tr([html.Td(df.index[i])] + [ 
            html.Td(df.iloc[i][col]) for col in df.columns
        ]) for i in range(min(len(df), max_rows))]
    )

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(
    children=[
    dcc.Input(id="account", type="text", placeholder="Enter Account"),
    html.Div(id='table-container')
    ])                   

@app.callback(Output('table-container', 'children'),
    [Input('account', 'value')])
def update_table(account):
    df_query = df[df['Account'] == account].copy()
    df_query = df_query.groupby(['Account', 'Date', 'category']).agg({'Amount': 'sum'}).reset_index().pivot(values='Amount', columns='Date')
    df_query.index =  df.loc[df['Account'] == account, 'category'].copy()
    df_query.fillna(0, inplace=True)
    # print(df_query.to_dict(orient='records'))
    return generate_table(df_query)

if __name__ == '__main__':
    app.run_server(debug=True)

为了使 table 看起来更漂亮一些,您可以使用一些 css 来交替行颜色。在你 运行 你的 dash 应用程序 python 文件所在的同一目录中,创建一个名为 assets 的文件夹并创建一个名为 style.css 的新文件,其中包含以下内容:

tr:nth-child(even) {background: #CCC}
tr:nth-child(odd) {background: #FFF}

结果如下:

我找到了一个解决方法,您不必像 Derek 的回答那样通过它的 html 组件手动构建 table。

import pandas as pd
import plotly.graph_objects as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash_html_components import Div
from dash_table import DataTable
from dash.dependencies import Input, Output

df = pd.DataFrame([['1', '2021-01-31', 'category_1', 20],
                   ['1', '2021-01-31', 'category_3', 12],
                   ['1', '2021-02-28', 'category_1', 35],
                   ['1', '2021-02-28', 'category_2', 17],
                   ['1', '2021-02-28', 'category_3', 35],
                   ['1','2021-03-31', 'category_1', 12],
                   ['1','2021-03-31', 'category_2', 58],
                   ['1','2021-03-31', 'category_3', 23],
                   ['2', '2021-01-31', 'category_1', 29],
                   ['2', '2021-01-31', 'category_2', 66],
                   ['2', '2021-01-31', 'category_3', 22],
                   ['2', '2021-02-28', 'category_1', 53],
                   ['2', '2021-02-28', 'category_2', 71],
                   ['2', '2021-02-28', 'category_3', 32],
                   ['2','2021-03-31', 'category_1', 19],
                   ['2','2021-03-31', 'category_2', 2],
                   ['2','2021-03-31', 'category_3', 99],
                   ['3', '2021-02-28', 'category_1', 53],
                   ['3', '2021-02-28', 'category_2', 71],
                   ['3','2021-03-31', 'category_1', 19],
                   ['3','2021-03-31', 'category_2', 2],
                   ['3','2021-03-31', 'category_3', 99],
                   ['3','2021-03-31', 'category_4', 39]],
                   columns=['Account', 'Date', 'category', 'Amount'])


external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
                        dcc.Input(id="account", type="string", placeholder="Enter Account"),
                        html.Div(id='figure_1')
])

@app.callback(Output('figure_1', 'children'),
             [Input('account', 'value')])
def update_figura_2(account):
    df_query = df[df['Account'] == account].copy()
    df_query = df_query.groupby(['Account', 'Date', 'category']).agg({'Amount': 'sum'}).reset_index().pivot(values='Amount', columns='Date')
    df_query.index =  df.loc[df['Account'] == account, 'category'].copy()
    df_query.fillna(0, inplace=True)
    return [DataTable(columns=[{"name": i, "id": i} for i in df_query.columns],
                      data=df_query.to_dict('records'),                     
                      style_as_list_view=True,
                      fill_width=True,
                      style_cell={'font-size': '12px'},
                      style_header={'display': 'none'},
                      style_table={'height': '395px', 'overflowY': 'auto'})]


if __name__ == '__main__':
    app.run_server(debug=False)

其中的逻辑是在回调中创建 DataTable 图形并将其作为图形的子图形返回,因此返回的图形将按您需要的 columns/values 帐户进行过滤。