如何使用seaborn / matplotlib绘制动画散点图,以随着时间的变化改变固定或移动标记的颜色

时间:2019-04-22 04:48:18

标签: matplotlib animation seaborn scatter

我正试图绘制一段时间内几个空气污染传感器的读数。在第一种情况下,每个传感器的位置(纬度,长度)固定在散点图上,但在一天的不同时间或一年中的不同时间,颜色会根据污染程度而变化。 (在更高级的情况下,情况相同,但使用移动传感器,因此坐标将随时间变化并且颜色将随时间变化)。我在初始化和动画功能中使用'set_data'时遇到问题

我在SO和matplotlib文档的网上找到了大多数示例,这些示例与绘制动画折线图而不是散点图有关。标识为重复的链接涉及同一主题,但发现第一个选项很复杂,很难适应我自己相当简单的需求。给出的第二个解决方案给了我NameError: name 'xrange' is not defined,这已经证明是具有挑战性的,因为seaborn和散点图似乎与行的代码结构不同。因此,我问了这个问题。

我的最初目标是使用固定x和y的seaborn散点图,但根据污染程度在每个帧中更改色相(请参见下面的代码),但是设置初始化函数时似乎出现问题

AttributeError:“ AxesSubplot”对象没有属性“ set_data”

我随后尝试使用x,y和facecolors作为变量的matplotlib散点图,但存在相同的问题

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

# Suppose 3 fixed sensors, each with a result every day for 5 days
days = sorted(list(range(5))*3)
channel = (list(range(3)))*5
long = (random.sample(range(10, 20), 3))*5
lat = (random.sample(range(25, 35), 3))*5
colours = ['green', 'yellow', 'orange', 'red', 'brown']
colour = random.choices(colours, k=15)

# create dataframe
data = pd.DataFrame(list(zip(days, channel, long, lat, colour)), columns = ['day', 'sensor', 'x', 'y', 'colour'] )

# Set up the plot to be animated
fig, ax = plt.subplots(figsize=(10,10))
plt.xlim(10, 20)
plt.xlabel('Longitude',fontsize=20)
plt.ylim(25, 35)
plt.ylabel('Latitude',fontsize=20)
plt.title('Daily changes in pollution levels',fontsize=20)
p = sns.scatterplot([], [], hue= [], markers='o',s=500, ax=ax)
for i, txt in enumerate(data.sensor):
    ax.annotate(txt, xy=(data.x[i], data.y[i]), textcoords='offset points', xytext=(10,10), fontsize=20, weight='bold')

# initialization function 
def init(): 
    # creating an empty plot/frame 
    p.set_data([], [], []) 
    return p

# animation function 
def animate(i): 
    # x, y, hue values to be plotted 
    for j in range(0,3):
        x = data.loc[(data.day ==i) & (data.sensor ==j), 'x']
        y = data.loc[(data.day ==i) & (data.sensor ==j), 'y'] 
        hue = data.loc[(data.day ==i) & (data.sensor ==j), 'colour']  
    # set/update the x and y axes data 
        p.set_data(x, y, hue)  
    # return plot object 
    return p

# call the animator     
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=15, interval=20, blit=True) 

plt.show()

# save the animation as mp4 video file 
anim.save('sensors.mp4', writer = 'ffmpeg', fps = 5)

我怀疑还有其他错误,但是最大的障碍是错误消息AttributeError:当我尝试保存动画时,“ AxesSubplot”对象没有与初始化函数相关的属性“ set_data”,但是我找不到替代的方法这个。

非常感谢您对此错误或其他明显错误的建议

1 个答案:

答案 0 :(得分:0)

您在这里:

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

# Suppose 3 fixed sensors, each with a result every day for 5 days
days = sorted(list(range(5))*3)
channel = (list(range(3)))*5
long = (random.sample(range(10, 20), 3))*5
lat = (random.sample(range(25, 35), 3))*5
colours = ['green', 'yellow', 'orange', 'red', 'brown']
colour = random.choices(colours, k=15)

# create dataframe
data = pd.DataFrame(list(zip(days, channel, long, lat, colour)), columns = ['day', 'sensor', 'x', 'y', 'colour'] )
# print (data)## use this to better understand what is being plotted

# Set up the plot to be animated
# I'm using ax. here, but you could also just use plt.
fig, ax = plt.subplots(figsize=(5,5))
ax.set_xlim(10, 20)
ax.set_xlabel('Longitude',fontsize=20)
ax.set_ylim(25, 35)
ax.set_ylabel('Latitude',fontsize=20)
ax.set_title('Daily changes in pollution levels',fontsize=20)

for i, txt in enumerate(data.sensor):
    ax.annotate(txt, xy=(data.x[i], data.y[i]), textcoords='offset points', xytext=(10,10), fontsize=20, weight='bold')

# for convenience: define a function which prepares the data
def get_data(day=0,sensor_id=0):
    x = data.loc[(data.day ==day) & (data.sensor ==sensor_id), 'x']
    y = data.loc[(data.day ==day) & (data.sensor ==sensor_id), 'y'] 
    col = data.loc[(data.day ==day) & (data.sensor ==sensor_id), 'colour']  
    return x,y,col

# initialization function 
def init(): 
    # plot the first day (day=0) here:
    for j in range(3):
        x,y,col=get_data(day=0,sensor_id=j)
        scat = ax.scatter(x,y,c=col, s=100)
    return scat

# animation function 
def animate(i): 
    for j in range(0,3):        
        x,y,col=get_data(day=i,sensor_id=j)
        # print(i,col)## use this to understand "where" we are
        scat = ax.scatter(x,y,c=col, s=100)

    # return plot object 
    return scat

# call the animator     
# you are iterating over day=i, so you only have 5 frames here
# also you cant use blit=True here
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=5, interval=200,)

plt.show()