如何在Python中创建排序/排名堆叠面积图(带状图/流图)
在Python中实现排序堆叠面积图(带状图/流图)
这类图表的核心是每个时间点会根据数值大小重新调整堆叠区域的排序,实现类似PowerBI带状图的动态排名效果,下面分库给出具体实现方案:
一、使用Plotly实现(推荐,交互性强)
Plotly可以通过手动计算每个时间点的堆叠边界,结合Scatter的填充功能实现动态排序效果,步骤如下:
- 准备长格式时间序列数据
- 对每个时间点的数值排序,计算堆叠的起始(y0)和结束(y1)位置
- 绘制填充面积轨迹
import plotly.graph_objects as go import pandas as pd import numpy as np # 生成模拟数据(模拟1982年法国超过德国的场景) years = np.arange(1980, 2000) countries = ['德国', '法国', '英国', '意大利'] data = [] for year in years: vals = { '德国': 100 - (year-1980)*2 + np.random.randn()*3, '法国': 80 + (year-1980)*3 + np.random.randn()*3, '英国': 70 + (year-1980)*1.5 + np.random.randn()*2, '意大利': 60 + (year-1980)*1 + np.random.randn()*2 } for country, val in vals.items(): data.append({'年份': year, '国家': country, '数值': max(val, 0)}) df = pd.DataFrame(data) # 处理每个年份的排序与堆叠边界 processed_data = [] for year in years: year_df = df[df['年份'] == year].sort_values('数值', ascending=False) cumulative = 0 for _, row in year_df.iterrows(): processed_data.append({ '年份': year, '国家': row['国家'], 'y0': cumulative, 'y1': cumulative + row['数值'], '数值': row['数值'] }) cumulative += row['数值'] processed_df = pd.DataFrame(processed_data) # 绘制交互式图表 fig = go.Figure() # 为每个国家添加面积轨迹 for country in countries: country_df = processed_df[processed_df['国家'] == country] fig.add_trace(go.Scatter( x=country_df['年份'], y=country_df['y1'], fill='tonexty', name=country, hovertemplate='年份: %{x}<br>数值: %{customdata}<br>国家: %{name}', customdata=country_df['数值'] )) # 添加底部基准轨迹(用于填充到0) fig.add_trace(go.Scatter( x=years, y=[0]*len(years), fill=None, showlegend=False, line=dict(color='rgba(0,0,0,0)') )) fig.update_layout( title='排序堆叠面积图(带状图)', xaxis_title='年份', yaxis_title='数值', hovermode='x unified' ) fig.show()
二、使用Matplotlib实现(静态图表,定制化强)
Matplotlib没有原生支持动态排序堆叠面积图,需要手动计算每个国家在所有时间点的堆叠边界,再用fill_between绘制:
import matplotlib.pyplot as plt import pandas as pd import numpy as np # 复用上述模拟数据 years = np.arange(1980, 2000) countries = ['德国', '法国', '英国', '意大利'] data = [] for year in years: vals = { '德国': 100 - (year-1980)*2 + np.random.randn()*3, '法国': 80 + (year-1980)*3 + np.random.randn()*3, '英国': 70 + (year-1980)*1.5 + np.random.randn()*2, '意大利': 60 + (year-1980)*1 + np.random.randn()*2 } for country, val in vals.items(): data.append({'年份': year, '国家': country, '数值': max(val, 0)}) df = pd.DataFrame(data) # 颜色映射 colors = {'德国': '#1f77b4', '法国': '#ff7f0e', '英国': '#2ca02c', '意大利': '#d62728'} # 整理每个年份的排序结果 yearly_data = {} for year in years: year_df = df[df['年份'] == year].sort_values('数值', ascending=False) yearly_data[year] = (year_df['国家'].tolist(), year_df['数值'].tolist()) # 收集每个国家的所有时间点堆叠边界 country_bounds = {country: {'y0': [], 'y1': []} for country in countries} for year in years: countries_sorted, vals_sorted = yearly_data[year] current_bottom = 0 for country, val in zip(countries_sorted, vals_sorted): country_bounds[country]['y0'].append(current_bottom) country_bounds[country]['y1'].append(current_bottom + val) current_bottom += val # 绘制静态图表 plt.figure(figsize=(12, 6)) for country in countries: y0 = country_bounds[country]['y0'] y1 = country_bounds[country]['y1'] plt.fill_between(years, y0, y1, color=colors[country], alpha=0.7, label=country) plt.plot(years, y1, color=colors[country], linewidth=0.5) plt.title('排序堆叠面积图(Matplotlib实现)') plt.xlabel('年份') plt.ylabel('数值') plt.legend(loc='upper left') plt.grid(axis='y', linestyle='--', alpha=0.7) plt.show()
三、其他可选库
- Altair:声明式可视化库,可通过数据转换计算堆叠边界,语法简洁,适合小数据集场景
- Seaborn:无原生支持,但可基于Matplotlib的逻辑扩展实现
核心要点
这类图表的关键是每个时间点对数据按数值重新排序,再计算堆叠的上下边界,而非使用固定的堆叠顺序。Plotly适合做交互式图表,Matplotlib适合静态定制化需求。
内容的提问来源于stack exchange,提问作者Xtiaan
相关产品推荐
相关产品推荐

