1.3.3. 更复杂的数组

1.3.3.1. 更多数据类型

1.3.3.1.1. 类型转换

混合型操作中的“较大”类型:

>>> np.array([1, 2, 3]) + 1.5
array([ 2.5, 3.5, 4.5])

赋值不会改变类型!

>>> a = np.array([1, 2, 3])
>>> a.dtype
dtype('int64')
>>> a[0] = 1.9 # <-- float is truncated to integer
>>> a
array([1, 2, 3])

强制转换:

>>> a = np.array([1.7, 1.2, 1.6])
>>> b = a.astype(int) # <-- truncates to integer
>>> b
array([1, 1, 1])

四舍五入:

>>> a = np.array([1.2, 1.5, 1.6, 2.5, 3.5, 4.5])
>>> b = np.around(a)
>>> b # still floating-point
array([ 1., 2., 2., 2., 4., 4.])
>>> c = np.around(a).astype(int)
>>> c
array([1, 2, 2, 2, 4, 4])

1.3.3.1.2. 不同数据类型的大小

整数(带符号):

int8 8位
int16 16位
int32 32位(与32位平台上的int相同)
int64 64位(与64位平台上的int相同)
>>> np.array([1], dtype=int).dtype
dtype('int64')
>>> np.iinfo(np.int32).max, 2**31 - 1
(2147483647, 2147483647)

无符号整数:

uint8 8位
uint16 16位
uint32 32位
uint64 64位
>>> np.iinfo(np.uint32).max, 2**32 - 1
(4294967295, 4294967295)

浮点数:

float16 16位
float32 32位
float64 64位(与float相同)
float96 96位,平台相关(与np.longdouble相同)
float128 128位,平台相关(与np.longdouble相同)
>>> np.finfo(np.float32).eps
1.1920929e-07
>>> np.finfo(np.float64).eps
2.2204460492503131e-16
>>> np.float32(1e-8) + np.float32(1) == 1
True
>>> np.float64(1e-8) + np.float64(1) == 1
False

复数浮点数:

complex64 两个32位浮点数
complex128 两个64位浮点数
complex192 两个96位浮点数,平台相关
complex256 两个128位浮点数,平台相关

较小的数据类型

如果你不知道你需要特殊的数据类型,那么你只是可能不需要。

使用float32而不是float64的比较:

  • 在内存中和磁盘上大小相差一半

  • 需要内存带宽相差一半(在某些操作中可能会快一点)

    In [1]: a = np.zeros((1e6,), dtype=np.float64)
    
    In [2]: b = np.zeros((1e6,), dtype=np.float32)
    In [3]: %timeit a*a
    1000 loops, best of 3: 1.78 ms per loop
    In [4]: %timeit b*b
    1000 loops, best of 3: 1.07 ms per loop
  • 但是:舍入误差更大 —— 有时令人惊讶(不要使用它们,除非你真的需要它们)

1.3.3.2. 结构化数据类型

sensor_code (4字符字符串)
position (浮点数)
value (浮点数)
>>> samples = np.zeros((6,), dtype=[('sensor_code', 'S4'),
... ('position', float), ('value', float)])
>>> samples.ndim
1
>>> samples.shape
(6,)
>>> samples.dtype.names
('sensor_code', 'position', 'value')
>>> samples[:] = [('ALFA', 1, 0.37), ('BETA', 1, 0.11), ('TAU', 1, 0.13),
... ('ALFA', 1.5, 0.37), ('ALFA', 3, 0.11), ('TAU', 1.2, 0.13)]
>>> samples
array([('ALFA', 1.0, 0.37), ('BETA', 1.0, 0.11), ('TAU', 1.0, 0.13),
('ALFA', 1.5, 0.37), ('ALFA', 3.0, 0.11), ('TAU', 1.2, 0.13)],
dtype=[('sensor_code', 'S4'), ('position', '<f8'), ('value', '<f8')])

字段访问通过字段名索引:

>>> samples['sensor_code']    
array(['ALFA', 'BETA', 'TAU', 'ALFA', 'ALFA', 'TAU'],
dtype='|S4')
>>> samples['value']
array([ 0.37, 0.11, 0.13, 0.37, 0.11, 0.13])
>>> samples[0]
('ALFA', 1.0, 0.37)
>>> samples[0]['sensor_code'] = 'TAU'
>>> samples[0]
('TAU', 1.0, 0.37)

一次多个字段:

>>> samples[['position', 'value']]
array([(1.0, 0.37), (1.0, 0.11), (1.0, 0.13), (1.5, 0.37), (3.0, 0.11),
(1.2, 0.13)],
dtype=[('position', '<f8'), ('value', '<f8')])

花式索引像往常一样工作:

>>> samples[samples['sensor_code'] == 'ALFA']    
array([('ALFA', 1.5, 0.37), ('ALFA', 3.0, 0.11)],
dtype=[('sensor_code', 'S4'), ('position', '<f8'), ('value', '<f8')])

注意

有一些其他语法用于构造结构化数组,请参见此处此处

1.3.3.3. maskedarray:处理丢失数据(的传播)

  • 对于浮点数,可以使用NaN,但掩码适用于所有类型:

    >>> x = np.ma.array([1, 2, 3, 4], mask=[0, 1, 0, 1])
    
    >>> x
    masked_array(data = [1 -- 3 --],
    mask = [False True False True],
    fill_value = 999999)
    >>> y = np.ma.array([1, 2, 3, 4], mask=[0, 1, 1, 1])
    >>> x + y
    masked_array(data = [2 -- -- --],
    mask = [False True True True],
    fill_value = 999999)
  • 常用函数的掩码版本:

    >>> np.ma.sqrt([1, -1, 2, -2]) 
    
    masked_array(data = [1.0 -- 1.41421356237... --],
    mask = [False True False True],
    fill_value = 1e+20)

注意

还有其他有用的数组的类似类型


虽然这是一个关于numpy的章节的主题,让我们花一点时间回忆良好的编码实践,这从长远来看真的有回报:

良好做法

  • 显式变量名(不需要注释来解释变量中是什么)

  • 样式:逗号、=等后面带空格。

    Python代码样式指南文档字符串约定页面(以管理帮助字符串)中提供了一定数量的用于编写“漂亮”代码的规则(更重要的是,使用与其他人相同的约定)。Python代码样式指南Docstring约定页面(以管理帮助字符串)中给出。

  • 除了一些罕见的情况,变量名和注释用英语。