【问题标题】:How to initialize NumPy structured array with different default value for each column?如何为每列使用不同的默认值初始化 NumPy 结构化数组?
【发布时间】:2015-07-12 10:26:48
【问题描述】:

我正在尝试初始化一个大小为 (x,y) 的 NumPy 结构化 矩阵,其中 x 的值为 ~ 10^3,y 的值为 ~ 10^6

矩阵的第一列是一个ID(整数),其余是三元组(int8),其中三元组的每个成员应该有不同的默认值。

即假设默认值为[2,5,9] 我想初始化以下矩阵:

0 2 5 9 2 5 9 2 5 9 ...
0 2 5 9 2 5 9 2 5 9 ...
0 2 5 9 2 5 9 2 5 9 ...
0 2 5 9 2 5 9 2 5 9 ...
...

这里的问题(VS.this similar question)是每一列都有不同的唯一名称,应该记录

我能想到的初始化矩阵的最快方法是:

default_age       = 2
default_height    = 5
default_shoe_size = 9

columns = ["id", 
           "a_age", 
           "a_height", 
           "a_shoe_size", 
           "b_age", 
           "b_height", 
           "b_shoe_size",
           #...
           ]

y = len(columns)    
x = 10**4

# generate matrix
mat = numpy.zeros(shape=x,
                  dtype={"names"   : columns,
                         "formats" : ['i'] + ['int8'] * (len(columns) - 1)})
# fill the triplets with default values
for i in xrange(y/3):
    j = i * 3
    mat[mat.dtype.names[j+1]] = default_age
    mat[mat.dtype.names[j+2]] = default_height
    mat[mat.dtype.names[j+3]] = default_shoe_size

初始化这样一个矩阵的最快方法是什么?

谢谢!

【问题讨论】:

  • 您是否有理由不只使用pandas 数据帧?
  • 这里有些可疑。您正在创建一个二维数组(形状为(x, len(columns))),该数组的每个元素本身就是一个带有len(columns) 字段的结构。你确定那是你想要的吗? (我的猜测是你真的想要一个 一维 结构化数组。)
  • 虽然我还没有消化你的结构描述,但我的经验是,将数据逐个字段复制到结构化数组中通常是最快的方法。或者列出所有必要的元组。
  • @Warren Weckesser 你说得对 - 我的意思是创建一个 1D 结构化数组,编辑问题以反映这一点。谢谢!
  • 在担心“最快”之前,您应该给我们一个工作示例。您没有指定xy,您的mat[:,i+1] 索引将不适用于结构化数组。

标签: python arrays python-2.7 numpy matrix


【解决方案1】:

这是我对您的示例进行的调整,经过调整后可以运行。请注意,我按字段名称遍历列

dt=np.dtype({"names": columns, "formats" : ['i'] + ['int8'] * (len(columns) - 1)})
mat=np.zeros((10,),dtype=dt)
for i in range(1,7,3):
    mat[dt.names[i]]=default_age
    mat[dt.names[i+1]]=default_height
    mat[dt.names[i+2]]=default_shoe_size

生产

array([(0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9)], 
      dtype=[('id', '<i4'), ('collections.ChainMap(np.arange(6).reshape(3,2))[0]_age', 'i1'), ('a_height', 'i1'), ('a_shoe_size', 'i1'), ('b_age', 'i1'), ('b_height', 'i1'), ('b_shoe_size', 'i1')])

只要字段名称的数量大大少于行数,我认为这将与任何其他方式一样快或更快。

在我的示例x=(10,) 中。您的 mat[:,j+1] 表达式尚未更正以处理结构化一维​​数组。

如果您有很多列(字段)(与行数相比),结构化数组可能不是最好的方法。

如果您的所有字段都是“int”,我会使用常规的二维数组。当字段具有不同类型的元素时,结构化数组最有用。


这是一种使用这些值初始化常规二维数组的方法,并可选择将其转换为结构化数组

values=np.array([2,5,9])
x, y = 10, 2
mat1=np.repeat(np.repeat(values[None,:],y,0).reshape(1,3*y),x,0)

制作:

array([[2, 5, 9, 2, 5, 9],
       [2, 5, 9, 2, 5, 9],
       ...,
       [2, 5, 9, 2, 5, 9]])

在id列上添加

mat1=np.concatenate([np.zeros((x,1),int),mat1],1)
array([[0, 2, 5, 9, 2, 5, 9],
       [0, 2, 5, 9, 2, 5, 9],
       ...
       [0, 2, 5, 9, 2, 5, 9],
       [0, 2, 5, 9, 2, 5, 9]])

一个新的 dtype - 全部是普通的“int”:

dt1=np.dtype({"names"   : columns, "formats" : ['i'] + ['int'] * (len(columns) - 1)})
mat2=np.empty((x,),dtype=dt1)

如果操作正确,mat1data 应该与 mat2 的大小和字节顺序相同。在这种情况下,我可以“复制”它(实际上只是更改指针)。

mat2.data=mat1.data

mat2 看起来就像之前的 mat,只是 dtype 有点不同(使用 i4 而不是 i1 字段)

array([(0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9)], 
      dtype=[('id', '<i4'), ('a_age', '<i4'), ('a_height', '<i4'), ('a_shoe_size', '<i4'), ('b_age', '<i4'), ('b_height', '<i4'), ('b_shoe_size', '<i4')])

使用mat1 值初始化结构化数组的另一种方法是使用中间元组列表:

np.array([tuple(row) for row in mat1],dtype=dt)
array([(0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
       (0, 2, 5, 9, 2, 5, 9)], 
      dtype=[('id', '<i4'), ('a_age', 'i1'), ('a_height', 'i1'), ('a_shoe_size', 'i1'), ('b_age', 'i1'), ('b_height', 'i1'), ('b_shoe_size', 'i1')])

我没有运行时间测试,部分原因是我不知道您的 x,y 值是什么样的。

Convert structured array with various numeric data types to regular array

或从https://stackoverflow.com/a/21818731/901925 中的答案,np.ndarray 构造函数可用于使用预先存在的数据缓冲区创建一个新数组。它仍然需要使用dt1,所有i8 dtype。

np.ndarray((x,), dt1, mat1)

还有ndarray to structured_array and float to int,更多关于使用view v. astype 进行此转换。

【讨论】:

    【解决方案2】:

    您可以使用 numpy 提供的常用tilecolumn_stack 构建一个数组,然后使用np.core.records.fromarrays

    import numpy as np
    
    default_age       = 2
    default_height    = 5
    default_shoe_size = 9
    n_rows = 10
    
    columns = [
        "id", 
        "a_age", 
        "a_height", 
        "a_shoe_size", 
        "b_age", 
        "b_height", 
        "b_shoe_size",
        ]
    
    # generate matrix
    dtype = {
        "names": columns,
        "formats": ['i'] + ['int8'] * (len(columns) - 1)
        }
    
    ids = np.zeros(n_rows)
    people = np.tile([default_age, default_height, default_shoe_size], (n_rows,2))
    data = np.column_stack((ids, people))
    
    mat = np.core.records.fromarrays(list(data.T), dtype=dtype)
    

    这给出了:

    >>> mat
    rec.array([(0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
           (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
           (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9), (0, 2, 5, 9, 2, 5, 9),
           (0, 2, 5, 9, 2, 5, 9)], 
          dtype=[('id', '<i4'), ('a_age', 'i1'), ('a_height', 'i1'), ('a_shoe_size', 'i1'), ('b_age', 'i1'), ('b_height', 'i1'), ('b_shoe_size', 'i1')])
    

    【讨论】:

      【解决方案3】:

      您可以使用 for 循环填充默认值。例如,如果您在字典中有默认值:

      default_values = {
          "a_age": 3,
          "a_height": 5,
      }
      for column, value in default_values.items():
          mat[column] = value
      

      【讨论】:

      • 这与我建议的解决方案有何不同(在性能方面)?
      • 您的解决方案在哪里?
      • 问题本身。
      【解决方案4】:

      您可以使用枚举来表示列名

      class Columns(Enum):
          id = 0
          a_age = 1
          a_height = 2
          a_shoe_size = 3
          b_age = 4
          b_height = 5
          b_shoe_size = 6
          ...
      

      然后使用普通的数组初始化和访问语法,或者你想使用的任何对象。例如,您可以使用Columns.a_age 代替列索引。有关枚举的更多信息,请查看此处How can I represent an 'Enum' in Python?

      【讨论】:

        猜你喜欢
        • 2015-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-11
        • 1970-01-01
        • 2015-12-25
        • 2020-02-27
        • 2014-05-06
        相关资源
        最近更新 更多