本文不是原创,是对Machine Learning Plus网站一篇文章的翻译,自己在学习的时候看着不错,想翻译过来和大家一些学习交流。原文地址:Numpy Tutorial Part 1 – Introduction to Arrays
下面正式开始:
Numpy教程的第一部分涵盖了使用Numpy的ndarray进行分析、展现和操作的所有核心知识。Numpy是python中进行科学计算和数据操作的最基本的也是最强大的包。
目录
3. 如何查看一个数组的大小(size)和形状(shape)
1. Numpy简介
Numpy是python中处理数据最基本最强大的包。
如果您要进行数据分析或者机器学习项目,那么对numpy有一个深入的认识就几乎是必须的了。
因为其他数据分析的包都是基于numpy的,如pandas。用scikit-learn包进行机器学习也是非常依赖于numpy的。
那么numpy都提供什么功能呐?
一句话,numpy提供了一个非常出色的ndarry对象。 n-dimensional arrays的简称。
你可以在一个'ndarray'或者说'array'中,保存同一数据类型的多个数字。围绕数组的一系列方法使得numpy在进行数据计算和数据操作上非常的便捷。
你可能会说,我可以在python自带的对象里保存这些数据,比如列表对象等。而且也可以通过这些对象操作这些数据,那为什么我还要使用numpy呐?
其实,numpy中的数组比python中的列表有明显的优势。为了理解这一点,我们先创建一个数组。
2. 如何创建一个数组
创建数组的方法有很多,本文后面也都会讲到,但是最基本的方法是通过将一个列表传入np.array函数中来创建。
# 通过一个列表创建一维数组
import numpy as np
list1=[0,1,2,3,4]
arr1d=np.array(list1)
print(type(arr1d))
arr1d
#> class 'numpy.ndarray'
#> array([0, 1, 2, 3, 4])
数组和列表的关键区别在于,数组是用来处理向量运算而列表不是。
也就是说如果你运行一个函数,这个函数是作用在数组中的每一个元素上的,而不是整个数组的对象上。
例如,如果你想对列表中的每一个元素加2,直观的方式是如下操作
list1 + 2 #error
上面这样对列表的操作是错误的,但是对数组就可以这样操作。
# 对数组arr1d中每一个元素加2
arr1d +2
#> array([2,3,4,5,6])
数组的另一个特性是,数组一旦创建好之后就不能再修改大小。如果要修改就只能重建一个新的数组。但是列表却可以在创建好以后再修改大小。
上面是关于1维数组的,你同样可以通过传入列表的列表来创建二维数组。
# 通过列表的列表创建二维数组
list2 = [[0,1,2], [3,4,5], [6,7,8]]
arr2d = np.array(list2)
arr2d
#> array([[0, 1, 2],
#> [3, 4, 5],
#> [6, 7, 8]])
你还可以通过设置dtype参数的值,来设置数组的数据类型。常用的数据类型有 'float','int','bool','str'和'object'。
为了控制内存的分配,你还可以将dtype的参数设置为:'float32','float64','int8',int16' 或者'int3e2'。
#创建浮点数类型的数组
arr2d_f = np.array(list2, dtype='float')
arr2d_f
#> array([[ 0., 1., 2.],
#> [ 3., 4., 5.],
#> [ 6., 7., 8.]])
数字后面的小数点表示是浮点数的数据类型。你也可以通过astype函数转换数组的数据类型。
#转换为'int'类型
arr2d_f.astype('int')
#> array([[0, 1, 2],
#> [3, 4, 5],
#> [6, 7, 8]])
#转换成'int'后再转成'str'
arr2d_f.astype('int').astype('str')
#> array([['0', '1', '2'],
#> ['3', '4', '5'],
#> ['6', '7', '8']],
#> dtype='U21')
一个数组中所有元素的数据类型必须是相同的,但列表就可以不同。这是列表和元素的另一个显著的不同。
但是,如果你不确定数组中要传入什么类型的数据,或者你想在一个数组中传入字母和数组,你可以将dtype设置为'object'。
# 创建布尔数组
arr2d_b = np.array([1, 0, 10], dtype='bool')
arr2d_b
#> array([ True, False, True], dtype=bool)
# 创建一个可以传入数字和字符串的数组
arr1d_obj = np.array([1, 'a'], dtype='object')
arr1d_obj
#> array([1, 'a'], dtype=object)
最后,你总能通过.tolist()函数将数组转换成列表。
# 将一个数组转换回列表
arr1d_obj.tolist()
#> [1, 'a']
总结一下数组和列表的主要不同点:
1. 数组支持向量运算,列表不支持
2.数组创建后就不能修改大小,只能新建一个或者重写当前数组
3.一个数组只能包含一种数据类型的元素
4.同样数量的元素,数组占用的内存空间远小于列表
3. 如何查看一个数组的大小(size)和形状(shape)
为了认识数组,每一个数组都有一些我们想要知道的属性。
例如上面的数组arr2d,它通过一个列表的列表创建的,所以它有两个维度。可以显示为行和列,就像一个矩阵。
如果我基于一个列表的列表的列表创建的数组,则它就是3维度的,就像一个立方体。以此类推。
假如你有一个别人创建的数组,你会想知道这个数组的那些属性呐?例如:
它是一维,两维还是多维的?(ndim)
每一个维度中有多少元素?(shape)
数组的数据类型是什么?(dtype)
数组中元素的总数是多少?(size)
数组中一些样例数据(通过索引)
# 创建一个3行4列的两维数组
list2 = [[1, 2, 3, 4],[3, 4, 5, 6], [5, 6, 7, 8]]
arr2 = np.array(list2, dtype='float')
arr2
#> array([[ 1., 2., 3., 4.],
#> [ 3., 4., 5., 6.],
#> [ 5., 6., 7., 8.]])
# shape
print('Shape: ', arr2.shape)
# dtype
print('Datatype: ', arr2.dtype)
# size
print('Size: ', arr2.size)
# ndim
print('Num Dimensions: ', arr2.ndim)
#> Shape: (3, 4)
#> Datatype: float64
#> Size: 12
#> Num Dimensions: 2
4. 如何从数组同提取特定元素
arr2
#> array([[ 1., 2., 3., 4.],
#> [ 3., 4., 5., 6.],
#> [ 5., 6., 7., 8.]])
在数组中可以使用从0开始的索引提取特定元素,这一点有点类似python 的列表。但是和列表不同的是,数组可以在括号中使用多个索引,因为数组本身也是多维的。
# 提取前两行前两列
arr2[:2, :2]
list2[:2, :2] # error
#> array([[ 1., 2.],
#> [ 3., 4.]])
另外,数组还支持布尔索引。
布尔索引是指和被查询数组相同形状的索引数组,这个索引数组中只有True和False,被查询数据汇总所有True对应位置的元素都会被查询到。
# 可以通过对数组中所有元素进行判断获得一个布尔数组
b = arr2 > 4
b
#> array([[False, False, False, False],
#> [False, False, True, True],
#> [ True, True, True, True]], dtype=bool)
arr2[b]
#> array([ 5., 6., 5., 6., 7., 8.])
4.1 如何反转行和整个数组
反转数组和反转列表有点相似,但是如果要反转整个数组,就需要对所有维度反转。
# 只反转行
arr2[::-1, ]
#> array([[ 5., 6., 7., 8.],
#> [ 3., 4., 5., 6.],
#> [ 1., 2., 3., 4.]])
# 反转行和列
arr2[::-1, ::-1]
#> array([[ 8., 7., 6., 5.],
#> [ 6., 5., 4., 3.],
#> [ 4., 3., 2., 1.]])
4.2 如何表示缺失值和极限值
缺失值和极限值可以分别用np.nan和np.inf表示。
# 在数组中插入一个空值和极限值
arr2[1,1] = np.nan # not a number
arr2[1,2] = np.inf # infinite
arr2
#> array([[ 1., 2., 3., 4.],
#> [ 3., nan, inf, 6.],
#> [ 5., 6., 7., 8.]])
# 用-1 置换nan和infR. 这里不要使用 arr2 == np.nan
missing_bool = np.isnan(arr2) | np.isinf(arr2)
arr2[missing_bool] = -1
arr2
#> array([[ 1., 2., 3., 4.],
#> [ 3., -1., -1., 6.],
#> [ 5., 6., 7., 8.]])
4.3 如何计算数组的最大值,最小值和平均值
数据中有各自的方法来计算这些聚类值。
# mean(平均值), max(最大值) and min(最小值)
print("Mean value is: ", arr2.mean())
print("Max value is: ", arr2.max())
print("Min value is: ", arr2.min())
#> Mean value is: 3.58333333333
#> Max value is: 8.0
#> Min value is: -1.0
如何要计算各行各列,使用np.amin函数。
# 行和列的平均值 min
print("Column wise minimum: ", np.amin(arr2, axis=0))
print("Row wise minimum: ", np.amin(arr2, axis=1))
#> Column wise minimum: [ 1. -1. -1. 4.]
#> Row wise minimum: [ 1. -1. 5.]
计算行的最小值还好,但是如果在行上做其他计算呐?这个就需要使用np.apply_over_axix函数。
# 累积加和 第1个元素,第1个元素+第2个元素,第2个元素+第3个元素 ...
np.cumsum(arr2)
#> array([ 1., 3., 6., 10., 13., 12., 11., 17., 22., 28., 35., 43.])
5. 如何基于已有数组创建新数组
如果我们只是将现有数组的一部分分配给另一个数组,那么我们刚刚新建的这个数组其实在内存中是指向原有数组的。
也就是说,对新数组的任何修改都是对原有数组的修改。
为了避免原有数组被修改,我们就需要将原有数据复制一份copy().所有的numpy数组方法都来源于复制。
# 将arr2的一部分分配给arr2a. 并不是新建了一个数组.
arr2a = arr2[:2,:2]
arr2a[:1, :1] = 100 # 100 也会显示在 arr2 中
arr2
#> array([[ 100., 2., 3., 4.],
#> [ 3., -1., -1., 6.],
#> [ 5., 6., 7., 8.]])
# 将arr2 的一部分复制到 arr2b
arr2b = arr2[:2, :2].copy()
arr2b[:1, :1] = 101 # 101 就不会出现在 arr2 中
arr2
#> array([[ 100., 2., 3., 4.],
#> [ 3., -1., -1., 6.],
#> [ 5., 6., 7., 8.]])
6. 改变多维数组的形状或者将多维数组拍平
reshape函数是修改数组的元素排列方式来修改数组的形状,会保持数组的元素数不变。
flattening拍平是会将多维的数组变换成一维数组,并且是只会转成一维数组。
首先,我们将arr2从 3X4 转换成4X3
# 将arr2 从3X4 转换成 4X3
arr2.reshap(4,3)
#> array([[ 100., 2., 3.],
#> [ 4., 3., -1.],
#> [ -1., 6., 5.],
#> [ 6., 7., 8.]])
6.1 flatten()和ravel()的差别
有两个常用的将数组拍平的方法:flatten()和ravel().
两者的区别是,ravel()创建的新数组在内存中指向原有父数组。所以对新的数组的任何修改,都会修改原有父数组。但是ravel()的一个优点是因为它没有复制新的数组,所有会节省内存空间。
# 将arr2拍平成一维数组
arr2.flatten()
#> array([ 100., 2., 3., 4., 3., -1., -1., 6., 5., 6., 7., 8.])
# 对拍平后的数组的修改不会影响父数组
b1 = arr2.flatten()
b1[0] = 101 # changing b1 does not affect arr2
arr2
#> array([[ 100., 2., 3., 4.],
#> [ 3., -1., -1., 6.],
#> [ 5., 6., 7., 8.]])
# 修改ravel创建的新数组也会修改父数组.
b2 = arr2.ravel()
b2[0] = 101 # changing b2 changes arr2 also
arr2
#> array([[ 101., 2., 3., 4.],
#> [ 3., -1., -1., 6.],
#> [ 5., 6., 7., 8.]])
7. 如何使用numpy创建顺序,重复和随机数字
要创建一个顺序的数组,np.arange函数是随手就能用到的。
# 最小值默认是0
print(np.arange(5))
# 0 to 9
print(np.arange(0, 10))
# 从0到9,步长是2
print(np.arange(0, 10, 2))
# 10到1的递减数列
print(np.arange(10, 0, -1))
#> [0 1 2 3 4]
#> [0 1 2 3 4 5 6 7 8 9]
#> [0 2 4 6 8]
#> [10 9 8 7 6 5 4 3 2 1]
在np.arange函数中可以设置起止数值。但是如果你想指定数组中元素的个数的话,你就必须得手动计算步长了。
比如,你想创建一个数组包含1到50间等距的10个数字,你能计算出步长吗?
我们用np.linspace就可以实现这一点。
# 从1开始,在50结束
np.linspace(start=1, stop=50, num=10, dtype=int)
#> array([ 1, 6, 11, 17, 22, 28, 33, 39, 44, 50])
请注意,因为我们将dtype设置为'int',所有数字都是取了整数的。
与linspace相似的函数还有一个logspace。logspace是基于对数的。在np.logspace中,开始值和结束值是已固定值为底数给定的起止值为真数的对数。默认的底数是10.举个例子方便理解:
# 精确到小数点后两位
np.set_printoptions(precision=2)
# 以 10^1 开始,以 10^50结束
np.logspace(start=1, stop=50, num=10, base=10)
#> array([ 1.00e+01, 2.78e+06, 7.74e+11, 2.15e+17, 5.99e+22,
#> 1.67e+28, 4.64e+33, 1.29e+39, 3.59e+44, 1.00e+50])
np.zeros和np.ones函数可以以任意形状创建数组,数组中的值都是0或1.
np.zeros([2,2])
#> array([[ 0., 0.],
#> [ 0., 0.]])
np.ones([2,2])
#> array([[ 1., 1.],
#> [ 1., 1.]])
7.1 如何创建重复的顺序序列
np.tile会重复整个列表或者数组,np.repeat或重复每一个元素。
a = [1,2,3]
# 重复整个'a'两次
print('Tile: ', np.tile(a, 2))
# 重复两次'a'中的每一个元素
print('Repeat: ', np.repeat(a, 2))
#> Tile: [1 2 3 1 2 3]
#> Repeat: [1 1 2 2 3 3]
7.2 如何创建重复数字
random模块可以提供非常好用的创建任意形状的随机数的函数(也包括统计分布)。
# [0,1) 形状为 2,2 的随机数字
print(np.random.rand(2,2))
# 服从均值为1,方差为0的正态分布的形状为 2,2 的随机数字
print(np.random.randn(2,2))
# [0, 10) 间形状为 2,2 的随机整数
print(np.random.randint(0, 10, size=[2,2]))
# [0,1)一个随机数字
print(np.random.random())
# [0,1)间形状为2,2的随机数字
print(np.random.random(size=[2,2]))
# 在给定的列表中挑出10个元素,每一个元素出现概率相同
print(np.random.choice(['a', 'e', 'i', 'o', 'u'], size=10))
# 在给定的列表中挑出10个元素,,每个元素出现概率是给定的
print(np.random.choice(['a', 'e', 'i', 'o', 'u'], size=10, p=[0.3, .1, 0.1, 0.4, 0.1])) # picks more o's
#> [[ 0.84 0.7 ]
#> [ 0.52 0.8 ]]
#> [[-0.06 -1.55]
#> [ 0.47 -0.04]]
#> [[4 0]
#> [8 7]]
#> 0.08737272424956832
#> [[ 0.45 0.78]
#> [ 0.03 0.74]]
#> ['i' 'a' 'e' 'e' 'a' 'u' 'o' 'e' 'i' 'u']
#> ['o' 'a' 'e' 'a' 'a' 'o' 'o' 'o' 'a' 'o']
每次运行上面的函数时,都会得到不懂的随机数字。
那如果我们想让每次的随机数字都相同呐?这是就需要设定随机种子(random seed)或者随机状态(random state)。seed可以是任意值。唯一需要注意的就是每一次都将seed设定为同一个值,那么每次都能得到相同的随机值。
一旦创建了np.random.RandomState 所有np.random模块中的函数都可以用来创建randomstate对象。
# 创建random state
rn = np.random.RandomState(100)
# 创建[0,1) 间形状为 2,2 的随机值
print(rn.rand(2,2))
#> [[ 0.54 0.28]
#> [ 0.42 0.84]]
# 设定 random seed
np.random.seed(100)
# 创建[0,1) 间形状为2,2的随机值
print(np.random.rand(2,2))
#> [[ 0.54 0.28]
#> [ 0.42 0.84]]
7.3 如何获取所有唯一值和计数
np.unique函数可以获取数组中的非重复值,如何你还想得到每一个唯一值得重复次数,只要将 return_counts参数设置为'True'.
# 创建 [0,10)间大小为10的数组
np.random.seed(100)
arr_rand = np.random.randint(0, 10, size=10)
print(arr_rand)
#> [8 8 3 7 7 0 4 2 5 2]
# 获得唯一值和各个唯一值的重复次数
uniqs, counts = np.unique(arr_rand, return_counts=True)
print("Unique items : ", uniqs)
print("Counts : ", counts)
#> Unique items : [0 2 3 4 5 7 8]
#> Counts : [1 2 1 1 1 2 2]
完结。