python:NumPy基础(1),ndarray多维数组对象

利用python进行数据分析

第四章:NumPy基础:数组和矢量计算

精通面向数组的编程和思维方式是成为Python科学计算牛人的一大关键步骤。

NumPy的ndarray:一种多维数组对象

ndarray是一个通用的同构数据多维容器,也是N维数组对象。重点在于其中的所有元素必须是相同类型。每个数组都有一个shape(一个表示各维度大小的元组,即几行几列)和一个dtype(一个用于说明数组数据类型的对象)

1,创建ndarray

创建数组可利用array函数,能够将列表,元组和字典类型等转化为numpy数组。
>>> np.array([1,2,3])
array([1, 2, 3])
>>> np.array((1,2,3))
array([1, 2, 3])
>>> np.array([1,2,3]).dtype
dtype('int32')
列表和元组对象都能创建numpy数组,根据列表或元组的数据类型定义不同的dtype
>>> np.array({1:9,2:8,3:7})
array({1: 9, 2: 8, 3: 7}, dtype=object)
>>> np.array({1:9,2:8,3:7}).dtype
dtype('O')
字典对象也可创建numpy数组,但产生的是object映射关系对象的数据类型

>>> np.array([1.0,2,3])
array([ 1., 2., 3.])
>>> np.array([1.0,2,3]).dtype
dtype('float64')
函数会根据数据特点及数据形式自行定义数组的数据类型

同样嵌套序列(比如由一组等长列表组成的列表)将会被转换为一个多维数组:
>>> data=[[1,2,3,4],[5,6,7,8]]
>>> np.array(data)
array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> arr=np.array(data)
>>> arr.ndim #数组维度
2
>>> arr.shape #数组形状(2行4列)
(2L, 4L)

除np.array之外,还有一些函数也可新建数组。例如,zeros,ones,empty和arange
zeros:可创建指定长度或形状的全0的数组
ones:可创建指定长度或形状的全1的数组
empty:可创建一个没有任何具体值的数组
arange:可创建类似于range()产生的列表的等差数列的数组
>>> np.zeros(10)
array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
>>> np.ones((3,6))
array([[ 1., 1., 1., 1., 1., 1.], [ 1., 1., 1., 1., 1., 1.], [ 1., 1., 1., 1., 1., 1.]])
>>> np.empty((2,3,2))
array([[[ 2.64213422e-316, 2.64241722e-316],
[ 1.56167748e-316, 1.72083934e-316],
[ 1.72093815e-316, 1.55300010e-316]],

[[ 1.56019567e-316, 1.75202002e-316],
[ 1.78010983e-316, 1.99402602e-316],
[ 2.13570429e-316, 2.58423566e-316]]])
>>> np.empty((2,3,2)).ndim #三维数组
3
>>> np.arange(15) #0-14的等差数列
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

表4-1:数组创建函数
| 函数 | 说明 |
| array | 将输入数组(列表、元组、数组或其他序列类型)转换为ndarray。要么推断出dtype,要么显示指定dtype。默认直接复制输入数据 |
| asarray | 将输入转换为ndarray,如果输入本身就是一个ndarray就不进行复制 |
| arange | 类似于内置的range,但返回的是一个ndarray而不是列表 |
| ones、ones_like | 根据指定的形状和dtype创建一个全1数组。ones _like以另一个数组为参数,并根据其形状和dtype创建一个全1数组 |
| zeros、zeros_like | 类似于ones和ones _like,只不过产生的是全0数组 |
| empty、empty_like | 创建新数组,只分配内存空间但不填充任何值 |
| eye、identity | 创建一个正方的N x N单位矩阵(对角线为1,其余为0) |

>>> np.eye(2)
array([[ 1., 0.],
[ 0., 1.]])
>>> np.eye(10)
array([[ 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])

2,ndarray的数据类型

dtype(数据类型)是一个特殊的对象,它含有ndarray将一块内存解释为特定数据类型所需的信息:
>>> arr1=np.array([1,2,3], dtype=np.float64)
>>> arr2=np.array([1,2,3], dtype=np.int32)
>>> arr1.dtype
dtype('float64')
>>> arr2.dtype
dtype('int32')
表4-2:NumPy的数据类型
| 类型 | 类型代码 | 说明 |
| int32 | i4 | 有符号和无符号的32位(4个字节)整型 |
| float64 | f8或d | 标准的双精度浮点数。与C的double和Python的float对象兼容 |
| bool | ? | 存储True和False值的布尔类型 |
| object | O | Python对象类型 |
| string_ | S | 固定长度的字符串类型(每个字符1个字节)。例如,要创建一个长度为10的字符串,应该使用S10 |
| unicode_ | U | 固定长度的unicode类型(字节数有平台决定)。跟字符串的定义方式一样(如U10) |

ndarray的astype方式可以转换dtype:
可以将数值的整型转换成浮点型
>>> arr=np.array([1,2,3,4,5])
>>> arr.dtype
dtype('int64')
>>> float_arr=arr.astype(np.float64)
>>> float_arr.dtype
dtype('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)
也可将数字转换成字符串,或将字符串转换成数字
>>> arr.astype(np.string_)
array(['3.7', '-1.2', '-2.6', '0.5', '12.9', '10.1'], dtype='|S32')
>>> numeric_strings=np.array(['1.25', '-9.6', '42'], dtype=np.string_)
>>> numeric_strings.astype(np.float64)
array([ 1.25, -9.6 , 42. ])

3,数组和标量之间的运算

数组很重要,因为它使你不用编写循环即可对数据执行批量运算。这通常就叫做矢量化(vectorization)。大小相等的数组之间的任何算术运算都会将运算应用到元素级。
>>> arr=np.array([[1., 2., 3.], [4., 5., 6.]])
>>> arr * arr
array([[ 1., 4., 9.], [16., 25., 36.]])
>>> arr - arr
array([[0., 0., 0.], [0., 0., 0.]])
同样,数组与标量的算术运算也会将那个标量值传播到各个元素:
>>> 1/arr
array([[ 1. , 0.5 , 0.33333333], [ 0.25 , 0.2 , 0.16666667]])
>>> arr**0.5
array([[ 1. , 1.41421356, 1.73205081], [ 2. , 2.23606798, 2.44948974]])
不同大小的数组之间的运算叫做广播(broadcasting)

4,基本的索引和切片

NumPy数组的索引能够选取数据子集或单个元素的方式得到想要的值,对于一维数组选取方式很简单
>>> arr=np.arange(10)
>>> arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> arr[5]
5
>>> arr[5:8]
array([5, 6, 7])
>>> arr[5:8]=12
>>> arr
array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
将一个标量值赋值给一个切片时(如arr[5:8]=12),该值会自动传播(“广播”)到整个选区。
数组(array对象)切片与列表切片的区别在于:
数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反映到原数组上:
>>> arr_slice=arr[5:8]
>>> arr_slice[1]=12345
>>> arr
array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9])
>>> arr_slice[:]=64
>>> arr
array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])
由于NumPy的设计目的是处理大数据,所以假如NumPy坚持要将数据复制来复制去的话会产生核定的性能和内存问题。
如果想要得到的是ndarray切片的一份副本而非视图,就需要显式地进行复制操作,例如:arr[5:8].copy() 这种结果的元素赋值不影响arr这个数组的元素数值变化

对于高维度数组,各个索引位置上的元素不再是标量而是一维数组:
>>> arr2d=np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> arr2d
array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
>>> arr2d[2]
array([7, 8, 9])
可以对各个元素进行递归访问但如果利用“[][]”这种访问形式较为麻烦,所以可以传入一个以逗号隔开的索引列表来选取单个元素。所以下面的两种方式是等价的。
>>> arr2d[0][2]
3
>>> arr2d[0,2]
3

在多维数组中,如果省略了后面的索引,则返回对象会是一个维度低一点的ndarray
>>> 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]]])
arr3d为一个三维数组,所以,arr3d[0]将是一个二维数组(2 x 3数组):
>>> arr3d[0]
array([[1, 2, 3],
[4, 5, 6]])
标量值和数组都可以被赋值给arr3d[0]
>>> arr3d[0]=42
>>> arr3d
array([[[42, 42, 42],
[42, 42, 42]],
[[ 7, 8, 9],
[10, 11, 12]]])
>>> arr3d[1,0]
array([7, 8, 9])

切片索引
>>> arr2d[:2,1:]
array([[2, 3],
[5, 6]])
>>> arr2d[1,:2]
array([4, 5])

5,布尔型索引

布尔型数组是存有True或False值的布尔类型的数组
假设存在一个存储数据的数组及一个存储姓名的数组(含有重复项)
>>> names=np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
>>> names
array([‘Bob’, ‘Joe’, ‘Will’, ‘Bob’, ‘Will’, ‘Joe’, ‘Joe’],
dtype=’|S4’)
>>> from numpy.random import randn
>>> data=randn(7,4)
>>> data
array([[ 1.94811869, -0.72154605, -0.097195 , -0.95264291],
[-0.99789285, 1.37237902, -1.99265506, 0.08136949],
[-1.78356833, 0.06103501, 0.61498972, 0.82808179],
[-0.06601954, -0.58540441, 0.17425359, -1.05479406],
[-0.15820496, 0.01409749, -0.2288028 , 0.63077475],
[-2.01207912, -1.93250735, -1.25058964, 1.75701554],
[-1.78953802, -1.40652022, 0.15968546, -2.1197781 ]])
假设每个名字都对应data数组中的一行,而我们想要选出对应于名字“Bob”的所有行。可利用数组的比较运算(如==)可得到矢量化结果。因此,对于names和字符串“Bob”的比较运算将会产生一个布尔型数组
>>> names=='Bob'
array([ True, False, False, True, False, False, False], dtype=bool)
>>> data[names=='Bob']
array([[ 1.94811869, -0.72154605, -0.097195 , -0.95264291],
[-0.06601954, -0.58540441, 0.17425359, -1.05479406]])
这里说明一下布尔型数组的应用,names==’Bob’为布尔型数组,其第一和第四个值为True,所以映射到data这个二维数组当中时,则数组的第一行和第四行被取出,得到上述结果。
>>> data[0:,:1] #这里相当于取出数组的第一列
array([[ 1.94811869],
[-0.99789285],
[-1.78356833],
[-0.06601954],
[-0.15820496],
[-2.01207912],
[-1.78953802]])
>>> data[0:,:1][names=='Bob'] #布尔型取值应用到data数组的第一列,得到的结果是取出这一列当中的第一个和第四个数值
array([[ 1.94811869],
[-0.06601954]])
应用于布尔型取值时,布尔型数组的长度必须跟被索引的轴长度一致。所以,还可以将布尔型数组跟切片、整数(或整数序列)混合使用:
>>> data[names=='Bob', 2:]
array([[ -0.097195 , -0.95264291],
[ 0.17425359, -1.05479406]])
要选择除“Bob”以外的其他值,既可以使用不等于符号(!=),也可以通过负号(-)对条件进行否定,Python 2.7.13以上版本用波浪线(~)代替了负号
>>> names != 'Bob'
array([False, True, True, False, True, True, True], dtype=bool)
>>> data[~(names == 'Bob')]
如果选取names当中的两个组合,需要应用多个布尔条件,使用&(和)、|(或)之类的布尔算术运算符
>>> mask=(names == 'Bob') | (names == 'Will')
>>> mask
array([ True, False, True, True, True, False, False], dtype=bool)
>>> data[data<0]=0 #将数据当中小于0的数都赋值成0

6,花式索引

花式索引(Fancy indexing)是一个NumPy术语,是指利用整数数组进行索引
>>> arr=np.empty((8, 4))
>>> for i in range(8):
... ····arr[i]=i
>>> arr
array([[ 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.]])
为了以特定顺序选取子集,只需传入一个用于指定顺序的整数列表或ndarray即可:
>>> 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))
>>> arr
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, 27],
[28, 29, 30, 31]])
>>> arr[[1, 5, 7, 2]] #选取arr二维数组的第2,6,8,3行
array([[ 4, 5, 6, 7],
[20, 21, 22, 23],
[28, 29, 30, 31],
[ 8, 9, 10, 11]])
>>> arr[[1, 5, 7, 2], [0, 3, 1, 2]] #在上述结果选取之后, 分别选取第1行的第一个数值,第2行的第四个数值,第3行的第二个数值,第4行的第三个数值。
array([ 4, 23, 29, 10])
>>> arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])
[:, [0, 3, 1, 2]]方法相比于上面的[0, 3, 1, 2]方法,不同之处在于保留这个二维数组,同时对各个位置的值进行排序
同样np.ix_函数能够达到[:]所得到的结果,
np.ix_函数可将两个一维整数数组转换为一个用于选取方形区域的索引器
>>> arr[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])]
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])
花式索引跟切片不同,它总是将数据复制到新数组中。

7,数组转置和轴对换

转置(transpose)是重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制操作)。数组不仅有transpose方法,还有一个特殊的T属性:
>>> arr=np.arange(15).reshape((3, 5))
>>> arr
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
>>> arr.T
array([[ 0, 5, 10],
[ 1, 6, 11],
[ 2, 7, 12],
[ 3, 8, 13],
[ 4, 9, 14]])
对于高维数组,transpose需要得到一个由轴编号组成的元组才能对这些轴进行转置
>>> arr=np.arange(16).reshape((2, 2, 4))
>>> arr
array([[[ 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]]])
ndarray的另一种swapaxes转置方法,需要接受一对轴编号
>>> arr.swapaxes(1, 2)
array([[[ 0, 4],
[ 1, 5],
[ 2, 6],
[ 3, 7]],
[[ 8, 12],
[ 9, 13],
[10, 14],
[11, 15]]])

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值