使用Python+Matplotlib制作時序動態(tài)圖
一、項目效果及說明
最終的效果如下圖所示
有幾點說明:
1. 圖中所展示的是從2000年到2021年全國人口最多的5個省市的人口數量、順序位次以及變化情況;
2. 圖中數據來源于“國家統(tǒng)計局”網站上的公開資料,但是,在最終使用的時候,并沒有進行數據的核對;因為,僅僅是為了展示Python+Matplotlib在制作動態(tài)圖的使用,而不是進行人口或者某個地區(qū)的研究;總之,本展示圖,不針對任何地區(qū),也不構成任何學術研究結論;
3. 圖表中的顏色是系統(tǒng)隨機選取的,后面的程序中有詳細的說明;顏色不構成任何指射或者含義,僅僅是為了區(qū)分不同的數據;
4. 圖表中僅僅展示了人口最多的5個省市區(qū),如有需要,還可以更多,都可以在程序中設置。
5. 此文只是為了記錄自己的學習過程,與同好交流,僅此而已。
二、項目環(huán)境說明
這個程序是在Jupyter Notebook下調試運行的,使用的Anaconda建立的虛擬環(huán)境,具體版本如下:
Python:3.9.16
Pandas:1.5.3
Matplotlib:3.7.1
Ipython:8.13.2
Jupyter Notebook:6.5.4
三、項目思路
利用Matplotlib制作動態(tài)圖,我自己知道的思路有兩個:
第一個,就是利用Matplotlib畫出每一幀圖片,保存成為“.png”或者是“jpg”格式,然后再使用imageio包,把這些文件按照順序生成一個“GIF”文件,也會產生動畫的效果;
第二個,就是利用Matplotlib中的Animation函數直接生成“GIF”格式的動畫文件。
因為沒有看過源碼,所以,僅從自己的感受來看,第一個方法需要產生大量的中間文件,而且生成出來的動畫,總有跳幀的感覺,變化不是十分平滑,也許是我制作水平的問題,也歡迎有高手指教。我測試過以后,決定使用第二個辦法。這個辦法不僅是Matplotlib“內生”的函數,而且還支持包括“MP4”在內的多種格式,還沒有中間文件的產生。
Matplotlib的Animation函數還有很多的子類和功能,這次使用的只是其中之一。函數簽名具體大致如下:
FuncAnimation(fig,func,frames,init_func,interval,blit)
其參數為:
① fig為繪制動圖的畫布名稱;
② func為自定義動畫函數,在本文中這個函數是draw_barchart(),在程序中會有詳細的功能說明;
③ frames為動畫長度,一次循環(huán)包含的幀數,在函數運行時,其值會傳遞給函數func中定義的形參,本文的函數只定義了一個形參“year”,其具體含義,程序中會有說明;
④ init_func為自定義開始幀,即初始化函數,可省略;
⑤ interval為更新頻率,以ms計算;
四、程序及相關說明
from IPython.display import HTML import pandas as pd import matplotlib as mpl import numpy as np import matplotlib.pyplot as plt import matplotlib.ticker as ticker import matplotlib.animation as animation import seaborn as sns import matplotlib.pyplot as plt import random
以上是程序中用到所有庫,都是必須的。
plt.rcParams['font.sans-serif']=['SimHei'] plt.rcParams['axes.unicode_minus']=False
這兩句之所以要特別拿出來說,是因為我曾經“踩坑”;如果沒有這兩個設置,那么,Matplotlib在生成圖像以后,標簽上的中文就會成為亂碼。需要說明的是,第一個設置在這里直接使用了“賦值”,也可以把兩個列表(list)加起來,但是,必須要讓“['SimHei']”在整個列表的最前面,否則,還是沒有效果。
result = prepare_data()
這個是我自己加載數據的模塊,無非就是“pd.read_csv”來讀取一個數據文件,因為,和本文的主旨無關,就不詳細說明了。把結果列出來(見下圖),并做適當說明,是因為,后文的程序中和數據被“讀出來”以后的存儲結果相關。
數據的“columns”的名字(name)是“地區(qū)”,“index”的名字(name)是“年份”,這個順序只是“統(tǒng)計年鑒”上的順序,并沒有進行處理。后文的排序,每次都是根據“index”來進行“行排序”的。
# 生成了一批顏色,給每個地區(qū)賦予了一個不同的顏色,以后,用到這個地區(qū),就用這個顏色來渲染 # 為了防止顏色比較接近,在顏色生成以后,隨機打亂了,效果好了一點 color = sns.husl_palette(len(result.columns),h=15/360, l=.65, s=1).as_hex() random.shuffle(color) colors = dict(zip(result.columns.tolist(),color))
這個就是前文所說的“隨機選擇顏色”的內容,利用random.shuffle,打亂顏色列表(list),然后再做成了一個字典(dict),以后,每個省市區(qū)都有了固定的顏色,每次畫圖的時候,都來這個字典中讀取顏色,然后再進行渲染。確保在每次生成動畫中,每個數據的顏色保持不變。
def draw_barchart(year): # 這里年份,只是一個順序號碼 # 一般是個浮點數,小數點后面代表的是:一個時點的圖像需要幾次變換到下一個時點, # 小數就是次數的倒數,用于計算每次,圖像的位移量 # 整數在這里代表的是,索引的順序 # 這個表示的是x坐標的大小,也就是顯示出來的數據的個數 N_Display=5 # 這個取整是用來確認現在圖像屬于哪個時點的 year1=int(year) # 加一表示,圖像下一個時點的順序號 year2=year1+1 # 這個就是上面的注釋里說的小數的部分 location_x=year-year1 # 對不同的時點的數據進行排序,才能得到不同順序下的“地區(qū)”也就是“省市”的名稱 result_sort1 = result.sort_values(by=result.index[year1], axis=1, ascending=False) result_sort2 = result.sort_values(by=result.index[year2], axis=1, ascending=False) # 截取前5個省份,并倒序,可以讓人口最多的省份在上,而其他的則是依次遞減 area1 = list(reversed(list((result_sort1.columns)[:5]))) area2 = list(reversed(list((result_sort2.columns)[:5]))) # 這個列表表達式是為了計算出,本期排名榜中的各個成員,在下一期中的位置 # 同時,計算出,相對現在位置的偏移量 # 如果沒有出現在下一期中,則設置為‘0' # 具體計算內容是:i, elem in enumerate(area1)表示當前某個位置的名稱和在列表中的順序 # elem in area2 判斷某個當前位置在下一個時點中是否存在, # 如果不存在,則設置為‘0',否則按照下面的公式計算 # i +1 +(area2.index(elem)-i)*location_x 兩個時點的位置之差,乘以偏移量,表示每幀動畫移動的位置 # i +1 +就是確定下一幀動畫時,圖像的位置,加一是因為坐標是從“1”開始的 # 這個列表就是每一幀動畫的x軸的坐標 diff = [i +1 +(area2.index(elem)-i)*location_x if elem in area2 else 0 for i, elem in enumerate(area1)] # 取出要顯示的“人口數”,第二時點的數據取出來也是沒有用的,為了對稱,就這么寫了 pop_num1 = list(reversed(list(result_sort1.iloc[year1, :5]))) pop_num2 = list(reversed(list(result_sort2.iloc[year2, :5]))) # 開始畫圖,顏色就是按照前面設置的字典來去的,這樣,每一個元素的顏色會始終不變 ax.clear() ax.barh(diff, pop_num1, color=[colors[x] for x in area1]) # 給每一個柱狀圖的最右邊加上名字和數字,包括設定大小等 for i, (value, name, x) in enumerate(zip(pop_num1, area1, diff)): ax.text(value, x, name, size=16, ha='right') ax.text(value, x, value, size=16, ha='left') # 在畫布右方添加年份 ax.text(1, 0.1, result.index[year1], transform=ax.transAxes, size=46, ha='right') # 設置坐標軸的大小,確保在動態(tài)圖的過程中,圖像的外殼不動,好看一點 ax.set_xlim(0,15000) ax.set_ylim(0.5,N_Display+0.5) ax.set_xticks(ticks=np.arange(0,14000,1000)) ax.set_yticks(ticks=np.arange(N_Display,0,-1)) ax.set_yticklabels(labels=np.arange(N_Display,0, -1)) ax.margins(0, 0.01) ax.text(0.2, 1.01, '2000——2021年間人口最多的地區(qū)', transform=ax.transAxes, size=26, weight='light', ha='left') # 取消了邊框 plt.box(False)
自我感覺,程序中的注釋大約應該比較清楚了。這里就不再贅述了。放一張圖,來表示一下函數的執(zhí)行效果。
接下來,開始制作動畫并保存
# 開始制作動畫 # 這個才是整個圖像的設置 fig, ax = plt.subplots(figsize=(8, 7)) plt.subplots_adjust(left=0.12, right=0.98, top=0.85, bottom=0.1) # 這個語句是產生動畫的關鍵,第一個參數就是“畫布”,第二個參數是畫圖的函數, # 第三個產生參數的函數,這里產生的參數自動送到第二個參數中的函數,作為實參, # 這里就是一連串的數字,表示數據的索引,以及幾次移動到位的次數的倒數 # 在后面的參數是“間隔”的毫秒數 animator = animation.FuncAnimation(fig, draw_barchart, frames=np.arange(0, 21, 0.1),interval=20) # 這個直接在網頁上生成動畫 # HTML(animator.to_jshtml()) # 這個是用一個文件名直接保存成動畫,系統(tǒng)自動識別文件后綴,還可以是“MP4”格式的 animator.save('result.gif')
這個過程大約需要一點時間,然后就能在目錄文件下發(fā)現保存完成的“GIF”圖片了。
至此,時序動態(tài)圖制作完成。
到此這篇關于使用Python+Matplotlib制作時序動態(tài)圖的文章就介紹到這了,更多相關Python Matplotlib時序動態(tài)圖內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
matplotlib.pyplot.matshow 矩陣可視化實例
這篇文章主要介紹了matplotlib.pyplot.matshow 矩陣可視化實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06