【问题标题】:Mismatched dimensions on 'shuffled' NumPy array'shuffled' NumPy 数组上的尺寸不匹配
【发布时间】:2012-01-08 02:44:52
【问题描述】:

我或多或少是一个 Python 新手,正在研究这个 evolutionary Mona Lisa 实验的音频模拟。

以下代码旨在:

  1. 将给定的 .wav 文件读入 NumPy 数组。
  2. 检测波形中的“零交叉”,即当数组元素改变符号时。在这些点将数组拆分为波形“块”的嵌套列表。
  3. 将正面与负面块分开,然后打乱这些块并将它们重新组合成一个 NumPy 数组,正负交替。我不能使用 random.shuffle(),因为列表有超过 2000 个元素。
  4. 比较洗牌数组的“适应度” 与原始样本,定义为差的平方 在打乱后的数组和原始样本之间。

最终,我将添加复制、变异和选择,但现在我的适应度函数存在问题。拆分、混洗和重组后的数组与原始输入的维度不同,导致以下错误:

$ ValueError: operands could not be broadcast together with shapes (1273382) (1138213) 

每次运行程序时,第二个数组的尺寸都会有所不同,但始终在 1138000-1145000 左右。在拆分、洗牌和重组步骤中,我似乎丢失了一些块,我怀疑我在第 3 步的某个地方错误地使用了列表推导,但我不太清楚在哪里或为什么。出了什么问题?

# Import scipy audio tools, numpy, and randomization tools
import scipy
from scipy.io import wavfile

import numpy

from random import shuffle, randint

# Read a wav file data array, detect zero crossings, split at zero crossings, and return a nested list.
def process_wav(input):

    # Assign the wavefile data array to a variable.
    wavdata = input[1]

    # Detect zero crossings, i.e. changes in sign in the waveform data. The line below returns an array of the indices of elements after which a zero crossing occurs.
    zerocrossings = numpy.where(numpy.diff(numpy.sign(wavdata)))[0]
    # Increment each element in the array by one. Otherwise, the indices are off.
    zerocrossings = numpy.add(numpy.ones(zerocrossings.size, zerocrossings.dtype), zerocrossings)

    wavdatalist = wavdata.tolist()
    zerocrossingslist = zerocrossings.tolist()

    # Split the list at zero crossings. The function below splits a list at the given indices.      
    def partition(alist, indices):
        return [alist[i:j] for i, j in zip([0]+indices, indices+[None])]

    return partition(wavdatalist, zerocrossingslist)


# Accept a list as input, separate into positive and negative chunks, shuffle, and return a shuffled nested list
def shuffle_wav(list):

    # Separate waveform chunks into positive and negative lists.
    positivechunks = []
    negativechunks = []

    for chunk in list:
        if chunk[0] < 0:
            negativechunks.append(chunk)
        elif chunk[0] > 0:
            positivechunks.append(chunk)
        elif chunk[0] == 0:
            positivechunks.append(chunk)

    # Shuffle the chunks and append them to a list, alternating positive with negative.
    shuffledchunks = []
    while len(positivechunks) >= 0 and len(negativechunks) > 0:
        currentpositivechunk = positivechunks.pop(randint(0, len(positivechunks)-1))
        shuffledchunks.append(currentpositivechunk)
        currentnegativechunk = negativechunks.pop(randint(0, len(negativechunks)-1))
        shuffledchunks.append(currentnegativechunk)

    return [chunk for sublist in shuffledchunks for chunk in sublist]

def get_fitness(array, target):
    return numpy.square(numpy.subtract(target, array))

# Read a sample wav file. The wavfile function returns a tuple of the file's sample rate and data as a numpy array, to be passed to the process_wav() function.
input = scipy.io.wavfile.read('sample.wav')     

wavchunks = process_wav(input)  
shuffledlist = shuffle_wav(wavchunks)   
output = numpy.array(shuffledlist, dtype='int16')
print get_fitness(output, input[1])

scipy.io.wavfile.write('output.wav', 44100, output)

编辑:这是完整的回溯:

Traceback (most recent call last):
  File "evowav.py", line 64, in <module>
    print get_fitness(output, input[1])
  File "evowav.py", line 56, in get_fitness
    return numpy.square(numpy.subtract(target, array))
ValueError: operands could not be broadcast together with shapes (1273382) (1136678)`

【问题讨论】:

  • 请始终包含 COMPLETE 回溯,否则我们无能为力,因为我们只是猜测应该发生什么或为什么会发生。
  • 抱歉。我在上面添加了完整的回溯。
  • 您是否有指向您正在使用的 .wav 文件的链接,以便我们重现它?
  • 基本问题似乎在于洗牌逻辑。如果输入列表中正和负块的数量不同,则输出中总是会丢失一个块。这会导致输入和输出的大小不同,从而导致您看到的失败。
  • @ecmendenhall - 另外,没有必要使用numpy.square(numpy.subtract(target, array)) 之类的东西。使用(target - array)**2 更加pythonic(并且完全等价)同样,zerocrossings = numpy.add(numpy.ones(zerocrossings.size, zerocrossings.dtype), zerocrossings) 等价于zerocrossings += 1。 (实际上后者效率稍高一些,因为它会就地修改数组。)

标签: python arrays list numpy


【解决方案1】:

首先,让我们清理一些代码。

  1. 不要将listinput 等python 内置函数用作变量名来覆盖它们。 Python并没有严格阻止,但后面会引起意外。

  2. 无需显式调用z = numpy.add(x, y) 之类的东西。 z = x + y 更加 Pythonic 并且完全等效。 (假设 xy 是 numpy 数组。)类似地,没有必要创建一个新的数组,只是为 numpy 数组中的每个项目添加 1。如果您想要一份副本,只需致电x += 1x = x + 1

  3. 不要将 cmets 放在定义之上,而是将其放在下面。这不仅仅是一种风格约定,因为 python 的内置帮助和文档工具只能利用这些“文档字符串”,如果它们是第一个注释(或多行字符串,更常见的是,因此三引号)函数定义下方。

正如@talonmies 所指出的,您的问题来自您假设您拥有相同数量的正面和负面块的事实。有几种方法可以解决这个问题,但一种简单的方法是使用itertools.izip_longest

现在,举个例子……

import random
import itertools
import numpy
import scipy.io.wavfile

def main():
    """Read a wav file and shuffle the negative and positive pieces."""
    # Use unpacking to your advantage, and avoid using "input" as a var name
    samplerate, data = scipy.io.wavfile.read('sample.wav')     

    # Note, my sample.wav is stereo, so I'm going to just work with one channel
    # If yours is mono, you'd want to just pass "data" directly in
    left, right = data.T

    wavchunks = process_wav(left)  
    output = shuffle_wav(wavchunks).astype(numpy.int16)
    print get_fitness(output, samplerate)

    scipy.io.wavfile.write('output.wav', 44100, output)

def process_wav(wavdata):
    """Read a wav file data array, detect zero crossings, 
    split at zero crossings, and return a list of numpy arrays"""

    # I prefer nonzero to where, but either works in this case...
    zerocrossings, = numpy.diff(numpy.sign(wavdata)).nonzero()
    zerocrossings += 1
    indicies = [0] + zerocrossings.tolist() + [None]

    # The key is that we don't need to convert everything to a list.
    # Just pass back a list of views into the array. This uses less memory.
    return [wavdata[i:j] for i, j in zip(indicies[:-1], indicies[1:])]

def shuffle_wav(partitions):
    """Accept a list as input, separate into positive and negative chunks, 
    shuffle, and return a shuffled nested list."""

    # Instead of iterating through each item, just use indexing 
    poschunks = partitions[::2]
    negchunks = partitions[1::2]
    if poschunks[0][0] < 0:
        # Reverse the variable names if the first chunk wasn't positive.
        negchunks, poschunks = poschunks, negchunks

    # Instead of popping a random index off, just shuffle the lists...
    random.shuffle(poschunks)
    random.shuffle(negchunks)

    # To avoid the error you were getting, use izip_longest
    chunks = itertools.izip_longest(poschunks, negchunks, fillvalue=[])

    return numpy.hstack(item for sublist in chunks for item in sublist)


def get_fitness(array, target):
    """Compares sum of square differences between the two arrays."""
    # I'm going to assume that you wanted a single sum returned here...
    # Your original code returned an array.
    return ((array - target)**2).sum()

main()

【讨论】:

  • 谢谢你,乔!我一直很害怕itertools,但是是时候学习了——它似乎可以解决很多问题。你的风格笔记特别有用。我正在慢慢学习如何保持 Python 风格。
猜你喜欢
  • 2012-09-11
  • 2014-12-14
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-20
  • 2015-12-12
  • 2021-07-23
相关资源
最近更新 更多