具有日期时间和散点图交互的绘图破折号范围滑块

Posted

技术标签:

【中文标题】具有日期时间和散点图交互的绘图破折号范围滑块【英文标题】:plotly dash range slider with datetime and scatterplot interaction 【发布时间】:2021-10-30 15:30:50 【问题描述】:

我想在我的下拉列表中添加一个范围滑块,并将范围滑块设置为“Wallclock”日期时间以及允许范围滑块根据下拉值选择该胶囊的日期时间的交互。我设法找到了其他人这样做的几种方法,但似乎没有一种方法适合我的情况,尤其是回调和图表更新。谢谢!

数据看起来像这样。

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)

【问题讨论】:

【参考方案1】: 显然我无权访问您的 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")

【讨论】:

嗨,罗伯!感谢您的回复,这种方法是一个非常好的方法。可以根据胶囊调整日期范围滑块。例如,仅选择胶囊 2100015 会将范围滑块值更改为 22Dec20 到 26Dec20,而不是显示所有胶囊的整个日期范围。

以上是关于具有日期时间和散点图交互的绘图破折号范围滑块的主要内容,如果未能解决你的问题,请参考以下文章

Plotly-Dash:反应迟钝的情节

熊猫散点图日期时间

在核心绘图 iOS 中动态获取绘图范围的数据

如何通过3D绘图中的线连接多个散点图?

在python中绘制线和散点图

excel中的折线图和散点图出来的斜率怎么不一样