如何在不改变维度的情况下给numpy数组添加名称?
我有一个已经存在的两列的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 个回答
试着重新写一下X的定义:
X = np.array(zip(ID, RING))
这样你就不需要再定义Y = X.T了。
你确定你得到的 A
和 Y
的输出是正确的吗?我在用 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
。
这是因为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)
在一个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
首先,因为你的问题是关于给数组命名的,所以我觉得有必要指出,使用“结构化数组”来命名可能不是最好的方法。当我们处理表格时,通常会想给行和列命名。如果是这种情况,我建议你试试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]