简体   繁体   English

如何 plot 带有注释的水平堆叠条

[英]How to plot a horizontal stacked bar with annotations

  • I used the example for Discrete distribution as horizontal bar chart example on matplotlib Discrete distribution as horizontal bar chart to create a chart showing share of the vote in Shropshire elections 2017.我在 matplotlib 上将离散分布示例用作水平条形图示例 将离散分布用作水平条形图来创建一个图表,显示 2017 年什罗普郡选举中的投票份额。

  • However, because I did not know how to manipulate the data I had to manually enter my data in the program which is clearly down to my own ignorance.然而,由于我不知道如何操作数据,我不得不在程序中手动输入我的数据,这显然是我自己的无知造成的。

  • I have the relevant data in a CSV file and can therefore load it as a dataframe.我在 CSV 文件中有相关数据,因此可以将其加载为 dataframe。

    • The CSV has a row for each ward, of which there are 63 and columns for the % vote for each party (Tory, LD, Labour, Green, Indep) so 5 substantive columns. CSV 每个选区有一行,其中有 63 列,每个党派(保守党、LD、工党、绿党、独立党)的 % 投票数列有 5 个实质性列。
  • I wanted advice as to how to change the form of the data so it resembles the input for this chart.我想要关于如何更改数据形式的建议,使其类似于此图表的输入。

  • I am not sure what it is but seems possibly a dictionary type with key and value:我不确定它是什么,但似乎可能是带有键和值的字典类型:

My data reads in part:我的数据部分内容如下:

import pandas as pd
import matplotlib.pyplot as plt

category_names = ['Labour', 'LD', 'Indep', 'Green', 'Tory']
results = {'Abbey': [16, 56, 4,0, 24],
           'Albrighton': [0, 0, 32, 0, 68],
           'Alveley & Claverley': [0, 25, 0, 0, 75],
           'Bagley': [30, 30, 0, 0, 40],
           'Battlefield': [34, 0, 0, 9, 57],
           'Bayston Hill, Column & Sutton': [53, 4, 3, 7, 33],
           'Belle Vue': [43,28,0,5,24]}


# setup dataframe using the dict provided in the OP
df = pd.DataFrame(results, index=category_names)

# display(df)
        Abbey  Albrighton  Alveley & Claverley  Bagley  Battlefield  Bayston Hill, Column & Sutton  Belle Vue
Labour     16           0                    0      30           34                             53         43
LD         56           0                   25      30            0                              4         28
Indep       4          32                    0       0            0                              3          0
Green       0           0                    0       0            9                              7          5
Tory       24          68                   75      40           57                             33         24

  • I am trying to get the data to be formatted like this directly from the csv file when entered as a pandas dataframe.当输入为 pandas dataframe 时,我试图直接从 csv 文件中获取要像这样格式化的数据。

  • Have tried the values method and the to_dict method and while they get data looking similar they are not quite correct.尝试了 values 方法和to_dict方法,虽然他们得到的数据看起来很相似,但它们并不完全正确。

    • I believe there is a need to divide the data into keys and values but that is where my knowledge hits its limits.我相信有必要将数据划分为键和值,但这就是我的知识达到极限的地方。

Option 1: 'Party' as the y-axis选项 1: 'Party'作为 y 轴

Using matplotlib from version 3.4.2从版本 3.4.2 使用matplotlib

  • Use matplotlib.pyplot.bar_label使用matplotlib.pyplot.bar_label
    • See this answer for additional details and examples with .bar_label .有关.bar_label的更多详细信息和示例,请参阅此答案
  • See the matplotlib: Bar Label Demo page for additional formatting options.有关其他格式选项,请参阅matplotlib:Bar Label 演示页面。
  • Tested in pandas 1.3.2 , python 3.8 1. , and matplotlib 3.4.2 1.pandas 1.3.2python 3.8 1.matplotlib 3.4.2 1. 中测试
    • 1. Minimum version required 1. 所需的最低版本
    • labels = [f'{v.get_width():.0f}' if v.get_width() > 0 else '' for v in c ] without using the assignment expression ( := ) labels = [f'{v.get_width():.0f}' if v.get_width() > 0 else '' for v in c ]不使用赋值表达式 ( := )
  • Use .get_height() for vertical bars..get_height()用于垂直条。
ax = df.plot.barh(stacked=True, cmap='tab10', figsize=(16, 10))

for c in ax.containers:

    # format the number of decimal places and replace 0 with an empty string
    labels = [f'{w:.0f}' if (w := v.get_width()) > 0 else '' for v in c ]
    
    ax.bar_label(c, labels=labels, label_type='center')

Using matplotlib before version 3.4.2使用 3.4.2 之前matplotlib

  • Extract the .patch components in a loop, and then only plot annotations for values greater than 0.循环提取.patch组件,然后仅对大于 0 的值进行 plot 注释。
# plot 
ax = df.plot.barh(stacked=True, cmap='tab10', figsize=(16, 10))

# annotations:
for p in ax.patches:
    left, bottom, width, height = p.get_bbox().bounds
    if width > 0:
         ax.annotate(f'{width:0.0f}', xy=(left+width/2, bottom+height/2), ha='center', va='center')

在此处输入图像描述

Option 2: 'Ward' as the y-axis选项 2: 'Ward'作为 y 轴

  • Use pandas.DataFrame.T to swap the Index and Columns使用pandas.DataFrame.T交换IndexColumns
    • 'Ward' will now be the index and 'Party' will be the columns 'Ward'现在将是索引, 'Party'将是列
# transpose df from the OP so Party is the in the columns and Ward is the index
dft = df.T

# display(dft)
                               Labour  LD  Indep  Green  Tory
Abbey                              16  56      4      0    24
Albrighton                          0   0     32      0    68
Alveley & Claverley                 0  25      0      0    75
Bagley                             30  30      0      0    40
Battlefield                        34   0      0      9    57
Bayston Hill, Column & Sutton      53   4      3      7    33
Belle Vue                          43  28      0      5    24

Using matplotlib from version 3.4.2从版本 3.4.2 使用matplotlib

# plot
ax = df.T.plot.barh(stacked=True, figsize=(16, 10))

plt.legend(loc='center left', bbox_to_anchor=(1.0, 0.5))

# annotations:
for c in ax.containers:
    
    # format the number of decimal places and replace 0 with an empty string
    labels = [f'{w:.0f}' if (w := v.get_width()) > 0 else '' for v in c ]
    
    ax.bar_label(c, labels=labels, label_type='center')

Using matplotlib before version 3.4.2使用 3.4.2 之前matplotlib

# plot
ax = dft.plot.barh(stacked=True, figsize=(16, 10))

plt.legend(loc='center left', bbox_to_anchor=(1.0, 0.5))

# annotations:
for p in ax.patches:
    left, bottom, width, height = p.get_bbox().bounds
    if width > 0:
         ax.annotate(f'{width:0.0f}', xy=(left+width/2, bottom+height/2), ha='center', va='center')

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM