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

How to plot animated scatterplot using seaborn / matplotlib changing colour of fixed or moving marker over time

我正在尝试绘制多个空气污染传感器随时间变化的读数。在第一种情况下,每个传感器的位置(纬度、经度)在散点图上是固定的,但在一天中的不同时间或一年中的不同时间,颜色会根据污染程度而变化。 (在更高级的情况下,情况相同,但使用移动传感器,因此坐标会改变,颜色会随时间改变)。我 运行 在初始化和动画函数

中使用 'set_data' 遇到问题

我在 SO 和 matplotlib 文档上在线找到的大多数示例都与绘制动画线图而不是散点图有关。 link 被识别为重复项涵盖了相同的主题,但发现第一个选项很复杂并且很难适应我自己相当简单的需求。给出的第二个解决方案给了我 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)

我怀疑还有其他错误,但最大的障碍是当我尝试保存动画但我不能寻找替代方法。

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

给你:

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()