نمودارهای متحرک در پایتون — از صفر تا صد

۱۳۶۳ بازدید
آخرین به‌روزرسانی: ۰۸ مهر ۱۴۰۲
زمان مطالعه: ۴ دقیقه
نمودارهای متحرک در پایتون — از صفر تا صد

Matplotlib و Seaborn دو مورد از کتابخانه‌های پایتون هستند که نمودارهایی با ظاهر عالی ایجاد می‌کنند. اما این نمودارها همگی استاتیک هستند و به تصویر کشیدن تغییرات مقادیر به روشی دینامیک و با ظاهر مناسب کاری دشوار است. شاید برای شما نیز پیش آمده باشد که بخواهید در یکی از ارائه‌های خود یا در طی یک ویدئو یا پست در رسانه‌های اجتماعی، تغییرات رخ داده در یک مجموعه داده را به وسیله یک ویدئو کلیپ کوتاه به نمایش بگذارید. در این نوشته روش ایجاد نمودارهای متحرک را به شما آموزش می‌دهیم. شاید تعجب کنید اگر بدانید که برای انجام این کار، حتی نیاز نیست که کتابخانه‌های Matplotlib و Seaborn با هر کتابخانه دیگری که برای ترسیم نمودار استفاده می‌کنید را تغییر دهید.

میزان مرگ و میر بر اثر هروئین در ایالات متحده امریکا. تهیه شده در پایتون با استفاده از Seaborn

در این نوشته از برخی داده‌هایی که در مورد بحران مواد مخدر در ایالات متحده وجود دارد، استفاده شده است. این داده‌ها به صورت عمومی از سوی موسسه ملی مبارزه با مواد مخدر ایالات متحده و GDC انتشار یافته است که می‌توانید از این لینک دانلود کنید.

کتابخانه‌های مورد نیاز

در این نوشته از Matplotlib به اضافه Seaborn برای ترسیم و از Numpy به همراه Panda برای مدیریت داده‌ها استفاده شده است. Matplotlib برای ایجاد انیمیشن (متحرک‌سازی) تابع‌هایی دارد که می‌توانیم از آن‌ها بهره بگیریم.

بنابراین با ما همراه باشید تا همه وابستگی‌ها را ایمپورت بکنیم.

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation

اینک برای آماده شدن جهت متحرک‌سازی داده‌ها تنها باید داده‌ها را بارگذاری کرده و درون قاب داده Pandas قرار دهیم. زمانی که می‌خواهیم چندین نمودار در مورد اوردوز (Overdose) مواد مخدر مختلف ایجاد کنیم، بهتر است یک تابع برای بارگذاری داده‌ها از ردیف‌های مطلوب خود بنویسیم.

overdoses = pd.read_excel('overdose_data_1999-2015.xls',sheetname='Online',skiprows =6)
def get_data(table,rownum,title):

data = pd.DataFrame(table.loc[rownum][2:]).astype(float)
    data.columns = {title}   
    return data

نت‌بوک ژوپیتر

بنابراین اینک می‌توانیم کار خود را آغاز کرده و انیمیشن بسازیم. قبل از هر کاری اگر از نت‌بوک ژوپیتر (Jupiter notebook) استفاده می‌کنید، بهتر است این سلول را با یک matplotlib notebook% آغاز کنید تا بتوانید انیمیشن را به راحتی در نت‌بوک خودتان ببینید و بدین ترتیب بی درنگ در نت‌بوک ذخیره می‌شود.

در این نوشته ما قصد داریم اعداد و ارقام اوردوز هروئین را با استفاده از تابع get_data از جدول بازیابی کرده و آن را در قاب داده Pandas که دو ستون دارد قرار دهیم.

%matplotlib notebook
title = 'Heroin Overdoses'
d = get_data(overdoses,18,title)
x = np.array(d.index)
y = np.array(d['Heroin Overdoses'])
overdose = pd.DataFrame(y,x)
#XN,YN = augment(x,y,10)
#augmented = pd.DataFrame(YN,XN)
overdose.columns = {title}

در مرحله بعد یک تابع writer را مقداردهی اولیه می‌کنیم که از ffmpeg استفاده کرده و ویدئویی را با نرخ فریم 20 fps و نرخ بیت 1800 ثبت می‌کند.

Writer = animation.writers['ffmpeg']
writer = Writer(fps=20, metadata=dict(artist='Me'), bitrate=1800)

اینک تصویری با تعدادی برچسب ایجاد می‌کنیم. اطمینان حاصل کنید که محدوده‌هایی برای محورهای x و y تعیین کرده‌اید و این محدوده‌ها چندان از بازه داده‌هایی که قصد دارید نمایش دهید، دور نیست.

fig = plt.figure(figsize=(10,6))
plt.xlim(1999, 2016)
plt.ylim(np.min(overdose)[0], np.max(overdose)[0])
plt.xlabel('Year',fontsize=20)
plt.ylabel(title,fontsize=20)
plt.title('Heroin Overdoses per Year',fontsize=20)

تابع animation

بخش کلیدی انیمیشن، تابع animation است که در آن تعریف می‌کنیم در هر فریم از ویدئو چه اتفاقی قرار است رخ دهد. در این تابع از i برای نمایش اندیس فریم از انیمیشن استفاده می‌کنیم. با استفاده از این اندیس می‌توانیم محدوده داده‌هایی که باید در فریم مشاهده شوند را تعیین می‌کنیم. پس از انجام این کار از Seaborn برای ترسیم خطی نمودار این گزیده داده‌ها استفاده می‌کنیم. دو خط آخر صرفاً به این منظور هستند که ظاهر نمودار تا حدودی زیباتر شود.

def animate(i):
    data = overdose.iloc[:int(i+1)] #select data range
    p = sns.lineplot(x=data.index, y=data[title], data=data, color="r")
    p.tick_params(labelsize=17)
    plt.setp(p.lines,linewidth=7)

برای شروع انیمیشن از matplotlib.animation.FuncAnimation استفاده می‌کنیم که در آن تابع animation لینک شده و تعداد فریم‌های موجود در انیمیشن تعریف می‌شود. از این رو frames در واقع تعداد دفعات فراخوانی (animate(i را تعریف می‌کند.

ani = matplotlib.animation.FuncAnimation(fig, animate, frames=17, repeat=True)

برای ذخیره‌سازی این انیمیشن به صورت فایل mp4 می‌توانید به سادگی تابع ()ani.save را فراخوانی کنید. اگر بخواهید پیش از ذخیره کردن انیمیشن نگاهی به آن بیندازید می‌توانید از ()plt.show استفاده کنید.

ani.save('HeroinOverdosesJumpy.mp4', writer=writer)

بدین ترتیب در این مرحله انیمیشن چیزی شبیه تصویر زیر خواهد بود:

هموارسازی حرکت

این نمودار متحرک کارکرد صحیحی دارد؛ اما مشکل آن صرفاً این است که پرش زیادی دارد. برای جلوگیری از این پرش‌ها به نقاط داده‌ای بیشتری بین نقاطی که هم اینک وجود دارند، نیاز داریم. بدین منظور می‌توانیم از تابع دیگری که augment می‌نامیم، استفاده کنیم.

def augment(xold,yold,numsteps):
     xnew = []
     ynew = []
    for i in range(len(xold)-1):
    difX = xold[i+1]-xold[i]
        stepsX = difX/numsteps
        difY = yold[i+1]-yold[i]
        stepsY = difY/numsteps
        for s in range(numsteps):
            xnew = np.append(xnew,xold[i]+s*stepsX)
            ynew = np.append(ynew,yold[i]+s*stepsY)
    return xnew,ynew

اینک کافی است از این تابع در مورد داده‌های خود استفاده کنیم و تعداد فریم‌ها را در تابع matplotlib.animation.FuncAnimation افزایش دهیم. در این تابع augment را با numsteps = 10 فراخوانی می‌کنیم. این بدان معنی است که داده‌های آموزشی خود را به 160 مورد افزایش می‌دهیم و باید فریم را به صورت frames=60 تعیین کنیم. نتیجه کار نموداری بسیار هموارتر است؛ اما همچنان لبه‌های تیزی در نقاطی که تغییرات مقدار وجود دارد، مشاهده می‌شود.

برای رهایی از شر این لبه‌های تیز می‌توانیم از یک تابع هموارسازی به صورت زیر استفاده کنیم:

def smoothListGaussian(listin,strippedXs=False,degree=5):
    window=degree*2-1
    weight=np.array([1.0]*window)
    weightGauss=[]
    for i in range(window):
        i=i-degree+1
        frac=i/float(window)
        gauss=1/(np.exp((4*(frac))**2))
        weightGauss.append(gauss)
    weight=np.array(weightGauss)*weight
    smoothed=[0.0]*(len(listin)-window)
    for i in range(len(smoothed)): smoothed[i]=sum(np.array(listin[i:i+window])*weight)/sum(weight)
return smoothed

همچنین می‌توانیم پارامترهای رنگ و سبک‌بندی بیشتری به نمودار خود اضافه کنیم تا نمودار ظاهری منحصر به فرد بیابد.

sns.set(rc={'axes.facecolor':'lightgrey', 'figure.facecolor':'lightgrey','figure.edgecolor':'black','axes.grid':False})

سخن پایانی

بدین ترتیب موفق شدیم یک نمودار متحرک مناسب را به دست آوریم. در این نوشته تابع انیمیشن matplotlib را بر روی یک مثال نمایش دادیم. البته شما می‌توانید از این تابع برای هر گونه نموداری که می‌خواهید متحرک‌سازی سازی کنید بهره بگیرید. با اندکی تغییر دادن پارامترها و نوع نمودار در تابع ()animate می‌توانید گزینه‌های بی نهایت متنوعی به دست آورید. امیدواریم از این آموزش بهره‌مند شده باشید و بتوانید از کارکردهای matplotlib به نحو مناسبی استفاده کنید.

اگر این نوشته مورد توجه شما واقع شده، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۷ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
towardsdatascience
۶ دیدگاه برای «نمودارهای متحرک در پایتون — از صفر تا صد»

سلام خسته نباشید.
چرا من با این read_excel() got an unexpected keyword argument ‘sheetname’ خطا مواجه میشوم؟؟

سلام خسته نباشید.
چرا من با این read_excel() got an unexpected keyword argument ‘sheetname’ خطا مواجه میشوم؟؟

سلام خطا زیر نمایش می دهد.
RuntimeError: Requested MovieWriter (ffmpeg) not available

الگوریتم هموارسازی باعث خراب شدن داده ها می شود بطوری که از سال 2006 به بعد دو منحنی (اصلی و هموار شده) کاملا از هم جدا می شوند. در کار پژوهشی جدی ، استفاده از آن توصیه نمی شود.

مشکل حل شد!
تنها اشکالی که به نظر می رسد در هموارسازی، تعداد نقاط از 160 به 151 کاهش می یابد. نیاز به کاهش نقاط ضروری به نظر نمی رسد!

سپاس از شما
فقط در قسمت آخر، متوجه نشدم که تابع smoothListGaussian را چگونه استفاده می کنیم و آرگومان ورودی آن ، listin ، چیست؟
ممنون می شوم توضیح دهید. آیا همان overdose است؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *