Python绘制自定义颜色与标签的马赛克/Marimekko图表问题咨询
问题解决:自定义颜色的Marimekko图表实现
一、Plotly中给品牌固定自定义颜色(排序后保持不变)
核心思路是先给每个品牌绑定固定颜色,不管每年怎么排序,生成trace时直接调用对应颜色:
- 先定义品牌-颜色映射字典:
brand_color_map = { "Brand X": "#2c3e50", "Brand Y": "#e74c3c", "Brand Z": "#2ecc71", # 按你的品牌列表补充 }
- 对每年的数据按y值排序后,生成trace时指定颜色:
假设你的数据是按年份分组的,比如yearly_data是每个年份对应的品牌和y值列表,排序后循环创建trace:
import plotly.graph_objects as go fig = go.Figure() # 假设yearly_data是按年份整理好的字典,key是年份,value是[(brand, y_value), ...] for year, brand_values in yearly_data.items(): # 按y值对当前年份的品牌排序 sorted_brands = sorted(brand_values, key=lambda x: x[1], reverse=True) # 提取排序后的品牌和数值 brands = [b[0] for b in sorted_brands] values = [b[1] for b in sorted_brands] # 逐个添加trace,从颜色映射字典取对应颜色 for brand, val in zip(brands, values): fig.add_trace(go.Bar( name=brand, x=[year], y=[val], marker_color=brand_color_map[brand], # 固定颜色 legendgroup=brand, # 确保图例分组,切换时同步显示 showlegend=True if year == list(yearly_data.keys())[0] else False # 只在第一个年份显示图例 )) fig.update_layout(barmode='stack') fig.show()
这样不管每年品牌排序怎么变,每个品牌的颜色始终和字典里的绑定,不会乱。
二、非Plotly方案:用Matplotlib实现Marimekko图表
Matplotlib可以通过手动计算每个柱子的宽度和位置来实现可变x轴宽度的Marimekko图,步骤如下:
- 准备数据并计算关键参数:
import matplotlib.pyplot as plt import numpy as np # 示例数据:年份、品牌、销售额(或其他数值) data = [ ("2020", "Brand X", 100), ("2020", "Brand Y", 150), ("2020", "Brand Z", 50), ("2021", "Brand X", 120), ("2021", "Brand Y", 180), ("2021", "Brand Z", 60), ("2022", "Brand X", 150), ("2022", "Brand Y", 200), ("2022", "Brand Z", 70), ] # 计算每个年份的总数值(用于确定x轴宽度) year_totals = {} for year, _, val in data: year_totals[year] = year_totals.get(year, 0) + val total_all = sum(year_totals.values()) # 每个年份的x轴宽度占比 year_widths = {year: tot / total_all for year, tot in year_totals.items()} # 计算每个年份的x轴起始和结束位置 x_pos = 0 year_x_ranges = {} for year in year_totals.keys(): width = year_widths[year] year_x_ranges[year] = (x_pos, x_pos + width) x_pos += width # 品牌颜色映射(和Plotly里的保持一致) brand_color_map = { "Brand X": "#2c3e50", "Brand Y": "#e74c3c", "Brand Z": "#2ecc71", }
- 绘制Marimekko图:
fig, ax = plt.subplots(figsize=(10, 6)) # 按品牌分组处理数据 brands = list(brand_color_map.keys()) for brand in brands: # 收集该品牌每年的数值及对应x范围 brand_data = [(year, val) for year, b, val in data if b == brand] # 计算每个年份中该品牌的占比(y轴高度) brand_year_shares = [] x_starts = [] widths = [] for year, val in brand_data: total_year = year_totals[year] share = val / total_year x_start, x_end = year_x_ranges[year] x_starts.append(x_start) widths.append(x_end - x_start) brand_year_shares.append(share) # 绘制该品牌的所有块 ax.bar(x_starts, brand_year_shares, width=widths, bottom=0, color=brand_color_map[brand], label=brand) # 设置x轴刻度(对应年份的中间位置) x_ticks = [(start + end)/2 for start, end in year_x_ranges.values()] ax.set_xticks(x_ticks) ax.set_xticklabels(year_x_ranges.keys()) # 设置y轴为百分比 ax.set_ylabel("Market Share (%)") ax.set_yticks(np.arange(0, 1.1, 0.1)) ax.set_yticklabels([f"{int(p*100)}%" for p in np.arange(0, 1.1, 0.1)]) ax.legend(title="Brands") plt.title("Marimekko Chart (Matplotlib)") plt.tight_layout() plt.show()
这个方法通过手动计算每个年份的x轴宽度(基于总占比),然后每个品牌在对应年份的柱子里绘制占比高度,实现了可变x轴宽度的Marimekko效果,同时每个品牌颜色固定。
内容的提问来源于stack exchange,提问作者TomS
相关产品推荐
相关产品推荐

