#Numpy Numpy最重要的一个特点就是其N维数组对象,
import numpy as np
data=np.random.randn(2,3)
data
array([[ 0.24196957, -0.53141846, -0.1848278 ],
[-0.59430093, 0.71353871, 1.25313374]])
#然后进行数学运算,
data*10
array([[ 2.41969565, -5.3141846 , -1.84827803],
[-5.9430093 , 7.13538706, 12.53133743]])
#ndarray是一个通用的同构数据多维容器,
data.shape
(2, 3)
data.dtype
dtype('float64')
#创建ndarray 使用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]])
#因为data2是列表的列表,Numpy数组arr2的两个维度的shape是从data2引入的。可以用属性ndim和shape验证,
arr2.ndim
2
arr2.shape
(2, 4)
#除非特别说明,np.array会尝试为新建的这个数组推断出一个较为合适的数据类型。数据类型保存在一个特殊的dtype对象中。
arr1.dtype
dtype('float64')
arr2.dtype
dtype('int32')
#除np.array之外,还有一些函数也可以新建数组。比如,zeros和ones分别可以创建指定长度或形状的全0或全1数组。empty也可以创建一个没有任何具体值的数组。要用这些方法创建多维数组。只需传入一个表示形状的元组即可,
#Python进行标量运算,
import numpy as np
data=np.random.randn(2,3)
data
array([[ 0.27651725, 0.10337636, 0.02689578],
[-0.99750231, 0.82515835, 0.65031969]])
data*10
array([[ 0.28348092, 9.80767803, 4.69578654],
[-0.51565805, 2.90089754, 4.36994602]])
#ndarray里的元素必须是同类型的,
data.shape
(2, 3)
data.dtype
dtype('float64')
#创建ndarray
#使用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的结构大小可以使用ndim和shape验证,
print(arr2.ndim)
print(arr2.shape)
2
(2, 4)
#np.array会尝试为新建的这个数组推断出一个较为合适的数据类型
arr1.dtype
dtype('float64')
arr2.dtype
dtype('int32')
#还有其他的特殊函数可以创建数组,
np.zeros(10)
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
np.zeros((3,6))
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
np.empty((2,3,2))
array([[[1.5e-322, 0.0e+000],
[0.0e+000, 0.0e+000],
[0.0e+000, 0.0e+000]],
[[0.0e+000, 0.0e+000],
[0.0e+000, 0.0e+000],
[0.0e+000, 0.0e+000]]])
#arange是Python内置函数range的数组版,
arr3=np.arange(15)
arr3
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])
#可以对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')
#可以通过ndarray的astype方法明确地将一个数组从一个dtype转换成另一个dtype,
arr=np.array(range(1,6))
arr
array([1, 2, 3, 4, 5])
arr.dtype
dtype('int32')
float_arr=arr.astype(np.float64)
float_arr.dtype
dtype('float64')
#如果将浮点数转换成整数,则小数部分将会被截取删除,
arr=np.array([3.7,-1.2,-2.6,0.5])
arr.astype(np.int32)
array([ 3, -1, -2, 0])
#如果某字符串数组表示的全是数字,也可以使用astype将其转换成数值形式,
numeric_strings=np.array(['1.25','-9.6','42.0'])
numeric_strings.astype(float)
array([ 1.25, -9.6 , 42. ])
#数组的dtype和astype联合起来使用,
int_array=np.arange(10)
calibers=np.array([.22,.270,.357,.380])
int_array.astype(calibers.dtype)
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
#还可以使用简洁的类型代码来表示dtype,
empty_unit32=np.empty(8,dtype='u4')
empty_unit32
array([ 97, 0, 708, 0, 0, 41, 0,
7274572], dtype=uint32)
#NumPy数组的运算 NumPy数组的运算被称为矢量化,大小相等的数组之间的任何算数运算都会将运算应用到元素级,
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]])
#大小相同的数组之间的比较会生成布尔值数组,
arr2=np.array([[0.,4.,1.],[7.,2.,12.]])
arr2>arr
array([[False, True, False],
[ True, False, True]])
#基本的索引和切片 NumPy数组的索引方式和Python列表的功能差不多,
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_slice=arr[5:8]
arr_slice
array([12, 12, 12])
#此时修改arr_slice中的值,变动也会体现在原始数组arr中,
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通常用于处理大数据,如果经常产生副本,对性能和内存来说是巨大的负担。如果需要一个副本,则可以使用明确的复制操作,如arr[5:8].copy()。
#对于高维数组,如二维数组中,各索引位置上的元素不再是标量而是一维数组,
arr2d=np.array([[1,2,3],[4,5,6],[7,8,9]])
arr2d[2]
array([7, 8, 9])
#对单个元素的访问可以通过下面两种方式,
arr2d[0][2]
3
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]]])
arr3d[0]
array([[1, 2, 3],
[4, 5, 6]])
#标量值和数组都可以被赋值给arr3d[0]:
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_values
arr3d
array([[[ 1, 2, 3],
[ 4, 5, 6]],
[[ 7, 8, 9],
[10, 11, 12]]])
#切片索引 ndarray的切片语法跟Python列表这样的一维对象差不多,
arr
array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])
arr[1:6]
array([ 1, 2, 3, 4, 64])
#对于之前的二维数组arr2d,其切片方式稍显不同,
arr2d
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
arr2d[:2]
array([[1, 2, 3],
[4, 5, 6]])
#可以看出,它是沿着第0轴(即第一个轴)切片的,也就是说,切片是沿着一个轴向选取元素的。表达式arr2d[:2]可以被认为是“选取arr2d的前两行”。 可以一次传入多个切片,就像传入多个索引那样,
arr2d[:2,1:]
array([[2, 3],
[5, 6]])
#像这样进行切片时,只能得到相同维度的数组试图。通过将整数索引和切片混合,可以得到低维度的切片。
#例如,我可以选取第二行的前两列,
arr2d[1,:2]
array([4, 5])
#相似的,还可以选择第三列的前两行,
arr2d[:2,2]
array([3, 6])
#注意,只有冒号表示选取整个轴,因此可以像下面这样对高维轴进行切片,
arr2d[:,:1]
array([[1],
[4],
[7]])
#当然,对切片表达式的赋值操作也会被扩散到整个选区,
arr2d[:2,1:]=0
arr2d
array([[1, 0, 0],
[4, 0, 0],
[7, 8, 9]])
#布尔型索引 来看这样一个例子,假设我们有一个用于存储数据的数组以及一个存储姓名的数组(含有重复项)。这里使用numpy.random中的randn函数生成一些正态分布的随机数据,
names=np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
data=np.random.randn(7,4)
names
array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='<U4')
data
array([[-0.72929159, -1.05505895, -1.80527508, -1.76075012],
[-0.30013644, -1.09876993, 1.14227701, 0.9749356 ],
[ 0.02485894, -0.29752688, 0.05278477, 0.05965085],
[-2.01578985, -0.54568068, 1.4825157 , -0.76427341],
[-1.37754357, 0.89577877, 0.38936491, 0.12302993],
[-1.01892466, 0.36543203, -0.95033212, 0.14296077],
[ 0.16447869, -0.30373993, -1.08427409, 0.35703474]])
#假设每个名字都对应data数组中的一行,而我们要选出对应于名字"Bob"的所有行。跟算术运算一样,数组的比较运算(如==)也是矢量化的。因此,对names和字符串"Bob"的比较运算将会产生一个布尔型数组,
names=='Bob'
array([ True, False, False, True, False, False, False])
#这个布尔型数组可用于数组索引,
data[names=='Bob']
array([[-0.72929159, -1.05505895, -1.80527508, -1.76075012],
[-2.01578985, -0.54568068, 1.4825157 , -0.76427341]])
#布尔型数组的长度必须跟被索引的轴的长度一致。此外,还可以将布尔型数组跟切片、整数(或整数序列)混合使用,
data[names=='Bob',2:]
array([[-1.80527508, -1.76075012],
[ 1.4825157 , -0.76427341]])
data[names=='Bob',3]
array([-1.76075012, -0.76427341])
#要选择除"Bob"以外的其它值,既可以使用不等于符号(!=),也可以使用~对条件进行否定,
names!='Bob'
array([False, True, True, False, True, True, True])
data[~(names=='Bob')]
array([[-0.30013644, -1.09876993, 1.14227701, 0.9749356 ],
[ 0.02485894, -0.29752688, 0.05278477, 0.05965085],
[-1.37754357, 0.89577877, 0.38936491, 0.12302993],
[-1.01892466, 0.36543203, -0.95033212, 0.14296077],
[ 0.16447869, -0.30373993, -1.08427409, 0.35703474]])
#~操作符用来反转条件很好用,
cond=names=='Bob'
data[~cond]
#选取这三个名字中的两个需要组合应用多个布尔条件,使用&(和)、|(或)之类的布尔算术运算符即可,
mask=(names=='Bob')|(names=='Will')
mask
array([ True, False, True, True, True, False, False])
data[mask]
array([[-0.72929159, -1.05505895, -1.80527508, -1.76075012],
[ 0.02485894, -0.29752688, 0.05278477, 0.05965085],
[-2.01578985, -0.54568068, 1.4825157 , -0.76427341],
[-1.37754357, 0.89577877, 0.38936491, 0.12302993]])
#通过布尔型索引选取数组中的数据,将总是创建数据的副本,即使返回一模一样的数组也是如此。
#通过布尔型数组设置值是一种经常用到的手段,
data[data<0]=0
data
array([[0. , 0. , 0. , 0. ],
[0. , 0. , 1.14227701, 0.9749356 ],
[0.02485894, 0. , 0.05278477, 0.05965085],
[0. , 0. , 1.4825157 , 0. ],
[0. , 0.89577877, 0.38936491, 0.12302993],
[0. , 0.36543203, 0. , 0.14296077],
[0.16447869, 0. , 0. , 0.35703474]])
#通过一维布尔数组设置整行或整列的值也很简单,
data[names!='Joe']=7
data
array([[7. , 7. , 7. , 7. ],
[0. , 0. , 1.14227701, 0.9749356 ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[7. , 7. , 7. , 7. ],
[0. , 0.36543203, 0. , 0.14296077],
[0.16447869, 0. , 0. , 0.35703474]])
#花式索引 指的是利用整数数组进行索引,
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.]])
#一次传入多个索引数组会有一点特别,返回的是一个一维数组,其中的元素对应各个索引元组,
import numpy as np
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],[0,3,1,2]]
array([ 4, 23, 29, 10])
#最终选出的是元素(1,0)、(5,3)、(7,1)和(2,2)。
arr[2,2]
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]])
#需要注意的是,花式索引跟切片不一样,它总是将数据复制到新的数组中。
#数组转置和轴对换 转置是重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制操作)。数组不仅有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]])
#在进行矩阵计算时,经常需要用到该操作,比如利用np.dot计算矩阵内积,
arr=np.random.randn(6,3)
arr
array([[ 0.34018807, -0.99497988, -0.83401466],
[-0.52301693, 0.33212901, -1.29108115],
[ 0.09797345, -1.21559153, 0.23442256],
[-0.21862493, 0.47126292, 0.25222533],
[-0.0698465 , 0.80123139, 0.73676429],
[-1.1191916 , 0.52330158, 0.57508837]])
np.dot(arr.T,arr)
array([[ 1.70413865, -1.37595284, -0.33573457],
[-1.37595284, 3.71586245, 1.126188 ],
[-0.33573457, 1.126188 , 3.3545908 ]])
#对于高维数组,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]]])
#这里,第一个轴被换成了第二个,第二个轴被换成了第一个,最后一个轴不变。 简单的转置可以使用.T,它其实就是进行轴对换而已。ndarray还有一个swapaxes方法,它需要接受一对轴编号,
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]]])
#swapaxes也是返回源数据的视图(不会进行任何复制操作)。
#通用函数:快速的元素级数组函数 通用函数(即ufunc)是一种对ndarray中的数据执行元素级运算的函数。你可以将其看作简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。 许多ufunc都是简单的元素级变体,如sqrt何exp,
arr=np.arange(10)
arr
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.sqrt(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])
#这些都是一元(unary)ufunc。另外一些(如add或maximum)接受2个数组(因此也叫二元(binary)ufunc),并返回一个结果数组,
x=np.random.randn(8)
y=np.random.randn(8)
print(x)
print(y)
[-1.37735775 0.04879793 -0.96628907 0.44505186 0.28815692 0.15684078
-2.04134521 -0.79681344]
[-0.28786033 -0.83412657 -1.0238366 -1.07778094 1.12706803 -1.06965119
0.62355178 0.41979772]
np.maximum(x,y)
array([-0.28786033, 0.04879793, -0.96628907, 0.44505186, 1.12706803,
0.15684078, 0.62355178, 0.41979772])
#这里,numpy.maximum计算了x和y中元素级别最大的元素。虽然并不常见,但有些ufunc的确可以返回多个数组。modf就是一个例子,它是Python内置函数divmod的矢量化版本,它会返回浮点数数组的小数和整数部分,
arr=np.random.randn(7)*5
arr
array([-0.38406951, 5.10326198, 4.97659185, 5.44730668, -2.0358795 ,
2.09383597, 5.95875792])
remainder,whole_part=np.modf(arr)
print(remainder)
print(whole_part)
[-0.38406951 0.10326198 0.97659185 0.44730668 -0.0358795 0.09383597
0.95875792]
[-0. 5. 4. 5. -2. 2. 5.]
#Ufuncs接受out选项参数,可以让它们在数组的原地进行操作,
arr
array([-0.38406951, 5.10326198, 4.97659185, 5.44730668, -2.0358795 ,
2.09383597, 5.95875792])
np.sqrt(arr)
<ipython-input-22-b58949107b3d>:1: RuntimeWarning: invalid value encountered in sqrt
np.sqrt(arr)
array([ nan, 2.25904006, 2.23082762, 2.33394659, nan,
1.44700932, 2.44105672])
np.sqrt(arr,arr)
<ipython-input-23-e3ca18b15869>:1: RuntimeWarning: invalid value encountered in sqrt
np.sqrt(arr,arr)
array([ nan, 2.25904006, 2.23082762, 2.33394659, nan,
1.44700932, 2.44105672])
#利用数组进行数据处理
#NumPy数组使你可以将许多种数据处理任务表述为简洁的数组表达式(否则需要编写循环)。用数组表达式代替循环的做法,通常被称为矢量化。一般来说,矢量化数组运算要比等价的纯Python方式快上一两个数量级。后边将学习到的广播,就是针对矢量化计算的强大手段。
#在一组值(网格型)上计算函数sqrt(x^2+y^2)。np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中的所有的(x,y)对):
points=np.arange(-5,5,0.01)
xs,ys=np.meshgrid(points,points)
print(xs)
print(ys)
[[-5. -4.99 -4.98 ... 4.97 4.98 4.99]
[-5. -4.99 -4.98 ... 4.97 4.98 4.99]
[-5. -4.99 -4.98 ... 4.97 4.98 4.99]
...
[-5. -4.99 -4.98 ... 4.97 4.98 4.99]
[-5. -4.99 -4.98 ... 4.97 4.98 4.99]
[-5. -4.99 -4.98 ... 4.97 4.98 4.99]]
[[-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]])
#使用matplotlib创建这个二维数组的可视化,
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()
plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
Text(0.5, 1.0, 'Image plot of $\\sqrt{x^2 + y^2}$ for a grid of values')
#将条件逻辑表述为数组运算
#numpy.where函数是三元表达式x if condition else y的矢量化版本。假设我们有一个布尔数组和两个值数组,
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])
#假设我们想要根据cond中的值选取xarr和yarr的值:当cond中的值为True时,选取xarr的值,否则从yarr中选取。列表推导式的写法应该如下所示:
result=[(x if c else y)
for x,y,c in zip(xarr,yarr,cond)]
result
[1.1, 2.2, 1.3, 1.4, 2.5]
#但这种方法存在问题。第一是,它对大数组的处理速度不是很快;第二,无法用于多维数组。若使用np.where,则可以将该功能写得非常简洁,
result=np.where(cond,xarr,yarr)
result
array([1.1, 2.2, 1.3, 1.4, 2.5])
#np.where的第二个和第三个参数不必是数组,他们都可以是标量值。在数据分析工作中,where通常用于根据另一个数组而产生一个新的数组。假设有一个由随机数据组成的矩阵,你希望将所有正值替换为2,将所有负值替换成-2.
arr=np.random.randn(4,4)
arr
array([[ 0.4281781 , 1.67061203, 1.53149978, -1.43829444],
[ 1.21270881, 2.11405251, -0.30159247, -0.78166238],
[ 0.09725756, -0.11740961, -0.38626231, -2.02865091],
[-0.49166712, 0.02415993, 0.84271294, 0.13029182]])
arr>0
array([[ True, True, True, False],
[ True, True, False, False],
[ True, False, False, False],
[False, True, True, True]])
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,可以将标量和数组结合起来。例如,我可以用常数2代替arr中的所有正的值,
np.where(arr>0,2,arr)
array([[ 2. , 2. , 2. , -1.43829444],
[ 2. , 2. , -0.30159247, -0.78166238],
[ 2. , -0.11740961, -0.38626231, -2.02865091],
[-0.49166712, 2. , 2. , 2. ]])
#由此可见传给where的数组大小可以不相等,甚至可以是标量。
#数学和统计方法 可以通过数组上的一组数学函数对整个数组或某个轴向的数据进行统计计算。sum、mean以及标准差std等聚合计算(aggregation,通常叫约简(reduction))既可以当做数组的实例方法调用,也可以当做顶级NumPy函数使用。 这里,我生成一些正态分布随机数据,然后做了聚类统计,
arr=np.random.randn(5,4)
arr
array([[-1.40761295, -0.1246999 , -0.14986214, -0.51978306],
[-1.00757657, 0.17408701, -0.71825631, 0.1459836 ],
[ 0.55108203, -0.20246887, 1.12414628, -0.00999244],
[ 1.88829168, 0.46254338, 0.71655702, 0.54093511],
[ 0.30456791, -0.12780026, 0.75090016, 0.13187372]])
arr.mean()
0.12614576980749564
np.mean(arr)
0.12614576980749564
arr.sum()
2.522915396149913
#mean和sum这类函数可以接受一个axis选项参数,用于计算该轴向上的统计值,最终结果是一个少一维的数组,
arr.mean(axis=1)
array([-0.55048951, -0.35144057, 0.36569175, 0.9020818 , 0.26488538])
arr.sum(axis=0)
array([0.32875211, 0.18166135, 1.723485 , 0.28901693])
#这里,arr.mean(1)是计算行的平均值,arr.sum(0)是计算每列的和。其它如cumsum和cumprod之类的方法则不聚合,而是产生一个由中间结果组成的数组,
arr=np.array([0,1,2,3,4,5,6,7])
arr.cumsum()%累加
array([ 0, 1, 3, 6, 10, 15, 21, 28], dtype=int32)
#在多维数组中,累加函数返回的是同样大小的数组,但是会根据每个低维的切片沿着标记轴计算部分聚类,
arr=np.array([[0,1,2],[3,4,5],[6,7,8]])
arr
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
arr.cumsum(axis=0)
array([[ 0, 1, 2],
[ 3, 5, 7],
[ 9, 12, 15]], dtype=int32)
arr.cumprod(axis=1)
array([[ 0, 0, 0],
[ 3, 12, 60],
[ 6, 42, 336]], dtype=int32)
#用于布尔型数组的方法
#在这些方法中,布尔型会被强制转换为1(True)和0(False)。因此sum经常被用来对布尔型数组中的True值计数。
arr=np.random.randn(100)
(arr>0).sum()
51
#另外还有两个方法any和all,它们对布尔型数组都非常有用。any用于测试数组中是否存在一个或多个True,而all则检查数组中所有值是否都是True。
bools=np.array([False,False,True,False])
bools.any()
True
bools.all()
False
#这两个方法也能用于非布尔型数组,所有非0元素将会被当做True。
#排序 跟Python内置的列表类型一样,NumPy数组也可以通过sort方法就地排序,
arr=np.random.randn(6)
arr
array([ 0.43512049, 0.69488049, -1.31877992, 0.16312914, 0.9669167 ,
1.39490109])
arr.sort()
arr
array([-1.31877992, 0.16312914, 0.43512049, 0.69488049, 0.9669167 ,
1.39490109])
#多维数组可以在任何一个轴向上进行排序,只需将轴编号传给sort即可,
arr=np.random.randn(5,3)
arr.sort(1)
arr
array([[-2.41400738, -0.15958622, 0.74391639],
[-0.39677431, 0.23427563, 0.83588186],
[-1.86258501, -1.30677844, 0.32493963],
[-0.35985093, 0.04956017, 1.18517892],
[ 0.33586449, 0.8797045 , 1.22129438]])
#顶级方法np.sort返回的是数组的已排序副本,而就地排序则会修改数组本身。计算数组分位数最简单的办法是对其进行排序,然后选取特定的位置的值,
large_arr=np.random.randn(1000)
large_arr.sort()
large_arr[int(0.05*len(large_arr))]
-1.6304912022691522
#唯一化以及其它的集合逻辑
#NumPy提供了一些针对一维ndarray的基本集合运算。最常用的可能要数np.unique了,它用于找出数组中的唯一值并返回已排序的结果。
names=np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
np.unique(names)
array(['Bob', 'Joe', 'Will'], dtype='<U4')
ints=np.array([3, 3, 3, 2, 2, 1, 1, 4, 4])
np.unique(ints)
array([1, 2, 3, 4])
#拿跟np.unique等价的纯Python代码来对比一下,
sorted(set(names))
['Bob', 'Joe', 'Will']
#另一个函数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])
#用于数组的文件输入输出 np.save和np.load是读写磁盘数组数据的两个主要函数。默认情况下,数组是以未压缩的原始二进制格式保存爱扩展名为.npy的文件中的,
arr=np.arange(10)
np.save('some_array',arr)
#如果文件路径末尾没有扩展名.npy,则该扩展名会被自动加上。然后就可以通过np.load读取磁盘上的数组,
np.load('some_array.npy')
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
#通过np.savez可以将多个数组保存到一个未压缩文件中,将数组以关键字参数的形式传入即可,
np.savez('array_archive.npz',a=arr,b=arr)
#加载.npz文件时,你会得到一个类似字典的对象,该对象会对各个数组进行延迟加载,
arch=np.load('array_archive.npz')
arch['b']
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
#线性代数
#NumPy提供了一个用于矩阵乘法的dot函数,
import numpy as np
x=np.array([[1.,2.,3.],[4.,5.,6.]])
y=np.array([[6.,23.],[-1,7],[8,9]])
x.dot(y)
array([[ 28., 64.],
[ 67., 181.]])
#x.dot(y)等价于np.dot(x,y)
np.dot(x,y)
array([[ 28., 64.],
[ 67., 181.]])
#积运算之后将会得到一个一维数组,
#一个二维数组跟一个大小合适的一维数组的矩阵点积运算之后将会得到一个一维数组,
)
np.dot(x,np.ones(3))
array([ 6., 15.])
#@运算符也可以用作类似功能,
x@np.ones(3)
array([ 6., 15.])
#numpy.linalg中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的东西。他们跟MATLAB等语言使用的是相同的行业标准线性代数库。
5,5
from numpy.linalg import inv,qr
X=np.random.randn(5,5)
mat=X.T.dot(X)
mat
inv(mat)
array([[ 0.18955169, 0.03494426, 0.09148555, -0.16606473, 0.01455775],
[ 0.03494426, 1.2867844 , -0.52817579, -0.54905566, 0.43160176],
[ 0.09148555, -0.52817579, 3.00948548, -0.18829612, -0.3374321 ],
[-0.16606473, -0.54905566, -0.18829612, 0.5170278 , -0.09857346],
[ 0.01455775, 0.43160176, -0.3374321 , -0.09857346, 0.32467764]])
mat
mat.dot(inv(mat))
array([[ 1.00000000e+00, 1.75143927e-16, -6.08103607e-18,
4.11145483e-16, 8.62652949e-18],
[ 1.37205671e-16, 1.00000000e+00, 4.96087703e-17,
1.21782398e-16, 1.11686539e-16],
[-2.18147307e-17, 5.11320989e-17, 1.00000000e+00,
1.09050221e-16, -1.73042339e-16],
[ 1.92589931e-16, 1.38432901e-15, -9.11568606e-17,
1.00000000e+00, 1.32981925e-16],
[-2.06503119e-17, -6.50299639e-16, 3.35371330e-17,
1.38541096e-16, 1.00000000e+00]])
r
q,r=qr(mat)
r
array([[-15.05312503, -9.14455982, -1.1777531 , -14.48809778,
7.92080382],
[ 0. , -4.19917515, -0.40632224, -4.15959301,
5.48777435],
[ 0. , 0. , -0.66146479, -1.63393942,
-3.14022518],
[ 0. , 0. , 0. , -1.44292934,
-0.8988637 ],
[ 0. , 0. , 0. , 0. ,
1.55139528]])
#模块对Python内置的random进行了补充,增加了一些用于高效生成多种概率分布的样本值的函数。
#伪随机数的生成
#numpy.random模块对Python内置的random进行了补充,增加了一些用于高效生成多种概率分布的样本值的函数。
samples=np.random.normal(size=(4,4))
samples
array([[-0.56496673, -0.03676665, -2.24110428, 2.69999403],
[-1.75173197, -0.57804387, 0.55530846, -0.40673796],
[ 0.6022951 , -1.91237031, -0.32146252, 0.01679694],
[-0.8354337 , 0.96233919, -0.21772245, -0.08703434]])
#这里的伪随机数,是因为他们都是通过算法基于随机数生成器种子,在确定性的条件下生成的。可以使用NumPy的np.random.seed更改随机数生成种子,
1234
np.random.seed(1234)
#numpy.random的数据生成函数使用了全局的随机种子。要避免全局状态,可以使用numpy.random.RandomState,创建一个与其它隔离的随机数生成器,
rng=np.random.RandomState(1234)
rng.randn(10)
array([ 0.47143516, -1.19097569, 1.43270697, -0.3126519 , -0.72058873,
0.88716294, 0.85958841, -0.6365235 , 0.01569637, -2.24268495])
#随机漫步
#我们通过模拟随机漫步来说明如何运用数组运算。从0开始,步长1和-1出现的概率相等。下面是一个通过内置的random模块以纯Python的方式实现1000步的随机漫步,
plt.plot(walk[:100])
import random
import matplotlib.pyplot as plt
position=0
walk=[position]
steps=1000
for i in range(steps):
step=1 if random.randint(0,1) else -1
position+=step
walk.append(position)
plt.plot(walk[:100])
[<matplotlib.lines.Line2D at 0x8d610d0>]
#不难看出,这其实就是随机漫步中各步的累计和,可以用一个数组运算来实现。因此,我们用np.random模块一次性随机产生1000个“掷硬币”结果,将其分别设置为1或-1,然后计算累计和,
steps
nsteps=1000
draws=np.random.randint(0, 2, size=nsteps)
steps=np.where(draws>0,1,-1)
walk=steps.cumsum()
#有了这些数据之后,我们就可以沿着漫步路径做一些统计工作了,比如求取最大值和最小值,
walk.min()
-31
walk.max()
17
,
#假设我们想要知道本次随机漫步需要多久才能距离初始0点至少10步远(任何方向均可)。np.abs(walk)>=10就可以得到一个布尔型数组,可以使用argmax来解决这个问题,返回的是该布尔型数组第一个最大值的索引(True就是最大值),
(np.abs(walk)>=10).argmax()
203
#numpy.random函数传入一个二元元组即可,
#一次模拟多个随机漫步
#给numpy.random函数传入一个二元元组即可,
walks
nwalks=5000
nsteps=1000
draws=np.random.randint(0,2,size=(nwalks, nsteps))
steps=np.where(draws>0,1,-1)
walks=steps.cumsum(1)
walks
array([[ -1, -2, -3, ..., -34, -33, -34],
[ 1, 0, -1, ..., -32, -33, -34],
[ -1, -2, -1, ..., 20, 21, 22],
...,
[ -1, 0, -1, ..., 2, 3, 2],
[ -1, -2, -1, ..., 2, 3, 2],
[ -1, 0, 1, ..., -30, -29, -28]], dtype=int32)
walks.max()
140
walks.min()
-121
walks
type(walks)
numpy.ndarray
pe
walks.shape
(5000, 1000)
#使用any来计算30和-30的最小穿越时间,
hits30=(np.abs(walks)>=30).any(1)
hits30
array([ True, True, True, ..., True, False, True])
hits30.sum()
3384
#5000行数据中,有3384行数据的值穿越了30或-30
#然后利用该布尔型数组选出那些穿越了30(绝对值)的行,并使用argmax在轴1上选取穿越时间,
crossing_times=(np.abs(walks[hits30])>=30).argmax(1)
crossing_times.mean()
501.2594562647754
【4】NumPy基础
最新推荐文章于 2024-07-28 22:23:07 发布