python - 有没有一种简单的方法可以从 numpy.dtype.descr 中删除“填充”字段?
问题描述
语境
从numpy
1.16 版本开始,如果您访问结构化数组的多个字段,dtype
则结果数组的项大小将与原始数组相同,从而导致额外的“填充”:
与 1.15 相比,Numpy 1.16 的新行为导致在未索引字段的位置有额外的“填充”字节。您将需要更新任何依赖于具有“打包”布局的数据的代码。
这可能会导致问题,例如,如果您想稍后将字段添加到有问题的数组中:
import numpy as np
import numpy.lib.recfunctions
a = np.array(
[
(10.0, 13.5, 1248, -2),
(20.0, 0.0, 0, 0),
(30.0, 0.0, 0, 0),
(40.0, 0.0, 0, 0),
(50.0, 0.0, 0, 999)
], dtype=[('x', '<f8'), ('y', '<f8'), ('i', '<i8'), ('j', '<i8')]
) # some array stolen from here: https://stackoverflow.com/a/37081693/5472354
print(a.shape, a.dtype, a.dtype.names, a.dtype.descr)
# all good so far
b = a[['x', 'i']] # for further processing I only need certain fields
print(b.shape, b.dtype, b.dtype.names, b.dtype.descr)
# you will only notice the extra padding in the descr
# b = np.lib.recfunctions.repack_fields(b)
# workaround
# now when I add fields, this becomes an issue
c = np.empty(b.shape, dtype=b.dtype.descr + [('c', 'i4')])
c[list(b.dtype.names)] = b
c['c'] = 1
print(c.dtype.names)
print(c['f1'])
# the void fields are filled with raw data and were given proper names
# that can be accessed
现在一种解决方法是使用numpy.lib.recfunctions.repack_fields,它会删除填充,我将来会使用它,但是对于我以前的代码,我需要修复。(虽然可能存在问题,recfunctions
因为可能找不到模块;对我来说就是这种情况,因此是附加import numpy.lib.recfunctions
声明。)
问题
这部分代码是我用来向数组添加字段的代码(基于this):
c = np.empty(b.shape, dtype=b.dtype.descr + [('c', 'i4')])
c[list(b.dtype.names)] = b
c['c'] = 1
虽然(现在我知道了)使用numpy.lib.recfunctions.require_fields可能更适合添加字段。但是,我仍然需要一种方法来删除空字段b.dtype.descr
:
[('x', '<f8'), ('', '|V8'), ('i', '<i8'), ('', '|V8')]
这只是list
的tuples
,所以我想我可以构建一个或多或少尴尬的方式(沿着 的线descr.remove(('', '|V8'))
)来处理这个问题,但我想知道是否有更好的方法,特别是因为空隙的大小取决于遗漏字段的数量,例如从 V8 到 V16,如果连续有两个,依此类推(而不是每个遗漏字段都有一个新的 void)。所以代码会很快变得非常笨重。
解决方案
In [237]: a = np.array(
...: [
...: (10.0, 13.5, 1248, -2),
...: (20.0, 0.0, 0, 0),
...: (30.0, 0.0, 0, 0),
...: (40.0, 0.0, 0, 0),
...: (50.0, 0.0, 0, 999)
...: ], dtype=[('x', '<f8'), ('y', '<f8'), ('i', '<i8'), ('j', '<i8')]
...: )
In [238]: a
Out[238]:
array([(10., 13.5, 1248, -2), (20., 0. , 0, 0),
(30., 0. , 0, 0), (40., 0. , 0, 0),
(50., 0. , 0, 999)],
dtype=[('x', '<f8'), ('y', '<f8'), ('i', '<i8'), ('j', '<i8')])
b
观点:
In [240]: b = a[['x','i']]
In [241]: b
Out[241]:
array([(10., 1248), (20., 0), (30., 0), (40., 0), (50., 0)],
dtype={'names':['x','i'], 'formats':['<f8','<i8'], 'offsets':[0,16], 'itemsize':32})
重新包装的副本:
In [243]: c = rf.repack_fields(b)
In [244]: c
Out[244]:
array([(10., 1248), (20., 0), (30., 0), (40., 0), (50., 0)],
dtype=[('x', '<f8'), ('i', '<i8')])
In [245]: c.dtype
Out[245]: dtype([('x', '<f8'), ('i', '<i8')])
您在添加字段时过度填充的尝试:
In [247]: d = np.empty(b.shape, dtype=b.dtype.descr + [('c', 'i4')])
...: d[list(b.dtype.names)] = b
...: d['c'] = 1
In [248]: d
Out[248]:
array([(10., b'\x00\x00\x00\x00\x00\x00\x00\x00', 1248, b'\x00\x00\x00\x00\x00\x00\x00\x00', 1),
(20., b'\x00\x00\x00\x00\x00\x00\x00\x00', 0, b'\x00\x00\x00\x00\x00\x00\x00\x00', 1),
...],
dtype=[('x', '<f8'), ('f1', 'V8'), ('i', '<i8'), ('f3', 'V8'), ('c', '<i4')])
我第一次尝试制作不包含Void
字段的 dtype。我不知道简单的测试V
是否足够强大:
In [253]: [des for des in b.dtype.descr if not 'V' in des[1]]
Out[253]: [('x', '<f8'), ('i', '<i8')]
并从中创建一个新的dtype:
In [254]: d_dtype = _ + [('c','i4')]
所有这些都是正常的 python 列表和元组操作。我在其他recfunctions
. 我怀疑repack_fields
会做这样的事情。
现在我们用更简单的 dtype 创建一个新数组:
In [255]: d = np.empty(b.shape, dtype=d_dtype)
In [256]: d[list(b.dtype.names)] = b
...: d['c'] = 1
In [257]: d
Out[257]:
array([(10., 1248, 1), (20., 0, 1), (30., 0, 1), (40., 0, 1),
(50., 0, 1)], dtype=[('x', '<f8'), ('i', '<i8'), ('c', '<i4')])
我从repack_fields
构造一个新的、未填充的 dtype 的代码中提取:
In [262]: def foo(a):
...: fieldinfo = []
...: for name in a.names:
...: tup = a.fields[name]
...: fmt = tup[0]
...: if len(tup) == 3:
...: name = (tup[2], name)
...: fieldinfo.append((name, fmt))
...: print(fieldinfo)
...: dt = np.dtype(fieldinfo)
...: return dt
...:
...:
In [263]: foo(b.dtype)
[('x', dtype('float64')), ('i', dtype('int64'))]
Out[263]: dtype([('x', '<f8'), ('i', '<i8')])
这适用于dtype.fields
而不是dtype.descr
. 一个是dict
另一个列表。
In [274]: b.dtype
Out[274]: dtype({'names':['x','i'], 'formats':['<f8','<i8'], 'offsets':[0,16], 'itemsize':32})
In [275]: b.dtype.descr
Out[275]: [('x', '<f8'), ('', '|V8'), ('i', '<i8'), ('', '|V8')]
In [276]: b.dtype.fields
Out[276]: mappingproxy({'x': (dtype('float64'), 0), 'i': (dtype('int64'), 16)})
In [277]: b.dtype.fields['x']
Out[277]: (dtype('float64'), 0)
另一种获取有效descr
元组的方法b.dtype
:
In [278]: [des for des in b.dtype.descr if des[0] in b.dtype.names]
Out[278]: [('x', '<f8'), ('i', '<i8')]
推荐阅读
- r - 具有多列的索引/子集时间序列对象
- swift - CIImage 和 UIImageView 上的 CGAffineTransform 的区别
- .net-core - 指定 nuspec 文件时,dotnet pack 构建文件夹输出而不是 lib 文件夹
- ruby-on-rails - Capistrano - 将 Rails 应用程序部署到 Staging
- dc.js - 有没有一种有效的方法在 dc.js 中显示带有日期范围数据的时间表?
- javascript - JavaScript中未知大小的多维数组
- swift - 为什么转义闭包需要我们明确地引用 self ?
- google-bigquery - google bigquery中的scd2表实现
- java - 如何从没有键的 JSON 数组中的数组中读取值?
- python - 使用高斯图作为 pyplot 中的误差线