Python 中的广播 (Broadcasting in Python)
这是一个不同食物(每100g)中不同营养成分的卡路里含量表格,表格为3行4列,列表示不同的食物种类,从左至右依次为苹果,牛肉,鸡蛋,土豆。行表示不同的营养成分,从上到下依次为碳水化合物,蛋白质,脂肪。
那么,我们现在想要计算不同食物中不同营养成分中的卡路里百分比。
现在计算100g苹果中的碳水化合物卡路里百分比含量,首先计算苹果(100g)中三种营养成分卡路里总和56+1.2+1.8 = 59,然后用56/59 = 94.9%算出结果。
可以看出苹果中的卡路里大部分来自于碳水化合物,而牛肉则不同。
对于其他食物,计算方法类似。首先,按列求和,计算每种食物中(100g)三种营养成分总和,然后分别用不用营养成分的卡路里数量除以总和,计算百分比。
那么,能否不使用for循环完成这样的一个计算过程呢?
假设上图的表格是一个4行3列的矩阵,记为
,接下来我们要使用Python的numpy库完成这样的计算。我们打算使用两行代码完成,第一行代码对每一列进行求和,第二行代码分别计算每种食物每种营养成分的百分比。
在jupyter notebook中输入如下代码,按shift+Enter运行,输出如下。
import numpy as np
A = np.array([[56.0,0.0,4.4,68.0],
[1.2,104.0,52.0,8.0],
[1.8,135.0,99.0,0.9]])
print(A)
# axis=0是沿垂直方向求和
cal = A.sum(axis = 0)
print(cal)
# 计算百分比
# 用矩阵A除以这个1乘4 的矩阵,得到百分比矩阵
percentage = 100*A/cal.reshape(1,4)
print(percentage)
下面再来解释一下A.sum(axis = 0)
中的参数axis
。axis用来指明将要进行的运算是沿着哪个轴执行,在numpy中,0轴是垂直的,也就是列,而1轴是水平的,也就是行。
而第二个A/cal.reshape(1,4)
指令则调用了numpy中的广播机制。这里使用3x4的矩阵除以1x4的矩阵
。严格说,在第一行代码执行后,
已经是一个1x4的矩阵。所以其实并不需要再将矩阵
reshape
(重塑)成1x4 。但是当我们写代码时不确定矩阵维度的时候,通常会对矩阵进行reshape
重塑来确保得到我们想要的列向量或行向量。重塑操作reshape
是一个常量时间的操作,时间复杂度是,它的调用代价极低,所以不要怕调用
reshape
来确保矩阵是你想要的尺寸。
下面深入讲解矩阵如何做除法?怎么能用3x4的矩阵除以1x4的矩阵呢?
例子1:
如果你有一个4x1的向量,给它加一个数字100,Python会自动把这个数字100扩展成一个4x1的向量,这就是我们前面也用过的Python的广播机制(之前逻辑回归中加的是参数b)。
这种广播机制实用于行、列向量。
例子2:
一个 的矩阵加上一个
的矩阵,Python会自动将后者复制 m 次,会使其变成一个
的矩阵。
比如一个2x3的矩阵加上一个1x3的矩阵
例子3:
假如你有一个 的矩阵,让它加上一个
的矩阵,后者会在水平方向上复制n次。
总结一下,基本原则(在Python中):
在一个矩阵进行加减乘除时,广播机制会把维度不够的矩阵复制成满足的矩阵,再运算。
不懂的话,可以去看Numpy中广播机制的定义。
最后一句,如果是用Matlab、Octave的bsxFun的同学,其实会发现和Python的广播机制有类似的地方。广播机制不仅能让代码更少,程序更快地达到目的。