第七天
Numpy的学习(一)数组基础
Python中的数据操作几乎等同于Numpy数据操作,甚至Pandas工具也是构建在Numpy数组的基础之上的。
一、从Python列表创建数组
1.首先,可以用np.array从Python列表创建数组:
import numpy as np
np.array([1, 4, 2, 5, 3])
Numpy创建数组要求必须包含同一类型的数据。如果类型不匹配,Numpy将会向上转型(如果可行)。下面的整型被转换为浮点型。
np.array([3.14, 4, 2, 3])
如果明确设置数组的数据类型,可以用dtype关键字:
np.array([1, 2, 3, 4], dtype = 'float32')
另外,Numpy数组可以被指定为多为的。以下是用列表的列表初始化多为数组的一种方法:
np.array([range(i, i + 3) for i in [2, 4, 6]])
2.从头创建数组
面对大型数组的时候,用Numpy内置的方法从头创建数组是一种更高效的方法。以下是几个具体的例子:
创建一个长度为10的数组,数组的值都是0:
np.zeros(10, dtype=int)
创建一个3x5的浮点型数组,数组的值都是1:
np.ones((3, 5), dtype=float)
创建一个3x5的浮点型数组,数组的值都是3.14
np.full((3, 5), 3.14)
创建一个数组,从0开始到20结束,步长是2:
np.arange(0, 20, 2)
创建一个5个元素的数组,这5个数均匀地分配到0-1:
np.linespace(0, 1, 5)
创建一个3x3的,在0~1均匀分布的随机数组成的数组:
np.random.random((3, 3))
创建一个3x3的,均值为0、方差为1的数组:
np.random.normal(0, 1, (3, 3))
创建一个3x3的,[0, 10)区间的随机整型数组:
np.random.randint(0, 10, (3, 3))
创建一个3x3的单位矩阵:
np.eyes(3)
二、数组的属性
首先我们将用Numpy的随机数生成器设置一组种子值,以确保每次程序执行的时候都可以生成同样的随机数组。下面我们定义三个随机的数组:一个一维的、一个二维的和一个三维数组。
np.random.seed(0) # 设置随机数种子
x1 = np.random.randint(10, size=6)
x2 = np.random.randint(10, size=(3, 4))
x3 = np.random.randint(10, size=(3, 4, 5))
每个数组都有ndim(数组的维度)、shape(数组每个维度的大小)和size(数组的大小)等属性。
print("x3 ndim: ", x3.ndim) # 3
print("x3 ndim: ", x3.shape) # (3, 4, 5)
print("x3 ndim: ", x3.size) # 60
----------------------------------
另外一个有用的属性是dtype,它是数组的数据类型。
print("x3 dtype: ", x3.dtype) # int32
三、数组的索引
和Python列表一样,在一维数组中,你也可以通过中括号指定索引获取第i个值(从0开始计数)。
x1
array([8, 1, 1, 7, 3, 9])
x1[0] # 8
x1[4] # 3
为了获取数组的末尾的索引,可以用负值索引:
x1[-1] # 9
x1[-2] # 3
在多维数组中,可以用逗号分隔的索引元组获取元素:
x2
array([[3, 6, 7],
[2, 0, 3]
[5, 9, 4]])
------------------
x2[0, 0] # 3
x2[2, 0] # 5
当然也可以用上述索引方式修改元素值,x2[0, 0] = 5
值得注意的是和Python列表不同,Numpy数组是固定类型的。这意味着当你试图将一个浮点型值插入一个整型数组时,浮点值会被截短成整型。并且这种截短是自动完成的,不会给你提示或者警告,所以需要特别注意这一点。
四、数组的切分
前面用中括号获取单个数组元素,我们也可以用切片(slice)符号来获取子数组,切片符号用冒号(:)表示。Numpy切片语法和Python列表的标准切片语法相同。为了获取数组x的一个切片,可以用以下的方式:
x[start:stop:step]
如果以上三个参数都未指定,那么它们会被分别设置为默认值start=0、stop=维度的大小和step=1。
1.一维数组
x = np.narray(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
x[:5] # 前五个元素 [0, 1, 2, 3, 4]
x[5:] # 索引五之后的元素 [5, 6, 7, 8, 9]
x[4:7] # 中间索引4到7的数组,但不包括7 [4, 5, 6]
x[::2] # 每隔一个元素
x[1::2] # 每隔一个元素,从索引1开始
-------------------------------
当步长为负的时候,start和stop的参数默认是被交换的。
x[::-1] # 所有的元素, 逆序 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
2.多维数组
多维切片也用同样的方式处理,用冒号分割。
x2
array([[3, 6, 7],
[2, 0, 3]
[5, 9, 4]])
-------------------
x2[:2, :3] # [[3, 6, 7], [2, 0, 3]]
3.获取数组的行和列
一种常见的需求是获取数组的单行和单列。可以将索引与切片组合起来实现这个功能,用一个冒号(:)表示空切片。
print(x2[:, 0]) # [12 7 1] 获取x2的第一列
print(x2[0, :]) # [3 6 7] 获取x2的第一行
当然获取行的时候,处于语法的简介考虑,可以省略空的切片
print(x2[0]) # 等于x2[0, :]
4.非副本视图的子数组
有关数组切片有一点很重要也非常有用,那就是数组切片返回的是数组数据的视图,而不是数组数据的副本。也就是说当你改变数组的切片子数组的时候,原始数组的值也会被改变。这种默认的处理方式实际上非常有用:它意味着处理非常大的数据集时,可以获取或处理这些数据集的片段,而不是复制底层的数据缓存。
尽管数组视图有一些非常好的特性,但是在有些时候明确地复制数组里的数据或子数组也是非常有用的。可以简单通过copy()方法实现:
x2_sub_copy = x2[:2, :2].copy()
如果修改这个子数组的时候,原始数组不会被改变。
五、数组的变形
另一个有用的操作类型是数组的变形。数组变形最灵活的实现方式是通过reshape()函数来实现。列入,如果你希望将数字1~9放入一个3X3的矩阵中,可以采用如下方法:
grid = np.arange(1, 10).reshape((3, 3))
print(grid)
值得注意,如果希望该方法可行,那么原始数组的大小必须和变形后数组的大小一致。如果满足这个条件,reshape方法将会用到原始数组的一个非副本视图。但实际情况是,在非连续的数据缓存的情况下,返回非副本视图往往不可能实现。
六、数组的拼接和分裂
以上所有的操作都是针对单一数组的,但有时也需要将多个数组合并为一个数组,或将一个数组分裂成多个。
1.数组的拼接
拼接或连接Numpy中的两个数组主要由np.concatenate、np.vstack、np.hstack实现。
np.concatenate将数组元数组或数组列表作为第一个参数,如下:
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
np.concatenate([x, y])
# array([1, 2, 3, 4, 5, 6])
当然也可以一次拼接两个以上数组。
而且也可以用于二维数组的拼接。
grid = np.array([[1, 2, 3],
[4, 5, 6]])
沿着第一个轴拼接
np.concatenate([grid, grid])
# array([[1, 2, 3],
[4, 5, 6],
[1, 2, 3],
[4, 5, 6]])
沿着第二个轴拼接
np.concatenate([grid, grid], axis=1)
# array([[1, 2, 3, 1, 2, 3],
[4, 5, 6, 4, 5, 6]])
沿着固定维度处理数组时,使用np.vstack(垂直栈)和np.hstack(水平栈)函数会更简洁:
x = np.array([1, 2, 3])
grid = np.array([[9, 8, 7],
[6, 5, 4]])
# 垂直栈数组
np.vstack([x, grid])
# array([[1, 2, 3],
[9, 8, 7],
[6, 5, 4]]])
# 水平栈数组
y = np.array([[99],
[11]])
np.hstack([grid, y])
# array([[9, 8, 7, 99],
[6, 5, 4, 11]])
2.数组的分裂
与拼接相反的过程是分裂。分裂可以通过np.split、np.hsplit和np.vstack函数来实现。
可以向以上函数传递一个索引列表作为参数,索引列表记录的是分裂点的位置。
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]
值得注意的是,N分裂点会得到N+1个子数组。相关的np.hsplit和np.vstack也是类似。