【问题标题】:How to add names to a numpy array without changing its dimension?如何在不改变维度的情况下向 numpy 数组添加名称?
【发布时间】:2014-08-01 20:15:10
【问题描述】:

我有一个现有的两列 numpy 数组,我需要向其中添加列名。通过dtype 传递它们在下面Block 1 中显示的玩具示例中起作用。但是,对于我的实际数组,如 Block 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
nRings = 3
nn = [[nRings+1-n] * n for n in range(nRings+1)]
RING = reduce(lambda x, y: x+y, nn)
ID = range(1,len(RING)+1)
X = numpy.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')])

【问题讨论】:

    标签: python arrays numpy


    【解决方案1】:

    尝试重写X的定义:

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

    然后你就不需要定义 Y = X.T

    【讨论】:

    • 这看起来很奇怪。运行Z = np.array(zip(ID, RING)),然后运行numpy.array_equiv(Y,Z)(使用Y=X.T)产生'True'。所以Y 和Z 看起来相同(并显示dtype 的等效值)。但是当我做Z.dtype=dt 时,我得到了我想要的数组,而Y.dtype=dt 仍然给了我我没有的数组。知道为什么会这样吗??
    • 重写整个X?我以为只有X 中的columns names 应该受到影响?
    【解决方案2】:

    您完全确定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] 而不是写Y[0,0] 来获取第一个元素。要同时将此数组设为 2D,您还可以使用 reshape,就像使用 B 一样。

    【讨论】:

    • 是的,我确定那个输出。我刚刚通过在新的 Python 会话中重新运行来确认它('2.7.6 | Anaconda 1.9.2(32 位)和 Windows 7 64 位笔记本电脑上的 numpy 1.8.0)。感谢您其余的回答,作为 Python 新手,我需要一段时间来消化。不过有两个问题。 (1) 对于B,看起来我想做B.reshape(len(B),1) 来获得一个两列数组,对吧? (2) 我将如何以脚本方式“为 [我的] 第二个示例做 [] 类似的事情”。 (因为真正的数组将有 1000 多个元素,所以我无法像您在最后的代码块中那样写出数组。)
    • @JoshO'Brien,这就是关键,32 位。您是块 A 仅适用于您使用的是 32 位版本的 python/numpy。一般来说,更改数组的 dtype 可能是个坏主意,而且正如我们在这里看到的,会在不同的系统上产生不同的结果。
    • @BiRico 谢谢。即使A.dtype(在我手动分配给它之前)返回dtype('int32'),这也是个坏主意?那么我应该如何在不为 format 指定 some 值的情况下为数组分配名称,还是应该为其分配一些不同的格式? (FTR,我确定是我在这里遗漏了一些东西,但我确实怀疑位和格式是关键,这就是为什么我首先检查了 A.dtype 的值...)
    • @JoshO'Brien 关于将B 转换为二维数组的方法是正确的。为了回答你的第二个问题,我更新了我的答案。
    【解决方案3】:

    首先,因为您的问题是关于为数组命名,我觉得有义务指出使用“结构化数组”来命名可能不是最好的方法。当我们使用表格时,我们经常喜欢给行/列命名,如果是这种情况,我建议你尝试像pandas 这样的东西,这太棒了。如果你只是想在代码中组织一些数据,数组字典通常比结构化数组好得多,例如你可以这样做:

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

    除此之外,如果您想使用结构化数组,我认为这是最清晰的方法:

    import numpy as np
    
    nRings = 3
    nn = [[nRings+1-n] * n for n in range(nRings+1)]
    RING = reduce(lambda x, y: x+y, nn)
    ID = range(1,len(RING)+1)
    X = np.array([ID, RING])
    
    dt = {'names':['ID', 'Ring'], 'formats':[np.int, np.int]}
    Y = np.zeros(len(RING), dtype=dt)
    Y['ID'] = X[0]
    Y['Ring'] = X[1]
    

    【讨论】:

    • 太棒了。特别是作为 R 用户,pandas 看起来确实很棒,但我无法将它用于此应用程序。 (我需要 Python 代码在 arcpy 脚本中执行一些操作,作为 ArcGIS 工具箱分发给用户,这些用户只能使用 ArcGIS 安装程序提供给他们的 Python 安装。它包括 numpy 但不是 pandas,这就是为什么我的一只手(至少!)被绑在背后。)特别感谢您提供纯 numpy 解决方案。一有机会,我就会检查它如何与我的其余代码一起使用。
    • FWIW,help page for arcpy.da.ExtendTable 上的“代码示例”说明了为什么我特别需要一个 numpy“结构化数组”。
    • 是否还需要X=np.array([ID,RING])的中间创建?好像做Y['ID'] = ID和Y['ID'] = X[0]的效果一样吧?
    • @JoshO'Brien,你是对的。我只是假设您的数据以X 之类的数组开始,但如果您将其设置为ID 和RING,则无需创建X。
    • @BiRico 我意识到这是一个老问题,但你有什么理由避免使用 numpy 结构化数组来引用特定名称的列?
    【解决方案4】:

    这是因为Y不是C_CONTIGUOUS,可以通过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)
    

    【讨论】:

    • 令人着迷。不能说我完全理解 为什么 这会有所作为,但我现在看到 确实如此。此外,在记录中,看起来np.require(Y, requirements=['C']) 在不更改数组维度的情况下转换为 C_CONTIGUOUS 存储方面与 Y.copy() 做了 +/- 相同的事情。
    • 我认为 this documentation 可能是我了解为什么 F_CONTIGUOUS 转置数组 Y 在通过该 dtype 时的行为方式是正确的吗?
    • @JoshO'Brien,试试这个a = np.array((1, 2, 3, 4), dtype='&gt;i4'); print(a); dt = {'names':['i1', 'i2'], 'formats':[np.int32, np.int32]}; a.dtype = dt; print(a)。更改 numpy 数组的 dtype 是一个非常低级的操作,它破坏了使 numpy 很棒的所有抽象。事实上,我很惊讶 numpy 开发人员允许它如此轻松地完成。为了知道更改后您会得到什么,您必须对 numpy 的工作原理有所了解,并跟踪所有数组的内部结构,即内存布局、字节序、输入/输出数据类型、步幅。
    • @BiRico 很好,很有帮助的评论。我现在看到ravel() 和copy() 解决了C_CONTIGUOUS 问题,但它们并不“安全”。存储格式的变化。您制定的策略 - 使用所需的存储类型初始化一个新数组,然后将数据复制到其中 - 看起来更安全,因为复制/插入操作负责任何必要的类型转换。
    【解决方案5】:

    store-different-datatypes-in-one-numpy-array 另一个页面,包括一个很好的解决方案,可以将名称添加到可以用作列的数组中 示例:

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

    【讨论】:

    • 您能否解释一下这个链接以及为什么它是合适的?
    • @Mozahler link 它从扁平的 numpy 数组创建记录数组...例如: r = np.core.records.fromarrays([x1,x2,x3],names='a,b ,c')....x1,x2,x3 是扁平数组....a,b,c 是字段名
    • 您应该使用此信息更新您提供的答案,而不是在评论中回复。您的答案应该是有意义的,而不必阅读所有 cmets。
    • 这绝对是最直接最有用的答案。我们喜欢单线。在某个位置索引结果r,例如r[0] 给你第一行,而r['a'] 给你那一列。这对于在 N 维空间中存储点非常有用,其中每个维度都被命名。
    猜你喜欢
    • 1970-01-01
    • 2013-06-28
    • 2020-09-03
    • 2017-01-18
    • 2022-12-10
    • 2013-12-15
    相关资源
    最近更新 更多