如何在不改变维度的情况下给numpy数组添加名称?

11 投票
5 回答
67412 浏览
提问于 2025-04-18 09:27

我有一个已经存在的两列的numpy数组,现在我想给这两列加上名字。在下面块1的简单例子中,通过传递dtype可以成功给列命名。但是在我的实际数组中,如块2所示,使用同样的方法却意外地改变了数组的维度,这让我感到困惑。

我该如何把我实际的数组,也就是下面第二块中名为Y的数组,转换成像第一块中A那样有命名列的数组呢?

块1:(给A的列命名,没有改变维度)

import numpy as np
A = np.array(((1,2),(3,4),(50,100)))
A
# array([[  1,   2],
#        [  3,   4],
#        [ 50, 100]])
dt = {'names':['ID', 'Ring'], 'formats':[np.int32, np.int32]}
A.dtype=dt
A
# array([[(1, 2)],
#        [(3, 4)],
#        [(50, 100)]], 
#       dtype=[('ID', '<i4'), ('Ring', '<i4')])

块2:(给我实际的数组Y命名列时,改变了它的维度)

import numpy as np
## Code to reproduce Y, the array I'm actually dealing with
RING = [1,2,2,3,3,3]
ID = [1,2,3,4,5,6]
X = np.array([ID, RING])
Y = X.T
Y
# array([[1, 3],
#        [2, 2],
#        [3, 2],
#        [4, 1],
#        [5, 1],
#        [6, 1]])

## My unsuccessful attempt to add names to the array's columns    
dt = {'names':['ID', 'Ring'], 'formats':[np.int32, np.int32]}
Y.dtype=dt
Y
# array([[(1, 2), (3, 2)],
#        [(3, 4), (2, 1)],
#        [(5, 6), (1, 1)]], 
#       dtype=[('ID', '<i4'), ('Ring', '<i4')])

## What I'd like instead of the results shown just above
# array([[(1, 3)],
#        [(2, 2)],
#        [(3, 2)],
#        [(4, 1)],
#        [(5, 1)],
#        [(6, 1)]],
#       dtype=[('ID', '<i4'), ('Ring', '<i4')])

5 个回答

1

试着重新写一下X的定义:

X = np.array(zip(ID, RING))

这样你就不需要再定义Y = X.T了。

2

你确定你得到的 AY 的输出是正确的吗?我在用 Python 2.7.6 和 numpy 1.8.1 的时候得到的结果不一样。

我一开始得到的 A 的输出和你的一样,应该是这样的。在运行下面这段代码后,

dt = {'names':['ID', 'Ring'], 'formats':[np.int32, np.int32]}
A.dtype=dt

数组 A 的内容实际上是

array([[(1, 0), (3, 0)],
   [(2, 0), (2, 0)],
   [(3, 0), (2, 0)],
   [(4, 0), (1, 0)],
   [(5, 0), (1, 0)],
   [(6, 0), (1, 0)]], 
  dtype=[('ID', '<i4'), ('Ring', '<i4')])

这个结果对我来说比你提供的输出更有道理,因为 dtype 决定了数组中每个元素的数据类型,而新的定义说明每个元素应该包含两个字段,所以它确实包含了,但第二个字段的值被设为 0,因为之前没有给这个字段赋值。

不过,如果你想让 numpy 把你现有数组的列分组,使得每一行只包含一个元素,但每个元素又有两个字段,你可以稍微改一下代码。

因为需要用元组来让 numpy 将元素分组为更复杂的数据类型,你可以通过创建一个新数组,把现有数组的每一行变成一个元组来实现。这里有一个简单的示例:

import numpy as np
A = np.array(((1,2),(3,4),(50,100)))
dt = np.dtype([('ID', np.int32), ('Ring', np.int32)])
B = np.array(list(map(tuple, A)), dtype=dt)

使用这段简短的代码,数组 B 变成了

array([(1, 2), (3, 4), (50, 100)], 
  dtype=[('ID', '<i4'), ('Ring', '<i4')])

要把 B 变成一个二维数组,只需要写

B.reshape(len(B), 1) # in this case, even B.size would work instead of len(B)

对于第二个示例,类似的操作也需要进行,以便将 Y 变成一个结构化数组:

Y = np.array(list(map(tuple, X.T)), dtype=dt)

在对你的第二个示例进行这个操作后,数组 Y 看起来是这样的:

array([(1, 3), (2, 2), (3, 2), (4, 1), (5, 1), (6, 1)], 
  dtype=[('ID', '<i4'), ('Ring', '<i4')])

你会注意到这个输出和你预期的不一样,但这个结果更简单,因为你不需要写 Y[0,0] 来获取第一个元素,只需写 Y[0]。要把这个数组也变成二维的,你可以像处理 B 一样使用 reshape

3

这是因为Y不是连续存储的,你可以通过 Y.flags 来检查这一点:

  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

你可以先调用 Y.copy() 或者 Y.ravel()

dt = {'names':['ID', 'Ring'], 'formats':[np.int32, np.int32]}
print Y.ravel().view(dt) # the result shape is (6, )
print Y.copy().view(dt)  # the result shape is (6, 1)
7

在一个NumPy数组中存储不同的数据类型 还有一个页面提供了一个很好的解决方案,可以给数组添加名称,这样就可以用作列名。 例如:

r = np.core.records.fromarrays([x1,x2,x3],names='a,b,c')
# x1, x2, x3 are flatten array
# a,b,c are field name
12

首先,因为你的问题是关于给数组命名的,所以我觉得有必要指出,使用“结构化数组”来命名可能不是最好的方法。当我们处理表格时,通常会想给行和列命名。如果是这种情况,我建议你试试pandas,这个工具非常棒。如果你只是想在代码中整理一些数据,使用一个包含数组的字典通常比结构化数组要好得多,比如你可以这样做:

Y = {'ID':X[0], 'Ring':X[1]}

说完这些,如果你还是想使用结构化数组,我认为这里有一种最清晰的做法:

import numpy as np

RING = [1,2,2,3,3,3]
ID = [1,2,3,4,5,6]
X = np.array([ID, RING])

dt = {'names':['ID', 'Ring'], 'formats':[int, int]}
Y = np.zeros(len(RING), dtype=dt)
Y['ID'] = X[0]
Y['Ring'] = X[1]

撰写回答