【问题标题】:Plotly : Assign pandas series to customdata attribute of scattermapboxPlotly:将熊猫系列分配给 scattermapbox 的 customdata 属性
【发布时间】:2021-09-25 01:32:35
【问题描述】:

我正在使用 plotly scattermapbox 并且正在改进代码。对于 scattermapbox 的customdata 属性,我想分配多个熊猫系列。在回调中,我读取了这些数据并根据点击事件填充了一个模式弹出窗口。代码如下:

layout = html.Div([

                # Plot properties map
                dcc.Graph(id="map-graph"),

                html.Div([

                dbc.Modal(
                    [
                        dbc.ModalHeader("Info"),
                        dbc.ModalBody(
                            [

                                
                                dbc.Label("Name:"),
                                dbc.Label("Id"),
                                
                            ]
                        ),
                        dbc.ModalFooter(
                            [
                                dbc.Button("OK"),
                            ]
                        ),
                    ],
                    id="modal-1",
                ),

            ], style={"width": "50%"}),

)]

回调追加数据并为map-graph定义布局,只贴相关代码sn-p:

          result_df = pd.DataFrame({'id': [1,2,3,4],  
                                    'name':['a','b','c','d'], 
                                    'val': [5,6,7,8], 
                                    'lat':[.....], 
                                    'long':[....]
                      })  

           data = []

           lat = result_df["lat"]
           lng = result_df["long"]
           
           data.append({

                        "type": "scattermapbox",
                        "lat": lat,
                        "lon": lng,
                        "mode": "markers",
                        "clickmode": "event+select",
                        "customdata": { "Name": result_df["name"],
                                        "id": result_df["id"]},
                        "marker": {
                                   "symbol": "circle",
                                   "size": 12,
                                   "opacity": 0.7,
                                   "color": "black"
                                  }
                        }
           )

读取customdata的回调,解析为json数据,然后根据选择填充modal

@application.callback(
                          [
                               Output("modal-1","is_open"),

                               
                               Output("name","children"),
                               Output("val","children"),
                                                        ],
                          [
                               # Button clicks

                               Input("map-graph1","clickData"),
                               Input("c-button", "n_clicks"),
                               Input("close","n_clicks1")
                          ],

                          [
                                State("modal-1", "is_open")
                          ],
                    )
def display_popup(clickData, n_clicks, close, is_open):

    if clickData:

        res = json.dumps(clickData, indent=2)

        Name = clickData["points"][0]["customdata"]["Name"]
        id = clickData["points"][0]["customdata"]["id"]
        

        return(not is_open, Name, id,)

    elif close:

        return is_open

    else:

        return (no_update)

当我打印时,clickData 的响应如下所示:

{
  "points": [
    {
      "curveNumber": 0,
      "pointNumber": 15,
      "pointIndex": 15,
      "lon": -118.27600459999998,
      "lat": 34.0456796,
    }
  ]
}

响应中缺少customdata。如何将数据绑定回(lat, long) 点击map-graph 并使用自定义数据中的Nameid 等信息填充模态。

【问题讨论】:

    标签: python plotly mapbox plotly-dash


    【解决方案1】:
    • 已使用英国医院作为样本数据进行分散
    • 使用Plotly Express生成mapbox图形更简单
    • hover_data 使用 customdata 存储悬停数据。更新了模板,因此数据保留在跟踪中,但不会在悬停时显示
    • 您的 modal 没有任何地方可以放置 IDName,为此创建了另外两个 Div。没有美化
    • 回调比您编写的要简单得多。只有输入是clickData,输出非常明显。导航 clickData dict 以获取 IDName
    import dash_bootstrap_components as dbc
    import dash
    from dash.dependencies import Input, Output, State
    from jupyter_dash import JupyterDash
    import requests, io, json
    import pandas as pd
    import plotly.express as px
    
    # get some public addressess - hospitals.  data that has GPS lat / lon
    dfhos = pd.read_csv(io.StringIO(requests.get("http://media.nhschoices.nhs.uk/data/foi/Hospital.csv").text),sep="¬",engine="python",)
    
    # columns to be used as custom data, i.e. hoverdata
    cd = ["OrganisationCode", "Sector", "OrganisationName", "Postcode"]
    
    # update hovertemplate so it does not display all the columns added as customdata
    fig = px.scatter_mapbox(
        dfhos.sample(30),
        lat="Latitude",
        lon="Longitude",
        hover_name="OrganisationName",
        hover_data=cd,
    ).update_traces(
        hovertemplate="<b>%{hovertext}</b><br><br>Latitude=%{lat}<br>Longitude=%{lon}<br><extra></extra>",
    )
    
    fig.update_layout(
        mapbox={"style": "carto-positron", "zoom": 4},
        margin={"r": 0, "t": 0, "l": 0, "b": 0},
        clickmode="event+select"
    )
    
    # Build App
    app = JupyterDash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
    app.layout = dash.html.Div(
        [
            # Plot properties map
            dash.dcc.Graph(id="map-graph", figure=fig),
            dash.html.Div(
                [
                    dbc.Modal(
                        [
                            dbc.ModalHeader("Info"),
                            dbc.ModalBody(
                                [
                                    dbc.Label("Name:"),
                                    dash.html.Div(id="modal-name"),
                                    dbc.Label("Id"),
                                    dash.html.Div(id="modal-id"),
                                ]
                            ),
                            dbc.ModalFooter(
                                [
                                    dbc.Button("OK"),
                                ]
                            ),
                        ],
                        id="modal-1",
                    ),
                ],
                style={"width": "50%"},
            ),
        ]
    )
    
    @app.callback(
        [
            Output("modal-1", "is_open"),
            Output("modal-id", "children"),
            Output("modal-name", "children"),
        ],
        Input("map-graph", "clickData"),
    )
    def display_popup(clickData):
        if clickData:
            return (
                True,
                clickData["points"][0]["customdata"][cd.index("OrganisationCode")],
                clickData["points"][0]["customdata"][cd.index("OrganisationName")],
            )
        else:
            raise dash.exceptions.PreventUpdate
    
    # Run app and display result inline in the notebook
    app.run_server(mode="inline")
    

    使用图形对象创建图形

    df = dfhos.sample(30)
    fig = go.Figure(
        go.Scattermapbox(
            lat=df["Latitude"],
            lon=df["Longitude"],
            hovertext=df["OrganisationName"],
            customdata=df.loc[:, cd],
            hovertemplate="<b>%{hovertext}</b><br><br>Latitude=%{lat}<br>Longitude=%{lon}<br><extra></extra>",
        )
    )
    
    fig.update_layout(
        mapbox={"style": "carto-positron", "zoom": 4, "center": {"lat": df["Latitude"].mean(), "lon": df["Longitude"].mean()}},
        margin={"r": 0, "t": 0, "l": 0, "b": 0},
        clickmode="event+select",
    )
    
    

    【讨论】:

    • 谢谢。 plotly.express 确实简化了它。但是,我希望能够在 plotly scattermapbox 中做到这一点。它在遍历 pandas 数据框以构造 customdata 时对我有用,但由于不建议遍历 df,因此效率不高。
    • 好的 - 已通过使用图形对象而不是 Plotly Express 构建图形来补充答案
    • 所以我尝试了您提出的解决方案,即将熊猫列传递给customdata,但仍然遇到一些问题。错误如下:InvalidCallbackReturnValue: The callback for [map-graph.figure>]` 返回了一个类型为 tuple 的值,它不是 JSON 可序列化的。 ` 返回语句如下:return ({"data": data, "layout": layout})
    • 此行导致问题:customdata=df.loc[:, cd],。当我删除它时,地图会在我到达 clickData 以填充模式之前呈现良好。
    • 我升级了软件包以与您的一致。我仍然看到TypeError: Object of type DataFrame is not JSON serializable。我认为customdata 属性不喜欢我传递 DataFrame 对象。如果您不介意查看,请在聊天室中查看更多详细信息。
    猜你喜欢
    • 2015-04-14
    • 2018-12-02
    • 2023-01-20
    • 2013-05-29
    • 2016-08-30
    • 2022-08-04
    • 2017-08-12
    • 2013-02-23
    • 1970-01-01
    相关资源
    最近更新 更多