NumPy基础:数组和矢量计算
numpy(numerical python的简称)是高性能科学计算和数据分析的基础包,其主要功能如下: 1.快速高效的多维数组对象 ndarray;
2.直接对数组执行数学运算及对数组执行元素级计算的函数;
3.线性代数运算、随机数生成;
4.将 C、C++、Fortran 代码集成到 Python 的工具等。
一、numpy中的ndarray:一种多维数组对象
numpy中最重要的一个对象为N维数组对象(即ndarray),其实一个快速而灵活的大数据容器,可以利用这种数组对整块数据执行一些数学运算,其语法跟标量之间的运算是一样的。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = np.array([[1,2,3],[4,5,6]]) #定义一个数组
data
array([[1, 2, 3],
[4, 5, 6]])
data * 10 #数组上的每一个元素都乘上10
array([[10, 20, 30],
[40, 50, 60]])
data + data #对本身应用乘法
array([[ 2, 4, 6],
[ 8, 10, 12]])
1.创建ndarray
创建数组最简单的方法是使用array函数,它接受一切序列类型的对象
(1)简单的array操作
#创建简单的array
data1 = [6,7,5,8,0,1]
arr1 = np.array(data1)
arr1
array([6, 7, 5, 8, 0, 1])
#嵌套序列(由一组等长列表组成的列表)
data2 = [[1,2,3,4],[5,6,7,8]]
arr2 = np.array(data2)
arr2
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
arr2.shape #生成2行4列的数组
(2, 4)
arr2.ndim #查看arr2对象的维度
2
np.arange(15) #arrange是numpy的内置数组函数
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
(2)创建一个全1数组
##根据指定的形状和dtype创建一个全1数组
np.ones((3,1),dtype=int)
array([[1],
[1],
[1]])
np.ones(shape=(2,2),dtype=int) #创建一格两行两列数组
array([[1, 1],
[1, 1]])
#oones_like以另一个数组为参数,并根据其形状和dtype创建一个全1数组
np.ones_like(arr2)
array([[1, 1, 1, 1],
[1, 1, 1, 1]])
(3)创建一个全0数组
zeros和zeros_like的用法和上面的ones和one_likes类似,不过产生的是全0数组
np.zeros((3,1),dtype=int)
array([[0],
[0],
[0]])
np.zeros(shape=(2,2),dtype=int)
array([[0, 0],
[0, 0]])
np.zeros_like(arr2)
array([[0, 0, 0, 0],
[0, 0, 0, 0]])
(4)创建空数组,值分配内存空间不填充任何值
np.empty((3,1),dtype=int)
array([[976302128],
[976773732],
[842215226]])
np.empty(shape=(2,2),dtype=int)
array([[771267408, 553],
[771289168, 553]])
np.empty_like(arr2)
array([[ 713024880, 553, 2054778927, 1731096941],
[ 825241187, 512, 723190017, 553]])
(5)创建一个单位矩阵
np.eye(5) #创建5行5列的单位矩阵
array([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.]])
np.identity(5)
array([[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.]])
2.ndarray的数据类型
dtype(数据类型)是一个特殊的对象。它含有ndarray将一块内存解释为指定数据类型所需的信息。他是NumPy如此灵活和强大的原因之一。多数情况下,它们直接映射到相应的机器表示,这使得“读写磁盘上的二进制数据流”以及“集成低级语言代码(C\Fortran)”等工作变得更加简单。dtype命名方式为,类型名+表示元素位长的数字。标准双精度浮点型数据需要占用8字节(64位)。记作float64.常见的数据类型为:
(1)转换numpy的数据类型
arr =np.array([1,2,3,4,5])
arr.dtype
dtype('int32')
##将整数转换成浮点数
float_arr = arr.astype(np.float64) #使用ndarray的astype方法转换array中的类型
float_arr.dtype
##如果将浮点数转换成整数,则小数部分将会被截断
arr = np.array([3.7,-1.2,-2.6,0.5,12.9,10.2])
arr
array([ 3.7, -1.2, -2.6, 0.5, 12.9, 10.2])
arr.astype(np.int32)
array([ 3, -1, -2, 0, 12, 10])
##如果某字符串数组表示的全是数组,也可以使用astype将其转换为数值形式
numeric_string = np.array(['12','12.4','0.5','3.6'],dtype=np.string_)
numeric_string
array([b'12', b'12.4', b'0.5', b'3.6'], dtype='|S4')
numeric_string.astype(float)
array([12. , 12.4, 0.5, 3.6])
(2)dtype的另一用法
int_array = np.arange(10)
int_array
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
calibers = np.array([0.22,0.270,0.380,0.357,0.44,0.50],dtype=np.float64)
int_array.astype(calibers.dtype) #将nt_array的类型转换成calibers的数组类型
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
empty_unint32 = np.empty(8,dtype='u4')
empty_unint32
array([ 0, 1076363264, 3435973837, 1076415692, 0,
1071644672, 3435973837, 1074580684], dtype=uint32)
3.数组和标量之间的运算
数组不用编写函数也可以对数据进行批量运算,通常这叫做矢量化,大小相等的数组之间的任何运算都会应用到元素集
arr = np.array([[1.,2.,3.],[4.,5.,6.]])
arr
array([[1., 2., 3.],
[4., 5., 6.]])
arr * arr #对应的元素之间相乘
array([[ 1., 4., 9.],
[16., 25., 36.]])
1/arr #数组与标量之间的运算会将那个标量值传播到各个元素
array([[1. , 0.5 , 0.33333333],
[0.25 , 0.2 , 0.16666667]])
arr + 1
array([[2., 3., 4.],
[5., 6., 7.]])
4.基本的索引和切片
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
arr[5] #选取索引值为5的元素
5
arr[5:8]
array([5, 6, 7])
(1)数组切片是原始数组的视图
当将一个标量复制给一个切片时,该值会自动传播到整个选区,和列表的区别在于,数组切片是原始数组的视图数据不会被复制,视图上的任何修改都会直接反映在原始数据当中
arr[5:8]=12
arr
array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])
arr_slice = arr[5:8]
arr_slice[1] = 123456
arr
array([ 0, 1, 2, 3, 4, 12, 123456, 12,
8, 9])
arr_slice[:] = 64
arr
array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])
(2)索引方式
#对于高维数组,各索引上的元素不再是标量而是一维数组
arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
arr2d[2]
array([7, 8, 9])
arr2d[0][2] #等价于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[0] #为2*3数组
array([[1, 2, 3],
[4, 5, 6]])
old_values = arr3d[0].copy() #将arr3d[0]数组copy出来
arr3d[0] = 42
arr3d
array([[[42, 42, 42],
[42, 42, 42]],
[[ 7, 8, 9],
[10, 11, 12]]])
arr3d[0] = old_values
arr3d
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
arr3d[1,0]
array([7, 8, 9])
arr3d[1,1,0]
10
(3)切片索引
arr[1:6]
array([ 1, 2, 3, 4, 64])
arr2d
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
arr2d[:2] #切片是沿着0轴选取元素的
array([[1, 2, 3],
[4, 5, 6]])
arr2d[:2,1:] #冒号表示选取全轴
array([[2, 3],
[5, 6]])
5.布尔型索引
names = np.array(['Bob','Joe','Will','Joe','Joe','Will','Bob'])
data = np.random.randn(7,4) #生成7*4的随机数组
names
array(['Bob', 'Joe', 'Will', 'Joe', 'Joe', 'Will', 'Bob'], dtype='<U4')
data
array([[ 0.7828 , 1.17369867, -0.51179537, 1.5833616 ],
[-0.4660567 , -0.54100288, -1.85467031, -1.29799866],
[-0.79565091, 0.86270447, 0.11854395, 0.76857494],
[-1.00353168, -1.10157441, 0.16315636, -1.08319961],
[-1.1620543 , -0.87421106, 0.54198385, -0.37463907],
[-1.29676821, 0.36398684, 1.29366389, -2.30494422],
[ 0.83482259, 1.45588866, 0.035585 , -0.01188404]])
(1)将布尔型数组用于数组索引
names =='Bob'
array([ True, False, False, False, False, False, True])
data[names=='Bob'] #布尔型数组的长度必须跟被索引的轴长度一致
array([[ 0.7828 , 1.17369867, -0.51179537, 1.5833616 ],
[ 0.83482259, 1.45588866, 0.035585 , -0.01188404]])
(2)布尔型数组跟切片整数混用
data[names=='Bob',2:]
array([[-0.29708363, -1.74264034],
[ 1.44947877, -1.83951636]])
names !='Bob'
array([False, True, True, True, True, True, False])
mask = (names == 'Bob') | (names =='Will')
mask
data[mask]
array([[ 0.7828 , 1.17369867, -0.51179537, 1.5833616 ],
[-0.79565091, 0.86270447, 0.11854395, 0.76857494],
[-1.29676821, 0.36398684, 1.29366389, -2.30494422],
[ 0.83482259, 1.45588866, 0.035585 , -0.01188404]])
data #通过布尔值选取数组中的数据,将总是创建数据的副本,即使返回一模一样的数组也是
array([[ 0.7828 , 1.17369867, -0.51179537, 1.5833616 ],
[-0.4660567 , -0.54100288, -1.85467031, -1.29799866],
[-0.79565091, 0.86270447, 0.11854395, 0.76857494],
[-1.00353168, -1.10157441, 0.16315636, -1.08319961],
[-1.1620543 , -0.87421106, 0.54198385, -0.37463907],
[-1.29676821, 0.36398684, 1.29366389, -2.30494422],
[ 0.83482259, 1.45588866, 0.035585 , -0.01188404]])
#通过布尔型数组设置值
data[data<0] = 0
data #将数组中所有小于0的值设置为0
array([[0.7828 , 1.17369867, 0. , 1.5833616 ],
[0. , 0. , 0. , 0. ],
[0. , 0.86270447, 0.11854395, 0.76857494],
[0. , 0. , 0.16315636, 0. ],
[0. , 0. , 0.54198385, 0. ],
[0. , 0.36398684, 1.29366389, 0. ],
[0.83482259, 1.45588866, 0.035585 , 0. ]])
#通过布尔数组设置整行或列的值
data[names != 'Joe'] =7
data
array([[7. , 7. , 7. , 7. ],
[0. , 0. , 0. , 0. ],
[7. , 7. , 7. , 7. ],
[0. , 0. , 0.16315636, 0. ],
[0. , 0. , 0.54198385, 0. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ]])
6.花式索引
花式索引是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]] #选取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))
print(arr)
arr[[1,5,7,2],[0,3,1,2]] #最终选出的是元素
[[ 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]]
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]])
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]])
6.数组装置和轴对换
转置是重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制操作)
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]])
#在进行矩阵计算时,经常用到该操作,比如点乘
arr = np.random.randn(6,3) #生成正态分布的随机数
np.dot(arr.T,arr)
array([[19.23765901, -0.56062569, -1.51824277],
[-0.56062569, 8.57280472, -0.2883368 ],
[-1.51824277, -0.2883368 , 1.40749453]])
#对于高位数组,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]]])
arr
array([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7]],
[[ 8, 9, 10, 11],
[12, 13, 14, 15]]])
arr.swapaxes(1,2)
array([[[ 0, 4],
[ 1, 5],
[ 2, 6],
[ 3, 7]],
[[ 8, 12],
[ 9, 13],
[10, 14],
[11, 15]]])
二、通用函数:快速的元素级数组函数
通用函数(ufunc)时一种对ndarray中的数据执行元素运算的函数
1.一元ufunc(接受一个数组)
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.sqrt(arr) #对数组arr中的每一个元素都进行开方
array([0. , 1. , 1.41421356, 1.73205081, 2. ,
2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
np.exp(arr)
array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
arr = np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.sqrt(arr) #对数组arr中的每一个元素都进行开方
array([0. , 1. , 1.41421356, 1.73205081, 2. ,
2.23606798, 2.44948974, 2.64575131, 2.82842712, 3. ])
np.exp(arr)
array([1.00000000e+00, 2.71828183e+00, 7.38905610e+00, 2.00855369e+01,
5.45981500e+01, 1.48413159e+02, 4.03428793e+02, 1.09663316e+03,
2.98095799e+03, 8.10308393e+03])
2.二元ufunc(接受2个数组)
x = np.random.randn(8)
y = np.random.randn(8)
print(x)
[ 0.3379645 0.91301036 0.03531237 -0.63168541 0.24880695 0.30363243
1.25920703 0.66196622]
print(y)
[ 0.73785629 0.78889423 0.86871239 1.17064993 0.52956418 -0.28149036
-0.06980438 1.38918722]
np.maximum(x,y) #返回元素级的最大值
array([0.73785629, 0.91301036, 0.86871239, 1.17064993, 0.52956418,
0.30363243, 1.25920703, 1.38918722])
三、利用数组进行数据处理
numpy数组可以让你将很多的数据处理任务表述为简洁的数组表达式(不用写循环)。用数组表达式代替循环的做法,通常称为矢量化。一般来说,矢量化数组运算要比纯python方式快一两个数量级。
points = np.arange(-5,5,0.01) #产生以-5开始,5结尾的,步长为0.01的数组
xs,ys = np.meshgrid(points,points)
ys
array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ],
[-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99],
[-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98],
...,
[ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97],
[ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98],
[ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]])
z = np.sqrt(xs ** 2 + ys **2)
z
array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985,
7.06400028],
[7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
7.05692568],
[7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
7.04985815],
...,
[7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603,
7.04279774],
[7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354,
7.04985815],
[7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815,
7.05692568]])
plt.imshow(z,cmap=plt.cm.gray);
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x175d0461780>
1.将条件逻辑表述为数组运算
numpy.where(condition[, x, y])
1、这里x,y是可选参数,condition是条件,这三个输入参数都是array_like的形式;而且三者的维度相同
2、当conditon的某个位置的为true时,输出x的对应位置的元素,否则选择y对应位置的元素;
3、如果只有参数condition,则函数返回为true的元素的坐标位置信息;
其中,x和y不必是数组,他们可以是标量值,在数据分析工作中,where通常用于根据另一个数组来产生一个新的数组
xarr = np.array([1.1,1.2,1.3,1.4,1.5])
yarr = np.array([2.1,2.2,2.3,2.4,2.5])
cond = np.array([True,False,True,True,False])
result = np.where(cond,xarr,yarr) #如果cond中的值为true时,选用xarr中的值,否则选yarr
result
array([1.1, 2.2, 1.3, 1.4, 2.5])
arr = np.random.randn(4,4)
arr
array([[ 1.17493822, -0.00723688, -0.46557043, -2.18073746],
[-0.24026876, 0.46987161, -0.63083544, 2.05618015],
[ 2.22922277, -0.33930449, -0.90547486, 0.23120086],
[-0.48202242, 1.24147767, 1.6121988 , -0.10538597]])
np.where(arr>0,2,-2)
array([[ 2, -2, -2, -2],
[-2, 2, -2, 2],
[ 2, -2, -2, 2],
[-2, 2, 2, -2]])
np.where(arr>0,2,arr) #只将正值设置为2
array([[ 2. , -0.00723688, -0.46557043, -2.18073746],
[-0.24026876, 2. , -0.63083544, 2. ],
[ 2. , -0.33930449, -0.90547486, 2. ],
[-0.48202242, 2. , 2. , -0.10538597]])
2.数学和统计方法
可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算
arr = np.random.randn(5,4) #生成5行4列的正态分布数组
arr
array([[-1.17801449, 0.35805189, -1.41197885, 0.33963754],
[-0.47982315, -0.89158597, -1.56979371, 0.96818193],
[ 1.40337779, -0.45207102, 0.50699584, -1.16953798],
[ 1.26072493, 1.4086036 , -1.16634053, -0.92045532],
[-0.23829138, -0.41431587, 2.29571775, 1.57586293]])
arr.mean()
0.25033739284751244
np.mean(arr)
0.25033739284751244
arr.sum()
5.006747856950248
# mean和sum这类函数可以接受一个axis参数,用于计算该轴上的统计值,最终结果是一个少一维的数组
arr.mean(axis=1) #对arr中的每一行求均值
array([-0.47307598, -0.49325522, 0.07219116, 0.14563317, 0.80474336])
arr.sum(0)
array([ 0.76797371, 0.00868264, -1.3453995 , 0.7936891 ])
arr = np.array([[0,1,2],[3,4,5],[6,7,8]])
arr.cumsum(0) #对每一列累计求和
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]], dtype=int32)
arr.cumprod(1) #对每一行累计求积
array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]], dtype=int32)
3.用于布尔型数组的方法
arr = np.random.randn(100)
(arr>0).sum()#正数的数量
47
bools = np.array([False,False,True,True])
bools.any()
True
bools.all()
False
4.排序
和python的内置列表一样,numpy数组也可以使用sort方法排序
arr = np.random.randn(8)
arr
array([-0.98225165, -0.94115638, -0.09318555, -1.27174211, 0.16123717,
0.50834233, 0.66296585, 0.06165969])
arr.sort()
arr
array([-1.27174211, -0.98225165, -0.94115638, -0.09318555, 0.06165969,
0.16123717, 0.50834233, 0.66296585])
#多维数组可以在任何一个轴上进行排序,只需将轴编号传给sort即可
arr = np.random.randn(5,3)
arr
array([[ 0.71669596, 0.68598353, -1.04816718],
[ 0.29981084, 0.58693313, -0.6316833 ],
[-0.61062897, 0.65160441, -1.7339969 ],
[-0.31215004, -0.62215947, -1.89118853],
[-1.06230313, 0.15293179, 1.48461876]])
arr.sort(1) #对每一行进行排序
arr
array([[-1.04816718, 0.68598353, 0.71669596],
[-0.6316833 , 0.29981084, 0.58693313],
[-1.7339969 , -0.61062897, 0.65160441],
[-1.89118853, -0.62215947, -0.31215004],
[-1.06230313, 0.15293179, 1.48461876]])
##求5%分位数
large_arr = np.random.randn(1000)
large_arr.sort()
large_arr[int(0.05*len(large_arr))]
-1.7516715174563535
5.唯一化以及其他的集合逻辑
numpy提供了一些针对一维ndarray的基本集合运算。最常用的可能要数np.unique了,它用于找出数组中唯一值并返回已排序的结果
names=np.array(['zhuheng','xiaoyang','zhuheng','benben','xiaoyang','zhuheng'])
np.unique(names)
array(['benben', 'xiaoyang', 'zhuheng'], dtype='<U8')
#另一个函数np.in1d用于测试一个数组中的值在另一个数组中的成员资格,返回一个布尔型数组
values=np.array([6,0,0,3,2,5,6])
np.in1d(values,[2,3,6])
array([ True, False, False, True, True, False, True])