我正在使用Python Dash来构建一个应用程序,在那里我可以显示一个dash datatable,并将datable(底层pandas数据框架)的列手段报告为它下面的dash datable。 到目前为止,我已经试过了。
import dash
from dash.dependencies import Input, Output
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import json
wide_data = pd.DataFrame(np.array([[1.24, 2.34, 3.234], [4.24, .45, .06], [7, 8, 9]]),
columns=['a', 'b', 'c'])
df = pd.DataFrame(wide_data)
df = pd.DataFrame(wide_data)
df_floats = df.select_dtypes(include=['float64'])
df_floats_means = df_floats.mean(numeric_only=True)
df_floats_means = pd.DataFrame(df_floats_means)
df_floats_means_T = df_floats_means.T
app = dash.Dash(__name__)
app.layout = html.Div([
html.H1('Customer Complaints Dashboard'),
html.Label('Read and Writing Queries Filtering'),
# html.Label2('If "Read filter_query" is chosen, use operators (le, ge, <, <=, >, >=) + tag value to filter'),
# html.Label3('If "Write filter_query" is chosen, ___'),
dcc.RadioItems(
id='filter-query-read-write',
options=[
{'label': 'Read filter_query', 'value': 'read'},
],
value='read'
),
html.Br(),
dcc.Input(id='filter-query-input', placeholder='Enter filter query'), #this is for write functionality
html.Div(id='filter-query-output'), #this is associated with read screen
html.Hr(),
dash_table.DataTable(
id='datatable-advanced-filtering',
columns=[
{'name': i, 'id': i, 'deletable': True} for i in df.columns
# omit the id column
if i != 'id'
],
data=df.to_dict('records'),
editable=True,
page_action='native',
page_size=10,
filter_action="native",
fixed_columns={'headers': True, 'data': 1},
style_table={'minWidth': '100%'}
),
html.Hr(),
html.Div(id='datatable-query-structure', style={'whitespace': 'pre'}),
html.Hr(),
html.Label('Dataframe Means'),
dash_table.DataTable(
id='datatable-advanced-filtering2',
columns=[
{'name': i, 'id': i, 'deletable': True} for i in df_floats_means_T.columns
# omit the id column
if i != 'id'
],
data=df_floats_means_T.to_dict('records'),
editable=True,
page_action='native',
page_size=10,
# filter_action="native",
fixed_columns={'headers': True, 'data': 1},
style_table={'minWidth': '100%'}
),
html.Hr(),
html.Div(id='datatable-query-structure2', style={'whitespace': 'pre'}),
html.Hr(),
])
@app.callback(
[Output('filter-query-input', 'style'),
Output('filter-query-output', 'style')],
[Input('filter-query-read-write', 'value')]
)
def query_input_output(val):
input_style = {'width': '100%'}
output_style = {}
if val == 'read':
input_style.update(display='none')
output_style.update(display='inline-block')
else:
input_style.update(display='inline-block')
output_style.update(display='none')
return input_style, output_style
@app.callback(
Output('filter-query-output', 'children'),
[Input('datatable-advanced-filtering', 'filter_query')]
)
def read_query(query):
if query is None:
return "No filter query"
return dcc.Markdown('`filter_query = "{}"`'.format(query))
@app.callback(
Output('datatable-advanced-filtering2', 'data'),
[Input('datatable-advanced-filtering', 'data')]
)
def update_means(data):
dff = df
df_floats = dff.select_dtypes(include=['float64'])
df_floats_means = df_floats.mean(numeric_only=True)
df_floats_means = pd.DataFrame(df_floats_means)
df_floats_means_T = df_floats_means.T
converted_means = df_floats_means_T.to_dict('records')
return([converted_means])
if __name__ == '__main__':
app.run_server(threaded=True)
但我知道这是不对的,因为我在应用布局的这部分做任何过滤之前就在计算手段了 id='datatable-advanced-filtering2'
. 大家能不能协助我进行回调?
我是100%的新的仪表,我还没有看到很多指南如何适当地做这个。 任何援助将是如此非常感激。谢谢你的帮助。
我相信你可以使用 data
属性作为回调函数的输入,并将输出作为主数据表的 data
属性的手段DataTable。它的结构可能是这样的。
@app.callback(
Output('datatable-advanced-filtering2', 'data'),
[Input('datatable-advanced-filtering', 'data')]
)
def update_means(data):
# Use data to construct dataframe
# Find means using same method used at top of post
# Convert means df using .to_dict('records')
# Return converted dict
文档中对过滤有这样的描述(重点是我)。
默认情况下,这些转换是在客户端完成的。你的Dash回调可以通过监听数据属性作为输入来响应这些修改。.
你可以在这里阅读更多。https:/dash.plotly.comdatatableinteractivity。
编辑
所以,经过一番试验,我发现当 filter_action
设置为 'native'
的过滤实际上并没有更新它的数据表。data
属性。因为属性实际上并没有被更新,所以任何使用了 data
属性作为 Input
不会在过滤时发射。为了解决这个问题,使用 data
属性作为回调的输入,我们需要设置 filter_action
到 'custom'
,然后自己实现过滤回调。幸运的是,Dash文档中有一个如何做到这一点的例子。https:/dash.plotly.comdatatablefiltering.
利用这些文档中的信息,我能够让一个例子发挥作用。我让你自己去弄清楚过滤方法的内部工作原理,但下面的代码为我在本地运行,并显示了当主DataTable被过滤时DataTable更新的手段。我唯一做的其他修改是对 update_means
函数,据此我把它从返回一个 list
就像你的例子代码一样,返回一个 dict
的正确类型。data
属性。
import dash
from dash.dependencies import Input, Output
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import json
import numpy as np
from dash import Dash
# Instantiate df and calculate initial means
wide_data = pd.DataFrame(np.array([[1.24, 2.34, 3.234], [4.24, .45, .06], [7, 8, 9]]),
columns=['a', 'b', 'c'])
df = pd.DataFrame(wide_data)
df_floats = df.select_dtypes(include=['float64'])
df_floats_means = df_floats.mean(numeric_only=True)
df_floats_means = pd.DataFrame(df_floats_means)
df_floats_means_T = df_floats_means.T
# Operators used by custom filtering
operators = [['ge ', '>='],
['le ', '<='],
['lt ', '<'],
['gt ', '>'],
['ne ', '!='],
['eq ', '='],
['contains '],
['datestartswith ']]
# Initialize app
app = Dash(__name__)
# Init layout
app.layout = html.Div([
html.H1('Customer Complaints Dashboard'),
html.Label('Read and Writing Queries Filtering'),
# html.Label2('If "Read filter_query" is chosen, use operators (le, ge, <, <=, >, >=) + tag value to filter'),
# html.Label3('If "Write filter_query" is chosen, ___'),
dcc.RadioItems(
id='filter-query-read-write',
options=[
{'label': 'Read filter_query', 'value': 'read'},
],
value='read'
),
html.Br(),
# this is for write functionality
dcc.Input(id='filter-query-input', placeholder='Enter filter query'),
html.Div(id='filter-query-output'), # this is associated with read screen
html.Hr(),
dash_table.DataTable(
id='datatable-advanced-filtering',
columns=[
{'name': i, 'id': i, 'deletable': True} for i in df.columns
# omit the id column
if i != 'id'
],
data=df.to_dict('records'),
editable=True,
page_action='native',
page_size=10,
filter_action="custom",
fixed_columns={'headers': True, 'data': 1},
style_table={'minWidth': '100%'}
),
html.Hr(),
html.Div(id='datatable-query-structure', style={'whitespace': 'pre'}),
html.Hr(),
html.Label('Dataframe Means'),
dash_table.DataTable(
id='datatable-advanced-filtering2',
columns=[
{'name': i, 'id': i, 'deletable': True} for i in df_floats_means_T.columns
# omit the id column
if i != 'id'
],
data=df_floats_means_T.to_dict('records'),
editable=True,
page_action='native',
page_size=10,
filter_action="native",
fixed_columns={'headers': True, 'data': 1},
style_table={'minWidth': '100%'}
),
html.Hr(),
html.Div(id='datatable-query-structure2', style={'whitespace': 'pre'}),
html.Hr(),
])
@app.callback(
[Output('filter-query-input', 'style'),
Output('filter-query-output', 'style')],
[Input('filter-query-read-write', 'value')]
)
def query_input_output(val):
input_style = {'width': '100%'}
output_style = {}
if val == 'read':
input_style.update(display='none')
output_style.update(display='inline-block')
else:
input_style.update(display='inline-block')
output_style.update(display='none')
return input_style, output_style
@app.callback(
Output('filter-query-output', 'children'),
[Input('datatable-advanced-filtering', 'filter_query')]
)
def read_query(query):
if query is None:
return "No filter query"
return dcc.Markdown('`filter_query = "{}"`'.format(query))
# Callback to re-calculate means after filtering is
@app.callback(
Output('datatable-advanced-filtering2', 'data'),
[Input('datatable-advanced-filtering', 'data')]
)
def update_means(data):
# Calculate means from data currently stored in top datatable
dff = pd.DataFrame.from_dict(data)
df_floats = dff.select_dtypes(include=['float64'])
df_floats_means = df_floats.mean(numeric_only=True)
df_floats_means = pd.DataFrame(df_floats_means)
df_floats_means_T = df_floats_means.T
# Return means to means datatable
#THIS NOW RETURNS DICT INSTEAD OF LIST
converted_means = df_floats_means_T.to_dict('records')
return converted_means
def split_filter_part(filter_part):
'''Helper function for custom filtering'''
for operator_type in operators:
for operator in operator_type:
if operator in filter_part:
name_part, value_part = filter_part.split(operator, 1)
name = name_part[name_part.find('{') + 1: name_part.rfind('}')]
value_part = value_part.strip()
v0 = value_part[0]
if (v0 == value_part[-1] and v0 in ("'", '"', '`')):
value = value_part[1: -1].replace('\\' + v0, v0)
else:
try:
value = float(value_part)
except ValueError:
value = value_part
# word operators need spaces after them in the filter string,
# but we don't want these later
return name, operator_type[0].strip(), value
return [None] * 3
@app.callback(
Output('datatable-advanced-filtering', "data"),
[Input('datatable-advanced-filtering', "filter_query")])
def update_table(filter):
'''Callback that handles custom filtering of top datatable'''
if filter is None:
return df.to_dict('records')
filtering_expressions = filter.split(' && ')
dff = df
for filter_part in filtering_expressions:
col_name, operator, filter_value = split_filter_part(filter_part)
if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
# these operators match pandas series operator method names
dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
elif operator == 'contains':
dff = dff.loc[dff[col_name].str.contains(filter_value)]
elif operator == 'datestartswith':
# this is a simplification of the front-end filtering logic,
# only works with complete fields in standard format
dff = dff.loc[dff[col_name].str.startswith(filter_value)]
return dff.to_dict('records')
if __name__ == '__main__':
app.run_server(threaded=True)
如果这有帮助,或者你在运行代码时有问题,请告诉我。
@ncascale 非常感谢你,但这里有一个小问题。
def update_table(filter):
'''Callback that handles custom filtering of top datatable'''
filtering_expressions = filter.split(' && ')
dff = df
for filter_part in filtering_expressions:
col_name, operator, filter_value = split_filter_part(filter_part)
if operator in ('eq', 'ne', 'lt', 'le', 'gt', 'ge'):
# these operators match pandas series operator method names
dff = dff.loc[getattr(dff[col_name], operator)(filter_value)]
elif operator == 'contains':
dff = dff.loc[dff[col_name].str.contains(filter_value)]
elif operator == 'datestartswith':
# this is a simplification of the front-end filtering logic,
# only works with complete fields in standard format
dff = dff.loc[dff[col_name].str.startswith(filter_value)]
return dff.to_dict('records')
它可以工作,但会弹出一个错误。
AttributeError: 'NoneType' object has no attribute 'split'
这是不是因为我必须先用If语句检查 "filter "这个参数是否必须是None?