NPBG2E 2 Numpy 基础

NumPy 基础


最近是突然发现这本书有些 Numpy 的使用,然后里面涉及的一些例子是用了金融的例子,就想起来看一下,更多参考 《Python数据分析基础教程:NumPy学习指南(第2版)》

NumPy 数组对象

NumPy 中的 ndarray 是一个多维数组对象,该对象由两部分组成:

  • 实际的数据;
  • 描述这些数据的元数据(metadata)。

大部分的数组操作仅仅修改元数据部分,而不改变底层的实际数据。

NumPy 数组一般是同质的(但有一种特殊的数组类型例外,它是异质的),即数组中的所有元素类型必须是一致的。这样有一个好处:如果我们知道数组中的元素均为同一类型,该数组所需的存储空间就很容易确定下来。

与 Python 一样,NumPy 数组的下标也是从 0 开始的。数组元素的数据类型用专门的对象表示。

数组的 shape 属性返回一个元组(tuple),元组中的元素即为 NumPy 数组每一个维度上的大小。

创建多维数组

>>> m = np.array([np.arange(2), np.arange(2)])
>>> m
array([[0, 1],
       [0, 1]])
>>> m.shape
(2, 2)
>>>

np.array 函数可以依据给定的对象生成数组。给定的对象应是类数组,如 Python 中的列表。像这样的类数组对象是 np.array 函数的唯一必要参数,其余的诸多参数均为有默认值的可选参数。第二个常用的参数应该是 dtype

选取数组元素

有时候,我们需要选取数组中的某个特定元素。可以一次选取数组中的每个元素,尽管我觉得这不常用。需要注意的是,数组的下标是从 0 开始的。

>>> a = np.array([[1, 2], [3, 4]])
>>> a
array([[1, 2],
       [3, 4]])
>>> a[0, 0]
1
>>> a[0, 1]
2
>>> a[1, 0]
3
>>> a[1, 1]
4
>>>

对于数组 a,只需要用 a[m,n] 选取各数组元素,其中 mn 为元素下标。

NumPy 数值类型

Python 支持的数据类型有整型、浮点型以及复数型,但这些类型不足以满足科学计算的需求,因此 NumPy 添加了很多其他的数据类型。在实际应用中,我们需要不同精度的数据类型,它们占用的内存空间也是不同的。

类 型描 述
bool用一位存储的布尔类型(值为 TRUE 或 FALSE)
inti由所在平台决定其精度的整数(一般为 int32 或 int64)
int8整数,范围为 -128 至 127
int16整数,范围为 -32768 至 32767
int32整数,范围为 - 2 31 2^{31} 231 2 31 2^{31} 231 -1
int64整数,范围为 - 2 63 2^{63} 263 2 63 2^{63} 263 -1
uint8无符号整数,范围为 0 至 255
uint16无符号整数,范围为 0 至 65535
uint32无符号整数,范围为 0 至 2 32 2^{32} 232-1
uint64无符号整数,范围为 0 至 2 64 2^{64} 264-1
float16半精度浮点数(16 位):其中用 1 位表示正负号,5 位表示指数,10 位表示尾数
float32单精度浮点数(32 位):其中用 1 位表示正负号,8 位表示指数,23 位表示尾数
float64或float双精度浮点数(64 位):其中用1位表示正负号,11 位表示指数,52 位表示尾数
complex64复数,分别用两个 32 位浮点数表示实部和虚部
complex128或complex复数,分别用两个 64 位浮点数表示实部和虚部

对于浮点类型,我们可以使用 finfo() 函数获取信息:

>>> np.finfo(np.float32)
finfo(resolution=1e-06, min=-3.4028235e+38, max=3.4028235e+38, dtype=float32)
>>>

每一种数据类型均有对应的类型转换函数:

>>> np.float64(False)
0.0
>>> np.float64(42)
42.0
>>> np.int8(42.0)
42
>>> np.bool_(42)
True
>>> np.bool_(0)
False
>>> np.bool_(42.0)
True
>>> np.float64(True)
1.0
>>> np.float64(False)
0.0
>>>

在 NumPy 中,许多函数的参数中可以指定数据类型,通常这个参数是可选的:

>>> np.arange(7, dtype=np.uint8)
array([0, 1, 2, 3, 4, 5, 6], dtype=uint8)
>>>

需要注意的是,复数是不能转换为整数或者浮点,这将触发 TypeError 错误:

>>> np.int16(42.0 + 1.0j)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'complex'
>>>

不过,浮点数却可以转换为复数。

>>> np.complex64(1.0)
(1+0j)
>>>

数据类型对象

数据类型对象是 numpy.dtype 类的实例。如前所述,NumPy 数组是有数据类型的,更确切地说,NumPy 数组中的每一个元素均为相同的数据类型。数据类型对象可以给出单个数组元素在内存中占用的字节数,即 dtype 类的 itemsize 属性:

>>> a.dtype.itemsize
4
>>>

用字符指定数据类型

NumPy 可以使用字符来表示数据类型,这是为了兼容 NumPy 的前身 Numeric。不推荐使用字符,但有时会用到,因此下面还是列出了字符编码的对应表。应该优先使用 dtype 对象来表示数据类型,而不是这些字符编码。

数据类型字符
整数i
无符号整数u
单精度浮点数f
双精度浮点数d
布尔值b
复数D
字符串S
unicode 字符串U
void(空)V
>>> np.arange(7, dtype="f")
array([0., 1., 2., 3., 4., 5., 6.], dtype=float32)
>>> np.arange(7, dtype="D")
array([0.+0.j, 1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j, 6.+0.j])
>>>

数据类型构造函数

Python 类具有函数,如果它们属于一个类,则称为方法。其中一些方法很特殊,用于创建新对象。这些专门的方法称为构造函数。有很多的方法来创建一个数据类型,这里以浮点数为例:

>>> np.dtype(float)  # Use the general Python float
dtype('float64')
>>> np.dtype("f")  # Specify a single precision float with a character code
dtype('float32')
>>> np.dtype("d")  # Use a double precision float character code
dtype('float64')
>>>

还可以将两个字符作为参数传给数据类型的构造函数。此时,第一个字符表示数据类型,第二个字符表示该类型在内存中占用的字节数(2、4、8 分别代表精度为 16、32、64 位的浮点数):

>>> np.dtype("f8")
dtype('float64')
>>>

完整的 NumPy 数据类型列表可以在 sctypeDict.keys() 中找到:

>>> np.sctypeDict.keys()
dict_keys(['?', 0, 'byte', 'b', 省略一些
>>>

dtype 类的属性

dtype 类有很多有用的属性。char 可以获取数据类型的字符编码,type 属性对应于数组元素的数据类型:

>>> t = np.dtype("float64")
>>> t.char
'd'
>>> t.type
<class 'numpy.float64'>
>>>

str 属性可以给出数据类型的字符串表示,该字符串的首个字符表示字节序(endianness),后面如果还有字符的话,将是一个字符编码,接着一个数字表示每个数组元素存储所需的字节数。这里,字节序是指位长为 32 或 64 的字(word)存储的顺序,包括大端序(big-endian)和小端序(little-endian)。大端序是将最高位字节存储在最低的内存地址处,用 > 表示;与之相反,小端序是将最低位字节存储在最低的内存地址处,用 < 表示:

>>> t.str
'<f8'
>>>

创建自定义数据类型

自定义数据类型是一种异构数据类型,可以当做用来记录电子表格或数据库中一行数据的结构。

>>> t = np.dtype([("name", np.str_, 40), ("num_items", np.int32), ("price", np.float32)])
>>> t
dtype([('name', '<U40'), ('num_items', '<i4'), ('price', '<f4')])
>>> t['name']
dtype('<U40')
>>> itemz = np.array([("Meaning of life DVD", 42, 3.14), ("Butter", 13, 2.72)], dtype=t)
>>> itemz
array([('Meaning of life DVD', 42, 3.14), ('Butter', 13, 2.72)],
      dtype=[('name', '<U40'), ('num_items', '<i4'), ('price', '<f4')])
>>>

在用 np.array 函数创建数组时,如果没有在参数中指定数据类型,将默认为浮点数类型。而现在,我们想要创建自定义数据类型的数组,就必须在参数中指定数据类型,否则将触发 TypeError 错误。

一维数组的索引和切片

一维数组的切片操作与 Python 列表的切片操作很相似。

>>> a = np.arange(9)
>>> a[3:7]
array([3, 4, 5, 6])
>>> a[:7:2]
array([0, 2, 4, 6])
>>> a[::-1]
array([8, 7, 6, 5, 4, 3, 2, 1, 0])
>>>

和 Python 中一样,我们也可以利用负数下标翻转数组。

多维数组的切片和索引

ndarray 支持在多维数组上的切片操作。为了方便起见,我们可以用一个省略号(…)来表示遍历剩下的维度。

>>> b = np.arange(24).reshape(2, 3, 4)
>>> b.shape
(2, 3, 4)
>>> b
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>>
>>> b[0, 0, 0]
0
>>> b[:, 0, 0]
array([ 0, 12])
>>> b[0]
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> b[0, :, :]
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> b[0, ...]
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>>

多个冒号可以用一个省略号(…)来代替。

如果在多维数组中执行翻转一维数组的命令,将在最前面的维度上翻转元素的顺序:

>>> b[::-1]
array([[[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]],

       [[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]]])
>>>

改变数组的维度

除了 reshape 函数,还有一些函数可以将数组数组展平。ravel 函数与 flatten 函数的功能相同。不过,flatten 函数会请求分配内存来保存结果,而 ravel 函数只是返回数组的一个视图(view):

>>> b 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])
>>> b.ravel()
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
>>> b.flatten()
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23])
>>>

除了可以使用 reshape 函数,也可以直接用一个正整数元组来设置数组的维度,

>>> b.shape = (6, 4)
>>> b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
>>>

transpose 在线性代数中,转置矩阵是很常见的操作:

>>> b.transpose()
array([[ 0,  4,  8, 12, 16, 20],
       [ 1,  5,  9, 13, 17, 21],
       [ 2,  6, 10, 14, 18, 22],
       [ 3,  7, 11, 15, 19, 23]])
>>>

resizereshape 函数的功能一样,但 resize 会直接修改所操作的数组:

>>> b.resize((2, 12))
>>> b
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]])
>>>

数组的组合

NumPy 数组有水平组合、垂直组合和深度组合等多种组合方式,我们将使用 vstackdstackhstackcolumn_stackrow_stack 以及 concatenate 函数来完成数组的组合。

  • 水平组合:将 ndarray 对象构成的元组作为参数,传给 hstack 函数。
>>> a = np.arange(9).reshape(3, 3)
>>> b = 2 * a
>>> np.hstack((a, b))
array([[ 0,  1,  2,  0,  2,  4],
       [ 3,  4,  5,  6,  8, 10],
       [ 6,  7,  8, 12, 14, 16]])
>>>

也可以用 concatenate 函数来实现同样的效果:

np.concatenate((a, b), axis=1)
  • 垂直组合:垂直组合同样需要构造一个元组作为参数,只不过这次的函数变成了 vstack
>>> np.vstack((a, b))
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 0,  2,  4],
       [ 6,  8, 10],
       [12, 14, 16]])
>>> np.concatenate((a, b), axis=0)
>>>
  • 深度组合:将相同的元组作为参数传给 dstack 函数,即可完成数组的深度组合。所谓深度组合,就是将一系列数组沿着纵轴(深度)方向进行层叠组合。举个例子,有若干张二维平面内的图像点阵数据,我们可以将这些图像数据沿纵轴方向层叠在一起,这就形象地解释了什么是深度组合。
>>> np.dstack((a, b))
array([[[ 0,  0],
        [ 1,  2],
        [ 2,  4]],

       [[ 3,  6],
        [ 4,  8],
        [ 5, 10]],

       [[ 6, 12],
        [ 7, 14],
        [ 8, 16]]])
>>>
  • 列组合:column_stack 函数对于一维数组将按列方向进行组合:
oned = np.arange(2)
oned
twice_oned = 2 * oned
np.column_stack((oned, twice_oned))

而对于二维数组,column_stackhstack 的效果是相同的:

>>> np.column_stack((a, b))
array([[ 0,  1,  2,  0,  2,  4],
       [ 3,  4,  5,  6,  8, 10],
       [ 6,  7,  8, 12, 14, 16]])
>>> np.column_stack((a, b)) == np.hstack((a, b))
array([[ True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True]])
>>>
  • 行组合:当然,NumPy 中也有按行方向进行组合的函数,它就是 row_stack。对于两个一维数组,将直接层叠起来组合成一个二维数组。对于二维数组,row_stackvstack 的效果是相同的
>>> np.row_stack((oned, twice_oned))
array([[0, 1],
       [0, 2]])
>>> np.row_stack((a, b))
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 0,  2,  4],
       [ 6,  8, 10],
       [12, 14, 16]])
>>> np.row_stack((a, b)) == np.vstack((a, b))
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]])
>>>
函数说明
vstack()该函数垂直堆叠数组
dstack()该函数沿第三轴深度堆叠数组
hstack()该函数水平堆叠数组
column_stack()该函数将一维数组作为列堆叠以创建二维数组
row_stack()该函数垂直堆叠数组
concatenate()该函数连接数组的列表或元组

数组的分割

NumPy 数组可以进行水平、垂直或深度分割,相关的函数有 hsplitvsplitdsplitsplit。我们可以将数组分割成相同大小的子数组,也可以指定原数组中需要分割的位置。

  • 水平分割(理解为水平轴将被切割为 n 份):下面的代码将把数组沿着水平方向分割为3个相同大小的子数组:
>>> np.hsplit(a, 3)
[array([[0],
       [3],
       [6]]), array([[1],
       [4],
       [7]]), array([[2],
       [5],
       [8]])]
>>>

对同样的数组,调用 split 函数并在参数中指定参数 axis=1

np.split(a, 3, axis=1)
  • 垂直分割:vsplit 函数将把数组沿着垂直方向分割,
>>> np.vsplit(a, 3)
[array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])]
>>> np.split(a, 3, axis=0)
[array([[0, 1, 2]]), array([[3, 4, 5]]), array([[6, 7, 8]])]
>>>
  • 深度分割,不出所料,dsplit 函数将按深度方向分割数组。
>>> c = np.arange(27).reshape(3, 3, 3)
>>> c
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8]],

       [[ 9, 10, 11],
        [12, 13, 14],
        [15, 16, 17]],

       [[18, 19, 20],
        [21, 22, 23],
        [24, 25, 26]]])
>>> np.dsplit(c, 3)
[array([[[ 0],
        [ 3],
        [ 6]],

       [[ 9],
        [12],
        [15]],

       [[18],
        [21],
        [24]]]), array([[[ 1],
        [ 4],
        [ 7]],

       [[10],
        [13],
        [16]],

       [[19],
        [22],
        [25]]]), array([[[ 2],
        [ 5],
        [ 8]],

       [[11],
        [14],
        [17]],

       [[20],
        [23],
        [26]]])]
>>>

数组的属性

除了 shapedtype 属性以外,ndarray 对象还有很多其他的属性

  • ndim 属性,给出数组的维数,或数组轴的个数
  • size 属性,给出数组元素的总个数
  • itemsize 属性,给出数组中的元素在内存中所占的字节数
  • 如果你想知道整个数组所占的存储空间,可以用 nbytes 属性来查看。这个属性的值其实就是 itemsizesize 属性值的乘积
  • T 属性的效果和 transpose 函数一样
  • 对于一维数组,其 T 属性就是原数组。在 NumPy 中,复数的虚部是用 j 表示的。
  • real 属性,给出复数数组的实部。如果数组中只包含实数元素,则其 real 属性将输出原数组
  • imag 属性,给出复数数组的虚部
  • 如果数组中包含复数元素,则其数据类型自动变为复数型
  • flat 属性将返回一个 numpy.flatiter 对象,这是获得 flatiter 对象的唯一方式——我们无法访问 flatiter 的构造函数。这个所谓的“扁平迭代器”可以让我们像遍历一维数组一样去遍历任意的多维数组。可以用flatiter 对象直接获取一个数组元素或者获取多个元素。flat 属性是一个可赋值的属性。对 flat 属性赋值将导致整个数组的元素都被覆盖。
    different types of attributes of the ndarray class
>>> b
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]])
>>> b.ndim  # ndim 属性,给出数组的维数,或数组轴的个数
2
>>> b.size  # size 属性,给出数组元素的总个数
24
>>> b.itemsize  # itemsize 属性,给出数组中的元素在内存中所占的字节数
4
>>> b.nbytes  # 整个数组所占的存储空间
96
>>> b.size * b.itemsize
96
>>> b.resize(6, 4)
>>> b = np.arange(5)
>>> b
array([0, 1, 2, 3, 4])
>>> b.T  # T 属性的效果和 transpose 函数一样
array([0, 1, 2, 3, 4])
>>> b = np.array([1.0j + 1, 2.0j + 3])  # 复数的虚部是用j表示的
>>> b.real  # real 属性,给出复数数组的实部
array([1., 3.])
>>> b.imag  # imag 属性,给出复数数组的虚部
array([1., 2.])
>>> b.dtype  # 如果数组中包含复数元素,则其数据类型自动变为复数型
dtype('complex128')
>>> b.dtype.str
'<c16'
>>> b = np.arange(4).reshape(2, 2)
>>> b
array([[0, 1],
       [2, 3]])
>>> f = b.flat  # flat 属性将返回一个 numpy.flatiter 对象
>>> f
<numpy.flatiter object at 0x000001FFD955A270>
>>> for item in f:
...     print(item)
...
0
1
2
3
>>>
>>> b.flat[2]  # 用 flatiter 对象直接获取一个数组元
2
>>> b.flat[[1, 3]]  # 或者获取多个元素
array([1, 3])
>>> b.flat = 7  # flaT 属性是一个可赋值的属性。对 flat 属性赋值将导致整个数组的元素都被覆盖
>>> b.flat[[1, 3]] = 1
>>> b
array([[7, 1],
       [7, 1]])
>>>

数组的转换

可以使用 tolist 函数将 NumPy 数组转换成 Python 列表。

>>> b
array([[7, 1],
       [7, 1]])
>>> b.tolist()
[[7, 1], [7, 1]]
>>>

astype 函数可以在转换数组的数据类型

>>> b.astype(float)
array([[7., 1.],
       [7., 1.]])
>>>

后续将介绍 NumPy 中的常用函数,包括基本数学函数和统计函数等。

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值