Я считаю, что вы можете использовать свойство data
вашего основного DataTable в качестве ввода для функции обратного вызова, а Output - свойство 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
В документах говорится о фильтрации (выделено мной):
По умолчанию эти преобразования выполняются на стороне клиента. Ваши обратные вызовы Da sh могут реагировать на эти изменения, прослушивая свойство data как входное .
Вы можете прочитать больше здесь: https://dash.plotly.com/datatable/interactivity
РЕДАКТИРОВАТЬ
Итак, после небольшого экспериментирования я понял, что, когда filter_action
установлен на 'native'
, фильтрация DataTable на самом деле не обновить его атрибут data
. Поскольку атрибут на самом деле не обновляется, любой обратный вызов, использующий атрибут data
как Input
, не будет срабатывать при фильтрации. Чтобы обойти это и использовать атрибут data
в качестве входных данных для нашего обратного вызова, нам нужно установить filter_action
на 'custom'
, а затем самостоятельно реализовать обратный вызов фильтрации. К счастью, в документации Da sh есть пример того, как это сделать здесь: https://dash.plotly.com/datatable/filtering
Используя информацию из этих документов, я смог заставить пример работать. Я оставлю это на ваше усмотрение, чтобы выяснить внутреннюю работу метода фильтрации, но следующий код выполняется для меня локально и показывает средства обновления 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)
Сообщите мне, помогло ли это или возникли проблемы с запуском кода.