Python Plotly Treemap颜色变量未累加子节点值问题排查
解决Plotly Treemap父节点颜色未按子节点Sales_Amount累加值显示的问题
问题说明
使用Plotly绘制Treemap时,设置三个分类变量作为父子层级,用Sales_Quantity映射面积,Sales_Amount映射颜色。预期父节点颜色对应累加所有子节点Sales_Amount后的总值,但实际未实现——父节点hover显示的数值甚至低于单个子节点,比如Cat_1049总销售额约61.9M,但颜色仅对应5.4M。尝试过branchvalues参数、清理NaN、重排数据、使用go.Treemap等方法,均未解决。
示例数据
{'Category': {1171: 'Cat_1049', 1172: 'Cat_1049', 1173: 'Cat_1049', 1174: 'Cat_1049', 1175: 'Cat_1049'}, 'SubCategory_1': {1171: 6754, 1172: 6752, 1173: 6748, 1174: 6746, 1175: 6742}, 'SubCategory_2': {1171: 'MCW-145', 1172: 'MCW-145', 1173: 'MCW-145', 1174: 'MCW-145', 1175: 'MCW-141'}, 'Sales_Amount': {1171: 13509.043, 1172: 8662.54, 1173: 92531.29, 1174: 11312.07, 1175: 6427.037}, 'Sales_Quantity': {1171: 6, 1172: 4, 1173: 38, 1174: 5, 1175: 3}}
原代码
fig = px.treemap(df_test, path= [px.Constant('All'),'Category', 'SubCategory_1','SubCategory_2'], values='Sales_Quantity', color='Sales_Amount', color_continuous_scale=px.colors.sequential.Turbo) fig.update_layout(legend_title="", autosize=False, width=1440, height=900, margin=dict( l=0, r=15, b=10, t=30, pad=3 ),title={'text':(f'Sales Amount and Quantities by Groups'), 'y':0.99,'x':0.5,'xanchor':'center', 'yanchor': 'top','font_size':20}, legend=dict(orientation="h")) fig.show()
解决方案
Plotly Express的Treemap默认不会自动累加color字段的父节点值,必须手动预处理数据,计算各层级的累加值,再用go.Treemap手动构建节点,确保父节点的颜色对应子节点的销售额总和。
步骤1:预处理数据,生成所有层级的节点
import pandas as pd import plotly.graph_objects as go # 转换示例数据为DataFrame df = pd.DataFrame(data) # 存储所有层级的节点数据 nodes = [] # 1. 叶子节点:SubCategory_2层级 for _, row in df.iterrows(): node_id = f"{row['Category']}|{row['SubCategory_1']}|{row['SubCategory_2']}" parent_id = f"{row['Category']}|{row['SubCategory_1']}" nodes.append({ 'id': node_id, 'parent': parent_id, 'value': row['Sales_Quantity'], 'sales_amount': row['Sales_Amount'], 'label': row['SubCategory_2'] }) # 2. SubCategory_1层级:累加子节点的销售额和销量 sc1_groups = df.groupby(['Category', 'SubCategory_1']).agg( total_quantity=('Sales_Quantity', 'sum'), total_amount=('Sales_Amount', 'sum') ).reset_index() for _, row in sc1_groups.iterrows(): node_id = f"{row['Category']}|{row['SubCategory_1']}" parent_id = row['Category'] nodes.append({ 'id': node_id, 'parent': parent_id, 'value': row['total_quantity'], 'sales_amount': row['total_amount'], 'label': str(row['SubCategory_1']) }) # 3. Category层级:累加子节点的销售额和销量 cat_groups = df.groupby('Category').agg( total_quantity=('Sales_Quantity', 'sum'), total_amount=('Sales_Amount', 'sum') ).reset_index() for _, row in cat_groups.iterrows(): node_id = row['Category'] parent_id = 'All' nodes.append({ 'id': node_id, 'parent': parent_id, 'value': row['total_quantity'], 'sales_amount': row['total_amount'], 'label': row['Category'] }) # 4. 顶层节点All:累加所有数据的销售额和销量 all_total = df.agg( total_quantity=('Sales_Quantity', 'sum'), total_amount=('Sales_Amount', 'sum') ) nodes.append({ 'id': 'All', 'parent': '', 'value': all_total['total_quantity'], 'sales_amount': all_total['total_amount'], 'label': 'All' }) # 转换为DataFrame方便后续调用 nodes_df = pd.DataFrame(nodes)
步骤2:用go.Treemap绘制图表
fig = go.Figure(go.Treemap( ids=nodes_df['id'], parents=nodes_df['parent'], values=nodes_df['value'], labels=nodes_df['label'], marker=dict( colors=nodes_df['sales_amount'], colorscale='Turbo', colorbar=dict(title='Sales Amount') ), textinfo='label+value+percent parent', hovertemplate='<b>%{label}</b><br>' + 'Sales Quantity: %{value}<br>' + 'Sales Amount: %{customdata:,.2f}<extra></extra>', customdata=nodes_df['sales_amount'] )) fig.update_layout( legend_title="", autosize=False, width=1440, height=900, margin=dict(l=0, r=15, b=10, t=30, pad=3), title={ 'text': 'Sales Amount and Quantities by Groups', 'y': 0.99, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top', 'font_size': 20 }, legend=dict(orientation="h") ) fig.show()
方案说明
- 手动构建了从叶子节点到顶层的所有层级数据,确保每个父节点的
sales_amount是其所有子节点的累加值 - 使用
go.Treemap直接指定每个节点的id、parent、values和colors,完全控制节点的数值映射 - 通过
hovertemplate自定义悬浮提示,显示正确的累加销售额数据
内容的提问来源于stack exchange,提问作者Yleduap
相关产品推荐
相关产品推荐

