You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Plotly/Dash技术问询:如何实现数据表行索引唯一或正确高亮选中行?

解决散点图选中点对应数据表高亮错误的问题

这个问题的核心在于Plotly散点图中每个分类(trace)的pointIndex是独立从0开始计数的,而数据表的行索引是全局唯一的。当你选中不同分类里的点时,它们的pointIndex可能重复,导致错误地高亮同一行。

要解决这个问题,我们可以给每行数据添加一个全局唯一标识,并在散点图的自定义数据中携带这个标识,选中时通过该标识匹配数据表的对应行。

修改后的完整代码

import pandas as pd
import dash
from dash.dependencies import Input, Output
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import plotly.express as px

df = pd.DataFrame.from_dict(
    {'term': {0: 'GOCC:0043229', 1: 'GOCC:0098588', 2: 'GOCC:0005730', 3: 'GO:0005730', 4: 'GO:0005783', 5: 'GO:0031410', 6: 'KW-0732', 7: 'KW-0156', 8: 'KW-0010'},
     'description': {0: 'Intracellular organelle', 1: 'Bounding membrane of organelle', 2: 'Nucleolus', 3: 'nucleolus', 4: 'endoplasmic reticulum', 5: 'cytoplasmic vesicle', 6: 'Signal', 7: 'Chromatin regulator', 8: 'Activator'},
     'FG_count': {0: 370, 1: 92, 2: 126, 3: 31, 4: 63, 5: 23, 6: 9, 7: 410, 8: 500},
     'logFDR': {0: 3, 1: 4, 2: 5, 3: 6, 4: 7, 5: 8, 6: 5, 7: 1, 8: 2},
     'effectSize': {0: 0.053, 1: -0.049, 2: 0.046, 3: 0.047, 4: -0.040, 5: -0.027, 6: -0.024, 7: 0.025, 8: 0.025},
     'category': {0: 'TM', 1: 'TM', 2: 'TM', 3: 'GOCC', 4: 'GOCC', 5: 'UPK', 6: 'UPK', 7: 'GOCC', 8: 'UPK'}}
)

# 给每行数据添加全局唯一标识(直接用DataFrame原始索引)
df['unique_id'] = df.index

app = dash.Dash(__name__, prevent_initial_callbacks=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
max_marker_size = 40
sizeref = 2.0 * max(df["FG_count"]) / (max_marker_size ** 2)

app.layout = html.Div(id='general_div', children=[
    html.Div(id='first_row', children=[
        html.Div(dcc.Graph(id='scatter_plot', figure=px.scatter(
            data_frame=df, x="logFDR", y="effectSize", color="category", size="FG_count",
            hover_data={"term": True, "description": True, "FG_count": True, "logFDR": False, "effectSize": False, "category": False, "unique_id": False},
            # 在自定义数据中加入唯一标识
            custom_data=["term", "description", "FG_count", "unique_id"]
        ).update_traces(
            hovertemplate="<b>%{customdata[0]}</b><br>%{customdata[1]}<br>Size: %{customdata[2]}<extra></extra>",
            mode='markers',
            marker={'sizemode': 'area', 'sizeref': sizeref, 'sizemin': 3, }
        ).update_layout(hoverlabel=dict(font_size=12, )))),
    ]),
    html.Br(),
    html.Div(id="second_row", children=[
        html.Div(dash_table.DataTable(
            id='main_datatable',
            # 隐藏唯一标识列(可选,不需要显示时设置)
            columns= [{"name": colName, "id": colName, "hideable": True, "hidden": (colName == 'unique_id')} for colName in df.columns],
            data=df.to_dict('records'),
            sort_action="native",
            row_selectable="multi",
            selected_columns=[],
            selected_rows=[],
            style_as_list_view=True,
            style_cell={'minWidth': "10px", "width": "50px", "maxWidth": "80px", "fontSize": "12px", "font-family": "sans-serif", "text_align": "center", "border": "1px",},
        )),
    ]),
    html.Br(),
])

def update_table_style(selectedData):
    table_style_conditions = [
        {'if': {'row_index': 'odd'}, 'backgroundColor': "#F5F5F5", }
    ] + [
        {"if": {"state": "selected"}, "backgroundColor": "inherit !important", "border": "inherit !important", "text_align": "inherit !important", }
    ] + [
        {"if": {"state": "active"}, "backgroundColor": "inherit !important", "border": "inherit !important", "text_align": "inherit !important", }
    ]
    if selectedData is not None:
        # 从选中点的自定义数据中提取唯一标识,匹配数据表行
        selected_ids = [point['customdata'][3] for point in selectedData["points"]]
        selected_styles = [{'if': {'row_index': uid}, 'backgroundColor': 'gold'} for uid in selected_ids]
        return (selected_styles + table_style_conditions)
    return (table_style_conditions)

@app.callback(Output('main_datatable', 'style_data_conditional'), [Input('scatter_plot', 'selectedData')])
def display_selected_data(selectedData):
    table_style_conditions = update_table_style(selectedData)
    return table_style_conditions

if __name__ == '__main__':
    app.run_server(debug=True, host="127.0.0.1", port=8001)

关键改动说明

  1. 添加唯一ID列:给DataFrame新增unique_id列,用原始索引作为全局唯一标识,确保每行数据都有不重复的标记。
  2. 携带唯一ID到散点图:在custom_data中加入unique_id,这样选中点时能获取到对应行的唯一标识。
  3. 隐藏ID列(可选):如果不需要在数据表中显示unique_id,可以在列定义中设置hidden=True。
  4. 根据唯一ID匹配行:在回调函数中,从选中点的customdata里提取unique_id,用这个ID来定位数据表的行,彻底解决pointIndex重复导致的高亮错误问题。

这样修改后,无论你选中哪个分类里的点,数据表都会准确高亮对应的行,不会再出现重复或错误高亮的情况。

内容的提问来源于stack exchange,提问作者tryptofame

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:57:50