Трюк с matplotlib: заставить визуализацию двигаться

Python визуализация данных

автор:Viviane Kakerbeck

Скомпилировать:weakish

Примечание редактора: на самом деле в matplotlib есть малоизвестная функция animation.FuncAnimation, которая может принимать функции анимации, которые вы пишете для создания анимации. Вивиан Какербек демонстрирует использование этой функции на примере и знакомит с методами, позволяющими сделать анимацию более красивой за счет добавления данных и сглаживания по Гауссу.

Смерти от передозировки героина в США, созданные с использованием морских животных

Python matplotlib и seaborn — очень полезные библиотеки для построения графиков. Но все они создают статические изображения, и динамически и красиво описать изменения значений данных сложно. Было бы здорово, если бы ваша следующая презентация или следующий пост в блоге могли показать эволюцию ваших данных в анимационной графике? Более того, вы можете продолжать использовать matplotlib, seaborn или любую другую библиотеку, которая вам нравится.

Недавно я сделал анимационную графику для документального фильма об опиоидном кризисе в Америке, поэтому в этом посте я буду использовать соответствующие данные. Данные взяты из общедоступных данных Национального института по борьбе со злоупотреблением наркотиками и CDC, и их можно загрузить с:woohoo.drug abuse.gov/sites/немецкий-французский-U…

В этой статье для рисования графиков будут использоваться matplotlib и seaborn, а для обработки данных — numpy и pandas. 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 DataFrame. Я также написал вспомогательную функцию, которая загружает данные из интересующей строки, которые позже будут использоваться для построения графика.

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

Готово, вот основная часть этой статьи, как рисовать анимацию.

Прежде всего, если вы, как и я, используете блокнот jupyter, то я рекомендую вам использовать команду %matplotlib блокнота, чтобы вы могли просматривать анимацию прямо в блокноте, не дожидаясь сохранения для просмотра.

Я использовал вспомогательную функцию get_data, которую я написал ранее, чтобы получить передозировку героина, и упаковал ее в pandas DataFrame с двумя столбцами, один для года и один для передозировки.

%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}

Затем мы инициализируем писатель (писатель) для записи 20 кадров в секунду (битрейт 1800) на основе ffmpeg. Конечно, вы можете настроить эти параметры по мере необходимости.

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)

Ключом к созданию анимации является определение функции анимации, которая определяет, что происходит в каждом кадре видео. Здесь я представляет индекс кадра анимации. Вы можете выбрать диапазон данных, которые будут видны в i-frame. После этого я рисую выбранные данные, используя линейный график Seaborn. В последних двух строках я изменил некоторые размеры, чтобы график выглядел лучше.

def animate(i):
    data = overdose.iloc[:int(i+1)]  # 选定数据范围
    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, чтобы определить, сколько кадров должна содержать анимация, то есть определить, как часто вызывать animate(i) через параметрframes.

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

Затем просто вызовите ani.save(), чтобы сохранить анимацию в виде файла mp4. Если вы хотите увидеть результат перед сохранением, вы можете использовать plt.show().

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

Что ж, посмотрим на эффект.

Выглядит нормально, но немного нервничает. Чтобы смягчить явление джиттера, мы можем вставить в существующие данные какие-то промежуточные значения и сгладить их. Для этого мы определяем следующие функции увеличения данных:

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. Здесь я использую параметр numsteps=10 при вызове функции аугментации, то есть увеличиваю количество точек данных до 160, и соответственно количество кадров устанавливается равным кадрам=160. После увеличения данных результат выглядит намного более гладким, но там, где значение данных изменяется, кривая все еще имеет несколько острых углов, что выглядит не очень хорошо.

Чтобы изображение выглядело лучше, мы реализуем функцию сглаживания по Гауссу:

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 smoothe 

Я не буду обсуждать здесь детали сглаживания по Гауссу, заинтересованные читатели могут прочитать эту статью:Woohoo.Так называемый harden.com/I'm Боюсь/2008-11-…

Наконец, мы немного подкорректировали цвет и стиль и получили динамическую графику в начале статьи:

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

В этой статье на примере показано использование функций анимации matplotlib. Конечно, вы можете использовать его для любой графики, которую хотите анимировать. Просто настроив параметры и тип графика в функции animate(), возможности безграничны.

Надеюсь, вам понравится вся функциональность matplotlib и вы будете использовать ее с пользой. Кроме того, если вам интересен документальный фильм, о котором я упоминал ранее, вы можете посмотреть его на YouTube:youtu.be/7xrvuSDLHiY