写点什么

NumPy 之:ndarray 多维数组操作

发布于: 2021 年 05 月 19 日

简介 NumPy 一个非常重要的作用就是可以进行多维数组的操作,多维数组对象也叫做 ndarray。我们可以在 ndarray 的基础上进行一系列复杂的数学运算。


本文将会介绍一些基本常见的 ndarray 操作,大家可以在数据分析中使用。


创建 ndarray 创建 ndarray 有很多种方法,我们可以使用 np.random 来随机生成数据:


import numpy as np

Generate some random data

data = np.random.randn(2, 3)dataarray([[ 0.0929, 0.2817, 0.769 ],[ 1.2464, 1.0072, -1.2962]])除了随机创建之外,还可以从 list 中创建:


data1 = [6, 7.5, 8, 0, 1]arr1 = np.array(data1)array([6. , 7.5, 8. , 0. , 1. ])从 list 中创建多维数组:


data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]arr2 = np.array(data2)array([[1, 2, 3, 4],[5, 6, 7, 8]])使用 np.zeros 创建初始值为 0 的数组:


np.zeros(10)array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])创建 2 维数组:


np.zeros((3, 6))array([[0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 0., 0.]])使用 empty 创建 3 维数组:


np.empty((2, 3, 2))array([[[0., 0.],[0., 0.],[0., 0.]],


   [[0., 0.],    [0., 0.],    [0., 0.]]])
复制代码


注意,这里我们看到 empty 创建的数组值为 0,其实并不是一定的,empty 会从内存中随机挑选空间来返回,并不能保证这些空间中没有值。所以我们在使用 empty 创建数组之后,在使用之前,还要记得初始化他们。


使用 arange 创建范围类的数组:


np.arange(15)array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])指定数组中元素的 dtype:


arr1 = np.array([1, 2, 3], dtype=np.float64)arr2 = np.array([1, 2, 3], dtype=np.int32)ndarray 的属性可以通过 data.shape 获得数组的形状。


data.shape(2, 3)通过 ndim 获取维数信息:


arr2.ndim2 可以通过 data.dtype 获得具体的数据类型。


data.dtypedtype('float64')ndarray 中元素的类型转换在创建好一个类型的 ndarray 之后,还可以对其进行转换:


arr = np.array([1, 2, 3, 4, 5])arr.dtypedtype('int64')


float_arr = arr.astype(np.float64)float_arr.dtypedtype('float64')上面我们使用 astype 将 int64 类型的 ndarray 转换成了 float64 类型的。


如果转换类型的范围不匹配,则会自动进行截断操作:


arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])arr.astype(np.int32)


array([ 3, -1, -2, 0, 12, 10], dtype=int32)注意,这里是把小数截断,并没有向上或者向下取整。


ndarray 的数学运算数组可以和常量进行运算,也可以和数组进行运算:


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


arr * arr


array([[ 1., 4., 9.],[16., 25., 36.]])


arr + 10


array([[11., 12., 13.],[14., 15., 16.]])


arr - arr


array([[0., 0., 0.],[0., 0., 0.]])


1 / arr


array([[1. , 0.5 , 0.3333],[0.25 , 0.2 , 0.1667]])


arr ** 0.5


array([[1. , 1.4142, 1.7321],[2. , 2.2361, 2.4495]])数组之间还可以进行比较,比较的是数组中每个元素的大小:


arr2 = np.array([[0., 4., 1.], [7., 2., 12.]])


arr2 > arr


array([[False, True, False],[ True, False, True]])index 和切片基本使用先看下 index 和切片的基本使用,index 基本上和普通数组的使用方式是一样的,用来访问数组中某一个元素。


切片要注意的是切片后返回的数组中的元素是原数组中元素的引用,修改切片的数组会影响到原数组。

构建一维数组

arr = np.arange(10)


array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

index 访问

arr[5]5

切片访问

arr[5:8]array([5, 6, 7])

切片修改

arr[5:8] = 12array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])

切片可以修改原数组的值

arr_slice = arr[5:8]arr_slice[1] = 12345arr


array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8,9])

构建二维数组

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])arr2d[2]


array([7, 8, 9])

index 二维数组

arr2d[0][2]3

index 二维数组

arr2d[0, 2]3

构建三维数组

arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])arr3d


array([[[ 1, 2, 3],[ 4, 5, 6]],


   [[ 7,  8,  9],    [10, 11, 12]]])
复制代码

index 三维数组

arr3d[0]


array([[1, 2, 3],[4, 5, 6]])

copy 是硬拷贝,和原数组的值相互不影响

old_values = arr3d[0].copy()arr3d[0] = 42


arr3d


array([[[42, 42, 42],[42, 42, 42]],


   [[ 7,  8,  9],    [10, 11, 12]]])
复制代码


arr3d[0] = old_valuesarr3d


array([[[ 1, 2, 3],[ 4, 5, 6]],


   [[ 7,  8,  9],    [10, 11, 12]]])
复制代码

index 三维数组

arr3d[1, 0]


array([7, 8, 9])


x = arr3d[1]x


array([[ 7, 8, 9],[10, 11, 12]])


x[0]


array([7, 8, 9])index with sliceslice 还可以作为 index 使用,作为 index 使用表示的就是一个 index 范围值。


作为 index 表示的 slice 可以有多种形式。


有头有尾的,表示 index 从 1 开始到 6-1 结束:


arr[1:6]array([ 1, 2, 3, 4, 64])无头有尾的,表示 index 从 0 开始,到尾-1 结束:


arr2d[:2]array([[1, 2, 3],[4, 5, 6]])有头无尾的,表示从头开始,到所有的数据结束:


arr2d[:2, 1:]array([[2, 3],[5, 6]])arr2d[1, :2]array([4, 5])boolean indexindex 还可以使用 boolean 值,表示是否选择这一个 index 的数据。


我们先看下怎么构建一个 boolean 类型的数组:


names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])names == 'Bob'


array([ True, False, False, True, False, False, False])上面我们通过比较的方式返回了一个只包含 True 和 False 的数组。


这个数组可以作为 index 值来访问数组:

构建一个 7 * 4 的数组

data = np.random.randn(7, 4)


array([[ 0.275 , 0.2289, 1.3529, 0.8864],[-2.0016, -0.3718, 1.669 , -0.4386],[-0.5397, 0.477 , 3.2489, -1.0212],[-0.5771, 0.1241, 0.3026, 0.5238],[ 0.0009, 1.3438, -0.7135, -0.8312],[-2.3702, -1.8608, -0.8608, 0.5601],[-1.2659, 0.1198, -1.0635, 0.3329]])

通过 boolean 数组来访问:

data[names == 'Bob']array([[ 0.275 , 0.2289, 1.3529, 0.8864],[-0.5771, 0.1241, 0.3026, 0.5238]])在索引行的时候,还可以索引列:


data[names == 'Bob', 3]array([0.8864, 0.5238])可以用 ~符号来取反:


data[~(names == 'Bob')]array([[-2.0016, -0.3718, 1.669 , -0.4386],[-0.5397, 0.477 , 3.2489, -1.0212],[ 0.0009, 1.3438, -0.7135, -0.8312],[-2.3702, -1.8608, -0.8608, 0.5601],[-1.2659, 0.1198, -1.0635, 0.3329]])


我们可以通过布尔型数组设置值,在实际的项目中非常有用:


data[data < 0] = 0array([[0.275 , 0.2289, 1.3529, 0.8864],[0. , 0. , 1.669 , 0. ],[0. , 0.477 , 3.2489, 0. ],[0. , 0.1241, 0.3026, 0.5238],[0.0009, 1.3438, 0. , 0. ],[0. , 0. , 0. , 0.5601],[0. , 0.1198, 0. , 0.3329]])


data[names != 'Joe'] = 7array([[7. , 7. , 7. , 7. ],[0. , 0. , 1.669 , 0. ],[7. , 7. , 7. , 7. ],[7. , 7. , 7. , 7. ],[7. , 7. , 7. , 7. ],[0. , 0. , 0. , 0.5601],[0. , 0.1198, 0. , 0.3329]])


Fancy indexingFancy indexing 也叫做花式索引,它是指使用一个整数数组来进行索引。


举个例子,我们先创建一个 8 * 4 的数组:


arr = np.empty((8, 4))for i in range(8):arr[i] = iarrarray([[0., 0., 0., 0.],[1., 1., 1., 1.],[2., 2., 2., 2.],[3., 3., 3., 3.],[4., 4., 4., 4.],[5., 5., 5., 5.],[6., 6., 6., 6.],[7., 7., 7., 7.]])然后使用一个整数数组来索引,那么将会以指定的顺序来选择行:


arr[[4, 3, 0, 6]]array([[4., 4., 4., 4.],[3., 3., 3., 3.],[0., 0., 0., 0.],[6., 6., 6., 6.]])还可以使用负值来索引:


arr[[-3, -5, -7]]array([[5., 5., 5., 5.],[3., 3., 3., 3.],[1., 1., 1., 1.]])花式索引还可以组合来使用:


arr = np.arange(32).reshape((8, 4))arrarray([[ 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, 27],[28, 29, 30, 31]])上面我们构建了一个 8 * 4 的数组。


arr[[1, 5, 7, 2], [0, 3, 1, 2]]array([ 4, 23, 29, 10])然后取他们的第 2 列的第一个值,第 6 列的第三个值等等。最后得到一个 1 维的数组。


数组变换我们可以在不同维度的数组之间进行变换,还可以转换数组的轴。


reshape 方法可以将数组转换成为任意的形状:


arr = np.arange(15).reshape((3, 5))arrarray([[ 0, 1, 2, 3, 4],[ 5, 6, 7, 8, 9],[10, 11, 12, 13, 14]])数组还提供了一个 T 命令,可以将数组的轴进行对调:


arr.Tarray([[ 0, 5, 10],[ 1, 6, 11],[ 2, 7, 12],[ 3, 8, 13],[ 4, 9, 14]])对于高维数组,可以使用 transpose 来进行轴的转置:


arr = np.arange(16).reshape((2, 2, 4))arrarray([[[ 0, 1, 2, 3],[ 4, 5, 6, 7]],


   [[ 8,  9, 10, 11],    [12, 13, 14, 15]]])
复制代码


arr.transpose((1, 0, 2))array([[[ 0, 1, 2, 3],[ 8, 9, 10, 11]],


   [[ 4,  5,  6,  7],    [12, 13, 14, 15]]])
复制代码


上面的 transpose((1, 0, 2)) 怎么理解呢?


其含义是将 x,y 轴对调,z 轴保持不变。


上面我们通过使用 reshape((2, 2, 4))方法创建了一个 3 维,也就是 3 个轴的数组。 其 shape 是 2 * 2 * 4 。


先看下对应关系:


(0,0)-》 [ 0, 1, 2, 3]


(0,1)-》 [ 4, 5, 6, 7]


(1,0)-》 [ 8, 9, 10, 11]


(1,1)-》 [12, 13, 14, 15]


转换之后:


(0,0)-》 [ 0, 1, 2, 3]


(0,1)-》 [ 8, 9, 10, 11]


(1,0)-》[ 4, 5, 6, 7]


(1,1)-》 [12, 13, 14, 15]


于是得到了我们上面的的结果。


多维数组的轴转换可能比较复杂,大家多多理解。


还可以使用 swapaxes 来交换两个轴,上面的例子可以重写为:


arr.swapaxes(0,1)本文已收录于 http://www.flydean.com/09-python-numpy-ndarray/


最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 2021 年 05 月 19 日阅读数: 12
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
NumPy之:ndarray多维数组操作