如何按关联Slot分组设置条形宽度绘制广播频谱图
问题描述
我们有一个包含广播介质数据的DataFrame:
| MHz | Slot | dB | dB_median | |
|---|---|---|---|---|
| 0 | 10 | Slot1 | 20 | 20.5 |
| 1 | 20 | Slot1 | 21 | 20.5 |
| 2 | 30 | Slot2 | 19 | 19 |
| 3 | 40 | Slot3 | 18 | 18 |
| 4 | 50 | Slot4 | 21 | 19 |
| 5 | 60 | Slot4 | 17 | 19 |
| 6 | 70 | Slot4 | 20 | 19 |
| 7 | 80 | Slot5 | 22 | 22 |
| 8 | 90 | Slot6 | 19 | 19 |
| 9 | 100 | Slot6 | 19 | 19 |
字段说明:
MHz:载波频率Slot:业务名称dB:单载波测量值dB_median:单业务中位数值
一个业务可对应多个载波(即一个Slot对应多个MHz),需要绘制同一Slot的多个连续载波合并为完整色块的频谱图,但当前代码只能按Slot给单个条形染色,每个载波都是独立色块,不符合需求。
最小可复现代码
import pandas as pd import seaborn as sns import matplotlib.pyplot as plt df_test = pd.DataFrame({ "MHz":[10,20,30,40,50,60,70,80,90,100], "Slot":["Slot1","Slot1","Slot2","Slot3","Slot4", "Slot4","Slot4","Slot5","Slot6","Slot6"], "dB":[20,21,19,18,21,17,20,22,19,19], "dB_median":[20.5,20.5,19,18,19,19,19,22,19,19] }) plt.figure(figsize=(8,4)) g = sns.barplot( data=df_test, x="MHz", y="dB_median", hue="Slot" ) sns.move_legend(g, "lower center", ncol=6, bbox_to_anchor=(0.5, 1)) plt.show()
实现思路与解决方案
核心思路
放弃seaborn的条形图,直接用matplotlib绘制矩形色块:
- 按
Slot分组,计算每个业务的频率区间(最小/最大MHz)和对应的中位值 - 用
matplotlib.patches.Rectangle绘制覆盖整个频率区间的连续色块 - 可选叠加单载波的实际测量值作为标记
修改后的代码示例
import pandas as pd import matplotlib.pyplot as plt from matplotlib.patches import Rectangle from matplotlib.lines import Line2D df_test = pd.DataFrame({ "MHz":[10,20,30,40,50,60,70,80,90,100], "Slot":["Slot1","Slot1","Slot2","Slot3","Slot4", "Slot4","Slot4","Slot5","Slot6","Slot6"], "dB":[20,21,19,18,21,17,20,22,19,19], "dB_median":[20.5,20.5,19,18,19,19,19,22,19,19] }) # 分组计算每个Slot的频率范围、中位值和对应颜色 slot_groups = df_test.groupby("Slot").agg( min_mhz=("MHz", "min"), max_mhz=("MHz", "max"), median_db=("dB_median", "first"), color=("Slot", lambda x: plt.cm.tab10(df_test["Slot"].unique().tolist().index(x.iloc[0]))) ).reset_index() plt.figure(figsize=(8,4)) ax = plt.gca() freq_width = df_test["MHz"].diff().dropna().unique()[0] # 获取载波间隔(此处为10MHz) # 绘制每个Slot的连续色块 for _, row in slot_groups.iterrows(): # 调整矩形位置和宽度,确保覆盖所有对应载波 rect = Rectangle( (row["min_mhz"] - freq_width/2, 0), row["max_mhz"] - row["min_mhz"] + freq_width, row["median_db"], color=row["color"], alpha=0.7 ) ax.add_patch(rect) # 叠加单载波测量值(可选) ax.scatter(df_test["MHz"], df_test["dB"], color="black", zorder=5) # 设置坐标轴参数 ax.set_xlim(df_test["MHz"].min() - freq_width/2, df_test["MHz"].max() + freq_width/2) ax.set_ylim(0, df_test["dB"].max() + 2) ax.set_xlabel("MHz") ax.set_ylabel("dB") # 自定义图例 slot_legends = [Line2D([0], [0], color=row["color"], lw=4, label=row["Slot"]) for _, row in slot_groups.iterrows()] measure_legend = Line2D([0], [0], marker='o', color='w', markerfacecolor='black', label='单载波测量值') ax.legend(handles=slot_legends + [measure_legend], loc="lower center", ncol=6, bbox_to_anchor=(0.5, 1)) plt.tight_layout() plt.show()
关键说明
- 计算频率区间时,需结合载波间隔调整矩形位置,避免色块间出现间隙
- 使用内置配色
tab10自动分配Slot颜色,也可自定义颜色映射 - 散点标记可按需添加,用于展示单载波的实际测量值
内容的提问来源于stack exchange,提问作者Marco_CH
相关产品推荐
相关产品推荐

