Plotly交互式堆叠折线图切换年龄组时类别重叠的问题修复咨询
解决Plotly堆叠折线图切换年龄组时的异常问题
嘿,我帮你定位到问题啦!你的代码里有两个核心错误,导致切换年龄组按钮时混入了其他组的数据,咱们一步步来修正:
问题1:生成年龄组数据时未过滤对应年龄的记录
你在循环生成type_by_time_age的时候,没有筛选当前年龄组的患者数据——不管是20岁组还是30岁组,都是用全量的df来做分组统计,这就导致每个年龄组的df其实都是全年龄的数据,切换自然会出错。
修正方式:在循环里先筛选出对应年龄组的记录(假设你的age字段是类似20s、30s的格式,用str.startswith匹配):
type_by_time_age = [] df = p_info for age_group in ['20','30','40','50','60','70','80']: # 关键:筛选当前年龄组的患者数据 age_filtered_df = df[df['age'].str.startswith(age_group)] new_type_by_time = age_filtered_df.groupby(['week', 'infection_type']).size().unstack().fillna(0) type_by_time_age.append(new_type_by_time)
问题2:按钮切换的可见性数组计算错误
你原来的代码里,每个按钮对应9个trace可见,但实际上:
- 「All」组是5个感染类别的trace
- 每个年龄组也各是5个trace
总共有5 + 7*5 = 40个trace,你用9来计算可见范围,完全不对,导致显示了错误的trace集合。
修正方式:重新构建每个按钮对应的visible数组,按5个trace为一组来控制可见性:
menus = [] total_traces = 5 + len(type_by_time_age)*5 # 总trace数:5(All) + 7*5(各年龄组) for i, name in enumerate(['All','20','30','40','50','60','70','80']): visible = [False]*total_traces if i == 0: # 「All」组:前5个trace可见 visible[:5] = [True]*5 else: # 年龄组:对应索引范围的5个trace可见 start_idx = 5 + (i-1)*5 visible[start_idx:start_idx+5] = [True]*5 d = dict( label=name, method="update", args=[ {"visible": visible}, {"title": f"Where do most people get infected? (Age: {name})"} ] ) menus.append(d)
完整修正后的代码
import pandas as pd import plotly.graph_objects as go import plotly.express as px p_info = pd.read_csv('PatientInfo.csv',parse_dates=['symptom_onset_date', 'confirmed_date', 'released_date', 'deceased_date']) p_info = p_info.dropna(subset=['age']) p_info = p_info.dropna(subset=['infection_case']) # 分组感染类型 def group(x): if "Shincheonji Church" in x or "Onchun Church" in x or "Dongan Church" in x or "Geochang Church" in x or "SMR Newly Planted Churches Group" in x or "Pilgrimage to Israel" in x or "River of Grace Community Church" in x or "Biblical Language study meeting" in x: return "Church" elif "etc" in x: return "etc" elif "contact with patient" in x: return "Direct Contact with Patient" elif "overseas inflow" in x: return "Overseas" else: return "Group" p_info['infection_type'] = p_info['infection_case'].apply(group) p_info['week'] = p_info['confirmed_date'].dt.weekofyear # 全年龄组数据 type_by_time = p_info.groupby(['week', 'infection_type']).size().unstack().fillna(0) # 各年龄组数据(修正:添加年龄筛选) type_by_time_age = [] df = p_info for age_group in ['20','30','40','50','60','70','80']: age_filtered_df = df[df['age'].str.startswith(age_group)] new_type_by_time = age_filtered_df.groupby(['week', 'infection_type']).size().unstack().fillna(0) type_by_time_age.append(new_type_by_time) colors = px.colors.qualitative.Light24 x = type_by_time.index.tolist() categories = ['Church', 'Direct Contact with Patient', 'Group', 'Overseas', 'etc'] fig = go.Figure() # 添加全年龄组的trace for i, cat in enumerate(categories): fig.add_trace(go.Scatter( x=x, y=type_by_time[cat], hoverinfo='x+y', mode='lines', line=dict(width=0.5, color=colors[i]), name=cat, stackgroup='one', groupnorm='percent' )) # 添加各年龄组的trace for age_idx, df in enumerate(type_by_time_age): for i, cat in enumerate(categories): fig.add_trace(go.Scatter( x=df.index.tolist(), y=df[cat], hoverinfo='y', mode='lines', line=dict(width=0.5, color=colors[i]), name=cat, stackgroup=f"age_{age_idx}", # 用唯一的stackgroup标识 groupnorm='percent', visible=False )) fig.update_layout( title='Where do most people get infected?', showlegend=True, xaxis=dict( range=[4, 19], ticksuffix=' week' ), yaxis=dict( type='linear', range=[1, 100], ticksuffix='%' ), xaxis_title="weeks", yaxis_title="% in group of people who get infected", ) # 修正按钮的可见性控制 menus = [] total_traces = 5 + len(type_by_time_age)*5 for i, name in enumerate(['All','20','30','40','50','60','70','80']): visible = [False]*total_traces if i == 0: visible[:5] = [True]*5 else: start_idx = 5 + (i-1)*5 visible[start_idx:start_idx+5] = [True]*5 d = dict( label=name, method="update", args=[ {"visible": visible}, {"title": f"Where do most people get infected? (Age: {name})"} ] ) menus.append(d) fig.update_layout( updatemenus=[ dict( type="buttons", direction="right", active=0, x=1, y=1.2, buttons=menus, ) ] ) fig.show()
现在你再运行代码,点击不同年龄组按钮时,就只会显示对应年龄组的5个感染类型类别,hover时数据总和也会保持100%啦!
内容的提问来源于stack exchange,提问作者KensukeSuzuki
相关产品推荐
相关产品推荐

