混合类型的NumPy数组/矩阵

17
我想创建一个混合数据类型(字符串,整数,整数)的NumPy数组/矩阵(Nx3)。但是当我添加一些数据来附加到该矩阵时,我会收到一个错误:TypeError: invalid type promotion。请问有人能帮我解决这个问题吗?
当我使用示例数据创建一个数组时,NumPy将矩阵中的所有列都转换为“S”数据类型。我无法为数组指定数据类型,因为当我这样做时res = np.array(["TEXT", 1, 1], dtype='S, i4, i4') - 我会收到一个错误:TypeError: expected a readable buffer objecttemplates.py
import numpy as np
from pprint import pprint

test_array = np.zeros((0, 3), dtype='S, i4, i4')
pprint(test_array)

test_array = np.append(test_array, [["TEXT", 1, 1]], axis=0)
pprint(test_array)

print("Array example:")
res = np.array(["TEXT", 1, 1])
pprint(res)

输出:

array([], shape=(0L, 3L), 
  dtype=[('f0', 'S'), ('f1', '<i4'), ('f2', '<i4')])

 Array example:
 array(['TEXT', '1', '1'], dtype='|S4')

错误:

Traceback (most recent call last):

File "templates.py", line 5, in <module>
test_array = np.append(test_array, [["TEXT", 1, 1]], axis=0)

File "lib\site-packages\numpy\lib\function_base.py", line 3543, in append
return concatenate((arr, values), axis=axis)

TypeError: invalid type promotion

test_array = np.zeros((0,), dtype='S, i4, i4') 会生成一个包含0个“行”和3个命名为“列”的数组。实际上,它是一个一维数组。 - hpaulj
通常在 numpy 中,通过追加构建数组并不是一个好主意。更好的方法是将元素追加到列表中,然后从列表构建数组。或者从足够大的“空”数组开始,并插入值。或者连接数组。 - hpaulj
我刚刚查看了append代码。它实际上使用了concatenate。尝试创建一个带有“TEXT”数据的数组,并将其连接到test_array - hpaulj
5个回答

17

你的问题出在数据中。试试这个:

res = np.array(("TEXT", 1, 1), dtype='|S4, i4, i4')
或者
res = np.array([("TEXT", 1, 1), ("XXX", 2, 2)], dtype='|S4, i4, i4')

数据必须是元组或元组列表。从错误消息中不太明显,是吧?

另外,请注意,要想真正保存文本数据,必须指定文本字段的长度。如果你只想将文本保存为对象(数组中仅为引用),那么:

res = np.array([("TEXT", 1, 1), ("XXX", 2, 2)], dtype='object, i4, i4')

这通常非常有用,同样也是如此。


我认为不应该是一个元组。它会出现错误:ValueError: 所有输入数组必须具有相同的维数。 - Vit D
你会发现,如果你尝试使用 np.array(("TEXT", 1,1), dtype='S, i4, i4') 而不是 np.array(["TEXT", 1,1], dtype='S, i4, i4'),那么tuples是必需的。 - hpaulj

7
如果你不强求使用numpy,那么pandas DataFrame非常适合此任务。另外,你也可以将数组中的字符串字段指定为Python对象(例如:dtype='O, i4, i4')。此外,似乎append方法更喜欢元组列表而不是列表嵌套列表。我认为这可能与列表的可变性有关,但不确定。

3

首先,numpy使用固定的物理记录大小存储数组元素。因此,记录对象需要全部具有相同的物理大小。因此,您需要告诉numpy字符串的大小或保存指向其他位置存储的字符串的指针。在记录数组中,“S”转换为零长度字符串,这可能不是您想要的。

实际上,append方法会将整个数组复制到更大的物理空间以容纳新元素。例如:

import numpy as np
mtype = 'S10, i4, i4'
ta = np.zeros((0), dtype=mtype)
print id(ta)
ta = np.append(ta, np.array([('first', 10, 11)], dtype=mtype))
print id(ta)
ta = np.append(ta, np.array([('second', 20, 21)], dtype=mtype))
print id(ta)

每次用这种方式添加数据,拷贝速度会变慢,因为每次增长时需要分配和拷贝更多的内存。这就是为什么每次添加都会返回一个不同的值。如果你想在数组中存储大量的记录,最好从一开始就分配足够的空间,或者积累数据到列表中,然后在完成操作之后将列表收集到numpy结构化数组中。这还可以让你尽可能地缩短mtype中字符串的长度,同时保证足够长以容纳最长的字符串。

1
我认为这就是你试图实现的 - 创建一个所需dtype的空数组,然后向其中添加一个或多个数据集。结果将具有形状(N,),而不是(N,3)。
正如我在评论中指出的那样,np.append使用np.concatenate,因此我也在使用它。另外,我必须使test_array和x成为1d数组(分别为形状(0,)和(1,))。并且dtype字段是S10,足够大以包含“TEXT”。
In [56]: test_array = np.zeros((0,), dtype='S10, i4, i4')

In [57]: x = np.array([("TEST",1,1)], dtype='S10, i4, i4')

In [58]: test_array = np.concatenate((test_array, x))

In [59]: test_array = np.concatenate((test_array, x))

In [60]: test_array
Out[60]: 
array([('TEST', 1, 1), ('TEST', 1, 1)], 
      dtype=[('f0', 'S'), ('f1', '<i4'), ('f2', '<i4')])

这是一个从元组列表构建数组的示例:
In [75]: xl=('test',1,1)

In [76]: np.array([xl]*3,dtype='S10,i4,i4')
Out[76]: 
array([('test', 1, 1), ('test', 1, 1), ('test', 1, 1)], 
      dtype=[('f0', 'S10'), ('f1', '<i4'), ('f2', '<i4')])

-4

我不认为你可以用多个数据类型创建一个数组。但是,你可以创建一个具有多个数据类型的列表。

list = ["TEXT", 1, 1]
print(list)

提供

['TEXT', 1, 1]

1
我需要使用NumPy来完成这个任务,因为我之后会用到一些特定的NumPy方法来处理这些数据。 - Vit D
2
OP使用dtype创建了一个包含多个数据类型的数组。 - hpaulj

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