Numpy原理/常见操作以及广播原理

图像处理总是与多维数组、多维矩阵离不开,python中用于处理数组的高效数据库之一是Numpy,因此在此对Numpy的一些概念进行巩固,记录numpy常见的操作,最后将广播机制的常见使用进行记录。

Numpy的本质

numpy可以提供快速高效的多维数组对象ndarray,可以用于读写硬盘上基于数组的数据集工具,可以对数组进行元素级别的计算;Numpy在数据分析方便便另一个重要作用就是成为算法之间传递数据的容器。

Numpy的ndarray底层实现是一个数据块,dtype类型,表示数组形状的元组以及一个跨度元组。

比如一个二维的3 \times 5的int32 的ndarray,因为int32占据4字节,因此这个数组底层一个60字节的内存空间的数据块,dtype类型为np.int32,表示数组形状的元组(3,5),以及一个跨度元组(20,4).

数据类型决定了ndarray将一块内存解释为特定数据类型所需要的信息。数据类型是numpy强大灵活的原因,

如何理解跨度元组的概念?跨度元组中的每个整数是指为了前进到当前维度下一个元素需要跨过的字节数。一个3 \times 5 的数组,在第一个维度中有三个元素,每个元素是一个长度为5的int32型整数,因此从当前元素移动到下个元素需要跨过5 \times 4=20字节;而对于第二个维度,其移动到下个元素,只需要移动一个整数长度,也就是4.

常用的数组操作

创建数组
如果没有指定dtype,默认为float64

从已有的python数组生成

data1= [0,1,2,3,4,5]
arr1 = np.array(data1)

创建全1或者全0数组,empty,顺序数组

np.zeros(10)
np.zeros((3,6))
np.ones((3,6))
np.empty((2,3,3))
np.arange(15)

获得数组的属性

arr.dtype
arr.shape
arr.ndim

改变数据类型
float转为int会直接截断小数部分

int_arr = arr.astype(np.int32)

字符串转为整数,可以优雅的进行

numeric_string = np.array(['1.25','9.6'],dtype=np.string_)
numeric_string.astype(float)

改变数组形状
reshape操作不会复制任何数据,仅仅是改变其表示数组形状的元组

arr.reshape((2,5))

将多维数组扁平化有两个方法,其中ravel不会产生元数据的副本,而flatten则会返回数据的副本

arr.ravel()
arr2 = arr.flatten()

数组和数组之间的算数运算

相同大小的数组和数组的算数运算都是元素级别的。数组和标量的运算会将标量值传播到各个元素上。大小不同的数组之间的运算叫广播。
广播是一个值得单独提出来的模块。放在后面仔细讲。

数组的合并和拆分

numpy.concatenate按照指定轴将数组连接在一起,vstack等价于np.concatenate([arr1,arr2],axis=0),hstack等价于np.concatenate([arr1,arr2],axis=1)

arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([7,8,9],[10,11,12])
np.concatenate([arr1,arr2],axis=0)
#将会得到一个(4,3)形状的数组
np.concatenate([arr1,arr2],axis=1)
#将会得到一个(2,6)形状的数组

数组的复制

想要得到数组的复制而不是视图,需要用copy()函数

arr[5:8].copy()

基本索引和切片
数组的切片反应的是原始数组的视图,数据并不会被复制,因此对切片上的所有改动都会直接反应到原始数组上。

arr = np.arange(9).reshape((3,3))
# 索引
arr[2]
#得到的是np.array([7,8,9])
arr[0][2]
arr[0,2]

# 切片索引
#对于二维数组,可以进行这些类型的切片索引
arr[:2] #默认axis=0
arr[:2,1:]
arr[2,:1]
arr[:,:1]

# 布尔型索引
#bool型数组长度必须和被索引的轴长度一致。一般切片默认axis=0.
#通过bool型选取的数组总是创建数组的副本,即使是返回一模一样的数组也是如此。
names = np.array(['bob','Joe','Will','Bob']
data = np.randn(4,4)
data[names=='bob']
data[-(names=='bob')]
data[names=='bob',2:]

mask = (names=='bob')|(names=='Will')
data[mask]

#和上面的bool索引不同,通过bool型索引设置值会直接在原始的数组上进行改动。
data[data<0]=0

# 花式索引
# 花式索引是利用整数数组进行索引
#花式索引总是复制数据到新的数组
array = np.arange(32).reshape((8,4))
arr[[4,3,0,6]]  #得到第4,3,0,6行
arr[[-5,-3,-7]] 

arr[[1,5,7,2],[0,3,1,2]] #得到的是长度为4的一维数组
arr[[1,5,7,2],[:,0,3,1,2]] #得到的一个二维数组

数组的转置和轴对换

转置返回的是元数据的视图,并不会进行任何复制操作

# 属性方式
arr.T

# 计算内积
np.dot(arr.T,arr)

#高维情况
arr = np.arange(16).reshape((2,2,4))
arr.transpose((1,0,2))

arr.swapaxes(1,2)

通用函数
主要摘自参考文献中的《通用函数:快速的元素级数组函数》
一元通用函数

abs丶fabs  计算整数丶浮点数或复数的绝对值。对于非复数值,可以使用更快的fabs。
sqrt:计算各元素的平方根。
In [36]: np.sqrt(4)
square:计算各元素的平方。
In [38]: np.square(4)

exp:计算各元素的指数(e^x)
In [40]: np.exp(2)

#python3还有个函数为exp2,计算的是2的指数
In [41]: np.exp2(5)
Out[41]: 32.0
 

log丶log10丶log2丶log1p  分别为自然对数(底数为e)丶底数为10的log丶底数为2的log丶log(1+x)
In [42]: np.log(2)
Out[42]: 0.6931471805599453

sign:计算各元素的正负号:1(正数)丶0(零)丶-1(负数)
In [44]: np.sign(12)

ceil:计算各元素的ceiling值,即大于等于该值的最小整数
In [47]: np.ceil(15.971)
Out[47]: 16.0
 
floor:计算各元素的floor值,即小于等于该值的最大整数
In [48]: np.floor(-1.564)
Out[48]: -2.0

rint:将各元素四舍五入到最接近的整数,保留dtype
In [50]: np.rint(1.485)
Out[50]: 1.0
 
modf:将数组的小数和整数部分以两个独立数组的形式返回
In [52]: np.modf([1.5,2.9])
Out[52]: (array([0.5, 0.9]), array([1., 2.]))
 
isnan:返回一个表示“哪些值是NaN(这不是一个数字)”的布尔型数组
In [56]: np.isnan(NaN)
Out[56]: True

isfinite丶isinf:分别返回一个表示“哪些元素是有穷的(非inf,非NaN)”或“哪些元素是无穷的”的布尔型数组
复制代码
In [61]: np.isfinite(1/3)
Out[61]: True

cos丶cosh丶sin丶sinh:普通型和双曲型三角函数
In [65]: np.sin(30)
Out[65]: -0.9880316240928618
 
tan丶tanh丶arccos丶arccosh丶arcsin丶arcsinh丶arctan丶arctanh:反三角函数
logical_not:计算各元素not x的真值。相当于-arr
In [71]: np.logical_not(15)
Out[71]: False

二元通用函数ufunc

add:将数组中对应的元素相加
subtract:从第一个数组中减去第二个数组中的元素
multiply:数组元素相乘
divide丶floor_divide:除法或向下圆整除法(丢弃余数)
power:对第一个数组中的元素A,根据第二个数组中的相应元素B,计算A^B
maximum丶fmax:元素级的最大值计算。fmax将忽略NAN
minimum丶fmin:元素级的最小值计算。fmax将忽略NAN
mod:元素级的求模计算(除法的余数)
copysign:将第二个数组中的值的符号复制给第一个数组中的值。
greater丶greater_equal丶less丶less_equal丶equal丶not_equal:执行元素级的比较运算,最终产生布尔型数组。相当于运算符>丶>=丶<丶<=丶==丶!=
logical_and丶logical_or丶logical_xor:执行元素级的真值逻辑运算。相当于运算符&丶|丶^(与或异)

条件逻辑判断转为数组运算 where

#cond需要得到true/false的bool型数组
# x,y可以是大小相同的数组,也可以是标量+数组
res = np.where(cond,x,y)

arr=np.random.randn(4,4)
np.where(arr>0,2,arr)
np.where(arr>0,2,-2)

数学统计方法
这个很常用mean/sum,std标准差,var方差,min,max,argmin,argmax,cumsum所有元素的累计和,cumprod所有元素的累计积

arr=np.random.randn(4,3)
arr.mean()
arr.sum()
arr.mean(axis=1) 
#表示沿着该轴计算均值,比如这里沿着1轴计算,最终是4个个元素
------>axis=1   mean
1, 2, 3         2
4, 5, 6         5
7, 8, 9         8
10,11,12        11

#cumsum和cumprod返回的是一个由中间结果组成数组
arr.cumsum(0)
 |    1, 2, 3         2
 |    4, 5, 6         5
 |    7, 8, 9         8
\/    10,11,12        11
axis=0
cumsum(0)
 |    1, 2, 3  
 |    5, 7, 9        
 |    12,15,18        
\/    22,26,30       

bool型数组可以使用sum()得到True的数量,可以使用any()和all()函数得到是否存在ture和是否都是True.

bools = np.array([True,False,True,False])
(bools>0).sum()

# 这两个方法也可以用于非bools型数值数组,非0元素将被认为是True
bools.any()
True
bools.all()
False

arr.sort()函数用于就地排序,还可以传入axis沿着指定轴排序.顶级方法np.sort()函数用于返回排序副本,并不会改变原始数组的结果

唯一化函数和其他集合逻辑函数

np.unique(arr)得到数组中的唯一元素的排序数组
intersect1d(x,y) 得到x,y中得到公共元素的排序数组
union1d(x,y) 得到xy的并集有序数组
in1d(x,y) 得到x每个元素是否包含于y的bool数组
setdiff1d(x,y)元素在x中不在y中的元素集合
setor1d(x,y)得到存在于一个数组但是不存在于另一个数组的元素集合,理解为异或

数组输入输出

np.save和np.load,存放在npy后缀的文件中。

线性代数函数
直接对两个数组*得到的对应元素的积,当需要计算矩阵点积的时候需要使用代数函数。

z = np.dot(x,y)
z = x.dot(y)
mat = x.T.dot(x)
from numpy.linalg import inv,qr
diag 方阵的对角线元素,一位数组方式
trace 对角线元素之和
dot 矩阵点积
det 矩阵行列式
eig 矩阵特征值和特征向量
inv 矩阵的逆
qr 矩阵的QR分解
svd矩阵的奇异值分解

广播机制

广播是值不同形状的数组之间进行算数运算的执行方式。将标量和数组合并行就会发生最简单的广播——将标量数值广播到所有的元素上。

广播看起来很简单,但是也是有原则的。原则就是:

  1. 如果两个数组维度不一致,但是后缘维度的轴长相符,则可以广播
  2. 如果两个数组维度数量一致(比如都是三维),其中有一个轴为1,则含有1的这个数组会沿着1这个轴进行广播

具体来看,下面两个数组的维度不一致(4,3),(3,),但是从最后一个维度开始,arr1的第二维长度为3,和arr2的维度相同。arr1和arr2的shape并不一样,但是它们可以执行相加操作,这就是通过广播完成的,在这个例子当中是将arr2沿着0轴进行扩展。

arr1 = np.array([[0, 0, 0],[1, 1, 1],[2, 2, 2], [3, 3, 3]])  #arr1.shape = (4,3)
arr2 = np.array([1, 2, 3])    #arr2.shape = (3,)
arr_sum = arr1 + arr2

同理,如果有一个数组是(3,4,2),另一个数组是(4,2),那么虽然(3,4,2)和(4,2)的维度是不相同的,前者为3维,后者为2维。但是它们后缘维度的轴长相同,都为(4,2),所以可以沿着0轴进行广播。

但是如果一个数组是(4,3),另一个数组是(4,),那么因为后缘维度不一致,所以会报错。这种情况需要将第二个数组reshape(4,1),让其变成第二种情况。

对于一个数组是(4,3),另一个数组是(4,1)的,因为其第一轴长度相同,第二轴上有1,因此第二个数组会沿着轴1进行广播。

arr = np.range(12).reshape((4,3))
mean = arr.mean(1)  #mean.shape = (4,)
mean = mean.reshape((4,1))
demean = arr-mean

如何比较优雅的广播:上面的代码中,使用了reshape()函数改变了数组的形状,numpy还有另一种通过索引机制插入周的特殊语法,通过np.newaxis属性和全切片方式插入

arr = np.zeros((4,4))
arr_3d = arr[:,np.newaxis,:]

通过这样发的方式,我们就可以根据需要广播的轴定制需要广播的数组。比如数组形状(8,5,3),第二个数组为(8,5),想要沿着轴2广播,则先将其形状扩充为(8,5,1);第二个数组为(8,3),想要沿着轴1广播,则需要将其形状扩充为(8,1,3);第二个数组为(5,3),想要沿着轴0进行广播,则需要将其形状扩充为(1,5,3).perfect,感觉自己棒棒哒!

到这里大致介绍了广播的机制。

我们平时看见的标量和数组的广播中,标量没有维度,因此会全部被广播到各个维度。

如果有一个数组是(3,4,2),另一个数组是(1,2),同时满足了维度不一致和存在长度为1的维度,因此也可以进行广播,沿着轴0和轴1进行广播。

arr= np.arange(24).reshape((2,3,4))
mean = np.array([1,2,3,4]).reshape((1,4))
arr-mean

在tensorflow中,会遇到对于三通道的图片,需要对每个通道减去通道均值,这时候用到的就是这种广播机制。

主要参考资料

《利用python进行数据分析》书籍第4章和第12章

通用函数:快速的元素级数组函数

numpy中的广播机制

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341