查看原文
其他

浅入浅出数据分析之Hello NumPy系列(四)

小一 小一的学习笔记 2023-01-01

2020,努力做一个无可替代的人!


作者 | 小一

全文共2833字,阅读全文需10分钟



写在前面的话

没想到吧,NumPy 还有一小节,请珍惜 NumPy 最后的美好时光。

这一节的内容源自于一个朋友的提问,我在交流群里也分享过,具体问题会在正文中复现,知道你们好奇,往下看就好。

害,差点忘了,先回顾下前三节的内容,同学们自行复习:

听说抬起头看天眼眶才不会湿?

我想试试

正文

先来复现一下朋友当时问我的问题:

有一个数据a(DataFrame类型、3行4列的数据),数据b(Series类型、[0,1,2]),比较a和b后输出布尔型数组

看一下代码演示

# 创建 DataFrame 
import pandas as pd
df_a = pd.DataFrame(np.arange(12).reshape(34))
# 输出
   0  1   2   3
0  0  1   2   3
1  4  5   6   7
2  8  9  10  11

# 创建 Series
import numpy as np
series_b = pd.Series(np.arange(3))
# 输出 
0    0
1    1
2    2
dtype: int32
代码可以左右滑动,你还不知道?

就这样两个数据类型,我刚开始一听第一反应是:这咋比较?数据类型都不一样,维度也不一样。

直到我翻开书看了一下才想起这个概念:广播

先看一下比较的结果是什么

df_a > series_b
# 输出
      0      1      2      3
0  False  False  False  False
1   True   True   True  False
2   True   True   True  False

如果你知道为什么会出现上面这个结果,那恭喜你,这一小节你可以直接跳过相信我,你是最NB的

不知道的同学我们还是要继续的,看完今天的文章想必你会明白。


广播

上面问题提到的一个概念,也是今天唯一的一个知识点:广播

广播指的是不同形状的数组之间的算术运算的执行方式。

首先,将标量数组和数组合并时就会发生简单的广播。

# 创建数组
data_arr1 = np.arange(5)

# 输出
[0 1 2 3 4]

data_arr1 * 5
# 输出
0  5 10 15 20]

在上面的乘法运算中,标量值4你也可以看做是一个一行一列的数组,被广播到其他所有的元素上。

这是最简单的标量的广播,那如果是数组的广播呢?

看例子:

# 创建4行3列的二维数组
data_arr2 = np.array([[000], [111], [222], [333]])
# 输出
[[0 0 0]
 [1 1 1]
 [2 2 2]
 [3 3 3]]

# 创建一维数组
data_arr3 = np.array([123])
# 输出
[1 2 3]

# A + B
data_arr2 + data_arr3
# 输出
[[1 2 3]
 [2 3 4]
 [3 4 5]
 [4 5 6]]

没想到吧,竟然可以计算而没有报错。

这是因为数组 data_arr3 在0轴上做的广播(灰色数字),将原本1行3列的数组广播成4行3列,从而可以与 data_arr2 进行计算

ok,想必你应该清楚广播是什么作用了吧

当然,广播也不是说所有情况都会广播,人家也是有原则的。


何为广播?

如果两个数组的后缘维度(trailing dimension,即从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为它们是广播兼容的广播会在缺失和(或)长度为1 的维度上进行。

这句话是理解广播的核心,老实说,看懂了吗?

不懂往下看,抬起头看天是看不到答案的,只会不让眼泪流下来!

广播主要发生在三种情况下:

  • 一种是两个数组的维度不相等,但是它们的后缘维度的轴长相符

  • 另一种是两个数组的维度相同,对应维度的轴长要么相等要么任意一个为1

  • 上面两种的结合体


第一种情况

两个数组的维度不相等,但是它们的后缘维度的轴长相符

以上面的例子为例

# 输出数组形状/维度
data_arr2.shape
data_arr3.shape

# 输出
(43)
(3,)

可以看到,data_arr2 (4, 3)是二维的,data_arr3 (3,)是一维的,两个数组的维度不相等。同时,它们的后缘维度都是3,后缘维度的轴长相等。

完美符合我们的第一种情况,所以可以进行相加运算。

同样的道理,多维数组也遵循广播原则。

举个图例:

以此类推:

  • (2,4,2,4) 和 (2,3,4) 是兼容的

  • (2,4,2,4) 和 (2,4) 是兼容的

  • (2,4,2,4) 和 (4) 是兼容的

只是它们需要扩展的轴个数不同。

第二种情况

两个数组的维度相同,对应维度的轴长要么相等要么任意一个为1

这个就比较容易理解了,两个维度相同的数组,对应的维度长度有两种情况:

要么长度相同,要么有一个长度为1

# 创建4行1列的二维数组
data_arr4 = np.array([[1234]]).reshape(41)
# 输出
[[1]
 [2]
 [3]
 [4]]

# 创建1行3列的二维数组
data_arr5 = np.array([[123]])
# 输出
[[1 2 3]]

这两个数组都是二维数组分别是(4, 1)、(1, 3)

与我们的 data_arr2 (4, 3) 相比,根据第二种情况:

(4, 3) 与 (4, 1) 维度相同,第一维长度相同,第二维存在长度为1;

(4, 3) 与 (1, 3) 维度相同,第一维存在长度为1,第二维长度相同;

(4, 1) 与 (1, 3) 维度相同,第一维存在长度为1,第二维存在长度为1。

所以,以上三个数组之间互相兼容。

稍微画个图例:

这个时候,第二个二维数组会在1轴上进行广播(灰色数字)

第三种情况

明白了第一种和第二种形式的广播,第三种就是两者的结合体。

举几个简单的例子你就明白了:

(4, 2, 5) 和 (1, 5) 是兼容的。但是不满足第一、第二种情况

(4, 2, 5) 和 (2, 1) 是兼容的。同样不满足第一、第二种情况


看完这三种情况,是不是清楚了一些?

再来细品一下广播的定义

如果两个数组的后缘维度的轴长度相符或其中一方的长度为1,则认为它们是广播兼容的

所以,当我们拿到两个数组,需要判断是否兼容时,先从后缘维度开始,依次往前。

要么对应维度的长度相等,要么对应维度存在某一方的长度为1。

若两个数组的每一个维度都符合这个要求,那这两个数组一定是兼容的。


总结一下:

今天主要讲到一个概念:广播。

正文最后的总结我觉得很到位了,不妨多读几遍理解一下。

看到广播概念的时候,我猜大部分人刚开始也会很懵逼,字都认识,说了个啥?

所以我列举了三种情况去分解这个概念,还记得是哪三种吗?

  • 两个数组的维度不相等,但是它们的后缘维度的轴长相符

  • 两个数组的维度相同,对应维度的轴长要么相等要么任意一个为1

  • 上面两种的结合体

看完这三种情况后,我们回过头再来看概念,是不是就清晰多了?

ok,《Hello NumPy》系列就完结了,一共四小节,希望对你们有用!



写在后面的话

静下心来写技术文真的也太舒服了吧,希望你们也能静下心舒服的看文章。

我一直以为这几天做的最认真的事就是洗手,结果发现我写文章也可以这么认真。

都怪文章名字没起好,对不起自己。说到这,我的眼眶又湿了。

最近不能去外面吃好的,下班一有空就窝家里写文章,都饿瘦了。

之前买的一箱泡面差不多都吃完了,和我爸妈视频的时候他们都说我饿瘦了

不说了,眼眶全湿了…

下个系列见

碎碎念一下
我发现了自己文章起名字的能力真的太差了,还挺影响阅读量的
就像这个系列的名字一样,贼差劲!
知错就改,下个系列一定好好起名


不过名字没起好不妨碍你们点在看吧?



Python 系列入门:准备工作  运算符  数据类型①  数据类型②  流程控制  函数  文件  模块  异常进阶:面向对象  正则表达式  多线程与多进程  JSON操作  时间序列  数据库操作  邮件发送高阶:事半功倍的高阶函数  NumPy系列① NumPy系列②  NumPy系列③技巧:学习技巧①  学习技巧②  学习技巧③爬虫系列

基础:准备工作  爬虫实现方式  Requests详解  BeautifulSoup详解

进阶:获取动态数据①  获取动态数据②

实战:爬取豆瓣电影  豆瓣电影分析  爬取链家租房

其他系列

学排版:Markdown排版教程

领资料:数据分析干货礼包

非技术系列讲故事:小一的故事  和二三四们的故事听故事:投稿故事①投故事:看故事之前先看我



好巧啊,你也读到这了!    

点个在看让我看到你

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存