Plotly:有没有办法将点击点的数据保存在列表中?

Plotly: is there a way to save the data of a clicked point in a list?

我有一个带有悬停功能的二维绘图。当您将鼠标悬停在每个点上时,会出现与该点关联的标签(例如 'image 2, cluster 1')。如果我要单击该点(而不是将鼠标悬停在该点上),我希望将标签附加到现有列表中。之所以这样,是因为我以后想用这个点的数据来做别的事情。是否有在线示例演示如何执行此操作——已查看文档但尚未找到相关内容。谢谢!

您需要使用回调来执行此类操作(注册 on_click())。已将 clicked 定义为点击点列表。演示了如何使用 ipwidgetsdash

实现这一点

ip 小部件

import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import ipywidgets as widgets
from pathlib import Path
import json

x = np.random.uniform(-10, 10, size=50)
y = np.sin(x)
clicked = []

# construct figure that has holders for points, interpolated line and final lines
fig = go.FigureWidget(
    [
        go.Scatter(x=x, y=y, mode="markers", name="base_points"),
    ]
)
fig.update_layout(template="simple_white")

out = widgets.Output(layout={"border": "1px solid black"})
out.append_stdout("Output appended with append_stdout\n")

# create our callback function
@out.capture()
def base_click(trace, points, selector):
    global clicked
    clicked.append(points.__dict__)

fig.data[0].on_click(base_click)

widgets.HBox([fig, out])

破折号

from jupyter_dash import JupyterDash
import dash
from dash.dependencies import Input, Output, State
import numpy as np
import json

clicked = []

# Build App
app = JupyterDash(__name__)
app.layout = dash.html.Div(
    [
        dash.dcc.Graph(
            id="fig",
            figure=go.Figure(go.Scatter(x=x, y=y, mode="markers", name="base_points")),
        ),
        dash.html.Div(id="debug"),
    ]
)


@app.callback(
    Output("debug", "children"),
    Input("fig", "clickData"),
)
def point_clicked(clickData):
    global clicked
    clicked.append(clickData)

    return json.dumps(clickData)

# Run app and display result inline in the notebook
app.run_server(mode="inline")

默认情况下可用的 hoverData 以及一些示例数据是这样的:

{
  "points": [
    {
      "curveNumber": 1,
      "pointNumber": 7,
      "pointIndex": 7,
      "x": 1987,
      "y": 74.32,
      "bbox": {
        "x0": 420.25,
        "x1": 426.25,
        "y0": 256,
        "y1": 262
      }
    }
  ]
}

我不太确定你所说的 'label' 是什么意思,所以我只能假设它是跟踪的名称或类似的名称,例如 Plotly 文档中的示例:

但是如您所见,在 hoverData 字典中并不容易获得。这意味着您还必须使用此信息来引用您的图形结构,这样您最终会得到这样的结果:

[['New Zealand', 2002, 79.11]]

只要您愿意使用 Plotly Dash,这都不是问题。我已经为您做了一个完整的设置,应该可以满足您的要求。在下图中的应用程序中,您会发现一个图形以及两个字符串输出字段。第一个字段显示您在图中单击的最后一个点的信息。每次单击时,都会将一个新元素添加到名为 store 的列表中。最后的字段显示来自同一次点击的完整信息。

你的问题的答案是,是的,有一种方法可以将点击点的数据保存在列表中。一种方法是通过以下使用 clickdata 引用图形对象的回调,将这些引用存储在列表中,并在每次单击新元素时附加新元素。

应用程序

完整代码:

import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
from dash import dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
import warnings

warnings.simplefilter(action='ignore', category=FutureWarning)

# app info
app = JupyterDash(__name__)
styles = {
    'pre': {
        'border': 'thin lightgrey solid',
        'overflowX': 'scroll'
    }
}

# data
df = px.data.gapminder().query("continent=='Oceania'")

# plotly figure
fig = px.line(df, x="year", y="lifeExp", color="country", title="No label selected")
fig.update_traces(mode="markers+lines")

app.layout = html.Div([
    dcc.Graph(
        id='figure1',
        figure=fig,
    ),

    html.Div(className
             ='row', children=[
        html.Div([
            dcc.Markdown(d("""Hoverdata using figure references""")),
            html.Pre(id='hoverdata2', style=styles['pre']),
        ], className='three columns'),
                 
                     html.Div([
            dcc.Markdown(d("""
              
              Full hoverdata
            """)),
            html.Pre(id='hoverdata1', style=styles['pre']),
        ], className='three columns')   
    ]),
    
])

# container for clicked points in callbacks
store = []

@app.callback(
    Output('figure1', 'figure'),
    Output('hoverdata1', 'children'),
    Output('hoverdata2', 'children'),
    [Input('figure1', 'clickData')])
def display_hover_data(hoverData):
    
    if hoverData is not None:
        traceref = hoverData['points'][0]['curveNumber']
        pointref = hoverData['points'][0]['pointNumber']
        store.append([fig.data[traceref]['name'],
                      fig.data[traceref]['x'][pointref],
                     fig.data[traceref]['y'][pointref]])
        fig.update_layout(title = 'Last label was ' + fig.data[traceref]['name'])
        return fig, json.dumps(hoverData, indent=2), str(store)
    else:
        return fig, 'None selected', 'None selected'

app.run_server(mode='external', port = 7077, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True)