使用 np.view() 更改 numpy 1.14 中的结构化数组

try*_*ink 7 python numpy python-3.x

我有一个具有混合数据类型(即浮点数、整数和字符串)的 numpy 结构化数组。我想选择数组的一些列(所有列仅包含浮点数),然后按列获取行的总和,作为标准 numpy 数组。初始数组的形式类似于:

\n\n
some_data = np.array([(\'foo\', 3.5, 2.15), (\'bar\', 2.8, 5.3), (\'baz\', 1.2, 3.7)], \n                     dtype=[(\'col1\', \'<U20\'), (\'A\', \'<f8\'), (\'B\', \'<f8\')])\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于这个例子,我想对 A 列和 B 列求和,得到np.array([7.5, 11.15])。使用 numpy \xe2\x89\xa41.13,我可以这样做:

\n\n
get_cols = [\'A\', \'B\']\ndesired_sum = np.sum(some_data[get_cols].view((\'<f8\', len(get_cols))), axis=0)\n
Run Code Online (Sandbox Code Playgroud)\n\n

随着 numpy 1.14 的发布,此方法现在失败并显示,这是numpy 1.14 对结构化数组的处理ValueError: Changing the dtype to a subarray type is only supported if the total itemsize is unchanged进行更改的结果。(用户 bbengfort对此答案中有关此更改的 FutureWarning 发表了评论。)

\n\n

鉴于结构化数组的这些变化,如何从结构化数组子集中获得所需的总和?

\n

hpa*_*ulj 2

In [165]: some_data = np.array([('foo', 3.5, 2.15), ('bar', 2.8, 5.3), ('baz', 1.2, 3.7)], dtype=[('col1', '<U20'), ('A', '<f8'), ('B', '<f8')])
     ...:                      
In [166]: get_cols = ['A','B']
In [167]: some_data[get_cols]
Out[167]: 
array([( 3.5,  2.15), ( 2.8,  5.3 ), ( 1.2,  3.7 )],
      dtype=[('A', '<f8'), ('B', '<f8')])
Run Code Online (Sandbox Code Playgroud)

只需读取字段值就可以了。在 1.13 中我们收到警告

In [168]: some_data[get_cols].view(('<f8', len(get_cols)))
/usr/local/bin/ipython3:1: FutureWarning: Numpy has detected that you may be viewing or writing to an array returned by selecting multiple fields in a structured array. 

This code may break in numpy 1.13 because this will return a view instead of a copy -- see release notes for details.
  #!/usr/bin/python3
Out[168]: 
array([[ 3.5 ,  2.15],
       [ 2.8 ,  5.3 ],
       [ 1.2 ,  3.7 ]])
Run Code Online (Sandbox Code Playgroud)

使用推荐的副本,没有警告:

In [169]: some_data[get_cols].copy().view(('<f8', len(get_cols)))
Out[169]: 
array([[ 3.5 ,  2.15],
       [ 2.8 ,  5.3 ],
       [ 1.2 ,  3.7 ]])
In [171]: np.sum(_, axis=0)
Out[171]: array([  7.5 ,  11.15])
Run Code Online (Sandbox Code Playgroud)

在你的原始数组中,

dtype([('col1', '<U20'), ('A', '<f8'), ('B', '<f8')])
Run Code Online (Sandbox Code Playgroud)

一个A,B切片将两个f8项目散布在 20U 的项目中。改变view这种混合的数据类型是有问题的。这就是为什么使用副本更可靠。

由于U20占用 4*20 字节,因此总数itemsize为 96,是 8 的倍数。我们可以将整个内容转换为f8,重塑并“丢弃”U20列:

In [183]: some_data.view('f8').reshape(3,-1)[:,-2:]
Out[183]: 
array([[ 3.5 ,  2.15],
       [ 2.8 ,  5.3 ],
       [ 1.2 ,  3.7 ]])
Run Code Online (Sandbox Code Playgroud)

它不是很漂亮,我不推荐它,但它可以让您深入了解结构化数据的排列方式。

view结构化数组有时很有用,但正确使用通常有点棘手。

如果这两个数字字段通常一起使用,我建议使用复合数据类型,例如:

In [184]: some_data = np.array([('foo', [3.5, 2.15]), ('bar', [2.8, 5.3]), ('baz
     ...: ', [1.2, 3.7])], 
     ...:                      dtype=[('col1', '<U20'), ('AB', '<f8',(2,))])
     ...:                      
     ...:                      
In [185]: some_data
Out[185]: 
array([('foo', [ 3.5 ,  2.15]), ('bar', [ 2.8 ,  5.3 ]),
       ('baz', [ 1.2 ,  3.7 ])],
      dtype=[('col1', '<U20'), ('AB', '<f8', (2,))])
In [186]: some_data['AB']
Out[186]: 
array([[ 3.5 ,  2.15],
       [ 2.8 ,  5.3 ],
       [ 1.2 ,  3.7 ]])
Run Code Online (Sandbox Code Playgroud)

genfromtxt接受这种风格dtype