`line_mapbox` 和第二个图之间的交叉过滤器

Crossfilter between `line_mapbox` and second plot

我是 plotly 的新手,想可视化来自 运行 activity 的数据。假设我有一个包含以下列的数据框:

df = pd.DataFrame(
    {
        "time": time,
        "latitude": latitude,
        "longitude": longitude,
        "altitude": altitude,
        "heartrate": heartrate,
    }
)

我想要两个图,一个是绘制纬度与经度的地图,另一个是绘制时间与心率(或高度)的图。 但我希望两个图都被 linked。因此,如果我在第二个图中 select 一个 y 范围,我只想在地图上看到那些纬度-经度对,它们各自的时间值在我 select 的范围内在第二个情节中编辑。同样,如果我在地图上 select 点,我想在该 selection 点中寻找最小和最大时间值,并希望在第二个图中仅绘制这些点。

这是一张包含一些虚拟数据的屏幕截图:

我不知道如何 link 这两个情节,所以任何帮助表示赞赏! 源码在这里给出:

import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objs as go

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

np.random.seed(0)
# Random dummy data
n = 100
time = np.linspace(0, 1, n)
latitude = 50 + 0.001 * np.cumsum(np.random.randn(n))
longitude = 2 + 0.001 * np.cumsum(np.random.randn(n))
altitude = (time - 0.5) ** 2
heartrate = 100 + np.cumsum(np.random.randn(n))
df = pd.DataFrame(
    {
        "time": time,
        "latitude": latitude,
        "longitude": longitude,
        "altitude": altitude,
        "heartrate": heartrate,
    }
)

fig = px.line_mapbox(df, lat="latitude", lon="longitude", zoom=12, height=800)
fig.update_layout(mapbox_style="stamen-terrain")


app.layout = html.Div(
    [
        html.Div(
            [
                dcc.Graph(id="mymap", figure=fig),
            ]
        ),
        html.Div(
            [
                dcc.Graph(id="time-series"),
                dcc.Dropdown(
                    id="column",
                    options=[
                        {"label": i, "value": i} for i in ["altitude", "heartrate"]
                    ],
                    value="altitude",
                ),
            ]
        ),
    ]
)


def lineplot(x, y, title="", axis_type="Linear"):
    return {
        "data": [go.Scatter(x=x, y=y, mode="lines")],
    }


@app.callback(
    dash.dependencies.Output("time-series", "figure"),
    [
        dash.dependencies.Input("column", "value"),
    ],
)
def update_timeseries(column):
    x = df["time"]
    y = df[column]
    return lineplot(x, y)


app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})


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

这是一个选项,我将线图框更改为散点图框,这样您就可以使用框 select

并查看已完成的 selected 地图中的数据和折线图中的过滤器的代码

import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objs as go

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

np.random.seed(0)
# Random dummy data
n = 100
time = np.linspace(0, 1, n)
latitude = 50 + 0.001 * np.cumsum(np.random.randn(n))
longitude = 2 + 0.001 * np.cumsum(np.random.randn(n))
altitude = (time - 0.5) ** 2
heartrate = 100 + np.cumsum(np.random.randn(n))
df = pd.DataFrame(
    {
        "time": time,
        "latitude": latitude,
        "longitude": longitude,
        "altitude": altitude,
        "heartrate": heartrate,
    }
)

fig = go.Figure(go.Scattermapbox(
    mode = "markers+lines",
    lon = df.longitude,
    lat = df.latitude,
    marker = {'size': 10}))
fig.update_layout(
    mapbox={
        'style': "stamen-terrain",
        'center' : dict(
            lat=50,
            lon=2
        ),
        'zoom': 12})


app.layout = html.Div(
    [
        html.Div(
            [
                dcc.Graph(id="mymap", figure=fig),
            ]
        ),
        html.Div(
            [
                dcc.Graph(id="time-series"),
                dcc.Dropdown(
                    id="column",
                    options=[
                        {"label": i, "value": i} for i in ["altitude", "heartrate"]
                    ],
                    value="altitude",
                ),
            ]
        ),
    ]
)


def lineplot(x, y, title="", axis_type="Linear"):
    return {
        "data": [go.Scatter(x=x, y=y, mode="lines")],
    }


@app.callback(
    Output("time-series", "figure"),
    [
        Input("column", "value"),
        Input("mymap", "selectedData")
    ],
)
def update_timeseries(column, selectedData):
    # add filter data by selectData points
    temp = df
    if selectedData is not None:
        sel_data = pd.DataFrame(selectedData['points'])
        temp = df.loc[(df.latitude.isin(sel_data.lat)) & (df.longitude.isin(sel_data.lon))]
    x = temp["time"]
    y = temp[column]
    return lineplot(x, y)


app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})


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