Skip to main content
 首页 » 编程设计

python之'shuffled' NumPy 数组上的维度不匹配

2025年05月04日7pander-it

我或多或少是一个 Python 新手,正在研究与此 evolutionary Mona Lisa 类似的音频实验。

下面的代码是为了:

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

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

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

第二个数组的维度在我每次运行程序时都不同,但总是在 1138000-1145000 左右。在拆分、洗牌和重组步骤中,我似乎丢失了一些 block ,我怀疑我在第 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)` 

请您参考如下方法:

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

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

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

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

正如@talonmies 指出的那样,您的问题来自这样一个事实,即您假设您拥有相同数量的正 block 和负 block 。有多种解决方法,但一种简单的方法是只使用 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()