不同行大小的numpy填充矩阵

4

我有一个numpy数组,其行大小不同

a = np.array([[1,2,3,4,5],[1,2,3],[1]])

我希望将这段文本转化为一个稠密的矩阵(固定的n x m大小,没有可变行)。到目前为止,我尝试了类似于以下内容的东西:

size = (len(a),5)    
result = np.zeros(size)
result[[0],[len(a[0])]]=a[0]

但是我收到了一个错误信息,告诉我:

形状不匹配:值数组的形状(5,)无法广播到形状为(1,)的索引结果

我还尝试使用np.pad进行填充,但根据numpy.pad的文档,似乎需要在pad_width中指定行的先前大小(这是变量的,用-1、0和最大行大小都产生了错误)。

我知道可以像这里所示那样对每一行进行填充,但我需要对更大的数据数组进行填充。

如果有人能够回答我的问题,我将非常高兴知道它。


2
这个输入值a=np.array([[1,2,3,4,5],[1,2,3],[1]])的预期输出是什么? - Divakar
1
只是澄清一下:你没有不同行大小的numpy数组。你拥有的是一个numpy对象数组。每个对象本身又是另一个数组。 - Imanol Luengo
result[i,:len(a[i])]=a[i] 这个语句是有效的。你需要在第二维使用切片(即 :)。你的列表数组基本上与列表的列表相同。无论如何,你都将迭代这些子列表。 - hpaulj
谢谢你们的澄清。我需要修复它。 - Diego Alejandro Gómez Pardo
3个回答

9

如果不迭代数组的行,就没有办法填充jagged array以使其失去不规则性。你甚至需要两次迭代数组:一次查找需要填充的最大长度,另一次进行实际填充。

你提供的代码建议可以完成任务,但效率不高,因为它在Python for循环中迭代行元素并添加零,而这个添加操作可以预先计算,从而将更多的代码推到C语言中。

以下代码预先计算了所需最小维度的数组,并用零填充,然后直接在原地添加来自不规则数组M的行,这样更加有效率。

import random
import numpy as np
M = [[random.random() for n in range(random.randint(0,m))] for m in range(10000)] # play-data

def pad_to_dense(M):
    """Appends the minimal required amount of zeroes at the end of each 
     array in the jagged array `M`, such that `M` looses its jagedness."""

    maxlen = max(len(r) for r in M)

    Z = np.zeros((len(M), maxlen))
    for enu, row in enumerate(M):
        Z[enu, :len(row)] += row 
    return Z

为您提供一些关于速度的想法:

from timeit import timeit
n = [10, 100, 1000, 10000]
s = [timeit(stmt='Z = pad_to_dense(M)', setup='from __main__ import pad_to_dense; import numpy as np; from random import random, randint; M = [[random() for n in range(randint(0,m))] for m in range({})]'.format(ni), number=1) for ni in n]
print('\n'.join(map(str,s)))
# 7.838103920221329e-05
# 0.0005027339793741703
# 0.01208890089765191
# 0.8269036808051169

如果你想在数组前面添加零而不是后面,那么代码需要进行简单的更改,这部分我会留给你自己完成。

谢谢你的回答,伙计。我已经按照这种方式处理了。 - Diego Alejandro Gómez Pardo

1
import tensorflow as tf

a = [[1,2,3,4,5],[1,2,3],[1]]
x = tf.keras.utils.pad_sequences(a, padding='post')
print(f"{type(x)}\n{x}")
-----
<class 'numpy.ndarray'>
[[1 2 3 4 5]
 [1 2 3 0 0]
 [1 0 0 0 0]]

import awkward as ak

a = [[1,2,3,4,5],[1,2,3],[1]]
ak.fill_none(ak.pad_none(a, 5, axis=-1), 0).to_numpy()
-----
array([[1, 2, 3, 4, 5],
       [1, 2, 3, 0, 0],
       [1, 0, 0, 0, 0]])

0
你可以使用numpy.pad来实现类似这样的操作。
import numpy as np
a = np.array([[1,2,3,4,5],[1,2,3],[1]])
l = np.array([len(a[i]) for i in range(len(a))])
width = l.max()
b=[]
for i in range(len(a)):
    if len(a[i]) != width:
        x = np.pad(a[i], (0,width-len(a[i])), 'constant',constant_values = 0)
    else:
        x = a[i]
    b.append(x)
b = np.array(b)
print(b)

上面的代码会输出类似于这样的内容。

b = [[1, 2, 3, 4, 5],
     [1, 2, 3, 0, 0],
     [1, 0, 0, 0, 0]]

您可以通过以下方式读取您输入的数据版本:

a = []
for i in range(len(b)):
    a.append(b[i][0:l[i]])
a = np.array(a)
print(a)

你将获得以下输出

a = array([array([1, 2, 3, 4, 5]), array([1, 2, 3]), array([1])], dtype=object)

希望这能帮助像我一样在解决问题上挣扎的人。谢谢。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接