【问题标题】:Fast indexing / value setting in a list of objects对象列表中的快速索引/值设置
【发布时间】:2014-10-03 03:42:31
【问题描述】:

我正在编写一个 python 包来将语音信号分解为其频谱谐波,以便它们中的每一个都可以表示为复指数。代码的(非常)简化描述如下:

# -*- coding: utf-8 -*-

import numpy as np

class HarmObj(object):

    # Creates an instance for each harmonic.
    def __init__(self, file_size):
        self.mag = np.zeros((file_size))
        self.phi = np.zeros((file_size))
        self.freq = np.zeros((file_size))

class SignalObj(object):

# Creates an instance for the synthesized speech signal, which is formed by the
# harmonics.
    def __init__(self, n_harm, file_size):
        self.n_harm = n_harm
        self.size = file_size
        self.harmonics = [HarmObj(self.size) for i in xrange(self.n_harm)]

    def update_values(self, a, freq, frame):
        for harmonic in xrange(self.n_harm):
            self.harmonics[harmonic].mag[frame] = np.abs(a[harmonic])
            self.harmonics[harmonic].phi[frame] = np.angle(a[harmonic])
            self.harmonics[harmonic].freq[frame] = freq[harmonic]

n_harm = 25
file_size = 100

signal = SignalObj(n_harm, file_size)

for frame in xrange(file_size):

    #Nevermind this part, is just a simplification ;)
    #----------------------------------------------
    real_part = np.random.normal(0.0, 0.9, n_harm)
    imag_part = np.random.normal(0.0, 0.9, n_harm)
    a = real_part + 1j*imag_part
    freq = np.random.normal(500.0, 200.0, n_harm)
    #-----------------------------------------------

    signal.update_values(a, freq, frame)

这个脚本运行没有问题。然而,由于在语音信号中谐波和帧的数量可能很大,因此由于SignalObj.update_values 方法中的for 循环,代码在计算上会有些繁重。

我相信像“numpy-array”这样的解决方案

    def update_values(self, a, freq, frame):
        self.harmonics[:].mag[frame] = np.abs(a[:])
        self.harmonics[:].phi[frame] = np.angle(a[:])
        self.harmonics[:].freq[frame] = freq[:]

会更快。但是上面的代码不适用于对象列表。 所以,我想知道是否有一种方法可以更改列表属性或创建我自己的结构,从而允许上述优化的值设置过程。

附言。 : 顺便说一句,逃避这个问题的一种可能的解决方案是丢弃HarmObj 类并将magphifreq 属性直接关联到SignalObj,即

class SignalObj(object):

    def __init__(self, n_harm, file_size):
        self.n_harm = n_harm
        self.size = file_size
        self.mag = np.zeros((n_harm, file_size))
        self.phi = np.zeros((n_harm, file_size))
        self.freq = np.zeros((n_harm, file_size))

    def update_values(self, a, freq, frame):
        self.mag[:, frame] = np.abs(a[:])
        self.phi[:, frame] = np.angle(a[:])
        self.freq[:, frame] = freq[:]

我在 MATLAB 版本中采用了这种解决方法,它确实更快。但是,由于 Python 更足智多谋,我想知道是否有更优雅和 Pythonic 的方式来完成这项工作;)。此外,将 HarmObj 实例分开将有助于我以后开始操作它们。

【问题讨论】:

  • HarmObj 只是是三个 numpy 数组的容器 - 没有其他属性吗?
  • 我想在类中添加一个方法,它将采用三个 numpy 数组并生成合成的单个谐波。然后SignalObj 将对所有这些谐波求和并生成合成信号。
  • 实际上,要生成谐波信号,我只需要幅度和相位。但是频率信号对于细化提取算法的结果是必要的。这个过程有助于提高最终合成信号的质量。
  • 我不是专家,但我认为您在使用自定义数据类型时无法获得numpy 的性能。您必须确定HarmObj 封装是否值得付出性能成本。如果合成/提取可以编写为 standalone 函数和 vectorized 那么您的解决方法可能是正确的方向 - 看来最终您将不得不遍历 @ 987654336@也是。 SignalObj.harmonics (H) 可以表示为 (n_harm, 3, file_size) ndarray。每个谐波将是 H[n, :, :] 并且每个帧将是 H[:, :, n]
  • 是的,可能最好按照您的建议保留 H ndarray 并利用 numpy 的性能。老实说,我不介意在整个合成/提取过程中直接使用 H ndarray。正如我所提到的,这就是我在使用 MATLAB 进行原型设计时所做的方式。但由于我想稍后与一些研究人员分享代码,我认为最终他们(也许我也是)想在未来的工作中操纵来自HarmObj 的数据。所以我试图让它的访问更加“用户友好”。

标签: python list oop object speech


【解决方案1】:

由于您使用创建self.harmonics的方法,此代码有一些错误

这是一个简单的演示,说明为什么会出现问题

class Foo()
    __init__(self, foo):
        self.foo = foo

foo = Foo('foo')
bar = [foo] * 3
for i in bar:
    print i.foo

这会返回。

foo
foo
foo

看起来不错...除了不是,因为它们都指向同一个foo 对象。所以

bar[0].foo = 'bar'
for i in bar:
    print i.foo

这会返回:

bar
bar 
bar

改用列表推导式。

bar = [Foo('foo') for i in range(3)]
for i in bar:
   print i.foo
bar[0].foo = 'bar'
for i in bar:
   print i.foo

这给出了所需的

foo
foo
foo
bar
foo
foo

对于列表的快速操作,您还可以列出理解。

def update_harmonic(harmonic):
    real_part = np.random.normal(0.0, 0.9, n_harm)
    imag_part = np.random.normal(0.0, 0.9, n_harm)
    a = real_part + 1j*imag_part
    freq = np.random.normal(500.0, 200.0, n_harm)
    signal.update_values(a, freq, frame)

self.harmonics = [x.update_harmonic() for x in self.harmonics] 

【讨论】:

  • 哎呀,确实,我以错误的方式创建了列表,已经在那里更正了。实际上,在我的原始代码中,我是从 for 循环开始的。但是当我在这里复制并编辑它时,我最终改变了它。
  • 我还用列表理解替换了for 循环,并且有效=)。我已经尝试在此处发布问题之前实现它,但我的版本引发了错误。
猜你喜欢
  • 1970-01-01
  • 2011-04-11
  • 2019-01-10
  • 1970-01-01
  • 2012-03-11
  • 1970-01-01
  • 1970-01-01
  • 2012-11-30
  • 1970-01-01
相关资源
最近更新 更多