一、创建ndarray
import numpy as np
data1 = [6, 7.5, 8, 0, 1]
arr1 = np.array(data1)
print(arr1)
1.嵌套序列:
data2 = [[1,2,3,4],[5,6,7,8]]
arr2 = np.array(data2)
print(arr2)
2.zreos,ones,empty:创建全0,全1,没有任何具体值的数组:
a1 = np.zeros(10)
a2 = np.zeros((3,6))
a3 = np.ones(5)
a4 = np.empty((2,3,2)) #一般返回未初始化的垃圾值
3.arrange: 内置函数range的数组版:
a5 = np.arange(15)
数组创建函数(数据类型基本是float64(浮点数))
二、ndarray的数据类型
1. dtype: (数据类型)将一块内存解释为特定数据类型:
arr1 = np.array([1,2,3],dtype=np.float64)
arr2 = np.array([1,2,3],dtype=np.int32)
print(arr1.dtype)
print(arr2.dtype)
numpy数据类型
2.astype方法:转换dtype:
arr = np.array([1,2,3,4,5])
print(arr.dtype)
float_arr = arr.astype(np.float64)
print(float_arr.dtype)
3.如果将浮点数转换为整数,小数部分将会截断:
arr = np.array([3.7,-1.2,-2.6,0.5,12.9,10.1])
arr.astype(np.int32)
print(arr.astype)
4.astype将数字转换为数值:
numeric_strings = np.array(['1.25','-9.6','42'],dtype=np.string_)
numeric_strings.astype(float)
print(numeric_strings)
5.dtype的另外一个用法:
int_array = np.arange(10)
calibers = np.array([.22,.270,.357,.380,.44,.50],dtype=np.float64)
print(int_array.astype(calibers.dtype))
6:用简洁的类型代码表示dtype:
empty_unit32 = np.empty(8,dtype='u4')
print(empty_unit32)
注意:调用astype无论如何都会创建出一个新的数组(原始数据的一份拷贝),即使新dtype跟老dtype相同也是如此。
警告:浮点数(比如float64和float32)只能表示近似的分数值。在复杂计算中,由于可能会积累一些浮点错误,因此比较操作只能在一定小数位以内有效。
三、数组和标量之间的运算
1.大小相等的数组之间的任何算术都会将运算应用到元素级:
arr = np.array([[1.,2.,3.],[4.,5.,6.]])
print(arr)
print(arr*arr)
print(arr - arr)
2.数组与标量的算术运算也会将那个标量值传播到各个元素:
print(1 / arr)
print(arr ** 0.5)
3.不同大小的数组之间的运算叫做广播。
四、基本的索引和切片
1.将一个标量值赋值给一个切片时,该值会自动传播到整个选区:
arr = np.arange(10)
print(arr[5])
print(arr[5:8])
arr[5:8] = 12
print(arr)
2.跟列表最重要的区别在于,数组切片是原始数组的视图。这意味着数据不会被复制,视图上的任何修改都会直接反馈到源数组上:
arr_slice = arr[5:8]
arr_slice[1] = 12345
print(arr)
arr_slice[:] = 64
print(arr)
警告:如果想要得到的是nadarry切片的一份副本而非视图,就需要显式地进行复制操作,例如arr[5:8].copy()。
3.在一个二维数组中,各索引位置上的元素不再是标量而是一维数组:
arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]])
print(arr2d)
4.以下两种索引方式是等价的:
print(arr2d[0][2])
print(arr2d[0,2])
二维数组索引方式
5.在多维数组中,若省略了后面的索引,则返回对象会是一个维度低一点的ndarray(它含有更高一级维度上的所有数据)。因此在223数组arr3d中:
arr3d = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
print(arr3d)
6.arr3d[0]是一个2*3数组:
print(arr3d[0])
7.标量值和数组都可以被赋值给arr3d[0]:
old_values = arr3d[0].copy()
arr3d[0] = 42
print(arr3d)
arr3d[0] = old_values
print(arr3d)
8.arr3d[1,0]可以访问索引以(1,0)开头的那些值(以一维数组的形式返回):
print(arr3d[1,0])
五、切片索引
1.和列表差不多
print(arr[1:6])
2.高维度可以在一个或多个轴上进行切片,可以与整数索引混用。切片是沿着一个轴向选取元素的。
print(arr2d)
print(arr2d[:2])
3.可以一次传入多个切片(只能得到相同维度的数组视图):
print(arr2d[:2,1:])
4.整数索引和切片混合(可以得到低维度的切片):
print(arr2d[1,:2])
print(arr2d[2,:1])
5.“只有冒号”表示选取整个轴,如,对高危轴进行切片:
print(arr2d[:,:1])
6.切片表达式的赋值操作也会被扩散到整个选区:
arr2d[:2,1:] = 0
print(arr2d)
六、布尔型索引
1.例:有一个存储数据的数组以及一个存储姓名的数组(含有重复项)。用numpy.random 中的randn函数生成一些正态分布的随机数据:
from numpy.random import randn
names = np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])
data = randn(7,4)
print(names)
2.筛选出对应与名字“Bob”的所有行,即names与“Bob”的比较运算产生一个布尔型数组:
print(names == 'Bob')
3.将这个布尔型数组用于数组索引(布尔型数组的长度必须和被索引轴长度一样):
print(data[names == 'Bob'])
4.将布尔型数组跟切片、整数混合使用:
print(data[names == 'Bob',2:])
print(data[names == 'Bob',3])
5.选取三个名字中的两个需要组合应用多个布尔条件,使用&(和)、|(或)之类的布尔算术运算符即可:
mask = (names == 'Bob') | (names == 'Will')
print(mask)
print(data[mask])
警告:Python关键字and和or在布尔型数组中无效。
6.通过布尔组设置值。将data中的所有负值都设置为0:
data[data < 0] = 0
print(data)
7.设置整行或列的值
data[names != 'Joe'] = 7
print(data)
七、花式索引
1.利用整数数组进行索引。例有个8*4的数组:
arr = np.empty((8,4))
for i in range(8):
arr[i] = i
print(arr)
2.为了以特定顺序选取子集,只要传入一个用于指定顺序的整数列表或ndarray即可:
print(arr[[4,3,0,6]])
3.负数索引将会从末尾开始选取行:
print(arr[[-3,-5,-7]])
4.一次传入多个索引数组返回的是一个一维数组,其元素对应各个索引元组:
arr = np.arange(32).reshape((8,4))
print(arr)
print(arr[[1,5,7,2],[0,3,1,2]])
5.选取矩阵的行列子集为矩形区域的方法:
print(arr[[1,5,7,2]][:,[0,3,1,2]])
6.另外一个方法:np.ix_函数,可以将两个一维整数数组转换为一个用于选取方形区域的索引器:
print(arr[np.ix_([1,5,7,2],[0,3,1,2])])
注意花式索引和切片不同,它总是将数据复制到新的数组中。
八、数组转置和轴对换
转置是重塑的一种特殊形式,它返回的是源数据的视图(不会进行任何复制操作)。
1.数组不仅有transpose方法,还有一个特殊的T属性:
arr = np.arange(15).reshape((3,5))
print(arr)
print(arr.T)
2.矩阵计算中利用np.dot计算矩阵内积X T X(简单的转置):
arr = np.random.randn(6,3)
print(np.dot(arr.T,arr))
3.高维数组,transpose需要得到一个由轴编号组成的元组才能对这些轴进行转置:
arr = np.arange(16).reshape((2,2,4))
print(arr)
print(arr.transpose((1,0,2)))
4.swapaxes方法,需要接受一对轴编号,也是返回源数据的视图(不会进行任何复制操作):
print(arr)
print(arr.swapaxes(1,2))
九、通用函数:快速的元素级数函数
通用函数(ufunc)一种对ndarray的数据执行元素级运算的函数。可以看作简单函数(接受一个或多个标量值,并产生一个或多个标量值)的矢量化包装器。
1.(一元)许多的ufunc都是简单的元素级变体,如sqrt和exp:
arr = np.arange(10)
print(np.sqrt(arr))
print(np.exp(arr))
2.(二元)如add或maximum接受两个数组,并返回一个结果数组:
x = randn(8)
y = randn(8)
print(x)
print(y)
print(np.maximum(x,y)) #元素级最大值
一元ufunc
二元ufunc
十、利用数组进行数据处理
矢量化:Numpy用数组表达式代替循环。矢量化数组运算快。
1.例:在一组值(网格型)上计算函数sqrt(x^ 2 + y^2)。np.meshgrid函数接受两个一维数组,并产生两个二维矩阵(对应于两个数组中所有的(x,y)对):
points = np.arange(-5,5,0.01) #1000个间隔相等的点
xs,ys = np.meshgrid(points,points)
print(ys)
2.对该函数的求值运算,把这两个数组当做两个浮点数那样编写表达式:
import matplotlib.pylab as plt
z = np.sqrt(xs ** 2 + ys ** 2)
print(z)
plt.imshow(z,cmap=plt.cm.gray);plt.colorbar()
plt.title("Image plot of $\sqrt{x^2 + y^2}$ for a grid of values")
十一、将条件逻辑表述为数组运算
Numpy.where函数是三元表达式x if condition else y的矢量化版本。
1.假设有一个布尔数组和两个值数组:
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])
2.根据cond中的值选取xarr和yarr的值:当cond中的值为True时,选取xarr的值,否则从yarr中选取。列表推导式的写法(两个问题:1.对大数据的处理速度慢。2.无法使用多维数组):
result = [(x if c else y)
for x,y,c in zip(xarr,yarr,cond)]
print(result)
.where方法(简洁):
result = np.where(cond,xarr,yarr)
print(result)
4.np.where的第二、三个参数不必是数组,他们可以是标量值。数据分析时,where用于根据另一个数组而产生一个新的数组。假设:一个由随机数据组成的矩阵,将所有正值替换为2,所有负值替换为-2(传递给where的数组大小可以不相等,可以是标量值):
arr = randn(4,4)
print(arr)
print(np.where(arr > 0, 2, -2))
print(np.where(arr > 0, 2, arr)) #只将正值设置为2
5.where能表述出更复杂的逻辑,例:有两个布尔类型数组cond1和cond2,希望根据4种不同的布尔值组合实现不同的赋值操作:
for i in range(n):
if cond1[i] and cond2[i]:
result.append(0)
elif cond1[i]:
result.append(1)
elif cond2[i]:
result.append(2)
else:
result.append(3)
6.for循环可以改写为一个嵌套的where表达式:
np.where(cond1 & cond2,0,
np.where(cond1,1,
np.where(cond2,2,3)))
7.利用布尔值可以当作0或1处理来写:
result = 1 * (cond1 - cond2) + 2 * (cond2 & - cond1) + 3 * - (cond1 | cond2)
十二、数学和统计方法
1.mean以及标准差std等聚合计算(约简)既可以当做数组的实例方法调用,也可以当做顶级Numpy函数使用:
arr = np.random.randn(5,4) #正态分布的数据
print(arr.mean())
print(np.mean(arr))
print(arr.sum())
2.mean和sum类可以接受一个axis参数(用于计算该轴向上的统计值),最终结果是一个少一维的数组:
print(arr.mean(axis=1))
print(arr.sum(0))
3.cumsum和cumprod类的方法则不聚合,而是产生一个由中间结果组成的数组:
arr = np.array([[0,1,2],[3,4,5],[6,7,8]])
print(arr.cumsum(0))
print(arr.cumprod(1))
基本数组统计方法
十三、用于布尔型数组的方法
1.以上方法,布尔值被强制转换为1(True)和0(False)。布尔型数组的True值计数常用sum:
arr = randn(100)
print((arr > 0).sum()) #正值的数量
2.any和all方法,any测试数组中是否存在一个或多个True,all检查数组中所有值是否都是True:
bools = np.array([False,False,True,False])
print(bools.any())
print(bools.all())
十四、排序
1.同列表,sort方法就地排序:
arr = randn(8)
print(arr)
print(arr.sort()) #返回值为none
print(arr)
2.多维数组可以在任何一个轴上进行排序,只需将轴编号传给sort:
arr = randn(5,3)
print(arr)
print(arr.sort(1))
print(arr)
3.顶级方法np.sort返回的是数组已排序副本,而就地排序则会修改数组本身。计算数组分位数最简单的办法是对其进行排序,然后选取特定位置的值:
large_arr = randn(1000)
print(large_arr.sort())
print(large_arr[int(0.05 * len(large_arr))]) #5%分位数
十五、唯一化以及其他的集合逻辑
1.np.unique用于找出数组的唯一值并返回已排序的结果:
names = np.array(['Bob','Joe','Will','Bob','Will','Joe','Joe'])
print(np.unique(names))
ints = np.array([3,3,3,2,2,1,1,4,4])
print(np.unique(ints))
2.与其等价的纯python代码对比:
print(sorted(set(names)))
.in1d用于测试一个数组中的值在另一个数组的成员资格,返回一个布尔型数组:
values = np.array([6,0,0,3,2,5,6])
print(np.in1d(values,[2,3,6]))
数组的集合运算
十六、用于数组的文件输入输出
numpy可以读写磁盘上的文本数据或二进制数据。
将数组以二进制格式保存到磁盘
1.np.save和np.load两个读写磁盘数组的主要函数。默认数组以未压缩的原始二进制格式保存在扩展名为.npy的文件中:
arr = np.arange(10)
np.save('some_arry',arr)
2.若文件路径末尾没有拓展名.npy,则该拓展名会被自动加上。通过np.load读取磁盘上的数组:
print(np.load('some_arry.npy'))
.saves可以将多个数组保存到一个压缩文件中,将数组以关键字参数形式传入即可:
print(np.savez('array_archive.npz',a=arr,b=arr))
4.加载.npz文件时会得到一个类似字典的对象,该对象会对各个数组进行延迟加载:
arch = np.load('array_archive.npz')
print(arch['b'])
存取文本文件
1.逗号分隔文件(CSV):
.savetxt执行的是相反的操作:将数组写到以某种分隔符隔开的文本文件中。genfromtxt和loadtxt差不多,只不过它面向的是结构化数组和缺失数据处理。
十七、线性代数
1.用于矩阵乘法的dot函数:
x = np.array([[1.,2.,3.],[4.,5.,6.]])
y = np.array([[6.,23.],[-1,7],[8,9]])
print(x)
print(y)
print(x.dot(y)) #相当于np.dot(x,y)
2.一个二维数组跟一个大小合适的一维数组的矩阵点积运算之后将会得到一个一维数组:
print(np.dot(x,np.ones(3)))
3.numpy.linalg中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的东西:
from numpy.linalg import inv,qr
X = randn(5,5)
mat = X.T.dot(X)
print(inv(mat))
print(mat.dot(inv(mat)))
常用的线性代数函数
十八、随机数生成
1.normal用来生成一个标准正态分布的样本数组:
samples = np.random.normal(size=(4,4))
print(samples)
2.生成大量样本值,numpy.random较快:
from random import normalvariate
N = 1000000
print(%timeit samples = [normalvariate(0,1) for _ in xrange(N)]
1 loops, best of 3: 1.33 s per loop)
print(%timeit np.random.normal(size=N)
10 loops, best of 3: 57.7 ms per loop) #书中代码因尝试运行报错仅参考,正改进中
部分numpy.random函数
十九、范例:随机漫步
1.通过模拟随机漫步来说明如何运用数组运算。例:从0开始,步长1和-1出现的概率相等。用纯python方式来实现1000步的随机漫步:
import random
position = 0
walk = [position]
steps = 1000
for i in range(steps): #书中xrange在python3中已没有
step = 1 if random.randint(0,1) else -1
position += step
walk.append(position)
2. 用np.random模块一次随机产生1000个“掷硬币”结果(即两个数中任选一个),将其设置为1或-1,然后计算累计和:
nsteps = 1000
draw = np.random.randint(0,2,size=nsteps)
steps = np.where(draw > 0,1,-1)
walk = steps.cumsum()
print(walk.min()) #统计最大值
print(walk.max()) #统计最小值
3. 一次模拟多个随机漫步。只要给numpy.random的函数传入一个二元元组就可以产生一个二维数组。计算5000个随机漫步过程(一行一个)的累计和:
nwalks = 5000
nsteps = 1000
draws = np.random.randint(0,2,size=(nwalks,nsteps)) #0或1
steps = np.where(draws > 0,1,-1)
walks = steps.cumsum(1)
print(walks)
4.其他分布方式得到漫步数据,使用不同的随机数生成函数,如normal来生成指定均值和标准差的正态分布数据:
steps = np.random.normal(loc=0,scale=0.25,
size=(nwalks,nsteps))