首页 > 解决方案 > 如何使用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}}

有了上面的数据,我想用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 figax对象使用不正确。但是,在函数中,我在每次迭代后draw_swarmplot重置fig和对象。ax尽管如此,我仍然需要在这两个函数之外进行初始化figax以免出现关于ax对象的错误。因此,我的问题是如何同时引用figax应该被引用,以及我遗漏了什么使我的代码无法按预期工作?

标签: pythonmatplotlibmoviepyswarmplot

解决方案


您的figax变量的范围受变量和范围文档的变量范围跨越边界部分的约束。具体相关,

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

请注意,警告“除非已经定义了同名的变量”实际上仅限于局部变量。正如示例中进一步阐明的那样

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)

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

输出

当然,如果您没有在每次迭代中使用清除函数之一覆盖figureand实例,则所有这些都可以避免: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']))

这也将产生上面显示的输出。


推荐阅读