如何用Python实现带层级筛选的分类学数据嵌套饼图可视化?
分类学数据嵌套饼图可视化及筛选解决方案
核心需求
- 展示所有门(Phylum)层级的嵌套饼图
- 筛选特定门(如Firmicutes),展示其下属的属(Genus)及对应种(Species)
- 筛选特定属(如Bacillus),展示其下属的种
- 实现分支突出叠加效果(选中分支保留原色,其余分支淡化)
数据预处理
先统一处理数据,确保按层级求和去重:
import pandas as pd import matplotlib.pyplot as plt from matplotlib.patches import Patch # 读取数据 df = pd.read_excel("TissueS35_Analysis_Report.xlsx", sheet_name="Species") # 保留所需列并去除空值 df = df[['Phylum', 'Genus', 'Species', 'Absolute Count']].dropna() # 按门-属-种层级分组求和 grouped_full = df.groupby(['Phylum', 'Genus', 'Species'])['Absolute Count'].sum().reset_index()
场景1:基础嵌套饼图(支持筛选)
以下函数可根据传入的筛选参数,自动生成对应层级的嵌套饼图:
def plot_nested_pie(filter_phylum=None, filter_genus=None): # 根据筛选条件过滤数据 if filter_phylum and filter_genus: # 筛选特定属对应的种 filtered = grouped_full[(grouped_full['Phylum'] == filter_phylum) & (grouped_full['Genus'] == filter_genus)] genus_total = filtered['Absolute Count'].sum() levels = [ {"names": [filter_genus], "values": [genus_total], "radius": 0.4, "colors": ["#ff7f0e"]}, {"names": filtered['Species'].tolist(), "values": filtered['Absolute Count'].tolist(), "radius": 0.7, "colors": plt.cm.tab20c.colors[:len(filtered)]} ] title = f"{filter_genus} 属的种层级分布" elif filter_phylum: # 筛选特定门对应的属和种 filtered = grouped_full[grouped_full['Phylum'] == filter_phylum] genus_grouped = filtered.groupby('Genus')['Absolute Count'].sum().reset_index() phylum_total = genus_grouped['Absolute Count'].sum() levels = [ {"names": [filter_phylum], "values": [phylum_total], "radius": 0.4, "colors": ["#1f77b4"]}, {"names": genus_grouped['Genus'].tolist(), "values": genus_grouped['Absolute Count'].tolist(), "radius": 0.7, "colors": plt.cm.tab20b.colors[:len(genus_grouped)]}, {"names": filtered['Species'].tolist(), "values": filtered['Absolute Count'].tolist(), "radius": 1.0, "colors": plt.cm.tab20c.colors[:len(filtered)]} ] title = f"{filter_phylum} 门的属-种层级分布" else: # 展示全部门-属-种 phylum_grouped = grouped_full.groupby('Phylum')['Absolute Count'].sum().reset_index() genus_grouped = grouped_full.groupby(['Phylum', 'Genus'])['Absolute Count'].sum().reset_index() levels = [ {"names": phylum_grouped['Phylum'].tolist(), "values": phylum_grouped['Absolute Count'].tolist(), "radius": 0.4, "colors": plt.cm.tab20.colors[:len(phylum_grouped)]}, {"names": genus_grouped['Genus'].tolist(), "values": genus_grouped['Absolute Count'].tolist(), "radius": 0.7, "colors": plt.cm.tab20b.colors[:len(genus_grouped)]}, {"names": grouped_full['Species'].tolist(), "values": grouped_full['Absolute Count'].tolist(), "radius": 1.0, "colors": plt.cm.tab20c.colors[:len(grouped_full)]} ] title = "全部分类层级分布" # 绘制图表 fig, ax = plt.subplots(figsize=(10, 10)) ax.set_title(title, fontsize=14) for level in levels: wedges, texts = ax.pie( level['values'], radius=level['radius'], colors=level['colors'], wedgeprops={"width": 0.3, "edgecolor": "white"}, labels=level['names'] if level['radius'] == 1.0 else None # 仅外层显示种标签避免拥挤 ) for text in texts: text.set_fontsize(8) # 添加对应层级的图例 if not filter_phylum: handles = [Patch(color=c, label=n) for c, n in zip(levels[0]['colors'], levels[0]['names'])] ax.legend(handles=handles, loc='center left', bbox_to_anchor=(1, 0.5), title='门(Phylum)') elif filter_phylum and not filter_genus: handles = [Patch(color=c, label=n) for c, n in zip(levels[1]['colors'], levels[1]['names'])] ax.legend(handles=handles, loc='center left', bbox_to_anchor=(1, 0.5), title='属(Genus)') else: handles = [Patch(color=c, label=n) for c, n in zip(levels[1]['colors'], levels[1]['names'])] ax.legend(handles=handles, loc='center left', bbox_to_anchor=(1, 0.5), title='种(Species)') ax.set(aspect="equal") plt.show()
调用示例
- 展示全部门层级:
plot_nested_pie()
- 筛选Firmicutes门:
plot_nested_pie(filter_phylum="Firmicutes")
- 筛选Firmicutes门下的Bacillus属(需数据中存在该属):
plot_nested_pie(filter_phylum="Firmicutes", filter_genus="Bacillus")
场景2:分支突出叠加效果
通过将未选中分支设为灰色,突出显示目标分支,实现类似示例的叠加效果:
def plot_highlighted_nested_pie(highlight_phylum=None, highlight_genus=None): # 全量数据分组 phylum_grouped = grouped_full.groupby('Phylum')['Absolute Count'].sum().reset_index() genus_grouped = grouped_full.groupby(['Phylum', 'Genus'])['Absolute Count'].sum().reset_index() # 定义颜色:选中分支用原色,其余用灰色 phylum_colors = [] for phylum in phylum_grouped['Phylum']: if phylum == highlight_phylum: phylum_colors.append(plt.cm.tab20.colors[phylum_grouped.index[phylum_grouped['Phylum'] == phylum][0]]) else: phylum_colors.append('#cccccc') genus_colors = [] for idx, row in genus_grouped.iterrows(): if row['Phylum'] == highlight_phylum and (not highlight_genus or row['Genus'] == highlight_genus): genus_colors.append(plt.cm.tab20b.colors[idx]) else: genus_colors.append('#cccccc') species_colors = [] for idx, row in grouped_full.iterrows(): if row['Phylum'] == highlight_phylum and (not highlight_genus or row['Genus'] == highlight_genus): species_colors.append(plt.cm.tab20c.colors[idx]) else: species_colors.append('#cccccc') # 绘制图表 fig, ax = plt.subplots(figsize=(10, 10)) highlight_text = f"{highlight_phylum}" if highlight_genus: highlight_text += f" > {highlight_genus}" ax.set_title(f"突出显示 {highlight_text} 分支", fontsize=14) # 绘制三层饼图 ax.pie( grouped_full['Absolute Count'], radius=1.0, colors=species_colors, wedgeprops={"width": 0.3, "edgecolor": "white"}, labels=grouped_full['Species'].tolist() ) ax.pie( genus_grouped['Absolute Count'], radius=0.7, colors=genus_colors, wedgeprops={"width": 0.3, "edgecolor": "white"} ) ax.pie( phylum_grouped['Absolute Count'], radius=0.4, colors=phylum_colors, wedgeprops={"width": 0.3, "edgecolor": "white"} ) # 添加选中分支的图例 if highlight_genus: handles = [Patch(color=plt.cm.tab20c.colors[idx], label=row['Species']) for idx, row in grouped_full.iterrows() if row['Genus'] == highlight_genus] ax.legend(handles=handles, loc='center left', bbox_to_anchor=(1, 0.5), title='选中的种') elif highlight_phylum: handles = [Patch(color=plt.cm.tab20b.colors[idx], label=row['Genus']) for idx, row in genus_grouped.iterrows() if row['Phylum'] == highlight_phylum] ax.legend(handles=handles, loc='center left', bbox_to_anchor=(1, 0.5), title='选中的属') ax.set(aspect="equal") plt.show()
调用示例
- 突出Firmicutes门:
plot_highlighted_nested_pie(highlight_phylum="Firmicutes")
- 突出Firmicutes门下的Staphylococcus属:
plot_highlighted_nested_pie(highlight_phylum="Firmicutes", highlight_genus="Staphylococcus")
数据示例
Phylum Genus Species Absolute Count 168 Proteobacteria Pseudomonas Unclassified 73745 152 Proteobacteria Klebsiella Unclassified 10777 190 Proteobacteria Unclassified Unclassified 4932 132 Proteobacteria Chromobacterium Unclassified 1840 84 Firmicutes Lysinibacillus boronitolerans 1780 104 Firmicutes Weissella ghanensis 1101 10 Actinobacteria Corynebacterium Unclassified 703 138 Proteobacteria Cupriavidus gilardii 586 93 Firmicutes Staphylococcus Unclassified 568 183 Proteobacteria Stenotrophomonas geniculata 542
内容的提问来源于stack exchange,提问作者Umar
相关产品推荐
相关产品推荐

