在模仿中精進數(shù)據(jù)可視化08:哪個省份的學(xué)子是熬夜冠軍?

添加微信號"CNFeffery"加入技術(shù)交流群
?1 簡介本文完整代碼及數(shù)據(jù)已上傳至我的
?Github倉庫https://github.com/CNFeffery/FefferyViz
大家好~熱衷于鉆研復(fù)刻優(yōu)秀數(shù)據(jù)可視化作品的費老師我??,最近的業(yè)余時間主要沉迷于撰寫「Python+Dash快速web應(yīng)用開發(fā)」系列文章,「在模仿中精進數(shù)據(jù)可視化」系列文章有兩個月沒更新了,今天繼續(xù)撿起來??。
我們今天要復(fù)刻的數(shù)據(jù)可視化作品,是前段時間在微博刷屏的下面這張網(wǎng)易數(shù)讀的作品,基于作業(yè)幫的用戶畫像數(shù)據(jù)對哪個地方的學(xué)習(xí)是“熬夜冠軍”進行了可視化表達:
圖1而下面我們就來基于matplotlib,復(fù)刻出這幅作品~
2.1 拆解主要視覺元素
其實這幅作品有些類似于我們這個系列文章開篇那一期「貝殼研究院」的圖,都是以半邊扇形為主體構(gòu)圖元素,在極坐標中對數(shù)據(jù)進行一系列表達,而今天的案例我們構(gòu)建扇形圖表選擇的是matplotlib中的「極坐標系」,非常簡單方便。
按照慣例,我們先來“肢解”一下這幅圖的主要構(gòu)圖元素:
- 「多子圖組合」
這幅作品中主要可以分為「主體扇形」子圖和右下角略微“出墻來”的「點綴扇形」子圖構(gòu)成,我們可以使用plt.subplots()創(chuàng)建底層畫板之后,再分別用fig.add_axes(rect, polar=True)來在不同位置插入不同大小的上述子圖;
- 「主體扇形底色交替填充」
首先我們可以觀察到在這幅圖的「主體扇形」右半圓中,背景色是由顏色交替切換的子扇形區(qū)域構(gòu)成的,且仔細觀察可以發(fā)現(xiàn)子扇形之間的交界處是有白色邊界線的。
這部分我們就可以使用到matplotlib中的fill_between()區(qū)域色彩填充功能,先生成指定數(shù)量的右半圓「等弧度」集合,其作用于「極坐標系」時傳入的第一個參數(shù)為「角度范圍」,第二個參數(shù)為「填充起點半徑值」,第三個參數(shù)為「填充終點半徑值」,白色交界線直接使用plot()繪制直線即可;
- 「極坐標柱狀圖與中央虛線」
在上述構(gòu)建的交替底色的基礎(chǔ)上,我們繼續(xù)來將每個地區(qū)的數(shù)值映射為極坐標柱狀圖的柱體高度,注意,這里的柱體顏色也是交替切換的,并且需要給每個柱體中央添加虛線點綴;
- 「主體扇形多規(guī)則文字標注」
在原作品中的「地區(qū)」及「深夜學(xué)習(xí)活躍指數(shù)」在角度旋轉(zhuǎn)上有三種規(guī)則方式,我們可以在一開始構(gòu)建數(shù)據(jù)時針對不同排名的地區(qū),打上用于區(qū)別類型的標簽,好在之后的繪圖過程中分別控制角度旋轉(zhuǎn)計算方式:
圖2
圖3至于其他的點綴元素,就不詳細說了,文章結(jié)尾的繪圖代碼里都有詳細的注釋。
2.2 完成復(fù)刻
在上述拆解的基礎(chǔ)上,我們就可以充分運用弧度跟角度之間的轉(zhuǎn)換,配合matplotlib和numpy來復(fù)刻出下面的效果啦,最后裁剪出的作品如下,是不是相當還原呢~:
圖4再放一張沒有拆掉“腳手架”(坐標軸線)的效果,你就會更加清楚我的構(gòu)圖邏輯了:

完整代碼如下,如有疑問歡迎在評論區(qū)與我進行交流:
#?生成每份子扇形區(qū)域的兩邊夾角
#?這里[::-1]是為了迎合matplotlib極坐標默認的角度位置
theta_group?=?(np.linspace(-0.5,?0.5,?32)*np.pi)[::-1]
#?創(chuàng)建圖床和原始axes對象
fig,?ax?=?plt.subplots(figsize=(10,?10))
############################
#?主體部分
############################
#?向原始圖床中插入極坐標系新axes對象
ax1?=?fig.add_axes([-0.5,?0,?1,?1],?polar=True)
#?繪制右半邊扇形區(qū)域最底層錯落的色帶填充
for?idx,?group?in?enumerate(fc.pairwise(theta_group)):
????
????#?當下標為偶數(shù)時,填充#e3effd色
????if?idx?%?2?==?0:
????????ax1.fill_between(group,?0.75,?3,?facecolor='#e3effd')
????#?當下標為奇數(shù)時,填充#fafbff色
????else:
????????ax1.fill_between(group,?0.75,?3,?facecolor='#fafbff')
#?繪制每份子扇形區(qū)域的中央虛線
for?idx,?group?in?enumerate(fc.pairwise(theta_group)):
????theta?=?(group[0]?+?group[1])?/?2
????ax1.plot([theta,?theta],?[0.75,?2.68],?linestyle='--',?color='#9fa0a0',??linewidth=0.25)
#?繪制極坐標柱狀圖,分別占據(jù)每份子扇形區(qū)域的對應(yīng)外擴長度
for?idx,?group?in?enumerate(fc.pairwise(theta_group)):
????theta?=?(group[0]?+?group[1])?/?2
????ax1.bar([theta],?[2.25*data.at[idx,?'深夜學(xué)習(xí)活躍指數(shù)']*0.01],?
????????????width=[np.pi?/?32],?bottom=0.75,?
????????????#?對下標分別為偶數(shù)與奇數(shù)的扇形繪制不同顏色
????????????facecolor='#6785f2'?if?idx?%?2?!=?0?else?'#7171fe',?
????????????edgecolor='white',?linewidth=0.1,?alpha=0.95,?zorder=9)
#?繪制子扇形區(qū)域之間交界處的白色邊界
for?theta?in?theta_group:
????ax1.plot([theta,?theta],?[1,?3],?color='white',??linewidth=0.2)
????
def?rotate_text(text,?group,?method):
????
????if?method?==?1:
????????return?text,?((group[0]?+?group[1])?*?0.5?/?np.pi)?*?180?-?90
????
????elif?method?==?2:
????????return?'\n'.join(list(text)),?((group[0]?+?group[1])?*?0.5?/?np.pi)?*?180
????
????elif?method?==?3:
????????return?text,?((group[0]?+?group[1])?*?0.5?/?np.pi)?*?180?-?90?+?180
????
#?地區(qū)+數(shù)值文字標注
for?idx,?group?in?enumerate(fc.pairwise(theta_group[::-1])):
????#?控制向data表的索引不越界
????if?idx?<?31:
????????
????????#?控制第一名的特殊字體顏色
????????if?data.at[30-idx,?'地區(qū)']?==?'江蘇':
????????????text_color,?value_color?=?'white',?'white'
????????else:
????????????text_color,?value_color?=?'black',?'#595757'
????????
????????#?利用前面定義的自編函數(shù)生成對應(yīng)的文字與旋轉(zhuǎn)角度
????????text,?angle?=?rotate_text(data.at[30-idx,?'地區(qū)'],?group,?method=data.at[30-idx,?'文字排布'])
????????
????????#?標注地區(qū)名稱
????????ax1.annotate(text,?xy=[(group[0]+group[1])?/?2,?2.925],?
?????????????????????va='center',?ha='center',?zorder=10,
?????????????????????color=text_color,
?????????????????????rotation=angle,
?????????????????????fontsize=11)
????????
????????#?標注深夜學(xué)習(xí)活躍指數(shù)
????????ax1.annotate(re.sub('\.$',?'',?str(data.at[30-idx,?'深夜學(xué)習(xí)活躍指數(shù)'])[:4]),?
?????????????????????xy=[(group[0]+group[1])?/?2,?2.79],?
?????????????????????va='center',?ha='center',?zorder=10,
?????????????????????rotation=angle,
?????????????????????fontsize=10,
?????????????????????color=value_color,
?????????????????????fontproperties='Times?New?Roman')
#?繪制外圍黑色虛線
ax1.plot(np.linspace(-0.38,?0.45,?1000)*np.pi,?[3.275]*1000,?
?????????linestyle='dashed',?color='#595655',?linewidth=0.75)
#?添加“0~2點學(xué)習(xí)活躍指數(shù)”標注
ax1.annotate('\n'.join(list('0~2點學(xué)習(xí)活躍指數(shù)')),?
?????????????xy=[0,?3.21],?
?????????????va='center',?
?????????????ha='center',?
?????????????ma='center',
?????????????zorder=10,
?????????????rotation=0,
?????????????fontsize=11,
?????????????color='black',
?????????????fontproperties='Microsoft?Yahei',
?????????????fontweight='bold',
?????????????bbox=dict(boxstyle="round",?fc="white",?ec="white",?alpha=1))
############################
#?右下角點綴
############################
#?向原始圖床中插入極坐標系新axes對象
ax2?=?fig.add_axes([0.25,?-0.7,?1,?1],?polar=True)
theta_group2?=?(np.linspace(-1.5,?-0.5,?32)*np.pi)[::-1]
#?繪制左半邊扇形區(qū)域最底層錯落的色帶填充
for?idx,?group?in?enumerate(fc.pairwise(theta_group2)):
????
????#?當下標為偶數(shù)時,填充#e3effd色
????if?idx?%?2?==?0:
????????ax2.fill_between(group,?0.75,?3,?facecolor='#e3effd')
????#?當下標為奇數(shù)時,填充#fafbff色
????else:
????????ax2.fill_between(group,?0.75,?3,?facecolor='#fafbff')
#?緊湊布局
fig.tight_layout(pad=0)
#?關(guān)閉所有axes的坐標軸線
ax.axis('off')
ax1.axis('off')
ax2.axis('off')
#?導(dǎo)出為圖片
fig.savefig('圖4.png',?dpi=500,?bbox_inches='tight',?pad_inches=0,?facecolor='white')
以上就是本文的全部內(nèi)容,歡迎在評論區(qū)與我進行交流討論~

加入知識星球【我們談?wù)摂?shù)據(jù)科學(xué)】
300+小伙伴一起學(xué)習(xí)!
· 推薦閱讀?·
秀啊,90行Python代碼開發(fā)個人云盤應(yīng)用
