如何正确地参考带有电影效果的动画的无花果和斧头

时间:2019-11-10 10:58:07

标签: python matplotlib moviepy

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

使用上面的数据,我想使用matplotlibmoviepy制作动画的群体图。但是,在下面的每一帧代码中,我都得到了额外的要点,但是保留了旧的要点。

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 figax对象使用不正确造成的。但是,在draw_swarmplot函数中,每次迭代后我都会重置figax对象。不过,我仍然需要在两个函数之外都初始化figax,以免出现关于ax对象的错误。因此,我的问题是应该同时引用figax以及我错过了什么导致我的代码无法正常工作?

3 个答案:

答案 0 :(得分:1)

figax变量的作用域受Variable Scope文档的Crossing BoundariesVariables and Scope部分的约束。特别相关,

  

当我们在函数内部使用赋值运算符(=)时,其默认行为是创建一个新的局部变量-除非在局部范围内已经定义了具有相同名称的变量。

请注意,警告“ 除非已经定义了相同名称的变量”,实际上仅限于 local 变量。正如example

中进一步阐明的那样
a = 0
def my_function():
    a = 3
    print(a)

my_function()
print(a)

将输出

3
0

这是因为

  

默认情况下,赋值语句在本地范围内创建变量。因此,函数内部的赋值不会修改全局变量[...]

如果要在函数中修改全局变量,请使用关键字global,如@iliar的回答所述。

但是不建议这样做-

  

请注意,从内部函数访问全局变量通常是非常不好的做法,而更糟的做法是修改它们。这使得很难将我们的程序安排成逻辑封装的部分,而这些部分不会以意想不到的方式相互影响。如果函数需要访问某些外部值,则应将值作为参数传递给函数。 [...]

两个选择是

  • 将其作为class
  • figax传递到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)

对于这样的简单情况,我可能会偏爱后者,但在更复杂的情况下,前者可能更可取。两者似乎都能正确生成所需的输出:

Output

当然,如果您没有在每次迭代中覆盖figureaxis实例,而使用清除功能之一,则可以避免所有这些情况:

  • 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函数本地的新对象axdraw_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)

enter image description here