如何用数据化过滤来报告列的意思,Python Dash。

问题描述 投票:1回答:1

我正在使用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%的新的仪表,我还没有看到很多指南如何适当地做这个。 任何援助将是如此非常感激。谢谢你的帮助。

python callback hyphen
1个回答
0
投票

我相信你可以使用 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)

如果这有帮助,或者你在运行代码时有问题,请告诉我。


0
投票

@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?

© www.soinside.com 2019 - 2024. All rights reserved.