封裝接口直接利用DataFrame繪製百分比柱狀圖
手動反爬蟲:原博地址
知識梳理不易,請尊重勞動成果,文章僅發佈在CSDN網站上,在其他網站看到該博文均屬於未經作者授權的惡意爬取信息
1. 背景前言
最近打比賽遇到的問題有點多,在繪製了堆疊柱狀圖之後,隊長說不僅要看到具體的數量多少的堆疊圖,還要看到具體佔比的百分比柱狀圖,具體的樣例可參考靈魂畫圖,因此也就產生了繪製百分比柱狀圖的需求
2. 官方網址示例
2.1 matplotlib_percentage_stacked_bar_plot
裏面的代碼是真的長,完全屬於一步步進行“堆磚塊”然後才形成的百分比堆疊圖,最終得出圖結果如下:
2.2 percent-stacked-barplot
這個網址給出的代碼比第一個網址給出的較爲簡潔,但是本質上都是屬於一個個“磚塊”的疊加,代碼及生成的示例圖形如下:
# libraries
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
import pandas as pd
# Data
r = [0,1,2,3,4]
raw_data = {'greenBars': [20, 1.5, 7, 10, 5], 'orangeBars': [5, 15, 5, 10, 15],'blueBars': [2, 15, 18, 5, 10]}
df = pd.DataFrame(raw_data)
# From raw value to percentage
totals = [i+j+k for i,j,k in zip(df['greenBars'], df['orangeBars'], df['blueBars'])]
greenBars = [i / j * 100 for i,j in zip(df['greenBars'], totals)]
orangeBars = [i / j * 100 for i,j in zip(df['orangeBars'], totals)]
blueBars = [i / j * 100 for i,j in zip(df['blueBars'], totals)]
# plot
barWidth = 0.85
names = ('A','B','C','D','E')
# Create green Bars
plt.bar(r, greenBars, color='#b5ffb9', edgecolor='white', width=barWidth)
# Create orange Bars
plt.bar(r, orangeBars, bottom=greenBars, color='#f9bc86', edgecolor='white', width=barWidth)
# Create blue Bars
plt.bar(r, blueBars, bottom=[i+j for i,j in zip(greenBars, orangeBars)], color='#a3acff', edgecolor='white', width=barWidth)
# Custom x axis
plt.xticks(r, names)
plt.xlabel("group")
# Show graphic
plt.show()
→ 輸出的結果爲:(這個結果相對好一點)
2.3 Discrete distribution as horizontal bar chart
官方給出的示例圖是下面這樣的,看的第一眼是非常符合自己的要求的,而且還有不同的配色以及主題的標籤設置及文本文字標註
接着就看一下給出的源碼,如下,代碼封裝的挺不錯的,還有可以直接調用的函數,按照官方示例的代碼運行後的確可以實現上方的圖形效果(內心狂喜~~ )
import numpy as np
import matplotlib.pyplot as plt
category_names = ['Strongly disagree', 'Disagree',
'Neither agree nor disagree', 'Agree', 'Strongly agree']
results = {
'Question 1': [10, 15, 17, 32, 26],
'Question 2': [26, 22, 29, 10, 13],
'Question 3': [35, 37, 7, 2, 19],
'Question 4': [32, 11, 9, 15, 33],
'Question 5': [21, 29, 5, 5, 40],
'Question 6': [8, 19, 5, 30, 38]
}
def survey(results, category_names):
"""
Parameters
----------
results : dict
A mapping from question labels to a list of answers per category.
It is assumed all lists contain the same number of entries and that
it matches the length of *category_names*.
category_names : list of str
The category labels.
"""
labels = list(results.keys())
data = np.array(list(results.values()))
data_cum = data.cumsum(axis=1)
category_colors = plt.get_cmap('RdYlGn')(
np.linspace(0.15, 0.85, data.shape[1]))
fig, ax = plt.subplots(figsize=(9.2, 5))
ax.invert_yaxis()
ax.xaxis.set_visible(False)
ax.set_xlim(0, np.sum(data, axis=1).max())
for i, (colname, color) in enumerate(zip(category_names, category_colors)):
widths = data[:, i]
starts = data_cum[:, i] - widths
ax.barh(labels, widths, left=starts, height=0.5,
label=colname, color=color)
xcenters = starts + widths / 2
r, g, b, _ = color
text_color = 'white' if r * g * b < 0.5 else 'darkgrey'
for y, (x, c) in enumerate(zip(xcenters, widths)):
ax.text(x, y, str(int(c)), ha='center', va='center',
color=text_color)
ax.legend(ncol=len(category_names), bbox_to_anchor=(0, 1),
loc='lower left', fontsize='small')
return fig, ax
survey(results, category_names)
plt.show()
這個官方給出的代碼,看似可以解決問題,但是直接調用之後發現,並不是百分比柱狀圖的結果,比如這裏將數據稍作修改,更改數據如下,然後再運行代碼
results = {
'Question 1': [10, 15, 17, 32, 26],
'Question 2': [26, 22, 29, 10, 13],
'Question 3': [35, 37, 7, 2, 19],
'Question 4': [32, 11, 9, 15, 33],
'Question 5': [21, 29, 5, 5, 40],
'Question 6': [8, 190, 5, 30, 38] #在19後面加個0
}
→ 輸出的結果爲:(實際上這個圖本身就不是百分比堆疊柱狀圖,根據官方的名稱是離散分佈的水平柱形圖)
然後百度搜索相關的python繪製的百分比堆疊柱狀圖基本上都是進行一個個“壘砌”的,看到百度搜索返回的第一個結果,竟然把這個官方的示例聲稱爲自己封裝的函數,標題還是繪製百分比柱狀圖,誤導讀者(本人也是被忽悠了,直到看到官網示例和實際調試)
3. 問題解決
既然沒有現成的可以直接copy
的代碼,而且也沒有像繪製堆疊圖時候直接就擁有stacked
的參數,所以就必須自己手動整理了。前面的第三個示例代碼的框架可以直接拿過來用,就不用再進行編寫了,只需要修改部分內容即可
3.1 全部代碼
仿照之前的代碼形式,進行代碼改寫,只需要傳入DataFrame數據即可,如下
def percentage_bar(df):
labels = df.index.tolist() #提取分類顯示標籤
results = df.to_dict(orient = 'list') #將數值結果轉化爲字典
category_names = list(results.keys()) # 提取字典裏面的類別(鍵-key)
data = np.array(list(results.values())) #提取字典裏面的數值(值-value)
category_colors = plt.get_cmap('RdYlGn')(np.linspace(0.15, 0.85, data.shape[0]))
#設置佔比顯示的顏色,可以自定義,修改括號裏面的參數即可,如下
#category_colors = plt.get_cmap('hot')(np.linspace(0.15, 0.85, data.shape[0]))
fig, ax = plt.subplots(figsize=(12, 9)) #創建畫布,開始繪圖
ax.invert_xaxis()#這個可以通過設置df中columns的順序調整
ax.yaxis.set_visible(False) #設置y軸刻度不可見
ax.set_xticklabels(labels=labels, rotation=90) #顯示x軸標籤,並旋轉90度
ax.set_ylim(0,1) #設置y軸的顯示範圍
starts = 0 #繪製基準
for i, (colname, color) in enumerate(zip(category_names, category_colors)):
heights = data[i,: ]/ data.sum(axis =0) #計算出每次遍歷時候的百分比
ax.bar(labels, heights, bottom=starts, width=0.5,label=colname, color=color,edgecolor ='gray') # 繪製柱狀圖
xcenters = starts + heights/2 #進行文本標記位置的選定
starts += heights #核心一步,就是基於基準上的百分比累加
#print(starts) 這個變量就是能否百分比顯示的關鍵,可以打印輸出看一下
percentage_text = data[i,: ]/ data.sum(axis =0) #文本標記的數據
r, g, b, _ = color # 這裏進行像素的分割
text_color = 'white' if r * g * b < 0.5 else 'k' #根據顏色基調分配文本標記的顏色
for y, (x, c) in enumerate(zip(xcenters, percentage_text)):
ax.text(y, x, f'{round(c*100,2)}%', ha='center', va='center',
color=text_color, rotation = 90) #添加文本標記
ax.legend(ncol=len(category_names), bbox_to_anchor=(0, 1),
loc='lower left', fontsize='large') #設置圖例
return fig, ax #返回圖像
比如把剛剛示例中的數據轉化爲DataFrame數據,如下
然後將df
作爲參數直接傳遞到percentage_bar
函數中,看看結果輸出,內部雖然設置x軸標籤旋轉90度,但是在函數外邊還是可以直接調整其方向,這裏就是取消旋轉了,而且在最後的文本標記中採用的是百分比進行進標註的,這樣更能清晰的明確各方的佔比情況
3.2 代碼試錯
還是和最初一樣,在最後一行數據的19後面添加一個0,變成190,這裏順帶將y軸的刻度範圍調整爲(0,1.1),來查看是否符合百分比的要求,然後執行代碼及輸出結果如下
4. 實操檢驗
上面已經對函數進行了試錯,無誤後直接將函數放置到自己自定義的第三方模塊下,方便自己調用,關於這步的操作,可以參考博客將自定義常用的一些函數封裝成可以直接調用的模塊方法
4.1 實操測試
4.2 更換標籤
如果需要更換標籤顯示(df的標籤與列標題互換),只需要將df轉置即可,如下
4.3 部分標籤輸出
如果需要輸出部分的標籤,可以直接對df的columns進行切片即可(如果更換標籤index轉置之後也是同理),輸出如下,比如這裏只取兩個標籤
最後,梳理不易,碼字分享希望能夠幫助大家。