data_dict = {'x': {(0, 0): 3760.448435678077,
(0, 12): 4851.68102541007,
(0, 2226): 5297.61518907981,
(0, 2479): 4812.134249142693,
(0, 2724): 4756.5295525777465,
(0, 3724): 3760.448435678077,
(0, 4598): 4763.265306122449,
(0, 4599): 5155.102040816327,
(0, 4600): 5191.836734693878,
(1, 0): 3822.238314568112,
(1, 12): 4856.1910324326145,
(1, 2226): 5304.678983022428,
(1, 2479): 4815.435125468252,
(1, 2724): 4761.889691080804,
(1, 3724): 3768.2889580569245,
(1, 4598): 4768.908833716798,
(1, 4599): 5159.900248610219,
(1, 4600): 5198.053973405109,
(2, 0): 3880.710643551325,
(2, 12): 4860.815600807341,
(2, 2226): 5311.463202354179,
(2, 2479): 4818.773880246848,
(2, 2724): 4767.173347353905,
(2, 3724): 3775.726731574509,
(2, 4598): 4774.4490391107765,
(2, 4599): 5164.871993247027,
(2, 4600): 5203.992167369707,
(3, 0): 3936.0658098882136,
(3, 12): 4865.552525110625,
(3, 2226): 5317.975399527148,
(3, 2479): 4822.152977310737,
(3, 2724): 4772.381182203056,
(3, 3724): 3782.7732491325282,
(3, 4598): 4779.888739700325,
(3, 4599): 5170.010331730589,
(3, 4600): 5209.661736027094,
(4, 0): 3988.491290089178,
(4, 12): 4870.399599918841,
(4, 2226): 5324.223126993423,
(4, 2479): 4825.574880492175,
(4, 2724): 4777.513856434266,
(4, 3724): 3789.4400036326792,
(4, 4598): 4785.230752881375,
(4, 4599): 5175.308321064745,
(4, 4600): 5215.073098816687,
(5, 0): 4038.1625164006414,
(5, 12): 4875.354619808369,
(5, 2226): 5330.2139372050915,
(5, 2479): 4829.04205362342,
(5, 2724): 4782.572030853543,
(5, 3724): 3795.7384879766646,
(5, 4598): 4790.477896049872,
(5, 4599): 5180.7590182533295,
(5, 4600): 5220.2366751779045,
(6, 0): 4085.2436834766995,
(6, 12): 4880.415379355583,
(6, 2226): 5335.955382614236,
(6, 2479): 4832.55696053673,
(6, 2724): 4787.5563662668965,
(6, 3724): 3801.6801950661807,
(6, 4598): 4795.632986601749,
(6, 4599): 5186.355480300186,
(6, 4600): 5225.16288455017,
(7, 0): 4129.888499451394,
(7, 12): 4885.5796731368655,
(7, 2226): 5341.4550156729465,
(7, 2479): 4836.122065064363,
(7, 2724): 4792.4675234803335,
(7, 3724): 3807.2766178029274,
(7, 4598): 4800.698841932945,
(7, 4599): 5192.090764209151,
(7, 4600): 5229.8621463729005,
(8, 0): 4172.2408853249335,
(8, 12): 4890.845295728588,
(8, 2226): 5346.720388833307,
(8, 2479): 4839.739831038576,
(8, 2724): 4797.306163299865,
(8, 3724): 3812.539249088603,
(8, 4598): 4805.678279439399,
(8, 4599): 5197.9579269840615,
(8, 4600): 5234.344880085516,
(9, 0): 4212.43562629731,
(9, 12): 4896.210041707129,
(9, 2226): 5351.759054547402,
(9, 2479): 4843.412722291625,
(9, 2724): 4802.072946531498,
(9, 3724): 3817.479581824906,
(9, 4598): 4810.574116517045,
(9, 4599): 5203.950025628757,
(9, 4600): 5238.621505127434,
(10, 0): 4250.598978423163,
(10, 12): 4901.671705648866,
(10, 2226): 5356.578565267323,
(10, 2479): 4847.1432026557695,
(10, 2724): 4806.7685339812415,
(10, 3724): 3822.1091089135375,
(10, 4598): 4815.389170561825,
(10, 4599): 5210.060117147079,
(10, 4600): 5242.702440938076,
(11, 0): 4286.849233720921,
(11, 12): 4907.228082130176,
(11, 2226): 5361.186473445152,
(11, 2479): 4850.933735963267,
(11, 2724): 4811.393586455103,
(11, 3724): 3826.4393232561943,
(11, 4598): 4820.126258969674,
(11, 4599): 5216.281258542863,
(11, 4600): 5246.5981069568625,
(12, 0): 4321.297246645838,
(12, 12): 4912.876965727434,
(12, 2226): 5365.590331532978,
(12, 2479): 4854.786786046375,
(12, 2724): 4815.948764759092,
(12, 3724): 3830.481717754576,
(12, 4598): 4824.788199136532,
(12, 4599): 5222.606506819949,
(12, 4600): 5250.318922623211,
(13, 0): 4354.046924629284,
(13, 12): 4918.6161510170205,
(13, 2226): 5369.797691982883,
(13, 2479): 4858.70481673735,
(13, 2724): 4820.434729699218,
(13, 3724): 3834.247785310383,
(13, 4598): 4829.377808458337,
(13, 4599): 5229.028918982174,
(13, 4600): 5253.875307376542,
(14, 0): 4385.195685194348,
(14, 12): 4924.443432575308,
(14, 2226): 5373.816107246958,
(14, 2479): 4862.690291868448,
(14, 2724): 4824.852142081489,
(14, 3724): 3837.7490188253105,
(14, 4598): 4833.897904331024,
(14, 4599): 5235.541552033379,
(14, 4600): 5257.277680656276,
(15, 0): 4414.834881979362,
(15, 12): 4930.356604978678,
(15, 2226): 5377.653129777288,
(15, 2479): 4866.74567527193,
(15, 2724): 4829.201662711913,
(15, 3724): 3840.9969112010617,
(15, 4598): 4838.351304150532,
(15, 4599): 5242.137462977402,
(15, 4600): 5260.53646190183,
(16, 0): 4443.050201835423,
(16, 12): 4936.353462803505,
(16, 2226): 5381.316312025957,
(16, 2479): 4870.873430780051,
(16, 2724): 4833.483952396497,
(16, 3724): 3844.002955339333,
(16, 4598): 4842.740825312798,
(16, 4599): 5248.809708818081,
(16, 4600): 5263.662070552626,
(17, 0): 4469.92203501027,
(17, 12): 4942.4318006261665,
(17, 2226): 5384.813206445053,
(17, 2479): 4875.07602222507,
(17, 2724): 4837.699671941253,
(17, 3724): 3846.7786441418243,
(17, 4598): 4847.069285213763,
(17, 4599): 5255.551346559254,
(17, 4600): 5266.664926048083,
(18, 0): 4495.525820288381,
(18, 12): 4948.589413023038,
(18, 2226): 5388.151365486662,
(18, 2479): 4879.3559134392435,
(18, 2724): 4841.849482152186,
(18, 3724): 3849.3354705102342,
(18, 4598): 4851.339501249362,
(18, 4599): 5262.355433204761,
(18, 4600): 5269.555447827619,
(19, 0): 4518.893324127626,
(19, 12): 4954.824094570498,
(19, 2226): 5391.338341602872,
(19, 2479): 4883.71556825483,
(19, 2724): 4845.934043835307,
(19, 3724): 3851.6849273462612,
(19, 4598): 4855.554290815534,
(19, 4599): 5269.21502575844,
(19, 4600): 5272.344055330656}}
使用上面的数据,我想使用matplotlib
和moviepy
制作动画的群体图。但是,在下面的每一帧代码中,我都得到了额外的要点,但是保留了旧的要点。
import numpy as np
import pandas as pd
from scipy.stats import gaussian_kde
from matplotlib import pyplot as plt
from moviepy.editor import VideoClip
from moviepy.video.io.bindings import mplfig_to_npimage
fps = 10
df = pd.DataFrame(data_dict)
fig, ax = plt.subplots(1, 1)
def swarm_plot(x):
kde = gaussian_kde(x)
density = kde(x) # estimate the local density at each datapoint
# ax.clear()
jitter = np.random.rand(*x.shape) - .5
# scale the jitter by the KDE estimate and add it to the centre x-coordinate
y = 1 + (density * jitter * 1000 * 2)
ax.scatter(x, y, s = 30, c = 'g')
# plt.axis('off')
return fig
def draw_swarmplot(t):
f = int(t * fps)
fig, ax = plt.subplots(1, 1)
dff = df.loc[f]
return mplfig_to_npimage(swarm_plot(dff['x']))
anim = VideoClip(lambda x: draw_swarmplot(x), duration=2)
anim.to_videofile('swarmplot.mp4', fps=fps)
结果,所有点都累积在动画中。我相信是由于matplotlib
fig
和ax
对象使用不正确造成的。但是,在draw_swarmplot
函数中,每次迭代后我都会重置fig
和ax
对象。不过,我仍然需要在两个函数之外都初始化fig
和ax
,以免出现关于ax
对象的错误。因此,我的问题是应该同时引用fig
和ax
以及我错过了什么导致我的代码无法正常工作?
答案 0 :(得分:1)
fig
和ax
变量的作用域受Variable Scope文档的Crossing Boundaries和Variables and Scope部分的约束。特别相关,
当我们在函数内部使用赋值运算符(=)时,其默认行为是创建一个新的局部变量-除非在局部范围内已经定义了具有相同名称的变量。
请注意,警告“ 除非已经定义了相同名称的变量”,实际上仅限于 local 变量。正如example
中进一步阐明的那样a = 0
def my_function():
a = 3
print(a)
my_function()
print(a)
将输出
3
0
这是因为
默认情况下,赋值语句在本地范围内创建变量。因此,函数内部的赋值不会修改全局变量[...]
如果要在函数中修改全局变量,请使用关键字global
,如@iliar的回答所述。
但是不建议这样做-
请注意,从内部函数访问全局变量通常是非常不好的做法,而更糟的做法是修改它们。这使得很难将我们的程序安排成逻辑封装的部分,而这些部分不会以意想不到的方式相互影响。如果函数需要访问某些外部值,则应将值作为参数传递给函数。 [...]
两个选择是
class
fig
和ax
传递到draw_swarmplot()
中。前者
class SwarmPlot:
def __init__(self):
self.fig, self.ax = plt.subplots(1, 1)
anim = VideoClip(lambda x: self.draw_swarmplot(x, self.fig, self.ax), duration=2)
anim.to_videofile('swarmplot.mp4', fps=fps)
def swarm_plot(self, x):
kde = gaussian_kde(x)
density = kde(x) # estimate the local density at each datapoint
jitter = np.random.rand(*x.shape) - .5
y = 1 + (density * jitter * 1000 * 2)
self.ax.scatter(x, y, s = 30, c = 'g')
return self.fig
def draw_swarmplot(self, t, fig, ax):
self.fig, self.ax = plt.subplots(1, 1)
f = int(t * fps)
dff = df.loc[f]
return mplfig_to_npimage(self.swarm_plot(dff['x']))
S = SwarmPlot()
后者
def draw_swarmplot(t, fig, ax):
fig, ax = plt.subplots(1, 1)
f = int(t * fps)
dff = df.loc[f]
return mplfig_to_npimage(swarm_plot(dff['x']))
anim = VideoClip(lambda x: draw_swarmplot(x, fig, ax), duration=2)
对于这样的简单情况,我可能会偏爱后者,但在更复杂的情况下,前者可能更可取。两者似乎都能正确生成所需的输出:
当然,如果您没有在每次迭代中覆盖figure
和axis
实例,而使用清除功能之一,则可以避免所有这些情况:
plt.cla()
清除当前轴plt.clf()
清除当前数字fig.clear()
清除图形fig
(如果plt.clf()
是当前图形,则等效于fig
)ax.clear()
清除轴ax
(如果plt.cla()
是ax
,则相当于ax.clear()
当前轴) plt.cla()
或fig, ax = plt.subplots(1, 1)
def swarm_plot(x):
kde = gaussian_kde(x)
density = kde(x) # estimate the local density at each datapoint
jitter = np.random.rand(*x.shape) - .5
y = 1 + (density * jitter * 1000 * 2)
ax.clear()
ax.scatter(x, y, s = 30, c = 'g')
return fig
def draw_swarmplot(t):
f = int(t * fps)
dff = df.loc[f]
return mplfig_to_npimage(swarm_plot(dff['x']))
可能是最合适的,并按如下方式使用
EINVAL
这还将产生上面显示的输出。
答案 1 :(得分:0)
def draw_swarmplot(t):
f = int(t * fps)
fig, ax = plt.subplots(1, 1)
dff = df.loc[f]
应该是
def draw_swarmplot(t):
global fig,ax
f = int(t * fps)
fig, ax = plt.subplots(1, 1)
dff = df.loc[f]
否则,它将初始化fig
函数本地的新对象ax
和draw_swarmplot
。为了分配给全局变量,您需要将它们声明为global
。
答案 2 :(得分:0)
代码的问题在于,由于fig, ax = plt.subplots(1, 1)
是在创建每一帧时调用的,因此您在每个帧上都使用draw_swarmplot(t)
重新创建了新图形。
要解决此问题,您只需在函数外部创建一次图形即可。为避免所有点累积,每次制作新帧时,请使用àx.clear()
清除轴。
由于代码不是很长,所以我将所有内容分组为一个make_frame(t)
函数。我认为这使代码更易于理解,但是您可以肯定地将其分成两个函数。我还添加了几行以防固定轴限制,而不是每帧不同。完整代码:
import numpy as np
import pandas as pd
from scipy.stats import gaussian_kde
from matplotlib import pyplot as plt
from moviepy.editor import VideoClip
from moviepy.video.io.bindings import mplfig_to_npimage
fps = 10
df = pd.DataFrame(data_dict)
fig, ax = plt.subplots()
# if you want to have fixed axis limits, use these
x_min = float(df.min())
x_max = float(df.max())
# for y values, set the values by eye inspection of the video
# since y values are randomnly draw at the creation of each frame
y_min = 0
y_max = 10
def make_frame(t) :
# select series
i = int(t * fps)
x = df.loc[i]['x']
# prepare data to plot
kde = gaussian_kde(x)
density = kde(x) # estimate the local density at each datapoint
jitter = np.random.rand(*x.shape) - .5
# scale the jitter by the KDE estimate and add it to the centre x-coordinate
y = 1 + (density * jitter * 1000 * 2)
# plot
ax.clear()
ax.scatter(x, y, s = 30, c = 'g')
# comment next two lines if you don't want fixed axis limits
ax.set_xlim(x_min, x_max)
ax.set_ylim(0, 2)
return mplfig_to_npimage(fig)
anim = VideoClip(make_frame, duration=2)
anim.to_videofile('swarmplot.mp4', fps=fps)
# uncomment to display in jupyter notebook
#anim.ipython_display(fps=fps, loop=True, autoplay=True)