带有日期时间和散点图交互的 plotly dash 范围滑块

plotly dash range slider with datetime and scatterplot interaction

我想在我的下拉列表中添加一个范围滑块,并使范围滑块成为 'Wallclock' 日期时间以及允许范围滑块根据下拉列表选择该胶囊的日期时间的交互价值。我设法找到了其他人已完成此操作的几种方法,但 none 似乎适用于我的情况,尤其是图形的回调和更新。谢谢!

数据看起来像这样。

Dash 看起来像这样。

代码如下所示。

import pandas as pd
import plotly.express as px  # (version 4.7.0)
import plotly.graph_objects as go
import numpy as np

import openpyxl
import dash  # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate

app = dash.Dash(__name__)
server = app.server

df = pd.read_excel("tcd vs rh 2.xlsx")
print(df)

capsuleID = df['Capsule_ID'].unique()
print(capsuleID)

capsuleID_names = sorted(list(capsuleID))
print(capsuleID_names)

capsuleID_names_1 = [{'label': k, 'value': k} for k in sorted(capsuleID)]
capsuleID_names_2 = [{'label': '(Select All)', 'value': 'All'}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2

app.layout = html.Div([

    html.H1("Relative Humidity vs TCD", style={'text-align': 'center'}),

    dcc.Dropdown(id="capsule_select",
                 options=capsuleID_names_all,
                 optionHeight=25,
                 multi=True,
                 searchable=True,
                 placeholder='Please select...',
                 clearable=True,
                 value=['All'],
                 style={'width': "100%"}
                 ),

    dcc.RangeSlider(id='slider',
                    min=df['Wallclock'].min(),
                    max=df['Wallclock'].max(),
                    value=[df.iloc[-101]['Wallclock'].timestamp(), df.iloc[-1]['Wallclock'].timestamp()]
                    ),

    html.Div([
        dcc.Graph(id="the_graph"),
    ]),

])

# -----------------------------------------------------------
@app.callback(
    Output('the_graph', 'figure'),
    Output('capsule_select', 'value'),
    Input('capsule_select', 'value'),
    Input('slider', 'value'),
)
def update_graph(capsule_chosen):
    lBound = pd.to_datetime(value[0], unit='s')
    uBound = pd.to_datetime(value[1], unit='s')
    filteredData = df.loc[(df['date'] >= lBound) & (df['date'] <= uBound)]

    dropdown_values = capsule_chosen

    if "All" in capsule_chosen:
        dropdown_values = capsuleID_names
        dff = df
    else:
        dff = df[df['Capsule_ID'].isin(capsule_chosen)]  # filter all rows where capsule ID is the capsule ID selected

    scatterplot = px.scatter(
        data_frame=dff,
        x="tcd",
        y="humidity",
        hover_name="Wallclock",
    )

    scatterplot.update_traces(textposition='top center')

    return scatterplot, dropdown_values


# ------------------------------------------------------------------------------

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

  • 显然我无法访问您的 Excel 电子表格,因此生成了一个具有相同形状的数据框
  • 采取的方法是使用带有 rangeslider 的第二个图形来实现滑块功能
  • 更新了 回调 以将此数字用作日期范围
  • 的输入
  • 使用 jupyter dash inline,这可以改回您的设置(注释行)

生成一些示例数据

import pandas as pd
import numpy as np
df = pd.DataFrame(
    {
        "Wallclock": pd.date_range(
            "22-dec-2020 00:01:36", freq="5min", periods=2000
        ),
        "tcd": np.linspace(3434, 3505, 2000) *np.random.uniform(.9,1.1, 2000),
        "humidity": np.linspace(63, 96, 2000),
    }
).pipe(lambda d: d.assign(Capsule_ID=(d.index // (len(d)//16))+2100015))

滑块是一个带有 rangeslider

的数字
import pandas as pd
import plotly.express as px  # (version 4.7.0)
import plotly.graph_objects as go
import numpy as np

import openpyxl
import dash  # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from jupyter_dash import JupyterDash

# app = dash.Dash(__name__)
# server = app.server
app = JupyterDash(__name__)


# df = pd.read_excel("tcd vs rh 2.xlsx")
# print(df)

capsuleID = df["Capsule_ID"].unique()
# print(capsuleID)

capsuleID_names = sorted(list(capsuleID))
# print(capsuleID_names)

capsuleID_names_1 = [{"label": k, "value": k} for k in sorted(capsuleID)]
capsuleID_names_2 = [{"label": "(Select All)", "value": "All"}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2

def slider_fig(df):
    return px.scatter(
                df.groupby("Wallclock", as_index=False).size(), x="Wallclock", y="size"
            ).update_layout(
                xaxis={"rangeslider": {"visible": True}, "title":None},
                height=125,
                yaxis={"tickmode": "array", "tickvals": [], "title": None},
                margin={"l": 0, "r": 0, "t": 0, "b": 0},
            )

app.layout = html.Div(
    [
        html.H1("Relative Humidity vs TCD", style={"text-align": "center"}),
        dcc.Dropdown(
            id="capsule_select",
            options=capsuleID_names_all,
            optionHeight=25,
            multi=True,
            searchable=True,
            placeholder="Please select...",
            clearable=True,
            value=["All"],
            style={"width": "100%"},
        ),
        dcc.Graph(
            id="slider",
            figure=slider_fig(df),
        ),
        html.Div(
            [
                dcc.Graph(id="the_graph"),
            ]
        ),
    ]
)

# -----------------------------------------------------------
@app.callback(
    Output("the_graph", "figure"),
    Output("capsule_select", "value"),
    Output("slider", "figure"),
    Input("capsule_select", "value"),
    Input('slider', 'relayoutData'),
    State("slider", "figure")
)
def update_graph(capsule_chosen, slider, sfig):
    dropdown_values = capsule_chosen

    if "All" in capsule_chosen:
        dropdown_values = capsuleID_names
        dff = df
    else:
        dff = df[
            df["Capsule_ID"].isin(capsule_chosen)
        ]  # filter all rows where capsule ID is the capsule ID selected

    
    if slider and "xaxis.range" in slider.keys():
        dff = dff.loc[dff["Wallclock"].between(*slider["xaxis.range"])]
    else:
        # update slider based on selected capsules
        sfig = slider_fig(dff)
        
    scatterplot = px.scatter(
        data_frame=dff,
        x="tcd",
        y="humidity",
        hover_name="Wallclock",
    )

    scatterplot.update_traces(textposition="top center")

    return scatterplot, dropdown_values, sfig


# ------------------------------------------------------------------------------

if __name__ == "__main__":
    #     app.run_server(debug=True)
    app.run_server(mode="inline")