前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Numpy的广播功能

Numpy的广播功能

作者头像
用户3577892
发布2020-06-12 16:33:22
1.7K0
发布2020-06-12 16:33:22
举报
文章被收录于专栏:数据科学CLUB数据科学CLUB

数组的计算:广播广播的介绍广播的规则广播的实际应用比较,掩码和布尔逻辑比较操作操作布尔数组将布尔数组作为掩码

《Python数据科学手册》读书笔记

数组的计算:广播

另外一种向量化操作的方法是利用 NumPy 的广播功能。

广播的介绍

对于同样大小的数组, 二进制操作是对相应元素逐个计算:

代码语言:javascript
复制
import numpy as np

a = np.array([, , ])
b = np.array([, , ])
a + b
代码语言:javascript
复制
array([, , ])

广播允许这些二进制操作可以用于不同大小的数组。例如, 可以简单地将一个标量(可以认为是一个零维的数组) 和一个数组相加:

代码语言:javascript
复制
a + 
代码语言:javascript
复制
array([, , ])

我们可以认为这个操作是将数值 5 扩展或重复至数组 [5, 5, 5], 然后执行加法。NumPy 广播功能的好处是, 这种对值的重复实际上并没有发生, 但是这是一种很好用的理解广播的模型。

代码语言:javascript
复制
M = np.ones((, ))
M
代码语言:javascript
复制
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])
代码语言:javascript
复制
M + a
代码语言:javascript
复制
array([[1., 2., 3.],
       [1., 2., 3.],
       [1., 2., 3.]])

这里这个一维数组就被扩展或者广播了。它沿着第二个维度扩展, 扩展到匹配 M 数组的形状。

以上的这些例子理解起来都相对容易, 更复杂的情况会涉及对两个数组的同时广播:

代码语言:javascript
复制
# 两个数组同时广播
a = np.arange()
b = np.arange()[:, np.newaxis]

print(a)
print(b)
代码语言:javascript
复制
[  ]
[[]
 []
 []]
代码语言:javascript
复制
a + b
代码语言:javascript
复制
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4]])

广播的规则

  • 如果两个数组的维度数不同,那么小维度数组的形状将会在最左边补1
  • 如果两个数组的形状在任何一个维度都不匹配,那么数组的形状将会沿着维度为1的维度扩展以匹配另外一个数组的形状
  • 如果两个数组的形状在任何一个维度都不匹配并且没有任何一个维度等于1,那么会发生异常
  1. example 1
代码语言:javascript
复制
M = np.ones((, ))
a = np.arange()
  • M.shape = (2, 3)
  • a.shape = (3,)

根据规则1

  • M.shape -> (2, 3)
  • a.shape -> (1, 3)

根据规则2

  • M.shape -> (2, 3)
  • a.shape -> (2, 3)
代码语言:javascript
复制
M + a
代码语言:javascript
复制
array([[1., 2., 3.],
       [1., 2., 3.]])
  1. example 2
代码语言:javascript
复制
a = np.arange().reshape((, ))
b = np.arange()
  • M.shape = (3, 1)
  • a.shape = (3,)

根据规则1

  • M.shape -> (3, 1)
  • a.shape -> (1, 3)

根据规则2

  • M.shape -> (3, 3)
  • a.shape -> (3, 3)
代码语言:javascript
复制
a + b
代码语言:javascript
复制
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4]])
  1. example 3
代码语言:javascript
复制
M = np.ones((, ))
a = np.arange()
  • M.shape = (3, 2)
  • a.shape = (3,)

根据规则1

  • M.shape -> (3, 2)
  • a.shape -> (1, 3)

根据规则2

  • M.shape -> (3, 2)
  • a.shape -> (3, 3)

根据规则3

最终形状不匹配,这两个数组不兼容

代码语言:javascript
复制
M + a
代码语言:javascript
复制
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-43-8cac1d547906> in <module>()
----> 1 M + a


ValueError: operands could not be broadcast together with shapes (,) (,) 
代码语言:javascript
复制
# 如果希望实现右边补齐,可以通过变形数组来实现
a[:, np.newaxis].shape
代码语言:javascript
复制
(3, 1)
代码语言:javascript
复制
M + a[:, np.newaxis]
代码语言:javascript
复制
array([[1., 1.],
       [2., 2.],
       [3., 3.]])

另外也需要注意, 这里仅用到了 + 运算符, 而这些广播规则对于任意二进制通用函数都是适用的。例如这里的 logaddexp(a, b) 函 数, 比起简单的方法, 该函数计算 log(exp(a) + exp(b)) 更准确:

代码语言:javascript
复制
np.logaddexp(M, a[:, np.newaxis])
代码语言:javascript
复制
array([[1.31326169, 1.31326169],
       [1.69314718, 1.69314718],
       [2.31326169, 2.31326169]])

广播的实际应用

  1. 数组的归一化
代码语言:javascript
复制
X = np.random.random((, ))
代码语言:javascript
复制
# 计算每一列的平均值
Xmean = X.mean()
Xmean
代码语言:javascript
复制
array([0.47874092, 0.54918989, 0.48897961])
代码语言:javascript
复制
# 实现归一化
X_centered = X - Xmean
代码语言:javascript
复制
# 检验每一列的均值是否为0
X_centered.mean()
代码语言:javascript
复制
array([ 1.66533454e-17, -8.88178420e-17,  0.00000000e+00])
  1. 画一个二维函数
代码语言:javascript
复制
# x and y have 50 steps from 0 to 5
x = np.linspace(, , )
y = np.linspace(, , )[:, np.newaxis]

z = np.sin(x) **  + np.cos( + y * x) * np.cos(x)
代码语言:javascript
复制
%matplotlib inline
import matplotlib.pyplot as plt
代码语言:javascript
复制
plt.imshow(z, origin='lower', extent=[, , , ],
           cmap='viridis')
plt.colorbar();

比较,掩码和布尔逻辑

比较操作

代码语言:javascript
复制
x = np.array([, , , , ])
代码语言:javascript
复制
x <   
代码语言:javascript
复制
array([ True,  True, False, False, False])
代码语言:javascript
复制
x >   
代码语言:javascript
复制
array([False, False, False,  True,  True])
代码语言:javascript
复制
x <=  
代码语言:javascript
复制
array([ True,  True,  True, False, False])
代码语言:javascript
复制
x >= 
代码语言:javascript
复制
array([False, False,  True,  True,  True])
代码语言:javascript
复制
x != 
代码语言:javascript
复制
array([ True,  True, False,  True,  True])
代码语言:javascript
复制
x ==  
代码语言:javascript
复制
array([False, False,  True, False, False])
代码语言:javascript
复制
# 利用复合表达式对两个数组进行逐元素比较
( * x) == (x ** )
代码语言:javascript
复制
array([False,  True, False, False, False])

运算符

对应通用函数

==

np.equal

<

np.less

>

np.greater

!=

np.not_equal

<=

np.less_equal

>=

np.greater_equal

代码语言:javascript
复制
# 比较运算通用函数可以用于任何形状大小的数组
rng = np.random.RandomState()# 确保每次产生的随机数组是一样的
x = rng.randint(, size=(, ))
x
代码语言:javascript
复制
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 6]])
代码语言:javascript
复制
x < 
代码语言:javascript
复制
array([[ True,  True,  True,  True],
       [False, False,  True,  True],
       [ True,  True, False, False]])

这样每次计算的结果都是布尔数组了。NumPy 提供了一些简明的模式来操作这些布尔结果。

操作布尔数组

给定一个布尔数组, 你可以实现很多有用的操作。首先打印出此前生成的二维数组 x:

代码语言:javascript
复制
print(x)
代码语言:javascript
复制
[[   ]
 [   ]
 [   ]]
  1. 统计个数
代码语言:javascript
复制
# 有多少个值小于6
np.count_nonzero(x < )
代码语言:javascript
复制
8
代码语言:javascript
复制
# 也可使用sum来进行计数,这个例子中F被解释成0,T被解释成1
np.sum(x < )
代码语言:javascript
复制
8
代码语言:javascript
复制
# 每行有多少个值小于6
np.sum(x < , axis=)
代码语言:javascript
复制
array([, , ])
代码语言:javascript
复制
# 有没有值大于8
np.any(x > )
代码语言:javascript
复制
True
代码语言:javascript
复制
# 有没有值小于0
np.any(x < )
代码语言:javascript
复制
False
代码语言:javascript
复制
# 是否所有值小于10
np.all(x < )
代码语言:javascript
复制
True
代码语言:javascript
复制
# 是否所有值等于6
np.all(x == )
代码语言:javascript
复制
False
代码语言:javascript
复制
# 是否每行的所有值都小于8
np.all(x < , axis=)
代码语言:javascript
复制
array([ True, False,  True])
  1. 布尔运算符
  • &:逻辑与
  • | :逻辑或
  • ~:逻辑取反

运算符

对应通用函数

&

np.bitwise_and

np.bitwise_or

~

np.bitwise_not

将布尔数组作为掩码

一种更强大的模式是使用布尔数组作为掩码, 通过该掩码选择数据的子数据集。

代码语言:javascript
复制
x
代码语言:javascript
复制
array([[5, 0, 3, 3],
       [7, 9, 3, 5],
       [2, 4, 7, 6]])
代码语言:javascript
复制
x < 
代码语言:javascript
复制
array([[False,  True,  True,  True],
       [False, False,  True, False],
       [ True,  True, False, False]])

现在为了将这些值从数组中选出, 可以进行简单的索引, 即掩码操作:

代码语言:javascript
复制
# 将小于5的值从数组中筛选出来
x[x < ]
代码语言:javascript
复制
array([, , , , , ])

and和or对整个对象执行单个布尔运算,而&和|对一个对象的内容执行多个布尔运算,对于Numpy布尔数组,后者是最常用的操作

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据科学CLUB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 数组的计算:广播
    • 广播的介绍
      • 广播的规则
        • 广播的实际应用
        • 比较,掩码和布尔逻辑
          • 比较操作
            • 操作布尔数组
              • 将布尔数组作为掩码
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档