Numpy 学习笔记

Numpy 学习笔记

一 Numpy 数据类型

1 Python 内部的整数

与C语言相比,Python 中的变量在赋值前无需声明变量类型,且可以重复赋给变量不同数据类型的数据,其原因在于 Python 中的数据结构与 C 语言不同。以长整型为例:

# Python 3.4 中长整型的 C 语言源码
struct _longobject {
    long ob_refcnt;			# 用于分配和释放内存的计数器
    PyTypeObject *ob_type;	# 存储数据类型
    size_t ob_size;			# 存储数据大小
    long ob_digit[1];		# 存储 long 型数字
}

可见,Python 中的长整型与 C 语言相比有许多额外的属性,这造就了 Python 语言的便捷性,但也降低了效率。

2 Python 内部的列表(list)

Python 中的列表可以存储多种类型的元素,列表中可同时存在字符、整型、浮点型等类型。其原因在于 Python 的列表存储的是一个个独立的 Python 对象,且 list 中存储的是若干指针,每一个指针指向独立的对象(形如[Python中的整数](#1 Python 内部的整数)),而 C 语言中的数组表示为内存中一段连续的数据。Python 中的 list 和 C 中的数组分别称为动态类型数组(dynamic-type)和固定类型数组(fixed-type)

两种存储结构

3 Python 内部的固定类型数组

显然的,在固定类型数组上进行运算虽然灵活性受阻,但更为高效。Python 中提供了内置的module以固定类型数组的形式来存储数据

import array
dynamic_list = list(range(10))
# 其中 'i' 表示数据为 integer 型
fixed_list = array.array('i', dynamic_list)

虽然 Python 已经提供了相应数组,但 NumPy 包中提供了类似的对象 ndarray,于此同时,该包中提供了极为丰富的运算方法,我们将在下文逐一了解。

4 创建一个 ndarray 对象

根据列表进行创建
a1 = np.array([1, 4, 2, 5, 3])

Numpy 数组要求数据类型一致,若列表中数据类型不同,如果条件允许,会进行自动类型转换。

a2 = np.array([1, 2.5])
# a2 = array([1. 2.5])

支持自定义数据类型

a3 = np.array([1, 2, 3, 4], dtype='float32')
# a3 = array([1. 2. 3. 4.] dtype=float32)

支持多维数组

a4 = np.array([range(i, i+3) for i in [2, 4, 6]])
'''
a4 = array([2 3 4]
		   [4 5 6]
		   [6 7 8])
'''
使用内置函数创建
# 创建一个有 10 个 0 的一维数组
np.zeros(10, dtype=int)
# 创建 3*5 的二维数组,元素全为 1.0
np.ones((3, 5), dtype=float)
# 自定义填充内容
np.full((3, 5), 3.14)
# 和 range() 功能相同
np.arange(0, 10, 2)
# 创建 5 个均匀分布在 [0,1] 区间的值
# 输出 array([0. 0.25 0.5 0.75 1.])
np.linspace(0, 1, 5)
# 创建 3*3 的随机矩阵,元素大小在 [0,1] 之间
np.random.random((3, 3))
# 创建均值为 0,方差为 1 的随机矩阵
np.random.normal(0, 1, (3, 3))
# 创建区间 [0,10) 内的整型矩阵
np.random.randint(0, 10, (3, 3))
# 创建对角阵
np.eye(3)
# 创建未初始化矩阵,内容由内存决定
np.empty(3)

5 NumPy 标准数据类型

NumPy 中提供了标准的数据类型,初始化数组时我们可以指定数据类型,以下两种定义方式是等价的

np.zeros(10, dtype='int16')
np.zeros(10, dtype=np.int16)

更多的数据类型见下表

更多的数据类型

二 NumPy 数组基础知识

1 NumPy 数组的属性

数组 x 定义如下

x = np.random.randint(10, size=(3, 4, 5))
属性名解释属性调用属性值
ndim数组的维度print(x.ndim)3
shape数组各个维度大小组成的元组print(x.shape)(3, 4, 5)
size数组中元素的个数print(x.size)60
dtype数组中元素的数据类型print(x.dtype)int64
itemsize每个元素所占字节print(x.itemsize)8 bytes
nbytes整个数组所占字节print(x.nbytes)480 bytes

2 数组索引

数组索引用于访问单个元素,其使用方法与 Python 中的列表类似

数组访问:

# 一维数组
x = np.array([1, 2, 3, 4, 5])
print(x[0])
print(x[-1])
''' 输出结果为
1
5
'''
# 二维数组
m = np.array([[1, 2, 3], [4, 5, 6]])
print(x[1, 2]) # 表示访问第1行第2列(从0开始计数)
print(x[-1, 2])
'''输出结果为
6
6
'''

于此同时,可以利用类似的方式修改数组中的元素

x = np.array([[1, 2, 3], [4, 5, 6]])
x[0, 0] = 0

3 数组切片

一维数组切片
x = np.arange(10)
print(x[:5])
print(x[5:])
print(x[4:7])
print(x[::2])
print(x[1::2])
print(x[::-1])
print(x[5:-1:-2])
二维数组切片

其中逗号前表示行的切片,逗号后表示列的切片

x = np.random.ranint(10, (3, 4))
print(x[:2, :3])
print(x[:3, ::2])
print(x[::-1, ::-1])
获取二维数组的某一行和某一列

将索引与切片相结合即可得到某一行和某一列

# 获取第 0 列
print(x[:, 0])
# 获取第 0 行
print(x[0, :])

值得注意的是,根据切片得到的子数组相当于原数组的引用(C++中的概念)。即,若我们改变索引子数组中的元素,原数组中的元素会对应的改变。

a = np.zeros((2, 2))
b = a[0, :]
b[0, 0] = 1
print(a)
'''输出结果
[[1 0]
 [0 0]]
'''

如果我们想要获取原数组的复制而非引用,可以使用copy函数

b = a[0, :].copy()

4 定义数组的形状

将一维数组转化成成 3 × 3 3\times3 3×3 的二维数组

x = np.arange(1, 10).reshape(3, 3)

将一个拥有 10 个元素的一维数组转化成 1 × 10 1\times10 1×10 10 × 1 10\times1 10×1 的二维数组

x = np.arange(10) # 创建一个一维数组
# 使用 reshape 方法
y1 = x.reshape(1, 10)
y2 = x.reshape(10, 1)
# 使用 newaxis 关键字
z1 = x[np.newaxis, :]
z2 = x[:, np.newaxis]

上述代码中,y1z1 等价,y2z2 等价

5 数组的合并与分解

数组的合并

数组的合并有三个方法,分别为 np.concatenate()np.vstack()np.hstack()

np.concatenate()数组的元组或列表作为其参数

x = np.array([1, 2, 3])
y = np.array([3, 2, 1])
z = np.array([6, 6, 6])
print(np.concatenate([x, y]))
# 输出:[1 2 3 3 2 1]
print(np.concatenate([x, y, z]))
# 输出:[1 2 3 3 2 1 6 6 6]

对于二维数组,使用 concatenate() 方法需指定合并的方向

x = np.zeros((2, 2))
y = np.ones((1, 1))
xy_in_vertical = np.concatenate([x, y], axis=0)
xy_in_horizontal = np.concatenate([x, y], axis=1)

对于二维数组的合并,使用 vstack()hstack() 会更为清晰

xy_in_vertical = np.vstack([x, y])
xy_in_horizontal = np.hstack([x, y])
数组的分解

对于 NumPy 中数组的分解,其提供了 np.split()np.vsplit()np.hsplit() 三个函数。其参数为一个整型数组,数组中的每个数字代表了一个分离点

x = [1, 2, 3, 99, 99, 3, 2, 1]
x1, x2, x3 = np.split(x, [3, 5])
print(x1, x2, x3)
# 输出:[1 2 3] [99 99] [3 2 1]

二维数组的分离

grid = np.arange(16).reshape(4, 4)
upper, lower = np.vsplit(grid, [2])
left, right = np.hsplit(gird, [2])

此外,NumPy 还提供了 np.dsplit() 来对第三维进行分解

三 NumPy 数组上的运算

NumPy 提供了极其丰富的函数支持,这些函数与 Python 内置的函数相比,可以大大提高运算速度。

考虑将数组 a 中的元素全部取倒数

  • 常规方法

    a = np.random.randint(1, 10, size=10000)
    for i in range(len(a)):
        a[i] = 1/a[i]
    
  • 使用内置函数

    a = np.random.randint(1, 10, size=10000)
    a = 1/a
    

第二种方法运算速度远远大于第一种方法,原因在于第一种方法使用 Python 自带的循环,每次运算均需要检测数据类型,而第二种方法采用 NumPy 自带的方法,在底层进行了优化,无需重复检测数据类型。

由此,在 Numpy 数组的运算中,尤其是大规模的数据处理,我们要尽可能的使用自带的函数来替代循环。

1 常见函数

运算符的重载

NumPy 数组支持直接使用运算符进行运算。例如:

x = np.arange(4)
print(x + 5)
# 输出 [5 6 7 8]
print((x + 5)//2)
# 输出 [2 3 3 4]

于此同时,每个运算符还有等价的函数形式,x+5 等价于 x.add(5)。以下为运算符重载的表格:

运算符等价函数
+np.add()
-np.substract()
-np.negative()(取相反数)
*np.multiply()
/np.divide()
//np.floor_divide()
**np.power()
%np.mod()
绝对值

np.abs()np.absolute() 两者等价,返回各项的绝对值,对于复数,返回复数的大小

三角函数

np.sin()np.cos()np.tan()np.arcsin()np.arccos()np.arctan()

指数函数和对数函数
  • 指数函数

    np.exp()np.exp2()np.power(),分别计算 e 的指数,2 的指数以及自定义的指数。

  • 对数函数

    np.log()np.log2()np.log10(),分别计算以 e 为底的对数,以 2 为底的对数和以 10 为底的对数

  • 若输入为微小值,可以使用如下两个函数

    • np.expm1(x):其输出为 e x − 1 e^x-1 ex1
    • np.log1p(x):其输出为 l o g ( 1 + x ) log(1+x) log(1+x)

2 较高级的函数特性

指定输出

使用函数中的 out 参数可以直接存储运算结果,对于常规运算如

x = np.arange(10)
y = np.empty(10)
y = np.multiply(x, 10)

其会首先将 np.multiply(x, 10) 的运算结果存储于临时数组中,再将临时数组的内容复制到 y 变量中,这样会带来额外的空间和时间的开销(尤其对于大规模的数据),可以采用如下方法:

x = np.arange(10)
y = np.empty(10)
np.multiply(x, 10, out=y)

其中 out 参数亦可指向某一个数组的索引

x = np.arange(5)
y = np.zeros(10)
np.power(2, x, out=y[::2])
print(y)
# 输出 [1 0 2 0 4 0 8 0 16 0]
agg 函数
  • reduce() 方法

    reduce() 方法可以加在任何的计算函数之后,该方法依次作用于数组中的元素,不断缩小数组的长度,直到数组中最后仅留下一个元素。

    x = np.arange(1, 6)
    print(np.add.reduce(x))
    # 输出结果:1+2+3+4+5=15
    print(np.multiply.reduce(x))
    # 输出结果:1*2*3*4*5=120
    
  • accumulate() 方法

    accumulate() 方法可以将上边 reduce() 运算过程中的中间结果存储在数组中在。

    x = np.arange(1, 6)
    print(np.add.accumulate(x))
    # 输出结果:[1 3 6 10 15]
    print(np.multiply.accumulate(x))
    # 输出结果:[1 2 6 24 120]
    
外积

使用 outer() 方法和其他计算函数的组合,可以计算两个不同数组之间的每一对元素之间的函数值

x = np.arange(1, 6)
print(np.multiply.outer(x, x))
''' 输出结果
[[1  2  3  4  5]
 [2  4  6  8 10]
 [3  6  9 12 15]
 [4  8 12 16 20]
 [5 10 15 20 25]]
'''

四 聚集函数

Python 中绝大部分聚集函数都提供有 NaN 安全版本,即可以忽略数组中的 NaN 值。

下表为部分 NumPy 中的聚集函数

函数名NaN 安全版本描述
np.sum()np.nansum()求和
np.prod()np.nanprod()求乘积
np.mean()np.nanmean()求平均值
np.std()np.nanstd()求标准差
np.var()np.nanvar()求方差
np.min()np.nanmin()求最小值
np.max()np.nanmax()求最大值
np.argmin()np.nanargmin()返回最小值的索引
np.argmax()np.nanargmax()返回最大值的索引
np.median()np.nanmedian()返回中位数
np.percentile()np.nanpercentile()以百分数的形式,展示
共有多少数字小于当前
位置的数字
np.any()N/A若任意元素不为0,则结果为真
np.all()N/A若所有元素不为0,则结果为假

二维数组同样可以应用上述函数,使用轴(axis)的概念即可,即通过指定轴来决定统计的方向(行,列),若不使用轴,则统计二维数组所有元素。

a = np.array([[1, 2, 3], [4, 5, 6]])
print(np.sum(a, axis=0))
# 输出:[5 7 9]
print(np.sum(a, axis=1))
# 输出:[6 15]
print(np.sum(a))
# 输出:21

附两个链接:

五 数组计算中的广播机制

当 NumPy 中两个不同大小的数组进行运算时,就需要用到广播机制来使得两个数组大小匹配,从而进行运算。广播机制源自 broadcasting,但我认为称其扩充机制更为贴切。

1 广播机制引入

对于相同大小的数组,二元运算函数会对两个数组中相同位置的元素进行计算

a = np.array([0, 1, 2])
b = np.array([5, 5, 5])
print(a + b)
# 输出:[5 6 7]

对于不同大小的两个数组,我们同样可以进行类似计算

print(a + 5)
# 输出:[5 6 7]

在这里,我们可以想象元素 5 被扩展成了 [5, 5, 5]

类似的

c = np.ones((3, 3))
print(a + c)
'''输出:
[[1 2 3]
 [1 2 3]
 [1 2 3]]
'''

在这里,我们可以认为数组 a 在运算中被自动扩展成了

[[0, 1, 2],
 [0, 1, 2],
 [0, 1, 2]]

更为复杂的例子:

x = np.arange(3)
y = np.arange(3)[:, np.newaxis] # 还记得该用法吗?
print(a + b)
'''输出:
[[0 1 2]
 [1 2 3]
 [2 3 4]]
'''

在上边的例子中,可以发现得到结果的维度与 xy 均不同。

上述例子广播机制图解

在实际的计算中,内存内部并不存在类似的过程,但上述过程可以帮助我们更好的理解广播机制的应用。

2 广播机制的规则

  1. 如果两个数组的维度不同,维度较小的那一个数组的 shape 属性左边加 1,直到两个数组维度相同
  2. 若两个数组的 shape 属性某一个位置上的值不同,且有一方该位置上的数字为 1,则将此处的 1 延伸至与另一个数组相同
  3. 若执行了 12 两个步骤后,大小仍不匹配,则产生错误

举例说明:

M = np.ones((2, 3))
a = np.arange(3)
  1. 初始时,M.shape=(2,3)a.shape=(3,)
  2. 使用规则 1,有 M.shape=(2,3)a.shape=(1,3)
  3. 使用规则 2,有 M.shape=(2,3)a.shape=(2,3)
  4. 使用规则 3,变化后两个数组大小匹配,不产生错误
a = np.arange(3).reshape((3, 1))
b = np.arange(3)
  1. 初始时,a.shape=(3, 1)b.shape=(3,)
  2. 使用规则 1,有 a.shape=(3,1)b.shape=(1,3)
  3. 使用规则 2,有 a.shape=(3,3)b.shape=(3,3)
  4. 使用规则 3,此时两个数组大小匹配,不产生错误

a = np.ones((3, 2))
b = np.arange(3)

a+b 运算无法满足广播规则,将产生报错。

3 广播机制的应用

中心化
X = np.random.random((10, 3)) # 产生十行数据,每一行表示一组观测值,一组观测值含三个数
Xmean = X.mean(axis=0)
X_centered -= X.mean # X_centered 的每一列都是中心化的数据
绘制二维函数图像

定义函数 z = f ( x , y ) z=f(x,y) z=f(x,y) ,广播机制有助于我们求该函数在不同坐标下的值。

假设定义 z = s i n 10 ( x ) + c o s ( 10 + x ⋅ y ) ⋅ c o s ( x ) z=sin^{10}(x)+cos(10+x\cdot y)\cdot cos(x) z=sin10(x)+cos(10+xy)cos(x),则可以通过下列运算计算其在各个坐标上的值

x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 50)[:, np.newaxis]
z = np.sin(x) + np.cos(10 + x * y) * np.cos(x)

我们可以进而绘制函数图像

import matplotlib.pyplot as plt
plt.imshow(z, origin='lower', extent=[0, 5, 0, 5], cmap='viridis')
plt.colorbar();

绘制出的函数图像

六 布尔掩码

1 比较运算符

NumPy 中除了提供通常的加减乘除等运算符,还提供了比较运算符,用于逐元素的比较两个数组中的元素,返回一个 Boolean 类型的数组。例如:

x = np.array([1, 2, 3, 4, 5])
print(x < 3)
# 输出:[True True False False False] dtype=bool
print(2 * x == x ** 2)
# 输出:[False True False False False] dtype=bool

常见的比较运算符以及对应的函数形式如下表:

比较运算符通用函数形式
==np.equal()
!=np.not_equal()
<np.less()
<=np.less_equal()
>np.greater()
>=np.greater_equal()

2 布尔数组的应用

对于数组之间比较生成的结果,可以大大方便我们对数据的处理。

首先有

x = [[5, 0, 3, 3], [7, 9, 3, 5], [2, 4, 7, 6]]

接着,我们可以对满足条件的元素个数进行统计

np.count_nonzero(x < 6)
np.sum(x < 6)

上述两种方法是等价的,其目的都是统计 x 中小于 6 的元素的个数。而对于 sum 方法,可以进一步统计每一维的数据:

np.sum(x < 6, axis=1) # 统计每一行小于 6 的元素的个数
# 返回数组为 [4 2 2]

可以判断是否所有元素或某一个元素为真

# 判断是否有大于 8 的元素
np.any(x > 8)
# 判断是否全都大于 8
np.all(x > 8)

我们可以使用布尔运算符来丰富我们的选择条件:

# 计算 x 中所有大于 2 且小于 8 的元素的个数
np.sum((x > 2) & (x < 8))

支持的布尔运算符及支持的函数形式:

运算符函数形式
&np.bitwise_and()
|np.bitwise_or()
^np.bitwise_xor()
~np.bitwise_not()

3 使用布尔数组作为掩码

如果我们想要知道数组中所有满足条件的元素有哪些,我们可以采用布尔数组作为掩码,例如:

x = [[1, 2, 3], [4, 5, 6]]
print(x < 5)
'''输出:
[[True True True]
 [True False False]]
'''
print(x[x < 5])
# 输出:[1 2 3 4]

在前边,有索引和切片两种访问 NumPy 数组的方式,而这里我们可以通过布尔数组对 NumPy 数组进行访问。

七 花式索引

花式索引是索引的升级版,可以通过传入数组的方式同时访问到多个元素。

1 花式索引介绍

创建这样一个数组

x = np.arange(1, 10, 2)
print(x)
# 输出:[1 3 5 7 9]

如果想要同时访问三个不同的元素,最简单的方法可以这样

print([x[1], x[2], x[4]])
# 输出:[3 5 9]

但是通过花式索引我们可以更简洁的访问

ind = [1, 2, 4]
print(x[ind])
# 输出:[3 5 9]

我们通过向数组传入一个索引列表,进而可以得到各个位置的元素。很重要的一点是,通过花式索引输出的结果的形状,仅与索引数组的形状有关,而与被索引数组的形状无关。例如:

ind = array([[0, 1], [2, 3]])
print(x[ind])
'''输出结果:
[[1 3]
 [5 7]]
'''

同样的,我们可以访问多维数组

x = np.arange([[1, 3, 7], [2, 4, 2]])
row = [0, 1, 1]
col = [0, 1, 2]
print(x[row, col])
'''输出结果:
[1 4 2]
'''

这里我们访问的位置是 (0, 0)、(1, 1)和(1, 2),即 rowcol 中的元素一一匹配形成三个元组对 x 进行访问。rowcol 的配对满足广播规则,例如:

x = np.array([[1, 3, 7], [2, 4, 2]])
row = np.array([0, 1, 1])
col = np.array([0, 1, 2])[:, np.newaxis]
print(x[row, col])
'''输出结果:
[[1 2 2]
 [3 4 4]
 [7 2 2]]
'''

2 混合索引

花式索引可以与普通索引方式、切片索引方式和掩码索引方式结合起来使用,例如:

x = np.arange(12).reshape(3, 4)
print(x)
'''
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
'''
# 普通索引与花式索引结合
print(x[2, [2, 0, 1]])
'''
[10  8  9]
'''
# 切片索引和花式索引结合
print(x[1:, [2, 0, 1]])
'''
[[ 6  4  5]
 [10  8  9]]
'''
# 掩码索引与花式索引结合
mask = np.array([1, 0, 1, 0], dtype=bool)
row = np.array([0, 1, 2])
print(x[row[:, np.newaxis], mask])
'''
[[ 0  2]
 [ 4  6]
 [ 8 10]]
'''

3 通过花式索引改变数组的值

简单的改变数组的值

x = np.arange(10)
i = np.array([2, 1, 8, 4])
x[i] = 99
print(x)
'''
[ 0 99 99  3 99  5  6  7 99  9]
'''

传入与索引等长的数列,随后改变其值

x = np.zeros(10)
x[i] = i
print(x)
'''
[0. 1. 2. 0. 4. 0. 0. 0. 8. 0.]
'''

若索引数组中存在相同的数,则对该索引对应位置的元素重复赋值

x = np.zeros(10)
x[[0, 0]] = [1, 2]
print(x)
'''
[2. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
'''

同时可以对索引使用运算符进行计算,对于相同索引不会重复相加

x = np.zeros(10)
x[[0, 0]] += 1
print(x)
'''
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
'''

如果想要对索引位置进行独立重复操作,可以使用 .at 方法

x = np.zeros(10)
i = [0, 0, 1]
np.add.at(x, i, 1)
print(x)
'''
[2. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
'''

八 数组排序

1 快排

使用 np.sort() 函数可以将排序后的结果存储在额外的数组中,而原数组不会发生改变

x = np.array([2, 1, 4, 3, 5])
np.sort(x)

使用数组的 sort() 方法,可以对原数组进行排序

x.sort()

使用 np.argsort() 函数,可以得到排序后的索引

ind = np.argsort(x)
print(x[ind])
'''
[1 2 3 4 5]
'''

同样的,可以单独对行排序或对列排序

rand = np.random.RandomState(42)
X = rand.randint(0, 10, (4, 6))
# 对每一列进行排序
np.sort(X, axis=0)
# 对每一行进行排序
np.sort(X, axis=1)

2 部分排序

借助 np.partition() 函数,我们可以得到数组中前 K 小的数字

X = np.array([7, 2, 3, 1, 6, 5, 4])
print(np.partition(X, 3))
'''
[2 1 3 4 6 5 7]
'''

此时,前 3 小的元素被放在前 3 个位置上,但是无论是前 3 个位置,还是后边的元素,都呈乱序排列。同样,部分排序也可以按列或按行排序。

九 NumPy 中的结构化数组

NumPy 中的数组同样支持复合数据类型的元素,可以按照如下方式进行定义

name = ['Alice', 'Bob', 'Cathy', 'Doug']
age = [25, 45, 37, 19]
weight = [55.0, 85.5, 68.0, 61.5]
data = np.zeros(4, dtype={'names':('name', 'age', 'weight'),
                          'formats':('U10', 'i4', 'f8')})
data['name'] = name
data['age'] = age
data['weight'] = weight
# 查看总体情况
print(data)
'''
[('Alice', 25, 55. ) ('Bob', 45, 85.5) ('Cathy', 37, 68. )
 ('Doug', 19, 61.5)]
'''

# 仅根据属性进行查看
print(data['name'])
'''
['Alice' 'Bob' 'Cathy' 'Doug']
'''

# 根据索引进行查看
print(data[0])
'''
('Alice', 25, 55.)
'''

# 进行更为复杂的索引
print(data[data['age']<30]['name'])
'''
['Alice' 'Doug']
'''

1 复合结构数组的创建

  • 字典法

    type1 = np.dtype({'names':('name', 'age', 'weight'),
                      'formats':('U10', 'i4', 'f8')})
    type2 = np.dtype({'names':('name', 'age', 'weight'),
                      'formats':((np.str_, 10), int, np.float32)})
    
  • 列表法

    type3 = np.dtype([('name', 'U10'), ('age', 'i4'), ('weight', 'f8')])
    
  • 元组法

    若不需要属性名,则可以直接定义数据类型

    type4 = np.dtype('S10,i4,f8')
    

其中的 'i4''f8' 为数据类型的缩写,其完整格式为 [> or <]xn ,其中 >< 可选,代表高位优先和地位优先,x 表示具体的数据类型,n 表示数据类型所占的字节数。

字符数据类型
b字节
i有符号整数
u无符号整数
f浮点类型
c浮点复数类型
S、a字符串类型
UUnicode 字符串
V空类型

2 较为复杂的结构

tp = np.dtype[('id', 'i8'), ('mat', 'f8', (3, 3))]

上述数据结构包含两个元素,第一个元素为一个 64 位整数,第二个类型为一个 3 × 3 3\times3 3×3 的矩阵,元素类型为 64 位浮点数。

由于 NumPy 的 dtype 类型直接对应于 C 语言的结构体,因此在编写 Python 和 C 语言的接口程序时,dtype 数据类型极为有用。

3 记录数组

NumPy 中提供了 np.recarray 类,该类可以使得用户通过 .属性名 的方式访问属性,而非通过字典键索引的方式访问属性。

name = ['Alice', 'Bob', 'Cathy', 'Doug']
age = [25, 45, 37, 19]
weight = [55.0, 85.5, 68.0, 61.5]
data = np.zeros(4, dtype={'names':('name', 'age', 'weight'),
                          'formats':('U10', 'i4', 'f8')})
data['name'] = name
data['age'] = age
data['weight'] = weight

data_rec = data.view(np.recarray)
print(data_rec.name)

这样可以更方便的访问元素,但 recarray 类上的访问速度慢于 ndarray 上的访问速度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值