From a6d501a725ce267870ebe7bf945f9c0f002a9121 Mon Sep 17 00:00:00 2001 From: jackfrued Date: Mon, 24 Feb 2025 13:17:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BA=86=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Day01-20/02.第一个Python程序.md | 6 +- Day01-20/03.Python语言中的变量.md | 10 +- Day01-20/07.分支和循环结构实战.md | 6 +- Day01-20/09.常用数据结构之列表-2.md | 6 +- Day01-20/11.常用数据结构之字符串.md | 2 +- Day01-20/12.常用数据结构之集合.md | 14 +- Day01-20/13.常用数据结构之字典.md | 66 +- Day01-20/16.函数使用进阶.md | 14 +- Day01-20/17.函数高级应用.md | 20 +- Day01-20/18.面向对象编程入门.md | 14 +- Day01-20/19.面向对象编程进阶.md | 10 +- Day01-20/20.面向对象编程应用.md | 24 +- Day21-30/21.文件读写和异常处理.md | 18 +- Day21-30/22.对象的序列化和反序列化.md | 8 +- Day21-30/23.Python读写CSV文件.md | 6 +- Day21-30/24.Python读写Excel文件-1.md | 16 +- Day21-30/25.Python读写Excel文件-2.md | 10 +- Day21-30/26.Python操作Word和PowerPoint文件.md | 8 +- Day21-30/27.Python操作PDF文件.md | 10 +- Day21-30/28.Python处理图像.md | 16 +- Day21-30/29.Python发送邮件和短信.md | 8 +- Day21-30/30.正则表达式的应用.md | 8 +- Day66-80/66.数据分析概述.md | 58 +- .../data/2018年北京积分落户数据.csv | 0 Day66-80/{ => code}/data/2020年销售数据.xlsx | Bin Day66-80/{ => code}/data/2022年股票数据.xlsx | Bin .../data/2023年北京积分落户数据.csv | 0 .../{ => code}/data/boston_house_price.csv | 0 .../{ => code}/data/某招聘网站招聘数据.csv | 0 Day66-80/code/day01.ipynb | 602 + Day66-80/code/day02.ipynb | 10086 ++++++++++++++++ Day66-80/code/day03.ipynb | 3385 ++++++ Day66-80/code/day04.ipynb | 1572 +++ Day66-80/code/day05.ipynb | 673 ++ Day66-80/code/day06.ipynb | 809 ++ Day66-80/code/day07.ipynb | 1246 ++ Day66-80/res/contents_of_data_analysis.png | Bin 0 -> 268289 bytes Day81-90/82.k最近邻算法.md | 2 +- Day81-90/88.神经网络模型.md | 6 +- Day81-90/90.机器学习实战.md | 4 +- Python学习资源汇总.md | 108 + 41 files changed, 18696 insertions(+), 155 deletions(-) rename Day66-80/{ => code}/data/2018年北京积分落户数据.csv (100%) rename Day66-80/{ => code}/data/2020年销售数据.xlsx (100%) rename Day66-80/{ => code}/data/2022年股票数据.xlsx (100%) rename Day66-80/{ => code}/data/2023年北京积分落户数据.csv (100%) rename Day66-80/{ => code}/data/boston_house_price.csv (100%) rename Day66-80/{ => code}/data/某招聘网站招聘数据.csv (100%) create mode 100644 Day66-80/code/day01.ipynb create mode 100644 Day66-80/code/day02.ipynb create mode 100644 Day66-80/code/day03.ipynb create mode 100644 Day66-80/code/day04.ipynb create mode 100644 Day66-80/code/day05.ipynb create mode 100644 Day66-80/code/day06.ipynb create mode 100644 Day66-80/code/day07.ipynb create mode 100644 Day66-80/res/contents_of_data_analysis.png create mode 100755 Python学习资源汇总.md diff --git a/Day01-20/02.第一个Python程序.md b/Day01-20/02.第一个Python程序.md index c145823..c2fade7 100755 --- a/Day01-20/02.第一个Python程序.md +++ b/Day01-20/02.第一个Python程序.md @@ -96,7 +96,7 @@ Visual Studio Code 是由微软开发能够在 Windows、 Linux 和 macOS 等操 按照行业惯例,我们学习任何一门编程语言写的第一个程序都是输出`hello, world`,因为这段代码是伟大的丹尼斯·里奇(C 语言之父,和肯·汤普森一起开发了 Unix 操作系统)和布莱恩·柯尼汉(awk 语言的发明者)在他们的不朽著作《*The C Programming Language*》中写的第一段代码,下面是对应的 Python 语言的版本。 -```Python +```python print('hello, world') ``` @@ -104,7 +104,7 @@ print('hello, world') 上面的代码只有一个语句,在这个语句中,我们用到了一个名为`print`的函数,它可以帮助我们输出指定的内容;`print`函数圆括号中的`'hello, world'`是一个字符串,它代表了一段文本内容;在 Python 语言中,我们可以用单引号或双引号来表示一个字符串。不同于 C、C++ 或 Java 这样的编程语言,Python 代码中的语句不需要用分号来表示结束,也就是说,如果我们想再写一条语句,只需要回车换行即可,代码如下所示。此外,Python 代码也不需要通过编写名为`main`的入口函数来使其运行,提供入口函数是编写可执行的 C、C++ 或 Java 代码必须要做的事情,这一点很多程序员都不陌生,但是在 Python 语言中它并不是必要的。 -```Python +```python print('hello, world') print('goodbye, world') ``` @@ -134,7 +134,7 @@ Python 中有两种形式的注释: 1. 单行注释:以`#`和空格开头,可以注释掉从`#`开始后面一整行的内容。 2. 多行注释:三个引号(通常用双引号)开头,三个引号结尾,通常用于添加多行说明性内容。 -```Python +```python """ 第一个Python程序 - hello, world diff --git a/Day01-20/03.Python语言中的变量.md b/Day01-20/03.Python语言中的变量.md index 977f446..924516f 100755 --- a/Day01-20/03.Python语言中的变量.md +++ b/Day01-20/03.Python语言中的变量.md @@ -16,7 +16,7 @@ 1. 整型(`int`):Python 中可以处理任意大小的整数,而且支持二进制(如`0b100`,换算成十进制是4)、八进制(如`0o100`,换算成十进制是64)、十进制(`100`)和十六进制(`0x100`,换算成十进制是256)的表示法。运行下面的代码,看看会输出什么。 - ```Python + ```python print(0b100) # 二进制整数 print(0o100) # 八进制整数 print(100) # 十进制整数 @@ -25,7 +25,7 @@ 2. 浮点型(`float`):浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如`123.456`)之外还支持科学计数法(如`1.23456e2`,表示$\small{1.23456 \times 10^{2}}$)。运行下面的代码,看看会输出什么。 - ```Python + ```python print(123.456) # 数学写法 print(1.23456e2) # 科学计数法 ``` @@ -53,7 +53,7 @@ 下面通过例子来说明变量的类型和变量的使用。 -```Python +```python """ 使用变量保存数据并进行加减乘除运算 @@ -71,7 +71,7 @@ print(a / b) # 3.75 在 Python 中可以使用`type`函数对变量的类型进行检查。程序设计中函数的概念跟数学上函数的概念非常类似,数学上的函数相信大家并不陌生,它包括了函数名、自变量和因变量。如果暂时不理解函数这个概念也不要紧,我们会在后续的内容中专门讲解函数的定义和使用。 -```Python +```python """ 使用type函数检查变量的类型 @@ -98,7 +98,7 @@ print(type(d)) # 下面的例子为大家演示了Python中类型转换的操作。 -```Python +```python """ 变量的类型转换操作 diff --git a/Day01-20/07.分支和循环结构实战.md b/Day01-20/07.分支和循环结构实战.md index 8c1808e..5c06f09 100755 --- a/Day01-20/07.分支和循环结构实战.md +++ b/Day01-20/07.分支和循环结构实战.md @@ -6,7 +6,7 @@ > **说明**:素数指的是只能被 1 和自身整除的正整数(不包括 1),之前我们写过判断素数的代码,这里相当于是一个升级版本。 -```Python +```python """ 输出100以内的素数 @@ -29,7 +29,7 @@ for num in range(2, 100): > **说明**:斐波那契数列(Fibonacci sequence),通常也被称作黄金分割数列,是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)在《计算之书》中研究理想假设条件下兔子成长率问题而引入的数列,因此这个数列也常被戏称为“兔子数列”。斐波那契数列的特点是数列的前两个数都是 1,从第三个数开始,每个数都是它前面两个数的和。按照这个规律,斐波那契数列的前 10 个数是:`1, 1, 2, 3, 5, 8, 13, 21, 34, 55`。斐波那契数列在现代物理、准晶体结构、化学等领域都有直接的应用。 -```Python +```python """ 输出斐波那契数列中的前20个数 @@ -125,7 +125,7 @@ for x in range(0, 21): > **说明**:CRAPS又称花旗骰,是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子,玩家通过摇两粒骰子获得点数进行游戏。简化后的规则是:玩家第一次摇骰子如果摇出了 7 点或 11 点,玩家胜;玩家第一次如果摇出 2 点、3 点或 12 点,庄家胜;玩家如果摇出其他点数则游戏继续,玩家重新摇骰子,如果玩家摇出了 7 点,庄家胜;如果玩家摇出了第一次摇的点数,玩家胜;其他点数玩家继续摇骰子,直到分出胜负。为了增加代码的趣味性,我们设定游戏开始时玩家有 1000 元的赌注,每局游戏开始之前,玩家先下注,如果玩家获胜就可以获得对应下注金额的奖励,如果庄家获胜,玩家就会输掉自己下注的金额。游戏结束的条件是玩家破产(输光所有的赌注)。 -```Python +```python """ Craps赌博游戏 diff --git a/Day01-20/09.常用数据结构之列表-2.md b/Day01-20/09.常用数据结构之列表-2.md index 59476a0..c28c4cc 100644 --- a/Day01-20/09.常用数据结构之列表-2.md +++ b/Day01-20/09.常用数据结构之列表-2.md @@ -115,7 +115,7 @@ print(nums2) 场景三: 有一个整数列表`nums1`,创建一个新的列表`nums2`,将`nums1`中大于`50`的元素放到`nums2`中。 -```Python +```python nums1 = [35, 12, 97, 64, 55] nums2 = [] for num in nums1: @@ -126,7 +126,7 @@ print(nums2) 使用列表生成式做同样的事情,代码如下所示。 -```Python +```python nums1 = [35, 12, 97, 64, 55] nums2 = [num for num in nums1 if num > 50] print(nums2) @@ -148,7 +148,7 @@ print(scores[0][1]) 如果想通过键盘输入的方式来录入5个学生3门课程的成绩并保存在列表中,可以使用如下所示的代码。 -```Python +```python scores = [] for _ in range(5): temp = [] diff --git a/Day01-20/11.常用数据结构之字符串.md b/Day01-20/11.常用数据结构之字符串.md index 2c5c386..d718774 100755 --- a/Day01-20/11.常用数据结构之字符串.md +++ b/Day01-20/11.常用数据结构之字符串.md @@ -130,7 +130,7 @@ print(len('goodbye, world')) # 14 #### 索引和切片 -字符串的索引和切片操作跟列表几乎区别,因为字符串也是一种有序序列,可以通过正向或反向的整数索引访问其中的元素。但是有一点需要注意,因为**字符串是不可变类型**,所以**不能通过索引运算修改字符串中的字符**。 +字符串的索引和切片操作跟列表、元组几乎没有区别,因为字符串也是一种有序序列,可以通过正向或反向的整数索引访问其中的元素。但是有一点需要注意,因为**字符串是不可变类型**,所以**不能通过索引运算修改字符串中的字符**。 ```python s = 'abc123456' diff --git a/Day01-20/12.常用数据结构之集合.md b/Day01-20/12.常用数据结构之集合.md index 967b5ef..a4b7458 100755 --- a/Day01-20/12.常用数据结构之集合.md +++ b/Day01-20/12.常用数据结构之集合.md @@ -1,6 +1,6 @@ ## 常用数据结构之集合 -在学习了列表和元组之后,我们再来学习一种容器型的数据类型,它的名字叫集合(set)。说到集合这个词大家一定不会陌生,在数学课本上就有这个概念。如果我们**把一定范围的、确定的、可以区别的事物当作一个整体来看待**,那么这个整体就是集合,集合中的各个事物称为集合的**元素**。通常,集合需要满足以下特性: +在学习了列表和元组之后,我们再来学习一种容器型的数据类型,它的名字叫集合(set)。说到集合这个词大家一定不会陌生,在数学课本上就有这个概念。如果我们**把一定范围的、确定的、可以区别的事物当作一个整体来看待**,那么这个整体就是集合,集合中的各个事物称为集合的**元素**。通常,集合需要满足以下要求: 1. **无序性**:一个集合中,每个元素的地位都是相同的,元素之间是无序的。 2. **互异性**:一个集合中,任何两个元素都是不相同的,即元素在集合中只能出现一次。 @@ -8,7 +8,7 @@ Python 程序中的集合跟数学上的集合没有什么本质区别,需要强调的是上面所说的无序性和互异性。无序性说明集合中的元素并不像列中的元素那样存在某种次序,可以通过索引运算就能访问任意元素,**集合并不支持索引运算**。另外,集合的互异性决定了**集合中不能有重复元素**,这一点也是集合区别于列表的地方,我们无法将重复的元素添加到一个集合中。集合类型必然是支持`in`和`not in`成员运算的,这样就可以确定一个元素是否属于集合,也就是上面所说的集合的确定性。**集合的成员运算在性能上要优于列表的成员运算**,这是集合的底层存储特性决定的,此处我们暂时不做讨论,大家记住这个结论即可。 -> **说明**:集合底层使用了哈希存储(散列存储),对哈希存储感兴趣的读者可以看看维基百科上“散列表”这个词条。 +> **说明**:集合底层使用了哈希存储(散列存储),对哈希存储不了解的读者可以先看看“Hello 算法”网站对[哈希表](https://www.hello-algo.com/chapter_hashing/)的讲解,感谢作者的开源精神。 ### 创建集合 @@ -31,7 +31,9 @@ set5 = {num for num in range(1, 20) if num % 3 == 0 or num % 7 == 0} print(set5) ``` -需要提醒大家,集合中的元素必须是`hashable`类型,使用哈希存储的容器都会对元素提出这一要求。所谓`hashable`类型指的是能够计算出哈希码的数据类型,通常不可变类型都是`hashable`类型,如整数(`int`)、浮点小数(`float`)、布尔值(`bool`)、字符串(`str`)、元组(`tuple`)等。可变类型都不是`hashable`类型,因为可变类型无法计算出确定的哈希码,所以它们不能放到集合中。例如:我们不能将列表作为集合中的元素;同理,由于集合本身也是可变类型,所以集合也不能作为集合中的元素。我们可以创建出嵌套的列表,但是我们不能创建出嵌套的集合,这一点在使用集合的时候一定要引起注意。 +需要提醒大家,集合中的元素必须是`hashable`类型,所谓`hashable`类型指的是能够计算出哈希码的数据类型,通常不可变类型都是`hashable`类型,如整数(`int`)、浮点小数(`float`)、布尔值(`bool`)、字符串(`str`)、元组(`tuple`)等。可变类型都不是`hashable`类型,因为可变类型无法计算出确定的哈希码,所以它们不能放到集合中。例如:我们不能将列表作为集合中的元素;同理,由于集合本身也是可变类型,所以集合也不能作为集合中的元素。我们可以创建出嵌套列表(列表的元素也是列表),但是我们不能创建出嵌套的集合,这一点在使用集合的时候一定要引起注意。 + +> **温馨提示**:如果不理解上面提到的哈希码、哈希存储这些概念,可以先放放,因为它并不影响你继续学习和使用 Python 语言。当然,如果是计算机专业的小伙伴,不理解哈希存储是很难被原谅的,要赶紧去补课了。 ### 元素的遍历 @@ -47,7 +49,7 @@ for elem in set1: ### 集合的运算 -Python 为集合类型提供了非常丰富的运算符,主要包括:成员运算、交集运算、并集运算、差集运算、比较运算(相等性、子集、超集)等。 +Python 为集合类型提供了非常丰富的运算,主要包括:成员运算、交集运算、并集运算、差集运算、比较运算(相等性、子集、超集)等。 #### 成员运算 @@ -130,7 +132,7 @@ print(set2.issuperset(set1)) # True ### 集合的方法 -刚才我们说过,Python 中的集合是可变类型,我们可以通过集合类型的方法向集合添加元素或从集合中删除元素。 +刚才我们说过,Python 中的集合是可变类型,我们可以通过集合的方法向集合添加元素或从集合中删除元素。 ```python set1 = {1, 10, 100} @@ -151,7 +153,7 @@ set1.clear() print(set1) # set() ``` -> **说明**:删除集合元素的`remove`方法在元素不存在时会引发`KeyError`错误,所以上面的代码中我们先通过成员运算判断元素是否在集合中。集合类型还有一个`pop`方法可以从集合中随机删除一个元素,该方法在删除元素的同时会获得被删除的元素,而`remove`和`discard`方法仅仅是删除元素,不会获得被删除的元素。 +> **说明**:删除元素的`remove`方法在元素不存在时会引发`KeyError`错误,所以上面的代码中我们先通过成员运算判断元素是否在集合中。集合类型还有一个`pop`方法可以从集合中随机删除一个元素,该方法在删除元素的同时会返回(获得)被删除的元素,而`remove`和`discard`方法仅仅是删除元素,不会返回(获得)被删除的元素。 集合类型还有一个名为`isdisjoint`的方法可以判断两个集合有没有相同的元素,如果没有相同元素,该方法返回`True`,否则该方法返回`False`,代码如下所示。 diff --git a/Day01-20/13.常用数据结构之字典.md b/Day01-20/13.常用数据结构之字典.md index e548323..7d5849f 100755 --- a/Day01-20/13.常用数据结构之字典.md +++ b/Day01-20/13.常用数据结构之字典.md @@ -1,8 +1,8 @@ ## 常用数据结构之字典 -迄今为止,我们已经为大家介绍了Python中的三种容器型数据类型(列表、元组、集合),但是这些数据类型仍然不足以帮助我们解决所有的问题。例如,我们需要一个变量来保存一个人的多项信息,包括:姓名、年龄、身高、体重、家庭住址、本人手机号、紧急联系人手机号,此时你会发现,我们之前学过的列表、元组和集合类型都不够好使。 +迄今为止,我们已经为大家介绍了 Python 中的三种容器型数据类型(列表、元组、集合),但是这些数据类型仍然不足以帮助我们解决所有的问题。例如,我们需要一个变量来保存一个人的多项信息,包括:姓名、年龄、身高、体重、家庭住址、本人手机号、紧急联系人手机号,此时你会发现,我们之前学过的列表、元组和集合类型都不够好使。 -```Python +```python person1 = ['王大锤', 55, 168, 60, '成都市武侯区科华北路62号1栋101', '13122334455', '13800998877'] person2 = ('王大锤', 55, 168, 60, '成都市武侯区科华北路62号1栋101', '13122334455', '13800998877') person3 = {'王大锤', 55, 168, 60, '成都市武侯区科华北路62号1栋101', '13122334455', '13800998877'} @@ -20,7 +20,7 @@ Python 程序中的字典跟现实生活中的字典很像,它以键值对( Python 中创建字典可以使用`{}`字面量语法,这一点跟上一节课讲的集合是一样的。但是字典的`{}`中的元素是以键值对的形式存在的,每个元素由`:`分隔的两个值构成,`:`前面是键,`:`后面是值,代码如下所示。 -```Python +```python xinhua = { '麓': '山脚下', '路': '道,往来通行的地方;方面,地区:南~货,外~货;种类:他俩是一~人', @@ -44,7 +44,7 @@ print(person) 当然,如果愿意,我们也可以使用内置函数`dict`或者是字典的生成式语法来创建字典,代码如下所示。 -```Python +```python # dict函数(构造器)中的每一组参数就是字典中的一组键值对 person = dict(name='王大锤', age=55, height=168, weight=60, addr='成都市武侯区科华北路62号1栋101') print(person) # {'name': '王大锤', 'age': 55, 'height': 168, 'weight': 60, 'addr': '成都市武侯区科华北路62号1栋101'} @@ -60,10 +60,16 @@ items3 = {x: x ** 3 for x in range(1, 6)} print(items3) # {1: 1, 2: 8, 3: 27, 4: 64, 5: 125} ``` -想知道字典中一共有多少组键值对,仍然是使用`len`函数;如果想对字典进行遍历,可以用`for`循环,但是需要注意,`for`循环只是对字典的键进行了遍历,不过没关系,在学习了字典的索引运算后,我们可以通过字典的键获取到和这个键对应的值。 +想知道字典中一共有多少组键值对,仍然是使用`len`函数;如果想对字典进行遍历,可以用`for`循环,但是需要注意,`for`循环只是对字典的键进行了遍历,不过没关系,在学习了字典的索引运算后,我们可以通过字典的键访问它对应的值。 -```Python -person = {'name': '王大锤', 'age': 55, 'height': 168, 'weight': 60, 'addr': '成都市武侯区科华北路62号1栋101'} +```python +person = { + 'name': '王大锤', + 'age': 55, + 'height': 168, + 'weight': 60, + 'addr': '成都市武侯区科华北路62号1栋101' +} print(len(person)) # 5 for key in person: print(key) @@ -71,9 +77,30 @@ for key in person: ### 字典的运算 -对于字典类型来说,成员运算和索引运算肯定是很重要的,前者可以判定指定的键在不在字典中,后者可以通过键获取对应的值或者向字典中添加新的键值对。值得注意的是,字典的索引不同于列表的索引,列表中的元素因为有属于自己有序号,所以列表的索引是一个整数;字典中因为保存的是键值对,所以字典需要用键去索引对应的值。需要**特别提醒**大家注意的是,**字典中的键必须是不可变类型**,例如整数(`int`)、浮点数(`float`)、字符串(`str`)、元组(`tuple`)等类型,这一点跟集合类型对元素的要求是一样的;很显然,之前我们讲的列表(`list`)和集合(`set`)不能作为字典中的键,字典类型本身也不能再作为字典中的键,因为字典也是可变类型,但是字典可以作为字典中的值。大家可以先看看下面的代码,了解一下字典的成员运算和索引运算。 +对于字典类型来说,成员运算和索引运算肯定是很重要的,前者可以判定指定的键在不在字典中,后者可以通过键访问对应的值或者向字典中添加新的键值对。值得注意的是,字典的索引不同于列表的索引,列表中的元素因为有属于自己有序号,所以列表的索引是一个整数;字典中因为保存的是键值对,所以字典需要用键去索引对应的值。需要**特别提醒**大家注意的是,**字典中的键必须是不可变类型**,例如整数(`int`)、浮点数(`float`)、字符串(`str`)、元组(`tuple`)等类型,这一点跟集合类型对元素的要求是一样的;很显然,之前我们讲的列表(`list`)和集合(`set`)不能作为字典中的键,字典类型本身也不能再作为字典中的键,因为字典也是可变类型,但是列表、集合、字典都可以作为字典中的值,例如: -```Python +```python +person = { + 'name': '王大锤', + 'age': 55, + 'height': 168, + 'weight': 60, + 'addr': ['成都市武侯区科华北路62号1栋101', '北京市西城区百万庄大街1号'], + 'car': { + 'brand': 'BMW X7', + 'maxSpeed': '250', + 'length': 5170, + 'width': 2000, + 'height': 1835, + 'displacement': 3.0 + } +} +print(person) +``` + +大家可以看看下面的代码,了解一下字典的成员运算和索引运算。 + +```python person = {'name': '王大锤', 'age': 55, 'height': 168, 'weight': 60, 'addr': '成都市武侯区科华北路62号1栋101'} # 成员运算 @@ -118,7 +145,7 @@ for key, value in person.items(): print(f'{key}:\t{value}') ``` -字典的`update`方法会用一个字典更新另一个字典中的键值对。例如,有两个字典`x`和`y`,当执行`x.update(y)`操作时,`x`跟`y`相同的键对应的值会`y`中的值被更新,而`y`中有但`x`中没有的键值对会直接添加到`x`中,代码如下所示。 +字典的`update`方法实现两个字典的合并操作。例如,有两个字典`x`和`y`,当执行`x.update(y)`操作时,`x`跟`y`相同的键对应的值会被`y`中的值更新,而`y`中有但`x`中没有的键值对会直接添加到`x`中,代码如下所示。 ```python person1 = {'name': '王大锤', 'age': 55, 'height': 178} @@ -127,7 +154,16 @@ person1.update(person2) print(person1) # {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'} ``` -可以通过`pop`或`popitem`方法从字典中删除元素,前者会返回键对应的值,但是如果字典中不存在指定的键,会引发`KeyError`错误;后者在删除元素时,会返回键和值组成的二元组。字典的`clear`方法会清空字典中所有的键值对,代码如下所示。 +如果使用 Python 3.9 及以上的版本,也可以使用`|`运算符来完成同样的操作,代码如下所示。 + +```python +person1 = {'name': '王大锤', 'age': 55, 'height': 178} +person2 = {'age': 25, 'addr': '成都市武侯区科华北路62号1栋101'} +person1 |= person2 +print(person1) # {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'} +``` + +可以通过`pop`或`popitem`方法从字典中删除元素,前者会返回(获得)键对应的值,但是如果字典中不存在指定的键,会引发`KeyError`错误;后者在删除元素时,会返回(获得)键和值组成的二元组。字典的`clear`方法会清空字典中所有的键值对,代码如下所示。 ```python person = {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'} @@ -141,7 +177,7 @@ print(person) # {} 跟列表一样,从字典中删除元素也可以使用`del`关键字,在删除元素的时候如果指定的键索引不到对应的值,一样会引发`KeyError`错误,具体的做法如下所示。 -```Python +```python person = {'name': '王大锤', 'age': 25, 'height': 178, 'addr': '成都市武侯区科华北路62号1栋101'} del person['age'] del person['addr'] @@ -154,7 +190,7 @@ print(person) # {'name': '王大锤', 'height': 178} **例子1**:输入一段话,统计每个英文字母出现的次数,按出现次数从高到低输出。 -```Python +```python sentence = input('请输入一段话: ') counter = {} for ch in sentence: @@ -204,7 +240,7 @@ x 出现了 1 次. > **说明**:可以用字典的生成式语法来创建这个新字典。 -```Python +```python stocks = { 'AAPL': 191.88, 'GOOG': 1186.96, @@ -226,4 +262,4 @@ print(stocks2) ### 总结 -Python 程序中的字典跟现实生活中字典非常像,允许我们**以键值对的形式保存数据**,再**通过键索引对应的值**。这是一种非常**有利于数据检索**的数据类型。再次提醒大家注意,**字典中的键必须是不可变类型**,字典中的值可以是任意类型。 +Python 程序中的字典跟现实生活中字典非常像,允许我们**以键值对的形式保存数据**,再**通过键访问对应的值**。字典是一种非常**有利于数据检索**的数据类型,但是需要再次提醒大家,**字典中的键必须是不可变类型**,列表、集合、字典等类型的数据都不能作为字典的键。 diff --git a/Day01-20/16.函数使用进阶.md b/Day01-20/16.函数使用进阶.md index 66fb214..692144d 100755 --- a/Day01-20/16.函数使用进阶.md +++ b/Day01-20/16.函数使用进阶.md @@ -6,7 +6,7 @@ 我们回到之前讲过的一个例子,设计一个函数,传入任意多个参数,对其中`int`类型或`float`类型的元素实现求和操作。我们对之前的代码稍作调整,让整个代码更加紧凑一些,如下所示。 -```Python +```python def calc(*args, **kwargs): items = list(args) + list(kwargs.values()) result = 0 @@ -18,7 +18,7 @@ def calc(*args, **kwargs): 如果我们希望上面的`calc`函数不仅仅可以做多个参数的求和,还可以实现更多的甚至是自定义的二元运算,我们该怎么做呢?上面的代码只能求和是因为函数中使用了`+=`运算符,这使得函数跟加法运算形成了耦合关系,如果能解除这种耦合关系,函数的通用性和灵活性就会更好。解除耦合的办法就是将`+`运算符变成函数调用,并将其设计为函数的参数,代码如下所示。 -```Python +```python def calc(init_value, op_func, *args, **kwargs): items = list(args) + list(kwargs.values()) result = init_value @@ -55,7 +55,7 @@ print(calc(1, mul, 1, 2, 3, 4, 5)) # 120 如果我们没有提前定义好`add`和`mul`函数,也可以使用 Python 标准库中的`operator`模块提供的`add`和`mul`函数,它们分别代表了做加法和做乘法的二元运算,我们拿过来直接使用即可,代码如下所示。 -```Python +```python import operator print(calc(0, operator.add, 1, 2, 3, 4, 5)) # 15 @@ -64,7 +64,7 @@ print(calc(1, operator.mul, 1, 2, 3, 4, 5)) # 120 Python 内置函数中有不少高阶函数,我们前面提到过的`filter`和`map`函数就是高阶函数,前者可以实现对序列中元素的过滤,后者可以实现对序列中元素的映射,例如我们要去掉一个整数列表中的奇数,并对所有的偶数求平方得到一个新的列表,就可以直接使用这两个函数来做到,具体的做法是如下所示。 -```Python +```python def is_even(num): """判断num是不是偶数""" return num % 2 == 0 @@ -82,7 +82,7 @@ print(new_nums) # [144, 64, 3600, 2704] 当然,要完成上面代码的功能,也可以使用列表生成式,列表生成式的做法更为简单优雅。 -```Python +```python old_nums = [35, 12, 8, 99, 60, 52] new_nums = [num ** 2 for num in old_nums if num % 2 == 0] print(new_nums) # [144, 64, 3600, 2704] @@ -110,7 +110,7 @@ print(new_strings) # ['in', 'zoo', 'pear', 'apple', 'waxberry'] 在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,也不需要考虑对函数的复用,那么我们可以使用 lambda 函数。Python 中的 lambda 函数是没有的名字函数,所以很多人也把它叫做**匿名函数**,lambda 函数只能有一行代码,代码中的表达式产生的运算结果就是这个匿名函数的返回值。之前的代码中,我们写的`is_even`和`square`函数都只有一行代码,我们可以考虑用 lambda 函数来替换掉它们,代码如下所示。 -```Python +```python old_nums = [35, 12, 8, 99, 60, 52] new_nums = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, old_nums))) print(new_nums) # [144, 64, 3600, 2704] @@ -120,7 +120,7 @@ print(new_nums) # [144, 64, 3600, 2704] 前面我们说过,Python 中的函数是“一等函数”,函数是可以直接赋值给变量的。在学习了 lambda 函数之后,前面我们写过的一些函数就可以用一行代码来实现它们了,大家可以看看能否理解下面的求阶乘和判断素数的函数。 -```Python +```python import functools import operator diff --git a/Day01-20/17.函数高级应用.md b/Day01-20/17.函数高级应用.md index 6abd0f5..0a5fbcb 100755 --- a/Day01-20/17.函数高级应用.md +++ b/Day01-20/17.函数高级应用.md @@ -6,7 +6,7 @@ Python 语言中,装饰器是“**用一个函数装饰另外一个函数并为其提供额外的能力**”的语法现象。装饰器本身是一个函数,它的参数是被装饰的函数,它的返回值是一个带有装饰功能的函数。通过前面的描述,相信大家已经听出来了,装饰器是一个高阶函数,它的参数和返回值都是函数。但是,装饰器的概念对编程语言的初学者来说,还是让人头疼的,下面我们先通过一个简单的例子来说明装饰器的作用。假设有名为`downlaod`和`upload`的两个函数,分别用于文件的上传和下载,如下所示。 -```Python +```python import random import time @@ -33,7 +33,7 @@ upload('Python从入门到住院.pdf') 现在有一个新的需求,我们希望知道调用`download`和`upload`函数上传下载文件到底用了多少时间,这应该如何实现呢?相信很多小伙伴已经想到了,我们可以在函数开始执行的时候记录一个时间,在函数调用结束后记录一个时间,两个时间相减就可以计算出下载或上传的时间,代码如下所示。 -```Python +```python start = time.time() download('MySQL从删库到跑路.avi') end = time.time() @@ -62,7 +62,7 @@ def record_time(func): 看懂这个结构后,我们就可以把记录时间的功能写到这个装饰器中,代码如下所示。 -```Python +```python import time @@ -85,7 +85,7 @@ def record_time(func): 写装饰器虽然颇费周折,但是这是个一劳永逸的骚操作,将来再有记录函数执行时间的需求时,我们只需要添加上面的装饰器即可。使用上面的装饰器函数有两种方式,第一种方式就是直接调用装饰器函数,传入被装饰的函数并获得返回值,我们可以用这个返回值直接替代原来的函数,那么在调用时就已经获得了装饰器提供的额外的能力(记录执行时间),大家试试下面的代码就明白了。 -```Python +```python download = record_time(download) upload = record_time(upload) download('MySQL从删库到跑路.avi') @@ -94,7 +94,7 @@ upload('Python从入门到住院.pdf') 在 Python 中,使用装饰器很有更为便捷的**语法糖**(编程语言中添加的某种语法,这种语法对语言的功能没有影响,但是使用更加方法,代码的可读性也更强,我们将其称之为“语法糖”或“糖衣语法”),可以用`@装饰器函数`将装饰器函数直接放在被装饰的函数上,效果跟上面的代码相同。我们把完整的代码为大家罗列出来,大家可以再看看我们是如何定义和使用装饰器的。 -```Python +```python import random import time @@ -133,7 +133,7 @@ upload('Python从入门到住院.pdf') 如果在代码的某些地方,我们想去掉装饰器的作用执行原函数,那么在定义装饰器函数的时候,需要做一点点额外的工作。Python 标准库`functools`模块的`wraps`函数也是一个装饰器,我们将它放在`wrapper`函数上,这个装饰器可以帮我们保留被装饰之前的函数,这样在需要取消装饰器时,可以通过被装饰函数的`__wrapped__`属性获得被装饰之前的函数。 -```Python +```python import random import time @@ -181,7 +181,7 @@ upload.__wrapped__('Python从新手到大师.pdf') Python 中允许函数嵌套定义,也允许函数之间相互调用,而且一个函数还可以直接或间接的调用自身。函数自己调用自己称为递归调用,那么递归调用有什么用处呢?现实中,有很多问题的定义本身就是一个递归定义,例如我们之前讲到的阶乘,非负整数`N`的阶乘是`N`乘以`N-1`的阶乘,即 $\small{N! = N \times (N-1)!}$ ,定义的左边和右边都出现了阶乘的概念,所以这是一个递归定义。既然如此,我们可以使用递归调用的方式来写一个求阶乘的函数,代码如下所示。 -```Python +```python def fac(num): if num in (0, 1): return 1 @@ -190,7 +190,7 @@ def fac(num): 上面的代码中,`fac`函数中又调用了`fac`函数,这就是所谓的递归调用。代码第2行的`if`条件叫做递归的收敛条件,简单的说就是什么时候要结束函数的递归调用,在计算阶乘时,如果计算到`0`或`1`的阶乘,就停止递归调用,直接返回`1`;代码第4行的`num * fac(num - 1)`是递归公式,也就是阶乘的递归定义。下面,我们简单的分析下,如果用`fac(5)`计算`5`的阶乘,整个过程会是怎样的。 -```Python +```python # 递归调用函数入栈 # 5 * fac(4) # 5 * (4 * fac(3)) @@ -211,7 +211,7 @@ print(fac(5)) # 120 再举一个之前讲过的生成斐波那契数列的例子,因为斐波那契数列前两个数都是`1`,从第三个数开始,每个数是前两个数相加的和,可以记为`f(n) = f(n - 1) + f(n - 2)`,很显然这又是一个递归的定义,所以我们可以用下面的递归调用函数来计算第​`n`个斐波那契数。 -```Python +```python def fib1(n): if n in (1, 2): return 1 @@ -224,7 +224,7 @@ for i in range(1, 21): 需要提醒大家,上面计算斐波那契数的代码虽然看起来非常简单明了,但执行性能是比较糟糕的。大家可以试一试,把上面代码`for`循环中`range`函数的第二个参数修改为`51`,即输出前50个斐波那契数,看看需要多长时间,也欢迎大家在评论区留下你的代码执行时间。至于为什么这么慢,大家可以自己思考一下原因。很显然,直接使用循环递推的方式获得斐波那契数列是更好的选择,代码如下所示。 -```Python +```python def fib2(n): a, b = 0, 1 for _ in range(n): diff --git a/Day01-20/18.面向对象编程入门.md b/Day01-20/18.面向对象编程入门.md index 59e9e7b..365fa94 100755 --- a/Day01-20/18.面向对象编程入门.md +++ b/Day01-20/18.面向对象编程入门.md @@ -28,7 +28,7 @@ 在 Python 语言中,我们可以使用`class`关键字加上类名来定义类,通过缩进我们可以确定类的代码块,就如同定义函数那样。在类的代码块中,我们需要写一些函数,我们说过类是一个抽象概念,那么这些函数就是我们对一类对象共同的动态特征的提取。写在类里面的函数我们通常称之为**方法**,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是`self`,它代表了接收这个消息的对象本身。 -```Python +```python class Student: def study(self, course_name): @@ -42,7 +42,7 @@ class Student: 在我们定义好一个类之后,可以使用构造器语法来创建对象,代码如下所示。 -```Python +```python stu1 = Student() stu2 = Student() print(stu1) # <__main__.Student object at 0x10ad5ac50> @@ -54,7 +54,7 @@ print(hex(id(stu1)), hex(id(stu2))) # 0x10ad5ac50 0x10ad5acd0 接下来,我们尝试给对象发消息,即调用对象的方法。刚才的`Student`类中我们定义了`study`和`play`两个方法,两个方法的第一个参数`self`代表了接收消息的学生对象,`study`方法的第二个参数是学习的课程名称。Python中,给对象发消息有两种方式,请看下面的代码。 -```Python +```python # 通过“类.方法”调用方法 # 第一个参数是接收消息的对象 # 第二个参数是学习的课程名称 @@ -74,7 +74,7 @@ stu2.play() # 学生正在玩游戏. 我们对上面的`Student`类稍作修改,给学生对象添加`name`(姓名)和`age`(年龄)两个属性。 -```Python +```python class Student: """学生""" @@ -94,7 +94,7 @@ class Student: 修改刚才创建对象和给对象发消息的代码,重新执行一次,看看程序的执行结果有什么变化。 -```Python +```python # 调用Student类的构造器创建对象并传入初始化参数 stu1 = Student('骆昊', 44) stu2 = Student('王大锤', 25) @@ -117,7 +117,7 @@ stu2.play() # 王大锤正在玩游戏. > **要求**:定义一个类描述数字时钟,提供走字和显示时间的功能。 -```Python +```python import time @@ -167,7 +167,7 @@ while True: > **要求**:定义一个类描述平面上的点,提供计算到另一个点距离的方法。 -```Python +```python class Point: """平面上的点""" diff --git a/Day01-20/19.面向对象编程进阶.md b/Day01-20/19.面向对象编程进阶.md index 7ba887d..c6b540f 100755 --- a/Day01-20/19.面向对象编程进阶.md +++ b/Day01-20/19.面向对象编程进阶.md @@ -6,7 +6,7 @@ 在很多面向对象编程语言中,对象的属性通常会被设置为私有(private)或受保护(protected)的成员,简单的说就是不允许直接访问这些属性;对象的方法通常都是公开的(public),因为公开的方法是对象能够接受的消息,也是对象暴露给外界的调用接口,这就是所谓的访问可见性。在 Python 中,可以通过给对象属性名添加前缀下划线的方式来说明属性的访问可见性,例如,可以用`__name`表示一个私有属性,`_name`表示一个受保护属性,代码如下所示。 -```Python +```python class Student: def __init__(self, name, age): @@ -30,7 +30,7 @@ Python 语言属于动态语言,维基百科对动态语言的解释是:“ 在 Python 中,我们可以动态为对象添加属性,这是 Python 作为动态类型语言的一项特权,代码如下所示。需要提醒大家的是,对象的方法其实本质上也是对象的属性,如果给对象发送一个无法接收的消息,引发的异常仍然是`AttributeError`。 -```Python +```python class Student: def __init__(self, name, age): @@ -44,7 +44,7 @@ stu.sex = '男' # 给学生对象动态添加sex属性 如果不希望在使用对象时动态的为对象添加属性,可以使用 Python 语言中的`__slots__`魔法。对于`Student`类来说,可以在类中指定`__slots__ = ('name', 'age')`,这样`Student`类的对象只能有`name`和`age`属性,如果想动态添加其他属性将会引发异常,代码如下所示。 -```Python +```python class Student: __slots__ = ('name', 'age') @@ -64,7 +64,7 @@ stu.sex = '男' 举一个例子,定义一个三角形类,通过传入三条边的长度来构造三角形,并提供计算周长和面积的方法。计算周长和面积肯定是三角形对象的方法,这一点毫无疑问。但是在创建三角形对象时,传入的三条边长未必能构造出三角形,为此我们可以先写一个方法来验证给定的三条边长是否可以构成三角形,这种方法很显然就不是对象方法,因为在调用这个方法时三角形对象还没有创建出来。我们可以把这类方法设计为静态方法或类方法,也就是说这类方法不是发送给三角形对象的消息,而是发送给三角形类的消息,代码如下所示。 -```Python +```python class Triangle(object): """三角形""" @@ -134,7 +134,7 @@ print(f'面积: {t.area}') 面向对象的编程语言支持在已有类的基础上创建新类,从而减少重复代码的编写。提供继承信息的类叫做父类(超类、基类),得到继承信息的类叫做子类(派生类、衍生类)。例如,我们定义一个学生类和一个老师类,我们会发现他们有大量的重复代码,而这些重复代码都是老师和学生作为人的公共属性和行为,所以在这种情况下,我们应该先定义人类,再通过继承,从人类派生出老师类和学生类,代码如下所示。 -```Python +```python class Person: """人""" diff --git a/Day01-20/20.面向对象编程应用.md b/Day01-20/20.面向对象编程应用.md index 9fa08a5..00fa3fa 100755 --- a/Day01-20/20.面向对象编程应用.md +++ b/Day01-20/20.面向对象编程应用.md @@ -10,7 +10,7 @@ 牌的属性显而易见,有花色和点数。我们可以用 0 到 3 的四个数字来代表四种不同的花色,但是这样的代码可读性会非常糟糕,因为我们并不知道黑桃、红心、草花、方块跟 0 到 3 的数字的对应关系。如果一个变量的取值只有有限多个选项,我们可以使用枚举。与 C、Java 等语言不同的是,Python 中没有声明枚举类型的关键字,但是可以通过继承`enum`模块的`Enum`类来创建枚举类型,代码如下所示。 -```Python +```python from enum import Enum @@ -21,14 +21,14 @@ class Suite(Enum): 通过上面的代码可以看出,定义枚举类型其实就是定义符号常量,如`SPADE`、`HEART`等。每个符号常量都有与之对应的值,这样表示黑桃就可以不用数字 0,而是用`Suite.SPADE`;同理,表示方块可以不用数字 3, 而是用`Suite.DIAMOND`。注意,使用符号常量肯定是优于使用字面常量的,因为能够读懂英文就能理解符号常量的含义,代码的可读性会提升很多。Python 中的枚举类型是可迭代类型,简单的说就是可以将枚举类型放到`for-in`循环中,依次取出每一个符号常量及其对应的值,如下所示。 -```Python +```python for suite in Suite: print(f'{suite}: {suite.value}') ``` 接下来我们可以定义牌类。 -```Python +```python class Card: """牌""" @@ -44,7 +44,7 @@ class Card: 可以通过下面的代码来测试下`Card`类。 -```Python +```python card1 = Card(Suite.SPADE, 5) card2 = Card(Suite.HEART, 13) print(card1) # ♠5 @@ -53,7 +53,7 @@ print(card2) # ♥K 接下来我们定义扑克类。 -```Python +```python import random @@ -85,7 +85,7 @@ class Poker: 可以通过下面的代码来测试下`Poker`类。 -```Python +```python poker = Poker() print(poker.cards) # 洗牌前的牌 poker.shuffle() @@ -94,7 +94,7 @@ print(poker.cards) # 洗牌后的牌 定义玩家类。 -```Python +```python class Player: """玩家""" @@ -113,7 +113,7 @@ class Player: 创建四个玩家并将牌发到玩家的手上。 -```Python +```python poker = Poker() poker.shuffle() players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')] @@ -134,7 +134,7 @@ for player in players: 修改后的`Card`类代码如下所示。 -```Python +```python class Card: """牌""" @@ -161,7 +161,7 @@ class Card: 通过对上述需求的分析,可以看出部门经理、程序员、销售员都是员工,有相同的属性和行为,那么我们可以先设计一个名为`Employee`的父类,再通过继承的方式从这个父类派生出部门经理、程序员和销售员三个子类。很显然,后续的代码不会创建`Employee` 类的对象,因为我们需要的是具体的员工对象,所以这个类可以设计成专门用于继承的抽象类。Python 语言中没有定义抽象类的关键字,但是可以通过`abc`模块中名为`ABCMeta` 的元类来定义抽象类。关于元类的概念此处不展开讲解,当然大家不用纠结,照做即可。 -```Python +```python from abc import ABCMeta, abstractmethod @@ -179,7 +179,7 @@ class Employee(metaclass=ABCMeta): 在上面的员工类中,有一个名为`get_salary`的方法用于结算月薪,但是由于还没有确定是哪一类员工,所以结算月薪虽然是员工的公共行为但这里却没有办法实现。对于暂时无法实现的方法,我们可以使用`abstractmethod`装饰器将其声明为抽象方法,所谓**抽象方法就是只有声明没有实现的方法**,**声明这个方法是为了让子类去重写这个方法**。接下来的代码展示了如何从员工类派生出部门经理、程序员、销售员这三个子类以及子类如何重写父类的抽象方法。 -```Python +```python class Manager(Employee): """部门经理""" @@ -213,7 +213,7 @@ class Salesman(Employee): 我们通过下面的代码来完成这个工资结算系统,由于程序员和销售员需要分别录入本月的工作时间和销售额,所以在下面的代码中我们使用了 Python 内置的`isinstance`函数来判断员工对象的类型。我们之前讲过的`type`函数也能识别对象的类型,但是`isinstance`函数更加强大,因为它可以判断出一个对象是不是某个继承结构下的子类型,你可以简单的理解为`type`函数是对对象类型的精准匹配,而`isinstance`函数是对对象类型的模糊匹配。 -```Python +```python emps = [Manager('刘备'), Programmer('诸葛亮'), Manager('曹操'), Programmer('荀彧'), Salesman('张辽')] for emp in emps: if isinstance(emp, Programmer): diff --git a/Day21-30/21.文件读写和异常处理.md b/Day21-30/21.文件读写和异常处理.md index fb51ef3..4d46f94 100755 --- a/Day21-30/21.文件读写和异常处理.md +++ b/Day21-30/21.文件读写和异常处理.md @@ -32,7 +32,7 @@ 下面的例子演示了如何读取一个纯文本文件(一般指只有字符原生编码构成的文件,与富文本相比,纯文本不包含字符样式的控制元素,能够被最简单的文本编辑器直接读取)。 -```Python +```python file = open('致橡树.txt', 'r', encoding='utf-8') print(file.read()) file.close() @@ -42,7 +42,7 @@ file.close() 除了使用文件对象的`read`方法读取文件之外,还可以使用`for-in`循环逐行读取或者用`readlines`方法将文件按行读取到一个列表容器中,代码如下所示。 -```Python +```python file = open('致橡树.txt', 'r', encoding='utf-8') for line in file: print(line, end='') @@ -57,7 +57,7 @@ file.close() 如果要向文件中写入内容,可以在打开文件时使用`w`或者`a`作为操作模式,前者会截断之前的文本内容写入新的内容,后者是在原来内容的尾部追加新的内容。 -```Python +```python file = open('致橡树.txt', 'a', encoding='utf-8') file.write('\n标题:《致橡树》') file.write('\n作者:舒婷') @@ -69,7 +69,7 @@ file.close() 请注意上面的代码,如果`open`函数指定的文件并不存在或者无法打开,那么将引发异常状况导致程序崩溃。为了让代码具有健壮性和容错性,我们可以**使用Python的异常机制对可能在运行时发生状况的代码进行适当的处理**。Python中和异常相关的关键字有五个,分别是`try`、`except`、`else`、`finally`和`raise`,我们先看看下面的代码,再来为大家介绍这些关键字的用法。 -```Python +```python file = None try: file = open('致橡树.txt', 'r', encoding='utf-8') @@ -160,7 +160,7 @@ BaseException 在Python中,可以使用`raise`关键字来引发异常(抛出异常对象),而调用者可以通过`try...except...`结构来捕获并处理异常。例如在函数中,当函数的执行条件不满足时,可以使用抛出异常的方式来告知调用者问题的所在,而调用者可以通过捕获处理异常来使得代码从异常中恢复,定义异常和抛出异常的代码如下所示。 -```Python +```python class InputError(ValueError): """自定义异常类型""" pass @@ -177,7 +177,7 @@ def fac(num): 调用求阶乘的函数`fac`,通过`try...except...`结构捕获输入错误的异常并打印异常对象(显示异常信息),如果输入正确就计算阶乘并结束程序。 -```Python +```python flag = True while flag: num = int(input('n = ')) @@ -194,7 +194,7 @@ while flag: 用`with`上下文语法改写后的代码如下所示。 -```Python +```python try: with open('致橡树.txt', 'r', encoding='utf-8') as file: print(file.read()) @@ -210,7 +210,7 @@ except UnicodeDecodeError: 读写二进制文件跟读写文本文件的操作类似,但是需要注意,在使用`open`函数打开文件时,如果要进行读操作,操作模式是`'rb'`,如果要进行写操作,操作模式是`'wb'`。还有一点,读写文本文件时,`read`方法的返回值以及`write`方法的参数是`str`对象(字符串),而读写二进制文件时,`read`方法的返回值以及`write`方法的参数是`bytes-like`对象(字节串)。下面的代码实现了将当前路径下名为`guido.jpg`的图片文件复制到`吉多.jpg`文件中的操作。 -```Python +```python try: with open('guido.jpg', 'rb') as file1: data = file1.read() @@ -225,7 +225,7 @@ print('程序执行结束.') 如果要复制的图片文件很大,一次将文件内容直接读入内存中可能会造成非常大的内存开销,为了减少对内存的占用,可以为`read`方法传入`size`参数来指定每次读取的字节数,通过循环读取和写入的方式来完成上面的操作,代码如下所示。 -```Python +```python try: with open('guido.jpg', 'rb') as file1, open('吉多.jpg', 'wb') as file2: data = file1.read(512) diff --git a/Day21-30/22.对象的序列化和反序列化.md b/Day21-30/22.对象的序列化和反序列化.md index 3365475..39658ec 100755 --- a/Day21-30/22.对象的序列化和反序列化.md +++ b/Day21-30/22.对象的序列化和反序列化.md @@ -67,7 +67,7 @@ let obj = { 在Python中,如果要将字典处理成JSON格式(以字符串形式存在),可以使用`json`模块的`dumps`函数,代码如下所示。 -```Python +```python import json my_dict = { @@ -91,7 +91,7 @@ print(json.dumps(my_dict)) 如果要将字典处理成JSON格式并写入文本文件,只需要将`dumps`函数换成`dump`函数并传入文件对象即可,代码如下所示。 -```Python +```python import json my_dict = { @@ -121,7 +121,7 @@ with open('data.json', 'w') as file: 我们可以通过下面的代码,读取上面创建的`data.json`文件,将JSON格式的数据还原成Python中的字典。 -```Python +```python import json with open('data.json', 'r') as file: @@ -215,7 +215,7 @@ pip install requests 获取国内新闻并显示新闻标题和链接。 -```Python +```python import requests resp = requests.get('http://api.tianapi.com/guonei/?key=APIKey&num=10') diff --git a/Day21-30/23.Python读写CSV文件.md b/Day21-30/23.Python读写CSV文件.md index d012482..ce63983 100755 --- a/Day21-30/23.Python读写CSV文件.md +++ b/Day21-30/23.Python读写CSV文件.md @@ -17,7 +17,7 @@ CSV文件可以使用文本编辑器或类似于Excel电子表格这类工具打 现有五个学生三门课程的考试成绩需要保存到一个CSV文件中,要达成这个目标,可以使用Python标准库中的`csv`模块,该模块的`writer`函数会返回一个`csvwriter`对象,通过该对象的`writerow`或`writerows`方法就可以将数据写入到CSV文件中,具体的代码如下所示。 -```Python +```python import csv import random @@ -44,7 +44,7 @@ with open('scores.csv', 'w') as file: 需要说明的是上面的`writer`函数,除了传入要写入数据的文件对象外,还可以`dialect`参数,它表示CSV文件的方言,默认值是`excel`。除此之外,还可以通过`delimiter`、`quotechar`、`quoting`参数来指定分隔符(默认是逗号)、包围值的字符(默认是双引号)以及包围的方式。其中,包围值的字符主要用于当字段中有特殊符号时,通过添加包围值的字符可以避免二义性。大家可以尝试将上面第5行代码修改为下面的代码,然后查看生成的CSV文件。 -```Python +```python writer = csv.writer(file, delimiter='|', quoting=csv.QUOTE_ALL) ``` @@ -63,7 +63,7 @@ writer = csv.writer(file, delimiter='|', quoting=csv.QUOTE_ALL) 如果要读取刚才创建的CSV文件,可以使用下面的代码,通过`csv`模块的`reader`函数可以创建出`csvreader`对象,该对象是一个迭代器,可以通过`next`函数或`for-in`循环读取到文件中的数据。 -```Python +```python import csv with open('scores.csv', 'r') as file: diff --git a/Day21-30/24.Python读写Excel文件-1.md b/Day21-30/24.Python读写Excel文件-1.md index 5030aae..6ddbc41 100755 --- a/Day21-30/24.Python读写Excel文件-1.md +++ b/Day21-30/24.Python读写Excel文件-1.md @@ -16,7 +16,7 @@ pip install xlwt xlrd xlutils 例如在当前文件夹下有一个名为“阿里巴巴2020年股票数据.xls”的 Excel 文件,如果想读取并显示该文件的内容,可以通过如下所示的代码来完成。 -```Python +```python import xlrd # 使用xlrd模块的open_workbook函数打开指定Excel文件并获得Book对象(工作簿) @@ -65,7 +65,7 @@ print(sheet.row_slice(3, 0, 5)) 写入 Excel 文件可以通过`xlwt` 模块的`Workbook`类创建工作簿对象,通过工作簿对象的`add_sheet`方法可以添加工作表,通过工作表对象的`write`方法可以向指定单元格中写入数据,最后通过工作簿对象的`save`方法将工作簿写入到指定的文件或内存中。下面的代码实现了将5 个学生 3 门课程的考试成绩写入 Excel 文件的操作。 -```Python +```python import random import xlwt @@ -93,7 +93,7 @@ wb.save('考试成绩表.xls') 在写Excel文件时,我们还可以为单元格设置样式,主要包括字体(Font)、对齐方式(Alignment)、边框(Border)和背景(Background)的设置,`xlwt`对这几项设置都封装了对应的类来支持。要设置单元格样式需要首先创建一个`XFStyle`对象,再通过该对象的属性对字体、对齐方式、边框等进行设定,例如在上面的例子中,如果希望将表头单元格的背景色修改为黄色,可以按照如下的方式进行操作。 -```Python +```python header_style = xlwt.XFStyle() pattern = xlwt.Pattern() pattern.pattern = xlwt.Pattern.SOLID_PATTERN @@ -107,7 +107,7 @@ for index, title in enumerate(titles): 如果希望为表头设置指定的字体,可以使用`Font`类并添加如下所示的代码。 -```Python +```python font = xlwt.Font() # 字体名称 font.name = '华文楷体' @@ -126,7 +126,7 @@ header_style.font = font 如果希望表头垂直居中对齐,可以使用下面的代码进行设置。 -```Python +```python align = xlwt.Alignment() # 垂直方向的对齐方式 align.vert = xlwt.Alignment.VERT_CENTER @@ -137,7 +137,7 @@ header_style.alignment = align 如果希望给表头加上黄色的虚线边框,可以使用下面的代码来设置。 -```Python +```python borders = xlwt.Borders() props = ( ('top', 'top_colour'), ('right', 'right_colour'), @@ -153,7 +153,7 @@ header_style.borders = borders 如果要调整单元格的宽度(列宽)和表头的高度(行高),可以按照下面的代码进行操作。 -```Python +```python # 设置行高为40px sheet.row(0).set_style(xlwt.easyxf(f'font:height {20 * 40}')) titles = ('姓名', '语文', '数学', '英语') @@ -170,7 +170,7 @@ for index, title in enumerate(titles): 实现公式计算的代码如下所示。 -```Python +```python import xlrd import xlwt from xlutils.copy import copy diff --git a/Day21-30/25.Python读写Excel文件-2.md b/Day21-30/25.Python读写Excel文件-2.md index e9e1d50..02580f1 100755 --- a/Day21-30/25.Python读写Excel文件-2.md +++ b/Day21-30/25.Python读写Excel文件-2.md @@ -16,7 +16,7 @@ pip install openpyxl 例如在当前文件夹下有一个名为“阿里巴巴2020年股票数据.xlsx”的 Excel 文件,如果想读取并显示该文件的内容,可以通过如下所示的代码来完成。 -```Python +```python import datetime import openpyxl @@ -63,7 +63,7 @@ for row_ch in range(2, sheet.max_row + 1): 下面我们使用`openpyxl`来进行写 Excel 操作。 -```Python +```python import random import openpyxl @@ -93,7 +93,7 @@ wb.save('考试成绩表.xlsx') 在使用`openpyxl`操作 Excel 时,如果要调整单元格的样式,可以直接通过单元格对象(`Cell`对象)的属性进行操作。单元格对象的属性包括字体(`font`)、对齐(`alignment`)、边框(`border`)等,具体的可以参考`openpyxl`的[官方文档](https://openpyxl.readthedocs.io/en/stable/index.html)。在使用`openpyxl`时,如果需要做公式计算,可以完全按照 Excel 中的操作方式来进行,具体的代码如下所示。 -```Python +```python import openpyxl from openpyxl.styles import Font, Alignment, Border, Side @@ -129,7 +129,7 @@ wb.save('考试成绩表.xlsx') 通过`openpyxl`库,可以直接向 Excel 中插入统计图表,具体的做法跟在 Excel 中插入图表大体一致。我们可以创建指定类型的图表对象,然后通过该对象的属性对图表进行设置。当然,最为重要的是为图表绑定数据,即横轴代表什么,纵轴代表什么,具体的数值是多少。最后,可以将图表对象添加到表单中,具体的代码如下所示。 -```Python +```python from openpyxl import Workbook from openpyxl.chart import BarChart, Reference @@ -175,7 +175,7 @@ wb.save('demo.xlsx') 运行上面的代码,打开生成的 Excel 文件,效果如下图所示。 -image-20210819235009026 + ### 总结 diff --git a/Day21-30/26.Python操作Word和PowerPoint文件.md b/Day21-30/26.Python操作Word和PowerPoint文件.md index 3bbfaa7..3281ca8 100755 --- a/Day21-30/26.Python操作Word和PowerPoint文件.md +++ b/Day21-30/26.Python操作Word和PowerPoint文件.md @@ -12,7 +12,7 @@ pip install python-docx 按照[官方文档](https://python-docx.readthedocs.io/en/latest/)的介绍,我们可以使用如下所示的代码来生成一个简单的 Word 文档。 -```Python +```python from docx import Document from docx.shared import Cm, Pt @@ -91,7 +91,7 @@ document.save('demo.docx') 对于一个已经存在的 Word 文件,我们可以通过下面的代码去遍历它所有的段落并获取对应的内容。 -```Python +```python from docx import Document from docx.document import Document as Doc @@ -125,7 +125,7 @@ for no, p in enumerate(doc.paragraphs): 接下来我们读取该文件,将占位符替换为真实信息,就可以生成一个新的 Word 文档,如下所示。 -```Python +```python from docx import Document from docx.document import Document as Doc @@ -194,7 +194,7 @@ pip install python-pptx 用 Python 操作 PowerPoint 的内容,因为实际应用场景不算很多,我不打算在这里进行赘述,有兴趣的读者可以自行阅读`python-pptx`的[官方文档](https://python-pptx.readthedocs.io/en/latest/),下面仅展示一段来自于官方文档的代码。 -```Python +```python from pptx import Presentation # 创建幻灯片对象 diff --git a/Day21-30/27.Python操作PDF文件.md b/Day21-30/27.Python操作PDF文件.md index e67661e..00ed135 100755 --- a/Day21-30/27.Python操作PDF文件.md +++ b/Day21-30/27.Python操作PDF文件.md @@ -12,7 +12,7 @@ pip install PyPDF2 `PyPDF2`没有办法从 PDF 文档中提取图像、图表或其他媒体,但它可以提取文本,并将其返回为 Python 字符串。 -```Python +```python import PyPDF2 reader = PyPDF2.PdfReader('test.pdf') @@ -35,7 +35,7 @@ pdf2text.py test.pdf 上面的代码中通过创建`PdfFileReader`对象的方式来读取 PDF 文档,该对象的`getPage`方法可以获得PDF文档的指定页并得到一个`PageObject`对象,通过`PageObject`对象的`rotateClockwise`和`rotateCounterClockwise`方法可以实现页面的顺时针和逆时针方向旋转,通过`PageObject`对象的`addBlankPage`方法可以添加一个新的空白页,代码如下所示。 -```Python +```python reader = PyPDF2.PdfReader('XGBoost.pdf') writer = PyPDF2.PdfWriter() @@ -54,7 +54,7 @@ with open('temp.pdf', 'wb') as file_obj: 使用`PyPDF2`中的`PdfFileWrite`对象可以为PDF文档加密,如果需要给一系列的PDF文档设置统一的访问口令,使用Python程序来处理就会非常的方便。 -```Python +```python import PyPDF2 reader = PyPDF2.PdfReader('XGBoost.pdf') @@ -73,7 +73,7 @@ with open('temp.pdf', 'wb') as file_obj: 上面提到的`PageObject`对象还有一个名为`mergePage`的方法,可以两个 PDF 页面进行叠加,通过这个操作,我们很容易实现给PDF文件添加水印的功能。例如要给上面的“XGBoost.pdf”文件添加一个水印,我们可以先准备好一个提供水印页面的 PDF 文件,然后将包含水印的`PageObject`读取出来,然后再循环遍历“XGBoost.pdf”文件的每个页,获取到`PageObject`对象,然后通过`mergePage`方法实现水印页和原始页的合并,代码如下所示。 -```Python +```python reader1 = PyPDF2.PdfReader('XGBoost.pdf') reader2 = PyPDF2.PdfReader('watermark.pdf') writer = PyPDF2.PdfWriter() @@ -99,7 +99,7 @@ pip install reportlab 下面通过一个例子为大家展示`reportlab`的用法。 -```Python +```python from reportlab.lib.pagesizes import A4 from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont diff --git a/Day21-30/28.Python处理图像.md b/Day21-30/28.Python处理图像.md index 88fa12b..bfb171e 100755 --- a/Day21-30/28.Python处理图像.md +++ b/Day21-30/28.Python处理图像.md @@ -25,7 +25,7 @@ Pillow 中最为重要的是`Image`类,可以通过`Image`模块的`open`函 1. 读取和显示图像 - ```Python + ```python from PIL import Image # 读取图像获得Image对象 @@ -44,7 +44,7 @@ Pillow 中最为重要的是`Image`类,可以通过`Image`模块的`open`函 2. 剪裁图像 - ```Python + ```python # 通过Image对象的crop方法指定剪裁区域剪裁图像 image.crop((80, 20, 310, 360)).show() ``` @@ -53,7 +53,7 @@ Pillow 中最为重要的是`Image`类,可以通过`Image`模块的`open`函 3. 生成缩略图 - ```Python + ```python # 通过Image对象的thumbnail方法生成指定尺寸的缩略图 image.thumbnail((128, 128)) image.show() @@ -63,7 +63,7 @@ Pillow 中最为重要的是`Image`类,可以通过`Image`模块的`open`函 4. 缩放和黏贴图像 - ```Python + ```python # 读取骆昊的照片获得Image对象 luohao_image = Image.open('luohao.png') # 读取吉多的照片获得Image对象 @@ -81,7 +81,7 @@ Pillow 中最为重要的是`Image`类,可以通过`Image`模块的`open`函 5. 旋转和翻转 - ```Python + ```python image = Image.open('guido.jpg') # 使用Image对象的rotate方法实现图像的旋转 image.rotate(45).show() @@ -95,7 +95,7 @@ Pillow 中最为重要的是`Image`类,可以通过`Image`模块的`open`函 6. 操作像素 - ```Python + ```python for x in range(80, 310): for y in range(20, 360): # 通过Image对象的putpixel方法修改图像指定像素点 @@ -107,7 +107,7 @@ Pillow 中最为重要的是`Image`类,可以通过`Image`模块的`open`函 7. 滤镜效果 - ```Python + ```python from PIL import ImageFilter # 使用Image对象的filter方法对图像进行滤镜处理 @@ -125,7 +125,7 @@ Pillow 中有一个名为`ImageDraw`的模块,该模块的`Draw`函数会返 要绘制如上图所示的图像,完整的代码如下所示。 -```Python +```python import random from PIL import Image, ImageDraw, ImageFont diff --git a/Day21-30/29.Python发送邮件和短信.md b/Day21-30/29.Python发送邮件和短信.md index 7183ebd..f60598a 100755 --- a/Day21-30/29.Python发送邮件和短信.md +++ b/Day21-30/29.Python发送邮件和短信.md @@ -16,7 +16,7 @@ 用手机扫码上面的二维码可以通过发送短信的方式来获取授权码,短信发送成功后,点击“我已发送”就可以获得授权码。授权码需要妥善保管,因为一旦泄露就会被其他人冒用你的身份来发送邮件。接下来,我们就可以编写发送邮件的代码了,如下所示。 -```Python +```python import smtplib from email.header import Header from email.mime.multipart import MIMEMultipart @@ -50,7 +50,7 @@ smtp_obj.sendmail( 下面的代码演示了如何发送带附件的邮件。 -```Python +```python import smtplib from email.header import Header from email.mime.multipart import MIMEMultipart @@ -95,7 +95,7 @@ smtp_obj.sendmail( 为了方便大家用 Python 实现邮件发送,我将上面的代码封装成了函数,使用的时候大家只需要调整邮件服务器域名、端口、用户名和授权码就可以了。 -```Python +```python import smtplib from email.header import Header from email.mime.multipart import MIMEMultipart @@ -151,7 +151,7 @@ def send_email(*, from_user, to_users, subject='', content='', filenames=[]): 接下来,我们可以通过`requests`库向平台提供的短信网关发起一个 HTTP 请求,通过将接收短信的手机号和短信内容作为参数,就可以发送短信,代码如下所示。 -```Python +```python import random import requests diff --git a/Day21-30/30.正则表达式的应用.md b/Day21-30/30.正则表达式的应用.md index b0d781b..90a4d3e 100755 --- a/Day21-30/30.正则表达式的应用.md +++ b/Day21-30/30.正则表达式的应用.md @@ -72,7 +72,7 @@ Python 提供了`re`模块来支持正则表达式相关操作,下面是`re` #### 例子1:验证输入用户名和QQ号是否有效并给出对应的提示信息。 -```Python +```python """ 要求:用户名必须由字母、数字或下划线构成且长度在6~20个字符之间,QQ号是5~12的数字且首位不能为0 """ @@ -102,7 +102,7 @@ if m1 and m2: -```Python +```python import re # 创建正则表达式对象,使用了前瞻和回顾来保证手机号前后不应该再出现数字 @@ -131,7 +131,7 @@ while m: #### 例子3:替换字符串中的不良内容 -```Python +```python import re sentence = 'Oh, shit! 你是傻逼吗? Fuck you.' @@ -144,7 +144,7 @@ print(purified) # Oh, *! 你是*吗? * you. #### 例子4:拆分长字符串 -```Python +```python import re poem = '窗前明月光,疑是地上霜。举头望明月,低头思故乡。' diff --git a/Day66-80/66.数据分析概述.md b/Day66-80/66.数据分析概述.md index 7ea14f1..4898bee 100755 --- a/Day66-80/66.数据分析概述.md +++ b/Day66-80/66.数据分析概述.md @@ -1,14 +1,24 @@ ## 数据分析概述 -当今世界对信息技术的依赖程度在不断加深,每天都会有大量的数据产生,我们经常会感到数据越来越多,但是要从中发现有价值的信息却越来越难。这里所说的信息,可以理解为对数据集处理之后的结果,是从数据集中提炼出的可用于其他场合的结论性的东西,而**从原始数据中抽取出有价值的信息**的这个过程我们就称之为**数据分析**,它是数据科学工作的一部分。 +当今世界,各行各业对信息技术的依赖程度在不断加深,每天都会有大量的数据产生,我们常常会感到数据越来越多,但是要从中发现有价值的信息却越来越难。这里所说的信息,可以理解为对数据集处理之后的结果,是从数据集中提炼出的可用于支撑和指导决策的东西,而**从原始数据中抽取出有价值的信息**的这个过程我们就称之为**数据分析**,它是数据科学的重要组成部分。 -> 定义:**数据分析是有针对性的收集、加工、整理数据并采用统计、挖掘等技术对数据进行探索、分析、呈现和解释的科学**。 +> **定义1**:数据分析是有针对性的收集、加工、整理数据并采用统计、挖掘等技术对数据进行探索、分析、呈现和解释的科学。 +> +> **定义2**:数据分析是通过收集、整理和分析数据,从中提取有价值的信息和洞察,以支持决策和优化过程的活动。(GPT-4o) +> +> **定义3**:数据分析是通过系统性的收集、整理、处理、检验和解释数据,从中提取有价值的信息、形成结论并支持决策的过程,其核心是利用统计、算法和逻辑方法揭示数据背后的规律、趋势或关联。(DeepSeek) -### 数据分析师的职责和技能栈 +对于想从事数据分析工作的人来说,需要掌握两个部分的技能,一是“数据思维”,二是“分析工具”,如下图所示。 -HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘等岗位都统称为数据分析岗位,但是根据工作性质的不同,又可以分为偏工程的**数据治理方向**、偏业务的**数据分析方向**、偏算法的**数据挖掘方向**、偏开发的**数据开发方向**、偏产品的**数据产品经理**。我们通常所说的数据分析师主要是指**业务数据分析师**,很多数据分析师的职业生涯都是从这个岗位开始的,而且这个岗位也是招聘数量最多的岗位。业务数据分析师在公司通常不属于研发部门而属于运营部门,所以这个岗位也称为**数据运营**或**商业分析**,这类人员通常也被称为“BI工程师”。通常招聘信息对这个岗位的描述(JD)是: +![](res/contents_of_data_analysis.png) + +上图中,分析工具部分其实是比较容易掌握的,像 SQL 或 Python 这样的编程语言,只要经过系统的学习和适量的练习,大部分人都是可以驾驭的;像 Power BI、Tableau 这样的商业智能工具,更是让我们通过“拖拉拽”操作就能完成数据的可视化并在此基础上产生商业洞察,上手难度会更低。相反,数据思维部分的内容对大多数新手来说是不太容易驾驭的,例如“统计思维”,很多人在读书的时候都学习过“概率论和统计学”这样的课程,但是当面对实际的业务场景时,却很难将这些知识映射到业务场景来解决现实的问题。此外,如果没有掌握基本的分析方法、没有理解常用的分析模型,没有相关业务知识的积累,即便我们拿到再多有用的数据,也会感觉无从下手,更不用说产生业务洞察发现商业价值了。所以,数据思维这个部分,除了系统的学习相关知识技能,还需要不断的在实际业务场景中积累和沉淀。 + +### 数据分析师的职责 + +HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘等岗位都统称为数据分析岗位,但是根据工作性质的不同,又可以分为偏工程的**数据治理方向**、偏业务的**商业分析方向**、偏算法的**数据挖掘方向**、偏应用的**数据开发方向**、偏产品的**数据产品经理**。我们通常所说的数据分析师主要是指**业务数据分析师**,很多数据分析师的职业生涯都是从这个岗位开始的,而且这个岗位也是招聘数量最多的岗位。有些公司会将业务数据分析师归属到具体的业务部门(市场、运营、产品等),有些公司有专门的数据部门(数据分析团队或数据科学团队),还有些公司数据分析师会直接服务高层决策,属于企业战略部门。正因如此,你在招聘网站上看到的把数据分析师称为**数据运营**、**商业分析师**、**BI工程师**就不会感到奇怪了。通常,我们在招聘网站看到的对业务数据分析师岗位职责(JD)的描述如下所示: 1. 负责相关报表的输出。 2. 建立和优化指标体系。 @@ -16,25 +26,31 @@ HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘 4. 优化和驱动业务,推动数字化运营。 5. 找出潜在的市场和产品的上升空间。 -根据上面的描述,作为业务数据分析师,我们的工作不是给领导一个简单浅显的结论,而是结合公司的业务,完成**监控数据**、**揪出异常**、**找到原因**、**探索趋势**等工作。作为数据分析师,不管是用 Python 语言、Excel、SPSS或其他的商业智能工具,工具只是达成目标的手段,**数据思维是核心技能**,从实际业务问题出发到最终**发现数据中的商业价值**是终极目标。数据分析师在很多公司只是一个基础岗位,精于业务的数据分析师可以向**数据分析经理**或**数据运营总监**等管理岗位发展;对于熟悉机器学习算法的数据分析师来说,可以向**数据挖掘工程师**或**算法专家**方向发展,而这些岗位除了需要相应的数学和统计学知识,在编程能力方面也比数据分析师有更高的要求,可能还需要有大数据存储和处理的相关经验。数据治理岗位主要是帮助公司建设数据仓库或数据湖,实现数据从业务系统、埋点系统、日志系统到分析库的转移,为后续的数据分析和挖掘提供基础设施。数据治理岗位对 SQL 和 HiveSQL 有着较高的要求,需要熟练的使用 ETL 工具,此外还需要对 Hadoop 生态圈有一个较好的认知。作为数据产品经理,除了传统产品经理的技能栈之外,也需要较强的技术能力,例如要了解常用的推荐算法、机器学习模型,能够为算法的改进提供依据,能够制定相关埋点的规范和口径,虽然不需要精通各种算法,但是要站在产品的角度去考虑数据模型、指标、算法等的落地。 +根据上面的描述,作为业务数据分析师,我们的工作不是给出一个简单浅显的结论,而是结合公司的业务,完成**监控数据**、**揪出异常**、**找到原因**、**探索趋势**等工作。不管你是用 Python 语言、Excel、Tableau、SPSS或其他的商业智能工具,工具只是达成目标的手段,**数据思维是核心技能**,从实际业务问题出发到最终**发现数据中的商业价值**是终极目标。数据分析师在很多公司只是一个基础岗位,精于业务的数据分析师可以向**数据分析经理**或**数据运营总监**等管理岗位发展;对于熟悉机器学习算法的数据分析师来说,可以向**数据挖掘工程师**或**算法专家**方向发展,这些岗位除了需要相应的数学和统计学知识,在编程能力方面也比数据分析师有更高的要求,可能还需要有大数据存储和处理的相关经验。 -以下是我总结的数据分析师的技能栈,仅供参考。 +这里顺便说一下其他几个方向,数据治理岗位主要是帮助公司建设数据仓库或数据湖,实现数据从业务系统、埋点系统、日志系统到数据仓库或数据湖的转移,为后续的数据分析和挖掘提供基础设施。数据治理岗位对 SQL 和 HiveSQL 有着较高的要求,需要熟练的使用 ETL 工具,此外还需要对 Hadoop 生态圈有较好的认知。作为数据产品经理,除了传统产品经理的技能栈之外,也需要较强的技术能力,例如要了解常用的推荐算法、机器学习模型,能够为算法的改进提供依据,能够制定相关埋点的规范和口径,虽然不需要精通各种算法,但是要站在产品的角度去考虑数据模型、指标、算法等的落地。 + +### 数据分析师的技能栈 + +数据分析师的技能栈也包括硬技能和软技能,以下是我对这个职位的理解,仅供参考。 1. 计算机科学(数据分析工具、编程语言、数据库) 2. 数学和统计学(数据思维、统计思维) -3. 人工智能(机器学习中的数据挖掘算法) +3. 人工智能(机器学习和深度学习算法) 4. 业务理解能力(沟通、表达、经验) -5. 总结和表述能力(商业PPT、文字总结) +5. 总结和表述能力(总结、汇报、商业 PPT) -### 数据分析的流程 +当然,对于一个新手来收,不可能用短时间掌握整个技能栈的内容,但是随着这份工作的深入,上面提到的东西多多少少都会涉猎到,大家可以根据实际工作的需求去深耕其中的某个或某些技能。 + +### 数据分析通用流程 我们提到数据分析这个词很多时候可能指的都是**狭义的数据分析**,这类数据分析主要目标就是生成可视化报表并通过这些报表来洞察业务中的问题,这类工作一般都是具有滞后性的。**广义的数据分析**还包含了数据挖掘的部分,不仅要通过数据实现对业务的监控和分析,还要利用机器学习算法,找出隐藏在数据背后的知识,并利用这些知识为将来的决策提供支撑,具备一定的前瞻性。 基本的数据分析工作一般包含以下几个方面的内容,当然因为行业和工作内容的不同会略有差异。 1. 确定目标(输入):理解业务,确定指标口径 -2. 获取数据:数据仓库(SQL提数)、电子表格、三方接口、网络爬虫、开放数据集等 -3. 清洗数据:包括对缺失值、重复值、异常值的处理以及相关的预处理(格式化、离散化、二值化等) +2. 获取数据:数据仓库、电子表格、三方接口、网络爬虫、开放数据集等 +3. 清洗数据:缺失值、重复值、异常值的处理以及其他预处理(格式化、离散化、二值化等) 4. 数据透视:排序、统计、分组聚合、交叉表、透视表等 5. 数据呈现(输出):数据可视化,发布工作成果(数据分析报告) 6. 分析洞察(后续):解释数据的变化,提出对应的方案 @@ -50,17 +66,23 @@ HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘 ### 数据分析相关库 -使用 Python 从事数据科学相关的工作是一个非常棒的选择,因为 Python 整个生态圈中,有大量的成熟的用于数据科学的软件包(工具库)。而且不同于其他的用于数据科学的编程语言(如:Julia、R),Python 除了可以用于数据科学,还能做很多其他的事情,可以说 Python 语言几乎是无所不能的。 +使用 Python 从事数据分析相关的工作是一个非常棒的选择,首先 Python 语言非常容易上手,而且整个 Python 生态圈中,有非常多成熟的用于数据科学的软件包和工具库。不同于其他的数据科学编程语言(如:Julia、R等),Python 除了可以用于数据科学,还能做很多其他的事情。 -#### 三大神器 +#### 经典的三大神器 -1. [NumPy](https://numpy.org/):支持常见的数组和矩阵操作,通过`ndarray`类实现了对多维数组的封装,提供了操作这些数组的方法和函数集。由于 NumPy 内置了并行运算功能,当使用多核 CPU 时,Numpy会自动做并行计算。 -2. [Pandas](https://pandas.pydata.org/):pandas 的核心是其特有的数据结构`DataFrame`和`Series`,这使得 pandas 可以处理包含不同类型数据的表格和时间序列,这一点是NumPy的`ndarray`做不到的。使用 pandas,可以轻松顺利的加载各种形式的数据,然后对数据进行切片、切块、处理缺失值、聚合、重塑和可视化等操作。 +1. [NumPy](https://numpy.org/):支持常见的数组和矩阵操作,通过`ndarray`类实现了对多维数组的封装,提供了操作这些数组的方法和函数。由于 NumPy 内置了并行运算功能,当使用多核 CPU 时,NumPy 会自动做并行计算。 +2. [Pandas](https://pandas.pydata.org/):pandas 的核心是其特有的数据结构`DataFrame`和`Series`,这使得 pandas 可以处理包含不同类型数据的表格和时间序列,这一点是 NumPy 的`ndarray`做不到的。使用 pandas,可以轻松顺利的加载各种形式的数据,然后对数据进行切片、切块、重塑、清洗、聚合、呈现等操作。 3. [Matplotlib](https://matplotlib.org/):matplotlib 是一个包含各种绘图模块的库,能够根据我们提供的数据创建高质量的图表。此外,matplotlib 还提供了 pylab 模块,这个模块包含了很多像 [MATLAB](https://www.mathworks.com/products/matlab.html) 一样的绘图组件。 #### 其他相关库 1. [SciPy](https://scipy.org/):完善了 NumPy 的功能,封装了大量科学计算的算法,包括线性代数、统计检验、稀疏矩阵、信号和图像处理、最优化问题、快速傅里叶变换等。 -2. [Seaborn](https://seaborn.pydata.org/):seaborn 是基于 matplotlib 的图形可视化工具,直接使用 matplotlib 虽然可以定制出漂亮的统计图表,但是总体来说还不够简单方便,seaborn 相当于是对 matplotlib 做了封装,让用户能够以更简洁有效的方式做出各种有吸引力的统计图表。 -3. [Scikit-learn](https://scikit-learn.org/):scikit-learn 最初是 SciPy 的一部分,提供了大量机器学习可能用到的工具,包括数据预处理、监督学习(分类、回归)、无监督学习(聚类)、模式选择、交叉检验等。 -4. [Statsmodels](https://www.statsmodels.org/stable/index.html):包含了经典统计学和经济计量学算法的库。 +2. [Polars](https://pola.rs/):一个高性能的数据分析库,旨在提供比 pandas 更快的数据操作。它支持大规模数据处理,并能够利用多核 CPU 来加速计算,在处理大规模数据集时可以用来替代 pandas。 +3. [Seaborn](https://seaborn.pydata.org/):seaborn 是基于 matplotlib 的图形可视化工具,直接使用 matplotlib 虽然可以定制出漂亮的统计图表,但是总体来说还不够简单方便,seaborn 相当于是对 matplotlib 做了封装,让用户能够以更简洁有效的方式做出各种有吸引力的统计图表。 +4. [Scikit-learn](https://scikit-learn.org/):scikit-learn 最初是 SciPy 的一部分,提供了大量机器学习可能用到的工具,包括数据预处理、监督学习(分类、回归)、无监督学习(聚类)、模式选择、交叉检验等。 +5. [Statsmodels](https://www.statsmodels.org/stable/index.html):包含了经典统计学和计量经济学算法的库,帮助帮助用户完成数据探索、回归分析、假设检验等任务。 +6. [PySpark](https://spark.apache.org/):Apache Spark(大数据处理引擎)的 Python 版本,用于大规模数据处理和分布式计算,能够在分布式环境中高效地进行数据清洗、转化和分析。 +7. [Tensorflow](https://www.tensorflow.org/):TensorFlow 是一个开源的深度学习框架,由 Google 开发,主要面向深度学习任务,常用于构建和训练机器学习模型(尤其是复杂的神经网络模型)。 +8. [Keras](https://keras.io/):Keras 是一个高层次的神经网络 API,主要用于构建和训练深度学习模型。Keras 适合深度学习初学者和研究人员,因为它让构建和训练神经网络变得更加简单。 +9. [PyTorch](https://pytorch.org/):PyTorch 是一个开源的深度学习框架,由 Facebook 开发,广泛用于研究和生产环境。PyTorch 是深度学习研究中的热门框架,在计算机视觉、自然语言处理等领域得到了广泛应用。 +10. [NLTK](https://www.nltk.org/) / [SpaCy](https://spacy.io/):自然语言处理(NLP)库。 diff --git a/Day66-80/data/2018年北京积分落户数据.csv b/Day66-80/code/data/2018年北京积分落户数据.csv similarity index 100% rename from Day66-80/data/2018年北京积分落户数据.csv rename to Day66-80/code/data/2018年北京积分落户数据.csv diff --git a/Day66-80/data/2020年销售数据.xlsx b/Day66-80/code/data/2020年销售数据.xlsx similarity index 100% rename from Day66-80/data/2020年销售数据.xlsx rename to Day66-80/code/data/2020年销售数据.xlsx diff --git a/Day66-80/data/2022年股票数据.xlsx b/Day66-80/code/data/2022年股票数据.xlsx similarity index 100% rename from Day66-80/data/2022年股票数据.xlsx rename to Day66-80/code/data/2022年股票数据.xlsx diff --git a/Day66-80/data/2023年北京积分落户数据.csv b/Day66-80/code/data/2023年北京积分落户数据.csv similarity index 100% rename from Day66-80/data/2023年北京积分落户数据.csv rename to Day66-80/code/data/2023年北京积分落户数据.csv diff --git a/Day66-80/data/boston_house_price.csv b/Day66-80/code/data/boston_house_price.csv similarity index 100% rename from Day66-80/data/boston_house_price.csv rename to Day66-80/code/data/boston_house_price.csv diff --git a/Day66-80/data/某招聘网站招聘数据.csv b/Day66-80/code/data/某招聘网站招聘数据.csv similarity index 100% rename from Day66-80/data/某招聘网站招聘数据.csv rename to Day66-80/code/data/某招聘网站招聘数据.csv diff --git a/Day66-80/code/day01.ipynb b/Day66-80/code/day01.ipynb new file mode 100644 index 0000000..0a8d1c6 --- /dev/null +++ b/Day66-80/code/day01.ipynb @@ -0,0 +1,602 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c664c108-059f-402a-b216-5ba4caa2d98b", + "metadata": {}, + "source": [ + "## Python数据分析第1天\n", + "\n", + "### 热身练习\n", + "\n", + "如下列表保存着本公司从2022年1月到12月五个销售区域(南京、无锡、苏州、徐州、南通)的销售额(以百万元为单位),请利用这些数据完成以下操作:\n", + "\n", + "```python\n", + "sales_month = [f'{i:>2d}月' for i in range(1, 13)]\n", + "sales_area = ['南京', '无锡', '苏州', '徐州', '南通']\n", + "sales_data = [\n", + " [32, 17, 12, 20, 28],\n", + " [41, 30, 17, 15, 35],\n", + " [35, 18, 13, 11, 24],\n", + " [12, 42, 44, 21, 34],\n", + " [29, 11, 42, 32, 50],\n", + " [10, 15, 11, 12, 26],\n", + " [16, 28, 48, 22, 28],\n", + " [31, 40, 45, 30, 39],\n", + " [25, 41, 47, 42, 47],\n", + " [47, 21, 13, 49, 48],\n", + " [41, 36, 17, 36, 22],\n", + " [22, 25, 15, 20, 37]\n", + "]\n", + "```\n", + "\n", + "1. 统计本公司每个月的销售额。\n", + "2. 统计本公司销售额的月环比。\n", + "3. 统计每个销售区域全年的销售额。\n", + "4. 按销售额从高到低排序销售区域及其销售额。\n", + "5. 统计全年最高的销售额出现在哪个月哪个区域。\n", + "6. 找出哪个销售区域的业绩最不稳定。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9d87cfc-deb0-46eb-b98c-2799a4908bc8", + "metadata": {}, + "outputs": [], + "source": [ + "sales_month = [f'{i:>2d}月' for i in range(1, 13)]\n", + "sales_area = ['南京', '无锡', '苏州', '徐州', '南通']\n", + "sales_data = [\n", + " [32, 17, 12, 20, 28],\n", + " [41, 30, 17, 15, 35],\n", + " [35, 18, 13, 11, 24],\n", + " [12, 42, 44, 21, 34],\n", + " [29, 11, 42, 32, 50],\n", + " [10, 15, 11, 12, 26],\n", + " [16, 28, 48, 22, 28],\n", + " [31, 40, 45, 30, 39],\n", + " [25, 41, 47, 42, 47],\n", + " [47, 21, 13, 49, 48],\n", + " [41, 36, 17, 36, 22],\n", + " [22, 25, 15, 20, 37]\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc581dfc-9108-46fa-ace2-60ace650434e", + "metadata": {}, + "outputs": [], + "source": [ + "# 魔法指令 - %whos - 查看变量\n", + "%whos" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a50e4c3e-6dc1-426f-977b-aef9a5c9a02f", + "metadata": {}, + "outputs": [], + "source": [ + "print = 100" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c0b54ca-1556-4a14-9a6a-b6bd6af5d822", + "metadata": {}, + "outputs": [], + "source": [ + "# 魔法指令 - %xdel - 删除变量\n", + "%xdel print" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe8eb05f-f45b-491a-b98e-6f6c924997ff", + "metadata": {}, + "outputs": [], + "source": [ + "# 1. 统计本公司每个月的销售额。\n", + "monthly_sales = []\n", + "for i, month in enumerate(sales_month):\n", + " monthly_sales.append(sum(sales_data[i]))\n", + " print(f'{month}销售额: {monthly_sales[i]}百万')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53e6bf88-e6a9-4ac9-a7fe-bd1d18ff88f5", + "metadata": {}, + "outputs": [], + "source": [ + "# 2. 统计本公司销售额的月环比。\n", + "for i in range(1, len(monthly_sales)):\n", + " temp = (monthly_sales[i] - monthly_sales[i - 1]) / monthly_sales[i - 1]\n", + " print(f'{sales_month[i]}: {temp:.2%}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5a130d6-b781-4ee3-a96b-d1fe5e3b4b90", + "metadata": {}, + "outputs": [], + "source": [ + "# 3. 统计每个销售区域全年的销售额。\n", + "arealy_sales = {}\n", + "for j, area in enumerate(sales_area):\n", + " temp = [sales_data[i][j] for i in range(len(sales_month))]\n", + " arealy_sales[area] = sum(temp)\n", + " print(f'{area}: {arealy_sales[area]}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7bd0510-5e68-4e58-ac3b-6c531f7abccb", + "metadata": {}, + "outputs": [], + "source": [ + "# 4. 按销售额从高到低排序销售区域及其销售额。\n", + "sorted_keys = sorted(arealy_sales, key=lambda x: arealy_sales[x], reverse=True)\n", + "for key in sorted_keys:\n", + " print(f'{key}: {arealy_sales[key]}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4b2f3e8-c5c2-481e-b277-9623d30892ac", + "metadata": {}, + "outputs": [], + "source": [ + "# 5. 统计全年最高的销售额出现在哪个月哪个区域。\n", + "max_value = sales_data[0][0]\n", + "max_i, max_j = 0, 0\n", + "for i in range(len(sales_month)):\n", + " for j in range(len(sales_area)):\n", + " temp = sales_data[i][j]\n", + " if temp > max_value:\n", + " max_value = temp\n", + " max_i, max_j = i, j\n", + "print(sales_month[max_i], sales_area[max_j])" + ] + }, + { + "cell_type": "markdown", + "id": "647d0a87-b672-4e0c-81cc-a3bbb76dca11", + "metadata": {}, + "source": [ + "总体方差:\n", + "$$\n", + "\\sigma^{2} = \\frac{1}{N} \\sum_{i=1}^{N}(x_{i} - \\mu)^{2}\n", + "$$\n", + "\n", + "样本方差:\n", + "$$\n", + "s^{2} = \\frac{1}{n - 1} \\sum_{i=1}^{n}(x_{i} - \\bar{x})^{2}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b43fb247-32fc-4e10-a9ee-488fd1f56a9a", + "metadata": {}, + "outputs": [], + "source": [ + "# 6. 找出哪个销售区域的业绩最不稳定。\n", + "import statistics as stats\n", + "\n", + "arealy_vars = []\n", + "for j, area in enumerate(sales_area):\n", + " temp = [sales_data[i][j] for i in range(len(sales_month))]\n", + " arealy_vars.append(stats.pvariance(temp))\n", + "sales_area[arealy_vars.index(max(arealy_vars))]" + ] + }, + { + "cell_type": "markdown", + "id": "3ea677d0-7a33-43e5-b10b-ddfcb82f7f6a", + "metadata": {}, + "source": [ + "### 三大神器\n", + "\n", + "1. numpy - Numerical Python - 核心是`ndarray`类型,可以用来表示N维数组,提供了一系列处理数据的运算、函数和方法。\n", + "2. pandas - Panel Data Set - 封装了和数据分析(加载、重塑、清洗、预处理、透视、呈现)相关的类型、函数和诸多的方法,为数据分析提供了一站式解决方案。它的核心有三个数据类型,分别是:`Series`、`DataFrame`、`Index`。\n", + "3. matplotlib - 封装了各种常用的统计图表,帮助我们实现数据呈现。\n", + "4. scipy - Scientific Python - 针对NumPy进行了很好的补充,提供了高级的数据运算的函数和方法。\n", + "5. scikit-learn - 封装了常用的机器学习(分类、聚类、回归等)算法,除此之外,还提供了数据预处理、特征工程、模型验证相关的函数和方法。\n", + "6. sympy - Symbolic Python - 封装了符号运算相关操作。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0db758cc-d83c-47c4-9a0b-c7ef5abd6c18", + "metadata": {}, + "outputs": [], + "source": [ + "# 魔法指令 - %pip - 调用包管理工具pip\n", + "# %pip install numpy pandas matplotlib openpyxl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8eb6970b-3907-4b84-af60-67cbf67f2e74", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fb76dec-cd51-4e79-9bd2-3b210ae20522", + "metadata": {}, + "outputs": [], + "source": [ + "np.__version__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6369df9-7577-496c-bfc1-2fce096c0162", + "metadata": {}, + "outputs": [], + "source": [ + "pd.__version__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb5733cd-38f7-4afd-b45b-70c1439ab36b", + "metadata": {}, + "outputs": [], + "source": [ + "# 将嵌套列表处理成二维数组\n", + "data = np.array(sales_data)\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da304104-8cf0-4425-b3b4-dcb148ac4b3a", + "metadata": {}, + "outputs": [], + "source": [ + "# 沿着1轴求和(每个月的销售额)\n", + "data.sum(axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1507ac63-f53b-4e36-a7fb-b9c636fd81ea", + "metadata": {}, + "outputs": [], + "source": [ + "# 沿着0轴求和(每个区域的销售)\n", + "data.sum(axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26be450d-44ba-4d83-9351-c52a13c2c338", + "metadata": {}, + "outputs": [], + "source": [ + "# 总体方差\n", + "data.var(axis=0).round(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81e5b2a0-c86e-4720-909f-ce8b1b6fdd58", + "metadata": {}, + "outputs": [], + "source": [ + "# 样本方差\n", + "data.var(axis=0, ddof=1).round(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba4e0f0a-e711-4041-8834-1e3be86ce8a4", + "metadata": {}, + "outputs": [], + "source": [ + "# 构造DataFrame对象(处理二维数据)\n", + "df = pd.DataFrame(data, columns=sales_area, index=sales_month)\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d1a6a43-6dfc-41e3-98c8-be2681e0d547", + "metadata": {}, + "outputs": [], + "source": [ + "# 求和(默认沿着0轴)\n", + "df.sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a478ec0e-499f-4e31-b8c2-ba45e691b834", + "metadata": {}, + "outputs": [], + "source": [ + "# 排序\n", + "df.sum().sort_values(ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f221833-855c-45ad-91b2-e3f4da627704", + "metadata": {}, + "outputs": [], + "source": [ + "# 求和(指定沿着1轴)\n", + "df.sum(axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80df8865-4ea0-4c72-a581-215cd953cfbe", + "metadata": {}, + "outputs": [], + "source": [ + "# 计算月环比\n", + "df.sum(axis=1).pct_change()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea4579c3-11cd-4179-9c96-8dbe9a033da2", + "metadata": {}, + "outputs": [], + "source": [ + "df['合计'] = df.sum(axis=1)\n", + "df['月环比'] = df['合计'].pct_change()\n", + "df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c660052-dded-4a0a-8b72-7747d3cae816", + "metadata": {}, + "outputs": [], + "source": [ + "# 渲染DataFrame\n", + "df.style.format(\n", + " formatter={'月环比': '{:.2%}'},\n", + " na_rep='------'\n", + ").bar(\n", + " subset='合计'\n", + ").background_gradient(\n", + " 'RdYlBu', subset='月环比'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a092c12c-dab6-4272-b1cd-5218998fcd90", + "metadata": {}, + "outputs": [], + "source": [ + "# 将DataFrame输出到Excel文件\n", + "df.to_excel('sales.xlsx', sheet_name='data')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54c3f505-e866-4c4e-a3f8-f55a71a95c3f", + "metadata": {}, + "outputs": [], + "source": [ + "# 魔法指令 - %config - 修改配置\n", + "# %config InlineBackend.figure_format = 'svg'\n", + "get_ipython().run_line_magic('config', 'InlineBackend.figure_format = \"svg\"')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3951055d-d5d2-4e4e-bbe7-a1b40a6731e0", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制柱状图\n", + "plt.figure(figsize=(8, 4), dpi=200)\n", + "df.plot(ax=plt.gca(), kind='bar', y='合计', legend=False)\n", + "plt.xticks(rotation=0)\n", + "plt.savefig('aa.png')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8a5236f7-072b-466c-9be3-afbab394f5cb", + "metadata": {}, + "source": [ + "### 魔法指令" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5c6a18b-2863-4855-8ef7-2c0aa99b7d5c", + "metadata": {}, + "outputs": [], + "source": [ + "# 查看当前工作路径 - print working directory\n", + "%pwd" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80a9f9e0-1528-40cf-910c-f3c8e5e7e3b9", + "metadata": {}, + "outputs": [], + "source": [ + "# 查看指定路径文件列表 - list directory contents\n", + "%ls" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "620a54ed-9c29-4058-9d20-c4df72ba4c62", + "metadata": {}, + "outputs": [], + "source": [ + "# 执行系统命令\n", + "%system date" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "659215ed-113a-4d8f-9036-0fcf47c96021", + "metadata": {}, + "outputs": [], + "source": [ + "# 保存运行过的代码\n", + "%save temp.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fc9c4e4-1423-40f3-b4ee-db2ba2e5d125", + "metadata": {}, + "outputs": [], + "source": [ + "# 加载指定文件内容\n", + "%load temp.py" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58a08283-561c-43d4-8db6-74cde401b8a9", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计代码执行时间\n", + "%timeit (1, 2, 3, 4, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22a271ab-3f5c-4167-b89e-66a31e891cbd", + "metadata": {}, + "outputs": [], + "source": [ + "# 查看历史输入\n", + "%hist" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4ffa792-f1a0-4be9-b2aa-642ee0b9a1ae", + "metadata": {}, + "outputs": [], + "source": [ + "# 查看魔法指令\n", + "%lsmagic" + ] + }, + { + "cell_type": "markdown", + "id": "a15db907-c068-41d7-a24c-8f1c5c20d4ec", + "metadata": {}, + "source": [ + "### 获取帮助" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e037694-9357-46b9-864a-c5f93e1aa8c8", + "metadata": {}, + "outputs": [], + "source": [ + "np.random?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11a97abd-d73d-493e-b727-9c4ded3e5060", + "metadata": {}, + "outputs": [], + "source": [ + "np.random.normal?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66503921-cd69-4394-80ea-7fecf6ecdc33", + "metadata": {}, + "outputs": [], + "source": [ + "np.random.r*?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Day66-80/code/day02.ipynb b/Day66-80/code/day02.ipynb new file mode 100644 index 0000000..9113ae0 --- /dev/null +++ b/Day66-80/code/day02.ipynb @@ -0,0 +1,10086 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f11bbe2b-7325-4bf0-abfb-4b4e64292145", + "metadata": {}, + "source": [ + "## NumPy入门\n", + "\n", + "NumPy是Python数据科学三方库中最为重要的基石,提供了数据存储和运算的能力,其他很多跟数据科学相关的库底层都依赖了NumPy。NumPy的核心是名为`ndarray`的数据类型,用来表示任意维度的数组,相较于Python的`list`,它具有以下优势:\n", + "\n", + "1. 有更好的性能,可以利用硬件的并行计算能力和缓存优化,相较于`list`在处理数据的性能上有着数量级的差异。\n", + "2. 功能更加强大,`ndarray`提供了丰富的运算和方法来处理数据,NumPy中还针对数组操作封装了大量的函数。\n", + "3. 向量化操作,NumPy中的函数以及`ndarray`的方法都是对作用于整个数组,无需使用显示的循环,代码更加简单优雅。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "15630f70-be3c-4690-96a6-0b134a685efb", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8a115f09-0477-4e62-a910-d9284f32fbd1", + "metadata": {}, + "outputs": [], + "source": [ + "# %save hello.py" + ] + }, + { + "cell_type": "markdown", + "id": "a8c3b5a1-9ad1-4511-b7f8-beacce89cf69", + "metadata": {}, + "source": [ + "### 创建数组对象\n", + "\n", + "1. 通过`array`/`asarray`函数将列表处理成数组对象\n", + "2. 通过`arange`函数指定起始值、终止值和跨度创建数组对象\n", + "3. 通过`linspace`函数指定起始值、终止值和元素个数创建等差数列\n", + "4. 通过`logspace`函数指定起始值(指数)、终止值(指数)、元素个数、底数(默认10)创建等比数列\n", + "5. 通过`fromstring`/`fromfile`函数从字符串或文件中读取数据创建数组对象\n", + "6. 通过`fromiter`函数通过迭代器获取数据创建数组对象\n", + "7. 通过生成随机元素的方式创建数组对象\n", + "8. 通过`zeros`/`zeros_like`函数创建全0元素的数组对象\n", + "9. 通过`ones`/`ones_like`函数创建全1元素的数组对象\n", + "10. 通过`full`函数指定元素值创建数组对象\n", + "11. 通过`eye`函数创建单位矩阵\n", + "12. 通过`tile`/`repeat`函数重复元素创建数组对象" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "a035b671-2f91-473c-ac2b-e25291cf664b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 2, 3, 4, 5], dtype=int32)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法一:通过array函数将列表处理成数组对象\n", + "array1 = np.array([1, 2, 3, 4, 5], dtype='i4')\n", + "array1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "28a8c2f7-c197-4d5e-9006-d99242edefee", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "numpy.ndarray" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(array1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "95eae152-6bfc-4707-bd04-50c7363e9315", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [4, 5, 6]])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2 = np.array([[1, 2, 3], [4, 5, 6]])\n", + "array2" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "3c662a6d-d017-4a85-b93c-8538f334db22", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 2, 3, 4, 5, 6, 7, 8, 9])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法二:通过arange函数指定范围创建数组对象\n", + "array3 = np.arange(1, 10)\n", + "array3" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "954605a1-1004-4595-ac90-523feac7f4e9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49,\n", + " 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array4 = np.arange(1, 100, 3)\n", + "array4" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e479f343-7033-4b73-a0ce-e7f04444e915", + "metadata": {}, + "outputs": [], + "source": [ + "# 方法三:通过linspace函数创建等差数列\n", + "array5 = np.linspace(-2 * np.pi, 2 * np.pi, 120)\n", + "array6 = np.sin(array5)\n", + "array7 = np.cos(array5)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "57b0e126-3276-4f10-b1d1-288597842d35", + "metadata": {}, + "outputs": [], + "source": [ + "%config InlineBackend.figure_format = 'svg'\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "62a6b0ed-acaf-45a1-90b0-7caf73184d09", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:45:17.121512\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure(figsize=(8, 4))\n", + "# 绘制折线图\n", + "plt.plot(array5, array6, marker='.', color='darkgreen')\n", + "plt.plot(array5, array7, marker='.', color='coral')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "fc6ed6f5-6844-47df-8dde-fd9e598af48d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024])" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法四:通过logspace函数创建等比数列\n", + "array8 = np.logspace(0, 10, num=11, base=2, dtype='i8')\n", + "array8" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "f3d2ba81-cf13-4d96-afef-f9221b4b4a68", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 11, 111, 2, 22, 222])" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法五:通过fromstring/fromfile/fromregex函数从字符串读取数据创建数组\n", + "array9 = np.fromstring('1, 11, 111, 2, 22, 222', sep=',', dtype='i8')\n", + "array9" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "1c691969-f93c-4e32-9ba5-ab5e074a6409", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.core.interactiveshell import InteractiveShell\n", + "\n", + "InteractiveShell.ast_node_interactivity = 'last_expr'" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "d8eb5b58-d129-459f-9969-543191fb1966", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47])" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array10 = np.fromfile('res/prime.txt', dtype='i8', sep='\\n', count=15)\n", + "array10" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "ff01ce19-fc81-41db-88a2-647299ec940c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 面试官:请说一下Python中的迭代器是什么?它跟生成器是什么关系?\n", + "# 迭代器是实现了迭代器协议的对象。在Python中迭代器协议是两个魔术方法:__iter__、__next__\n", + "# 我们可以通过next函数或者for-in循环从迭代器中获取数据\n", + "# 迭代器的编写相对比较麻烦,所以在Python中可以用创建生成器的方式简化迭代器语法\n", + "\n", + "\n", + "def fib(count):\n", + " a, b = 0, 1\n", + " for _ in range(count):\n", + " a, b = b, a + b\n", + " yield a\n", + "\n", + "\n", + "gen = fib(50)\n", + "gen" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "5a2c15a6-5744-4eb6-8f94-3132f7e0b1b6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 1, 2, 3, 5,\n", + " 8, 13, 21, 34, 55,\n", + " 89, 144, 233, 377, 610,\n", + " 987, 1597, 2584, 4181, 6765,\n", + " 10946, 17711, 28657, 46368, 75025,\n", + " 121393, 196418, 317811, 514229, 832040,\n", + " 1346269, 2178309, 3524578, 5702887, 9227465,\n", + " 14930352, 24157817, 39088169, 63245986, 102334155,\n", + " 165580141, 267914296, 433494437, 701408733, 1134903170,\n", + " 1836311903, 2971215073, 4807526976, 7778742049, 12586269025])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法六:通过fromiter函数从迭代器中读取数据创建数组对象\n", + "array11 = np.fromiter(fib(50), dtype='i8')\n", + "array11" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ca62e6fb-f0c9-4f12-acdb-978507e37f94", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[90, 45, 91, 71],\n", + " [85, 2, 98, 76],\n", + " [58, 50, 72, 13],\n", + " [66, 90, 26, 69],\n", + " [23, 44, 68, 98]])" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法七:通过生成随机元素创建数组对象\n", + "array12 = np.random.randint(0, 101, (5, 4))\n", + "array12" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "2ec313a2-bdd6-492f-9176-172b1ec54534", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.35742922, 0.49173669, 0.14993948, 0.15556126, 0.48435648,\n", + " 0.57329703, 0.7256331 , 0.96709102, 0.79687864, 0.95782978])" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array13 = np.random.random(10)\n", + "array13" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "c8ca9327-c30e-4068-b0bc-c18c49a48c89", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([155., 173., 172., ..., 171., 176., 157.])" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array14 = np.random.normal(169, 8.5, 5000).round(0)\n", + "array14" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "e6f0fe78-41bf-45c9-a433-cf3cc40cdc11", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:45:17.194053\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# 绘制直方图\n", + "plt.hist(array14, bins=15, color='#6B8A7A')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "88de114b-c346-4150-b33c-f2d14dd84193", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0., 0., 0., 0.],\n", + " [0., 0., 0., 0.],\n", + " [0., 0., 0., 0.],\n", + " [0., 0., 0., 0.],\n", + " [0., 0., 0., 0.]])" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法八:通过zeros/zeros_like函数创建全0元素的数组对象\n", + "array15 = np.zeros((5, 4))\n", + "array15" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "b03bc9d0-1274-46a4-a9b6-28f634a9d034", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0, 0, 0],\n", + " [0, 0, 0]])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array16 = np.zeros_like(array2)\n", + "array16" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "db9f61e1-d50a-4f7f-a1b4-66798c0976ef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.],\n", + " [1., 1., 1., 1.]])" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法九:通过ones/ones_like函数创建全0元素的数组对象\n", + "array17 = np.ones((5, 4))\n", + "array17" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "07ddd6d3-2e91-4a1b-857d-5f8b6867904d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 1, 1],\n", + " [1, 1, 1]])" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array18 = np.ones_like(array2)\n", + "array18" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "6b45e1db-8e1c-4af9-9495-c3a7f06b9311", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[100, 100, 100, 100],\n", + " [100, 100, 100, 100],\n", + " [100, 100, 100, 100],\n", + " [100, 100, 100, 100],\n", + " [100, 100, 100, 100]])" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法十:通过full函数指定值和形状创建数组对象\n", + "array19 = np.full((5, 4), 100)\n", + "array19" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "1ae4620a-11bd-464b-abc2-83f7f8b7e8ba", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n", + " [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n", + " [0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 1., 0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],\n", + " [0., 0., 0., 0., 0., 0., 0., 0., 1., 0.],\n", + " [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法十一:通过eye函数创建单位矩阵\n", + "# identify matrix --> I --> eye\n", + "array20 = np.eye(10)\n", + "array20" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "0ae939c8-0977-42b0-8d8a-351f55b0471e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3,\n", + " 3, 3, 3, 3, 3, 3, 3, 3])" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 方法十二:通过repeat/tile函数重复元素创建数组对象\n", + "array21 = np.repeat([1, 2, 3], 10)\n", + "array21" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "c1a8d343-a30d-4144-baa8-b000a65c070d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1,\n", + " 2, 3, 1, 2, 3, 1, 2, 3])" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array22 = np.tile([1, 2, 3], 10)\n", + "array22" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "96e8c639-1a14-453c-89a5-6fd74ca89a88", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[ 36, 33, 28],\n", + " [ 36, 33, 28],\n", + " [ 36, 33, 28],\n", + " ...,\n", + " [ 32, 31, 29],\n", + " [ 32, 31, 27],\n", + " [ 31, 32, 26]],\n", + "\n", + " [[ 37, 34, 29],\n", + " [ 38, 35, 30],\n", + " [ 38, 35, 30],\n", + " ...,\n", + " [ 31, 30, 28],\n", + " [ 31, 30, 26],\n", + " [ 30, 31, 25]],\n", + "\n", + " [[ 38, 35, 30],\n", + " [ 38, 35, 30],\n", + " [ 38, 35, 30],\n", + " ...,\n", + " [ 30, 29, 27],\n", + " [ 30, 29, 25],\n", + " [ 29, 30, 25]],\n", + "\n", + " ...,\n", + "\n", + " [[239, 178, 123],\n", + " [237, 176, 121],\n", + " [235, 174, 119],\n", + " ...,\n", + " [ 78, 68, 56],\n", + " [ 76, 66, 54],\n", + " [ 73, 65, 52]],\n", + "\n", + " [[238, 177, 120],\n", + " [236, 175, 118],\n", + " [234, 173, 116],\n", + " ...,\n", + " [ 80, 70, 58],\n", + " [ 78, 68, 56],\n", + " [ 74, 67, 51]],\n", + "\n", + " [[237, 176, 119],\n", + " [236, 175, 118],\n", + " [234, 173, 116],\n", + " ...,\n", + " [ 83, 71, 59],\n", + " [ 81, 69, 57],\n", + " [ 77, 68, 53]]], dtype=uint8)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 补充:读图片获得一个三维数组对象\n", + "guido_image = plt.imread('res/guido.jpg')\n", + "guido_image" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "dff598ad-ce04-4115-bffc-b70decd6a54e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(750, 500, 3)" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "b8e6b35a-e9bb-489e-ab54-9fc2874b5708", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:45:17.291166\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(guido_image)" + ] + }, + { + "cell_type": "markdown", + "id": "0e6bc238-b4f7-45ed-8d3e-194576f67fa9", + "metadata": {}, + "source": [ + "### 数组对象的属性\n", + "\n", + "1. `size` - 元素的个数\n", + "2. `dtype` - 元素的数据类型\n", + "3. `ndim` - 数组的维度\n", + "4. `shape` - 数组的形状\n", + "5. `itemsize` - 每个元素占用的内存空间大小(字节)\n", + "6. `nbytes` - 所有元素占用的内存空间大小(字节)\n", + "7. `T` - 转置\n", + "8. `flags` - 内存信息\n", + "9. `base` - 根基" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "699ac4e0-a11a-4f23-9469-052371e6a140", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 2, 3, 4, 5], dtype=int32)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array1" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "292a05b0-351f-4e6b-8968-aebe3b859b0e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 大小 - 元素个数\n", + "array1.size" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "0b382c41-8a42-4946-8d82-27c959d08cf8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dtype('int32')" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 数据类型\n", + "array1.dtype" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "17c532d9-f1bf-4da9-9a98-4b450997d32b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 维度\n", + "array1.ndim" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "c1d57809-19cd-4540-a23a-05d5d639b98b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(5,)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 形状 - 元组\n", + "array1.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "3376c6e2-ec8e-4743-81dd-2409fd869a52", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 每个元素占用内存空间大小(字节)\n", + "array1.itemsize" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "29f66406-f940-434f-8d03-c0706cfa412b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "20" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 所有元素占用内存空间大小(字节)\n", + "array1.nbytes" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "68066920-3062-41cf-9cf7-012850461d70", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [4, 5, 6]])" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "43e80ef1-edcf-45b7-8143-d4a159a71c0b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 4],\n", + " [2, 5],\n", + " [3, 6]])" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.T" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "ea0cc4f7-b504-458f-acdb-ff79d9730a19", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.size" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "b268f3dc-4652-4736-8831-b21e1f3e76d8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dtype('int64')" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.dtype" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "b93e1712-3e62-4464-90e7-d90847e1763e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.ndim" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "0f17fd54-6969-4104-9d0e-cfc70678e663", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "f573369f-07ef-4dea-a46a-a4f5d837ec5f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.itemsize" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "cc8887de-4600-47f4-beb3-37979ec079f4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "48" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.nbytes" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "9457f0c9-b433-4ec5-961d-6e8ebedad2db", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + " C_CONTIGUOUS : True\n", + " F_CONTIGUOUS : False\n", + " OWNDATA : True\n", + " WRITEABLE : True\n", + " ALIGNED : True\n", + " WRITEBACKIFCOPY : False" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2.flags" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "22adf5d7-c491-4558-af40-fa1a7fef7d6b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1125000" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image.size" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "adf08994-775b-4276-8392-95a9da821fb8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dtype('uint8')" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image.dtype" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "81f03343-beac-4fe2-9b5e-9e72ca4387ae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image.ndim" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "2d01a9e3-62f1-4145-83c3-3f16ac647975", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(750, 500, 3)" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "47576187-1b9b-4c08-b63a-9abe6a1352a9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image.itemsize" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "7304e015-1f67-4920-a045-baf66c60e9df", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1125000" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image.nbytes" + ] + }, + { + "cell_type": "markdown", + "id": "bca272d8-c859-4c9f-ad8f-76b8ba2926bf", + "metadata": {}, + "source": [ + "### 数组对象的运算\n", + "\n", + "#### 算术运算\n", + "\n", + "1. 与标量运算\n", + "2. 与数组运算 - 两个数组形状相同" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "5bec613e-df64-4a14-82a8-5ffa05d8ec48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([11, 12, 13, 14, 15], dtype=int32)" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array1 + 10" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "c2c2001a-4743-44ba-93e6-31b089196e31", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 5, 10, 15],\n", + " [20, 25, 30]])" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2 * 5" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "575bb5dc-bb20-471e-9237-e500d2f3796a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 4, 9],\n", + " [16, 25, 36]])" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2 ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "82bd25ad-a422-46cb-aa1c-41dba47fd54b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 1, 3],\n", + " [4, 7, 2]])" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp1 = np.random.randint(1, 10, (2, 3))\n", + "temp1" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "2a2a9ce0-41ef-417b-a082-b1a8b15213c6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 2, 3, 6],\n", + " [ 8, 12, 8]])" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp1 + array2" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "id": "af6ac896-2992-4e78-8fcc-9d2a55ccd188", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 2, 9],\n", + " [16, 35, 12]])" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp1 * array2" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "1cf8cb80-3ba2-4b6a-8985-a4b63bdceda2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 1, 27],\n", + " [ 256, 16807, 64]])" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp1 ** array2" + ] + }, + { + "cell_type": "markdown", + "id": "f6f13e4c-754a-4017-8898-52b00c97a910", + "metadata": {}, + "source": [ + "#### 比较运算\n", + "\n", + "1. 与标量运算\n", + "2. 与数组运算" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "bd4f7a63-341a-4a8a-9042-a2c286e606c6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, False, False, True, True])" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array1 > 3" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "id": "68ebdc1a-ca87-439b-9aaf-bc59742c04f0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[False, False, False],\n", + " [ True, True, True]])" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2 > 3" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "9d292f4f-3067-4fce-85aa-b4787bb90d24", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[False, False, False],\n", + " [False, True, False]])" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp1 > array2" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "ea44e117-566f-4fad-a9ee-6ab380461a75", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ True, False, True],\n", + " [ True, False, False]])" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp1 == array2" + ] + }, + { + "cell_type": "markdown", + "id": "5e2ed3d9-72ba-42ac-a1c3-00f469d7a3bc", + "metadata": {}, + "source": [ + "#### 逻辑运算\n", + "\n", + "1. 与标量的运算\n", + "2. 与数组的运算" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "621c9302-b475-4151-8627-36001424a38d", + "metadata": {}, + "outputs": [], + "source": [ + "temp2 = np.array([True, False, True, False, True])\n", + "temp3 = np.array([True, False, False, False, True])" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "355c4862-058d-47a0-9d36-331881f26c6e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ True, False, True, False, True])" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp2 & True" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "711b9eda-9fe2-49b6-903c-3921507abafa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ True, True, True, True, True])" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp2 | True" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "id": "0ec28456-84a7-4161-8bc6-765c4410ca7a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ True, False, False, False, True])" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp2 & temp3" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "id": "11c56fd5-ae20-4e55-96c5-aacf2b4e3df1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ True, False, True, False, True])" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp2 | temp3" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "afcc3303-0705-42b0-a503-b3efbd68590a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, True, False, True, False])" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "~temp2" + ] + }, + { + "cell_type": "markdown", + "id": "df7b1f3f-4d89-45ea-9982-69ae9069dc8c", + "metadata": {}, + "source": [ + "#### 索引运算\n", + "\n", + "1. 普通索引 - 跟列表的索引运算类似\n", + "2. 花式索引 - 用列表或数组充当数组的索引\n", + "3. 布尔索引 - 用保存布尔值的数组充当索引\n", + "4. 切片索引 - 跟列表的切片运算类似" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "e845e1b3-d137-4832-b016-ed8d64c18a8f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([42, 49, 40, 75, 55, 99, 44, 80, 74])" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4 = np.random.randint(1, 100, 9)\n", + "temp4" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "a6b49f68-f43b-42be-aa8b-0bbe4ca52f79", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(99)" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[5]" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "35bc13a1-1ce8-4bf8-ba6a-310d145788da", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(99)" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[-4]" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "4f53346f-a086-400f-84a7-a7ce264826bd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([42, 49, 40, 75, 55, 99, 44, 80, 74])" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[5] = 99\n", + "temp4" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "0f6f4528-191c-4ca8-809d-6503d4076a53", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[95, 91, 74, 23, 37],\n", + " [90, 74, 38, 87, 24],\n", + " [ 9, 85, 23, 33, 36],\n", + " [86, 76, 57, 12, 22]])" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5 = np.random.randint(1, 100, (4, 5))\n", + "temp5" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "7bcbf6e0-7e2e-4e41-bab8-cd9b99e67ad3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(38)" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[1][2]" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "a7a9a1c8-ecbc-4e7e-9f4e-3a7e8dadb249", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(38)" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[1, 2]" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "979c64b0-a600-4229-859c-252bb597185d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[95, 91, 74, 23, 37],\n", + " [90, 74, 38, 87, 24],\n", + " [ 9, 85, 23, 33, 36],\n", + " [86, 76, 57, 12, 99]])" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[-1, -1] = 99\n", + "temp5" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "4a5823e6-d3ff-411e-8f06-858dbdac006b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[95, 91, 74, 23, 37],\n", + " [90, 74, 38, 87, 24],\n", + " [ 9, 85, 23, 33, 36],\n", + " [86, 55, 57, 12, 99]])" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[-1, 1] = 55\n", + "temp5" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "6282b8c7-d23a-4079-ad98-88bc606ff93f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[36, 33, 28],\n", + " [36, 33, 28],\n", + " [36, 33, 28],\n", + " ...,\n", + " [32, 31, 29],\n", + " [32, 31, 27],\n", + " [31, 32, 26]], dtype=uint8)" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "0f7e83f3-4ab2-44a6-b537-c32645fe2abc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([36, 33, 28], dtype=uint8)" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image[0, 0]" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "03477e7b-04f2-4de3-bd4d-f070b1983304", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.uint8(33)" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido_image[0, 0, 1]" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "0e4f6f3f-0cef-4e6a-89d9-d3281f59c5d8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([49, 49, 49, 40, 40, 80, 99, 99])" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 花式索引 - fancy index - 用放整数的列表或者数组充当数组的索引\n", + "temp4[[1, 1, 1, 2, 2, -2, -4, -4]]" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "1ead8a5d-f4c0-4f5a-8709-b072ba676118", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([23, 74, 74, 33, 23, 23, 23])" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[[0, 1, 1, 2, 0, 0, 0], [3, 1, 1, -2, -2, -2, -2]]" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "5e31efc0-8f1a-4a8d-ab85-8e1cc592d5d8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([42, 75, 99, 80])" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 布尔索引 - 用放布尔值的数组或列表充当数组的索引 - 实现数据筛选\n", + "temp4[[True, False, False, True, False, True, False, True, False]]" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "6145ef49-423f-40e0-acc7-8a8f897f4fb6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, False, False, True, False, True, False, True, True])" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4 > 70" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "d48715c6-c743-4457-9896-211d1ad74f97", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([75, 99, 80, 74])" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[temp4 > 70]" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "efe7652f-1a75-4eed-9b33-2d52dfd25626", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ True, False, True, False, False, False, True, True, True])" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4 % 2 == 0" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "id": "4f2d6b1b-259d-4721-8dc6-326be4a73d57", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([42, 40, 44, 80, 74])" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[temp4 % 2 == 0]" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "id": "261f57fd-e06a-4aca-8c84-e70b43b42795", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, False, False, False, False, False, False, True, True])" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(temp4 > 70) & (temp4 % 2 == 0)" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "id": "5a52756e-e275-4750-b559-708bbe8fc045", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([80, 74])" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[(temp4 > 70) & (temp4 % 2 == 0)]" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "51be708e-afc5-47b2-9392-6c53738eb7d1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([42, 40, 75, 99, 44, 80, 74])" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[(temp4 > 70) | (temp4 % 2 == 0)]" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "93bd6975-bf01-4801-81e7-fc0bf7b21285", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ True, True, True, False, False],\n", + " [ True, True, False, True, False],\n", + " [False, True, False, False, False],\n", + " [ True, False, False, False, True]])" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5 > 70" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "id": "df324e6a-85d1-40de-acce-bfbb35e8a4cf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([95, 91, 74, 90, 74, 87, 85, 86, 99])" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[temp5 > 70]" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "id": "6388f55b-40af-4cd1-abbc-29da10641580", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([74, 90, 74, 86])" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[(temp5 > 70) & (temp5 % 2 == 0)]" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "id": "e30c750e-5517-4314-b6d1-4fc28c5a454b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([42, 49, 40, 75, 55, 99, 44, 80, 74])" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "id": "6e294ace-b236-4554-b34c-6f2b00bd295f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([40, 75, 55, 99, 44])" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 切片索引 - slice\n", + "temp4[2:7]" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "id": "1519ca3a-9844-4b20-a00f-96b61780d998", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([40, 55, 44])" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 切片索引 - slice\n", + "temp4[2:7:2]" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "id": "9b96d847-5aec-4e7f-b6a7-48f4deecf454", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([44, 99, 55, 75, 40])" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp4[6:1:-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "id": "5dbcfa29-9930-471e-81d3-100f22e6293d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[95, 91, 74, 23, 37],\n", + " [90, 74, 38, 87, 24],\n", + " [ 9, 85, 23, 33, 36],\n", + " [86, 55, 57, 12, 99]])" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "id": "a1307d26-d1a3-4201-9ffa-314a81300712", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[74, 38, 87],\n", + " [85, 23, 33]])" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[1:3, 1:4]" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "id": "e95ddb0d-ee31-4690-a91b-a0f0137ce07a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[33, 36],\n", + " [12, 99]])" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[2:, 3:]" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "id": "d69e9936-049a-4a66-af22-bb7feff1b9e7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[23, 33],\n", + " [57, 12]])" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[2:, 2:4]" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "befe1ce3-4742-4fd9-97de-3d9608ccf4c1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[95, 91, 74],\n", + " [90, 74, 38],\n", + " [ 9, 85, 23]])" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[:3, :3]" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "ba3fcdcd-f999-41d4-9e96-858dc0f3d70d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[95, 91, 74],\n", + " [90, 74, 38],\n", + " [ 9, 85, 23],\n", + " [86, 55, 57]])" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp5[:, :3]" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "753b5012-78fd-4a21-997e-132c5a15f636", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgAAAABACAYAAABsv8+/AAAAE3RFWHRUaXRsZQBncmF5IGNvbG9ybWFw9iBr6wAAABl0RVh0RGVzY3JpcHRpb24AZ3JheSBjb2xvcm1hcH2S+3MAAAAwdEVYdEF1dGhvcgBNYXRwbG90bGliIHYzLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZ2GZxVMAAAAydEVYdFNvZnR3YXJlAE1hdHBsb3RsaWIgdjMuOS4yLCBodHRwczovL21hdHBsb3RsaWIub3JnTz9adAAAAUBJREFUeJzt1rENQjEQBcFn998zRBRA8CXEziQO7POle7a9tu2cs/L5ce9N3n/7zvx/zj/9n/322/87++8AgBwBAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACBIAABAkAAAgCABAABBAgAAggQAAAQJAAAIEgAAECQAACDoDY2LBHzusuGnAAAAAElFTkSuQmCC", + "text/html": [ + "
gray
\"gray
under
bad
over
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plt.get_cmap('gray')" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "id": "2de0bf4c-61f4-48d9-a12b-9accbb883962", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[False, False, False, ..., False, False, False],\n", + " [False, False, False, ..., False, False, False],\n", + " [False, False, False, ..., False, False, False],\n", + " ...,\n", + " [ True, True, True, ..., False, False, False],\n", + " [ True, True, True, ..., False, False, False],\n", + " [ True, True, True, ..., False, False, False]])" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.mean(guido_image, axis=2) >= 128" + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "id": "780b60d2-c634-4294-baea-00e696143942", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:49:50.432942\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# 创建画布\n", + "plt.figure(figsize=(15, 9))\n", + "\n", + "# 原图\n", + "# 创建坐标系\n", + "plt.subplot(2, 4, 1)\n", + "plt.imshow(guido_image)\n", + "# 垂直翻转\n", + "plt.subplot(2, 4, 2)\n", + "plt.imshow(guido_image[::-1])\n", + "# 水平翻转\n", + "plt.subplot(2, 4, 3)\n", + "plt.imshow(guido_image[:, ::-1])\n", + "# 抠图\n", + "plt.subplot(2, 4, 4)\n", + "plt.imshow(guido_image[30:350, 80:310])\n", + "# 降采样\n", + "plt.subplot(2, 4, 5)\n", + "plt.imshow(guido_image[::10, ::10])\n", + "# 反色\n", + "plt.subplot(2, 4, 6)\n", + "plt.imshow(guido_image[:, :, ::-1])\n", + "# 灰度图\n", + "plt.subplot(2, 4, 7)\n", + "plt.imshow(guido_image[:, :, 0], cmap=plt.cm.gray)\n", + "# 二值化\n", + "plt.subplot(2, 4, 8)\n", + "plt.imshow(np.mean(guido_image, axis=2) >= 128, cmap='gray')\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "id": "abe85cca-abf4-4805-9e69-b2971021e741", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:54:56.111360\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# 局部马赛克效果\n", + "guido_image_copy = guido_image.copy()\n", + "\n", + "n = 12\n", + "\n", + "for i in range(120, 350, n):\n", + " for j in range(120, 310, n):\n", + " color = guido_image_copy[i, j]\n", + " guido_image_copy[i: i + n, j: j + n] = color\n", + "\n", + "plt.imshow(guido_image_copy)" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "id": "0e1f177e-db9d-4585-8c15-2cb5a928ccd4", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install pillow" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "id": "0143bdbe-afde-4741-8d11-2bcbe34de477", + "metadata": {}, + "outputs": [], + "source": [ + "# from PIL import Image\n", + "\n", + "# 灰度图\n", + "# Image.fromarray(guido_image[:, :, 0]).show()" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "id": "487ec2f5-97bc-413f-9a06-c8f6875ebab8", + "metadata": {}, + "outputs": [], + "source": [ + "# from PIL import ImageFilter\n", + "\n", + "# 滤镜效果\n", + "# Image.fromarray(guido_image).filter(ImageFilter.CONTOUR).show()" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "id": "15aa7ec2-8bff-45fb-89b8-19790838aff3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(750, 500, 3)" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "obama_image = plt.imread('res/obama.jpg')\n", + "obama_image.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "id": "e1df160a-3e97-4594-a0ba-43bbd3c384ae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:45:18.229325\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(obama_image)" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "id": "6c57d787-8a35-4a56-8b10-ceaa08b49612", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(750, 500, 3)" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp6 = (guido_image * 0.6 + obama_image * 0.4).astype('u1')\n", + "temp6.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "id": "54ece4ed-4346-458a-8b57-58a9930c6dce", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 116, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:45:18.326714\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(temp6)" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "id": "a56d7ce5-ec59-4d35-bbfd-52b33069c6f5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-19T22:45:18.419184\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "temp7 = np.random.randint(0, 256, (16, 16, 3))\n", + "plt.imshow(temp7)" + ] + }, + { + "cell_type": "markdown", + "id": "6649825a-9c1e-4190-adc4-a98c8c97a553", + "metadata": {}, + "source": [ + "### 数组对象的方法\n", + "\n", + "1. 获取描述性统计信息\n", + " - `sum`\n", + " - `cumsum` / `cumprod`\n", + " - `mean`\n", + " - `np.median`\n", + " - `stats.mode`\n", + " - `max`\n", + " - `min`\n", + " - `ptp`\n", + " - `np.quantile` / `stats.iqr`\n", + " - `var`\n", + " - `std`\n", + " - `stats.variation`\n", + " - `stats.skew`\n", + " - `stats.kurtosis`\n", + "2. 其他相关方法\n", + " - `round`\n", + " - `argmax` / `argmin`\n", + " - `nonzero`\n", + " - `copy` / `view`\n", + " - `astype`\n", + " - `clip`\n", + " - `reshape` / `resize`\n", + " - `dump` / `np.load`\n", + " - `tofile`\n", + " - `fill`\n", + " - `flatten` / `ravel`\n", + " - `sort` / `argsort`\n", + " - `swapaxes` / `transpose`\n", + " - `tolist`" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "id": "22e444ec-9b9e-4807-986a-1b3b229d87af", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install -U scipy" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "id": "4d7745c2-30fa-4a15-bae5-39b9597c1462", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy import stats" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "id": "d8b8fbbc-43d5-467b-a32a-116639baedac", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([76, 81, 85, 79, 83, 82, 91, 80, 87, 86, 70, 82, 84, 77, 83, 85, 76,\n", + " 74, 80, 80, 82, 76, 68, 77, 80, 78, 77, 73, 81, 76, 85, 81, 84, 85,\n", + " 74, 84, 70, 76, 78, 80, 86, 75, 94, 79, 84, 78, 72, 86, 74, 68])" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scores1 = np.fromstring(\n", + " '76, 81, 85, 79, 83, 82, 91, 80, 87, 86, '\n", + " '70, 82, 84, 77, 83, 85, 76, 74, 80, 80, '\n", + " '82, 76, 68, 77, 80, 78, 77, 73, 81, 76, '\n", + " '85, 81, 84, 85, 74, 84, 70, 76, 78, 80, '\n", + " '86, 75, 94, 79, 84, 78, 72, 86, 74, 68', \n", + " sep=',',\n", + " dtype='i8'\n", + ")\n", + "scores1" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "id": "7bb13bb7-ba31-459c-85ce-ef0dc85abd96", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(3982)" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 求和\n", + "scores1.sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "f7d4eb31-33f0-436e-a53b-98276d22ddef", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(3982)" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.sum(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "id": "b450c23a-26b8-4766-b15a-5470efb8e37a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 76, 157, 242, 321, 404, 486, 577, 657, 744, 830, 900,\n", + " 982, 1066, 1143, 1226, 1311, 1387, 1461, 1541, 1621, 1703, 1779,\n", + " 1847, 1924, 2004, 2082, 2159, 2232, 2313, 2389, 2474, 2555, 2639,\n", + " 2724, 2798, 2882, 2952, 3028, 3106, 3186, 3272, 3347, 3441, 3520,\n", + " 3604, 3682, 3754, 3840, 3914, 3982])" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 累积和 - cumulative sum\n", + "scores1.cumsum()" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "id": "882ff41a-4d0f-4a15-adf3-272acc398bda", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 76, 157, 242, 321, 404, 486, 577, 657, 744, 830, 900,\n", + " 982, 1066, 1143, 1226, 1311, 1387, 1461, 1541, 1621, 1703, 1779,\n", + " 1847, 1924, 2004, 2082, 2159, 2232, 2313, 2389, 2474, 2555, 2639,\n", + " 2724, 2798, 2882, 2952, 3028, 3106, 3186, 3272, 3347, 3441, 3520,\n", + " 3604, 3682, 3754, 3840, 3914, 3982])" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.cumsum(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "id": "5e43fa2e-c30e-40c3-8159-89819e5e368f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(79.64)" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 算术平均\n", + "scores1.mean()" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "id": "2f59ffef-619d-4a03-a496-3972d13ee33e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(79.64)" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.mean(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "id": "3bfdf16c-894e-47e8-925c-604418cc0eb2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(79.44812732667022)" + ] + }, + "execution_count": 127, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 几何平均\n", + "stats.gmean(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "id": "d73ce1b5-3956-4c12-9237-396ffdec44fd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(79.25499854665681)" + ] + }, + "execution_count": 128, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 调和平均\n", + "stats.hmean(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "id": "6d165492-6d0c-4f74-a2f1-75c95ca13b2d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(79.58695652173913)" + ] + }, + "execution_count": 129, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 去尾平均\n", + "stats.tmean(scores1, [70, 90])" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "99fbc4f2-220c-4ea0-9d24-8640c66c0115", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(79.58695652173913)" + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.mean(scores1[(scores1 >= 70) & (scores1 <= 90)])" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "id": "8b536052-4a67-43e5-9e0a-6005bd73a66c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.float64(80.0)" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 中位数\n", + "np.median(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "id": "390f8e61-a108-497d-b3cf-54bd25ae3a0e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(np.int64(76), np.int64(5))" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 众数\n", + "result = stats.mode(scores1)\n", + "result.mode, result.count" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "id": "bc729130-fdfa-47be-8a5c-ce7a474ab6ea", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(94)" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 最大值\n", + "scores1.max()" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "id": "f02f74e8-e27f-4169-8fda-2ebe4504f0d4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(94)" + ] + }, + "execution_count": 134, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.amax(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "id": "0a32b324-df80-43c8-b8b0-12b36218ef2e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(68)" + ] + }, + "execution_count": 135, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 最小值\n", + "scores1.min()" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "id": "95680bd0-075a-4832-b7b9-76cea6653e2b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.int64(68)" + ] + }, + "execution_count": 136, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.amin(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00d3a61a-d22b-4ee3-9327-8197abee91fc", + "metadata": {}, + "outputs": [], + "source": [ + "# 全距(极差)\n", + "np.ptp(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6fd57ed-7bb3-4593-90ce-2a79a9ea8d2e", + "metadata": {}, + "outputs": [], + "source": [ + "# 四分位距离\n", + "q1, q3 = np.quantile(scores1, [0.25, 0.75])\n", + "q3 - q1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3668ed4b-4205-4f6a-9a9d-6135a238515a", + "metadata": {}, + "outputs": [], + "source": [ + "# inter-quartile range\n", + "stats.iqr(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a3c5ed8-0331-4c94-8cac-6b947c7765fd", + "metadata": {}, + "outputs": [], + "source": [ + "# 总体方差\n", + "scores1.var()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2cdb1d1-179a-425f-930f-2ef7dda46a79", + "metadata": {}, + "outputs": [], + "source": [ + "np.var(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "493d242d-fb94-4ed9-b316-7f722f908d99", + "metadata": {}, + "outputs": [], + "source": [ + "# 样本方差\n", + "scores1.var(ddof=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b70dac6d-24f1-473e-bb27-200384202db0", + "metadata": {}, + "outputs": [], + "source": [ + "np.var(scores1, ddof=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0670037e-0ae2-461f-b01a-0f47ed036813", + "metadata": {}, + "outputs": [], + "source": [ + "# 总体标准差\n", + "np.std(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22e3361d-3bbf-4175-94e1-719ca022e5dd", + "metadata": {}, + "outputs": [], + "source": [ + "# 样本标准差\n", + "np.std(scores1, ddof=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9b8db0c-6e78-4efb-a494-62977ce1e4e7", + "metadata": {}, + "outputs": [], + "source": [ + "# 变异系数\n", + "stats.variation(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "029b2ccc-0dbb-460c-b84e-ea2b34cca3fb", + "metadata": {}, + "outputs": [], + "source": [ + "# 偏态系数\n", + "stats.skew(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c35b17a1-5abc-4444-acbf-d757de430dcd", + "metadata": {}, + "outputs": [], + "source": [ + "# 峰度系数\n", + "stats.kurtosis(scores1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9fba00d6-866e-4ab1-9caf-18b17fe08440", + "metadata": {}, + "outputs": [], + "source": [ + "# 箱线图\n", + "plt.boxplot(scores1, showmeans=True, whis=1.5)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1cd46156-6a31-47c1-b3c6-4db92d91ac8c", + "metadata": {}, + "outputs": [], + "source": [ + "# 直方图\n", + "plt.hist(scores1, bins=6)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c5340e8-ed88-432f-8656-f987c0972b80", + "metadata": {}, + "outputs": [], + "source": [ + "# 设置随机数的种子\n", + "np.random.seed(12)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2102845-986b-4e22-9a73-0964af537386", + "metadata": {}, + "outputs": [], + "source": [ + "scores2 = np.random.randint(60, 101, (10, 3))\n", + "scores2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e9f4921-6001-45e6-bfe0-aca2fe2aa03e", + "metadata": {}, + "outputs": [], + "source": [ + "scores2.mean()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e116ac94-7cc2-48bc-b563-2e3ccd262378", + "metadata": {}, + "outputs": [], + "source": [ + "scores2.mean(axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b7fc404-06cf-4c3e-9ef2-b67bd1d3e517", + "metadata": {}, + "outputs": [], + "source": [ + "scores2.mean(axis=1).round(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3cf52cc-48ca-49d5-bbe7-6d977e8e8343", + "metadata": {}, + "outputs": [], + "source": [ + "# axis=0 - 默认值 - 沿着0轴计算\n", + "stats.describe(scores2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8b60634-56cc-489e-b898-792925aa79a9", + "metadata": {}, + "outputs": [], + "source": [ + "# axis=None - 不沿着任何一个轴计算\n", + "stats.describe(scores2, axis=None)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c0ba4905-ae46-40d6-a6a3-b97f830fbee5", + "metadata": {}, + "outputs": [], + "source": [ + "# axis=1 - 沿着1轴计算\n", + "result = stats.describe(scores2, axis=1)\n", + "result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9adade58-2bf1-4c46-be0d-0ef1682382f4", + "metadata": {}, + "outputs": [], + "source": [ + "result.mean.round(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24bca7b9-af35-4943-94f9-90ce5851d136", + "metadata": {}, + "outputs": [], + "source": [ + "result.variance.round(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e90cd926-9f9d-4701-8f70-dc0f13ad24d9", + "metadata": {}, + "outputs": [], + "source": [ + "plt.boxplot(scores2, showmeans=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b58cd75-8df1-4593-a4cd-19c32f821f63", + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(14)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6d792920-4ec1-4817-9e5a-b1d975dcb9a7", + "metadata": {}, + "outputs": [], + "source": [ + "temp8 = np.random.random(10)\n", + "temp8" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0385af53-d682-42a7-84da-a16b4353a6ad", + "metadata": {}, + "outputs": [], + "source": [ + "# 四舍五入\n", + "temp9 = temp8.round(1)\n", + "temp9" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7864d43f-b4ce-41d6-b8e3-cd646a12a0c6", + "metadata": {}, + "outputs": [], + "source": [ + "# 最大值的索引\n", + "temp8.argmax()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "336bc616-896f-4186-ad4d-5f6baa048564", + "metadata": {}, + "outputs": [], + "source": [ + "# 最小值的索引\n", + "temp8.argmin()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29cab4c1-1e56-4b19-a3b2-aeb30be03a88", + "metadata": {}, + "outputs": [], + "source": [ + "# 调整数组的形状\n", + "temp10 = temp8.reshape((5, 2))\n", + "# temp10 = temp8.reshape((5, 2)).copy()\n", + "temp10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b8e2fe8-b917-4d40-ad48-d3935a1ab262", + "metadata": {}, + "outputs": [], + "source": [ + "temp10.base" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e266d969-8bd5-4572-9544-2569fd718156", + "metadata": {}, + "outputs": [], + "source": [ + "temp10.flags" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b7e5389-cdc3-4995-99f4-dc1386a5e291", + "metadata": {}, + "outputs": [], + "source": [ + "temp10.base is temp8" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f6f2561-5392-4887-b0d2-3f639fb8be43", + "metadata": {}, + "outputs": [], + "source": [ + "temp10[2, 1] = 0.999999\n", + "temp10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b739b914-57fd-4c3f-a106-a359e79391f9", + "metadata": {}, + "outputs": [], + "source": [ + "temp8" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb79055b-d7d6-4602-b960-890d33928e04", + "metadata": {}, + "outputs": [], + "source": [ + "temp8[3] = 0.0001\n", + "temp8" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f2932d9-45d3-427c-acef-a8ecc468747d", + "metadata": {}, + "outputs": [], + "source": [ + "temp10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "089a7d87-ccbc-47c8-a934-15a2eb298242", + "metadata": {}, + "outputs": [], + "source": [ + "# 调整数组大小\n", + "temp8.resize((3, 5), refcheck=False)\n", + "temp8.round(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6af25425-046a-4f19-a5a5-235d6fd17753", + "metadata": {}, + "outputs": [], + "source": [ + "temp11 = np.resize(temp8, (4, 5)).round(1)\n", + "temp11" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "260a3d51-8041-4515-a611-971267a0be8c", + "metadata": {}, + "outputs": [], + "source": [ + "# 非零元素的索引\n", + "temp9.nonzero()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ab48494-d866-49ec-a214-586f1fa8beb2", + "metadata": {}, + "outputs": [], + "source": [ + "# 类型转换\n", + "temp12 = np.random.randint(-100, 101, 10)\n", + "temp12" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69b3c1e2-cca1-45d5-9d0c-2ab51ab66014", + "metadata": {}, + "outputs": [], + "source": [ + "temp12.astype(np.float64)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a7c4ce2-2838-4537-b70a-10f922f2806a", + "metadata": {}, + "outputs": [], + "source": [ + "temp12.astype('f8')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f15abc61-401e-4198-84ab-4aa086cfdf75", + "metadata": {}, + "outputs": [], + "source": [ + "temp12.astype('i1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ce590c02-4eab-4d4c-830c-07e0e887a771", + "metadata": {}, + "outputs": [], + "source": [ + "temp13 = temp12.astype('u1')\n", + "temp13" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c6b04d0-21e1-4be3-a927-095381341f57", + "metadata": {}, + "outputs": [], + "source": [ + "temp13.flags" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "711a115b-7a7d-48c1-b9de-b51673f89ab9", + "metadata": {}, + "outputs": [], + "source": [ + "temp12.astype('U')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "831f8c9c-f949-442c-ac51-66b3bfc59e1b", + "metadata": {}, + "outputs": [], + "source": [ + "# 修剪\n", + "temp9.clip(min=0.3, max=0.7)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d40724a4-a7fa-42c1-9ecc-3207579e7d7a", + "metadata": {}, + "outputs": [], + "source": [ + "# 将数组持久化到(文本)文件\n", + "temp11.tofile('temp11.txt', sep=',')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4246bae6-ab59-4ee0-ae8d-7d2cc3f60d97", + "metadata": {}, + "outputs": [], + "source": [ + "temp13 = np.fromfile('temp11.txt', sep=',').reshape(4, 5)\n", + "temp13" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9d38dba-acb8-4745-be3f-76e7b2605c09", + "metadata": {}, + "outputs": [], + "source": [ + "# 将数组持久化到(二进制)文件\n", + "temp11.dump('temp11')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db38d63c-e279-4002-bf02-fed4f615319a", + "metadata": {}, + "outputs": [], + "source": [ + "# 从二进制文件(pickle序列化)中加载数组\n", + "temp14 = np.load('temp11', allow_pickle=True)\n", + "temp14" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fc26c0e-b518-4b23-a019-9e8e11e363e2", + "metadata": {}, + "outputs": [], + "source": [ + "temp15 = np.random.randint(1, 100, (2, 3, 4))\n", + "temp15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1884ac0d-65f9-410d-987f-7235ccd54d0f", + "metadata": {}, + "outputs": [], + "source": [ + "# 扁平化\n", + "temp16 = temp15.flatten()\n", + "temp16" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "369c19c8-ef7c-4740-b8ba-cf47f63f9fd7", + "metadata": {}, + "outputs": [], + "source": [ + "# 扁平化\n", + "temp17 = temp15.ravel()\n", + "temp17" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d7411df-a939-41f6-bee0-213b555ed666", + "metadata": {}, + "outputs": [], + "source": [ + "temp16.base is temp15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "980f80da-d3d6-45c9-86d0-733e0098e8f0", + "metadata": {}, + "outputs": [], + "source": [ + "temp16.flags" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4db6df5-e0c4-4f7b-bc0a-50655c036e77", + "metadata": {}, + "outputs": [], + "source": [ + "temp17.base is temp15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7339bcf2-a29f-4cf6-ac5f-8a81eb655698", + "metadata": {}, + "outputs": [], + "source": [ + "temp17.flags" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06d8dee5-528e-4345-b5a7-ddc0413cda09", + "metadata": {}, + "outputs": [], + "source": [ + "temp16[0] = 999\n", + "temp16" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cfa3191-5441-4de7-a300-6bd8aa11cb30", + "metadata": {}, + "outputs": [], + "source": [ + "temp15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a4ce0ee-2971-48b3-8834-9113464cf9af", + "metadata": {}, + "outputs": [], + "source": [ + "temp17[0] = 88\n", + "temp17" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7dd7fda5-d25e-4a9a-9202-9a5140fc3439", + "metadata": {}, + "outputs": [], + "source": [ + "temp15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe00bb4c-1e19-4988-8020-f2f4162840f2", + "metadata": {}, + "outputs": [], + "source": [ + "# 排序 - 返回排序后的新数组\n", + "np.sort(temp16)[::-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8fe37ba0-9bce-4bb1-bddc-3100a755439d", + "metadata": {}, + "outputs": [], + "source": [ + "# 排序 - 就地排序\n", + "temp16.sort()\n", + "temp16" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "afef80f1-5f26-46d3-9589-7eea82b224cc", + "metadata": {}, + "outputs": [], + "source": [ + "temp18 = np.random.randint(1, 100, 10)\n", + "temp18" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43f1d0f6-2a74-4512-a6ee-d285de2ee2aa", + "metadata": {}, + "outputs": [], + "source": [ + "# 给出索引的顺序 - 花式索引\n", + "temp18[temp18.argsort()]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23fc033c-a649-4da8-a0f8-1c7739e3996c", + "metadata": {}, + "outputs": [], + "source": [ + "# 转置\n", + "temp11.transpose()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ac95b32-c9ce-404b-a26e-1e8a2351a44b", + "metadata": {}, + "outputs": [], + "source": [ + "temp11.T" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "796f513d-211f-4915-969c-1850d4f569a2", + "metadata": {}, + "outputs": [], + "source": [ + "# 交换轴\n", + "temp11.swapaxes(0, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d9d0207-45d9-4ec0-bcef-9e7072501241", + "metadata": {}, + "outputs": [], + "source": [ + "temp15" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0fd160f1-710f-4591-853a-d2e960493bc4", + "metadata": {}, + "outputs": [], + "source": [ + "temp15.swapaxes(0, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77569990-b8f2-4ff1-b1f6-d17a942f1a32", + "metadata": {}, + "outputs": [], + "source": [ + "temp15.swapaxes(1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5970d5d4-f955-4af8-8c54-46a7fff9d0f9", + "metadata": {}, + "outputs": [], + "source": [ + "# 将数组处理成列表\n", + "list1 = temp16.tolist()\n", + "print(list1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c3e62e2-aaef-4250-a561-0f28dd51bf79", + "metadata": {}, + "outputs": [], + "source": [ + "list2 = temp11.tolist()\n", + "print(list2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37a0a054-34e3-4b2a-a07d-72a9599f012f", + "metadata": {}, + "outputs": [], + "source": [ + "list3 = temp15.tolist()\n", + "print(list3)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Day66-80/code/day03.ipynb b/Day66-80/code/day03.ipynb new file mode 100644 index 0000000..8f0cb77 --- /dev/null +++ b/Day66-80/code/day03.ipynb @@ -0,0 +1,3385 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6fc07f67-318b-4d79-8d4e-4eb8a2c61be2", + "metadata": {}, + "source": [ + "## NumPy进阶" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a9d74703-47d5-44f4-8566-eb7d5476c792", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d139c565-6bf2-4bf6-9d66-d2755b29d1db", + "metadata": {}, + "outputs": [], + "source": [ + "%config InlineBackend.figure_format = 'svg'\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "id": "d41a57e5-6009-455b-aff9-4f96682423fc", + "metadata": {}, + "source": [ + "### NumPy中的函数\n", + "\n", + "#### 通用一元函数" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5f881886-8aca-40cb-a9f3-4514e28b8fe3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1., 2., 3., inf, nan, -inf, nan, 5.])" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# inf - infinity\n", + "# nan - not a number\n", + "array1 = np.array([1, 2, 3, np.inf, np.nan, -np.inf, np.nan, 5])\n", + "array1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b6e891cc-035c-4e98-9406-1e78e3623e76", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dtype('float64')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array1.dtype" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "674995a2-e50a-45a6-b1ad-7a9f88331dd0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, False, False, False, True, False, True, False])" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.isnan(array1)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "358641bc-510c-4f1b-9df7-5dd54de47978", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1., 2., 3., inf, -inf, 5.])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array1[~np.isnan(array1)]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "20030d7d-822e-45c7-b962-3aaf706e133c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ True, True, True, False, False, False, False, True])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.isfinite(array1)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "357cc22a-7acf-46ee-9523-631134dc8eae", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1., 2., 3., 5.])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array1[np.isfinite(array1)]" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "c38f23a4-7d72-4ce4-9bc4-9f8cd9433fa8", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(0.5, 10, 72)\n", + "y1 = np.sin(x)\n", + "y2 = np.log2(x)\n", + "y3 = np.sqrt(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "08a272bc-8765-455b-b0d0-700872071cf4", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2024-09-22T23:37:54.080072\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.9.2, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# 定制画布\n", + "plt.figure(figsize=(8, 4))\n", + "# 绘制折线图\n", + "plt.plot(x, y1, marker='.', label='$y=sin(x)$')\n", + "plt.plot(x, y2, label='$y=log_{2}x$', linewidth=3, color='#9c9c9c')\n", + "plt.plot(x, y3, label='$y=\\sqrt{x}$', linestyle='-.', linewidth=0.5)\n", + "# 显示图例\n", + "plt.legend(loc='center right')\n", + "# 显示图表\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0b292f56-dab7-469e-89ed-0fb2114902aa", + "metadata": {}, + "source": [ + "#### 通用二元函数" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "8b67932a-481e-4e2d-9d83-00994a01d959", + "metadata": {}, + "outputs": [], + "source": [ + "array2 = np.array([0.1 + 0.2, 0.1 + 0.2 + 0.3])\n", + "array3 = np.array([0.3, 0.6])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "23581a64-7b02-4f3f-8a5f-ea49ec20e48a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, False])" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "array2 == array3" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "ddcb612c-c7aa-44c6-b8d3-1ee123fba534", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.False_" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.all(array2 == array3)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "40d454ad-8c60-4132-8dde-10726180e552", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 比较两个数组元素是否(几乎)完全相等 - 有误差容忍度\n", + "np.allclose(array2, array3)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f1aab287-9d50-4b32-94fa-26d7f183fde3", + "metadata": {}, + "outputs": [], + "source": [ + "array4 = np.array([1, 2, 3, 4, 5, 6])\n", + "array5 = np.array([2, 4, 6, 8, 10])" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ea25f2ad-e007-486b-a402-eb3436c9346c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([2, 4, 6])" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 交集\n", + "np.intersect1d(array4, array5)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "e4d2116c-c895-4597-95dc-fda67a1c99a8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 2, 3, 4, 5, 6, 8, 10])" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 并集\n", + "np.union1d(array4, array5)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "5348c4f2-4222-4904-bd07-a3c85e62e4c7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 3, 5])" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 差集\n", + "np.setdiff1d(array4, array5)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "9cd3f3e5-a986-469f-97ba-c739aa4b8577", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 3, 5, 8, 10])" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 对称差\n", + "np.setxor1d(array4, array5)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "78eb3a98-5992-452e-ab13-eaf578cab7a0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([False, True, False, True, False, True])" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 成员运算\n", + "# np.in1d(array4, array5)\n", + "np.isin(array4, array5)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "c7bbc624-dfdc-41d9-bfa2-7a4f3a387bce", + "metadata": {}, + "outputs": [], + "source": [ + "# 杰卡德相似度\n", + "user_a = np.array(['平板电脑', '尿不湿', '手机', '键盘', '手机支架', '奶瓶', '婴儿辅食', '基围虾', '巴沙鱼', '生抽', '沙拉酱'])\n", + "user_b = np.array(['平板电脑', '键盘', '充电宝', '补光灯', '生抽', '散热器', '笔记本电脑', '双肩包', '登山杖', '露营帐篷', '睡袋'])\n", + "user_c = np.array(['沐浴露', '维C泡腾片', '牛奶', '尿不湿', '平板电脑', '奶瓶', '婴儿辅食', '手机', '磨牙棒', '生抽', '基围虾'])" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "c4132979-1ef5-4e2b-93a4-2d6bfc66f38b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.15789473684210525" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.intersect1d(user_a, user_b).size / np.union1d(user_a, user_b).size" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "46dda506-908b-405a-8e9b-c14090da05b7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.4666666666666667" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.intersect1d(user_a, user_c).size / np.union1d(user_a, user_c).size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb5435f8-b8d4-4b37-88fb-13149a62660e", + "metadata": {}, + "outputs": [], + "source": [ + "np.setdiff1d(user_a, user_c)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8366b08-779d-4369-9f27-a9b4b9125782", + "metadata": {}, + "outputs": [], + "source": [ + "np.setdiff1d(user_c, user_a)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1ce07b5-ec70-4512-814a-e210148ed205", + "metadata": {}, + "outputs": [], + "source": [ + "# 余弦相似度\n", + "user = np.array([5, 1, 3])\n", + "mov1 = np.array([4, 5, 1])\n", + "mov2 = np.array([5, 1, 5])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ab70c0f-cfe7-4e10-b162-0feefb36f884", + "metadata": {}, + "outputs": [], + "source": [ + "# linear algebra\n", + "# np.dot - 点积\n", + "# np.linalg.norm - 模长\n", + "np.dot(user, mov1) / (np.linalg.norm(user) * np.linalg.norm(mov1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21a1caaf-2cf4-4cf8-836c-d244f4133098", + "metadata": {}, + "outputs": [], + "source": [ + "# np.arcos - 反余弦函数 - 弧度\n", + "# np.degrees - 弧度换算角度\n", + "np.degrees(np.arccos(np.dot(user, mov1) / (np.linalg.norm(user) * np.linalg.norm(mov1))))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b90ecb1-67d0-48be-84c5-54bf212f1292", + "metadata": {}, + "outputs": [], + "source": [ + "np.degrees(np.arccos(np.dot(user, mov2) / (np.linalg.norm(user) * np.linalg.norm(mov2))))" + ] + }, + { + "cell_type": "markdown", + "id": "c81c6238-f28c-44e8-ac54-94a69f5a6c4a", + "metadata": {}, + "source": [ + "#### 其他常用函数" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1df2d086-58cd-4324-b2cf-98d8d971a4d7", + "metadata": {}, + "outputs": [], + "source": [ + "array6 = np.array([1, 2, 3, 1, 1, 2, 2, 4, 5, 7, 3, 6, 6])\n", + "array6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70df0735-6680-4996-8cc3-10ac5d9102ab", + "metadata": {}, + "outputs": [], + "source": [ + "# 去重\n", + "array7 = np.unique(array6)\n", + "array7" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79ad92e0-da87-401d-aa70-0514a5b61d0e", + "metadata": {}, + "outputs": [], + "source": [ + "array8 = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])\n", + "array9 = np.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b099b5ee-d7df-4864-89f3-bb1c7a748a59", + "metadata": {}, + "outputs": [], + "source": [ + "# 在0轴方向(垂直)堆叠 - vertical\n", + "array10 = np.vstack((array8, array9))\n", + "array10" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "204b8e34-43e3-4a4f-9e8c-8734e72a041f", + "metadata": {}, + "outputs": [], + "source": [ + "# 在1轴的方向堆叠 - horizontal\n", + "np.hstack((array8, array9))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3301cc02-3ecf-4516-b2ff-e0fa6a9041c7", + "metadata": {}, + "outputs": [], + "source": [ + "# 数组的拼接\n", + "np.concatenate((array8, array9), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c956190c-1382-4416-9ab2-09a19f5567f6", + "metadata": {}, + "outputs": [], + "source": [ + "# 堆叠出更高维的数组\n", + "np.stack((array8, array9), axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a956be87-65e9-4391-b2be-bfbaaab7e7dc", + "metadata": {}, + "outputs": [], + "source": [ + "np.stack((array8, array9), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df88bd16-64c3-40af-adba-f7faed516159", + "metadata": {}, + "outputs": [], + "source": [ + "# 将一个数组拆分成多个数组\n", + "np.vsplit(array10, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "592ffc62-2a47-48bc-8789-3cad653d2893", + "metadata": {}, + "outputs": [], + "source": [ + "# 追加元素\n", + "np.append(array6, [10, 11, 12])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a3355ca-1ee7-4d8f-8567-c10fed5055f6", + "metadata": {}, + "outputs": [], + "source": [ + "# 插入元素\n", + "np.insert(array6, 1, [10, 20])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b824b66-7471-4669-aa99-8f62a6b8cb2b", + "metadata": {}, + "outputs": [], + "source": [ + "array11 = np.random.randint(1, 100, 10)\n", + "array11" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ff82b6f-9a96-40c2-a4d9-4aaec7d42e9a", + "metadata": {}, + "outputs": [], + "source": [ + "# 抽取元素 - 相当于布尔索引的作用\n", + "np.extract(array11 < 50, array11)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc00dac0-0b97-435c-960d-06c141de0b78", + "metadata": {}, + "outputs": [], + "source": [ + "# 给出一组条件和对应的处理数据的表达式,满足条件就执行对应的表达式,不满足条件取默认值\n", + "np.select([array11 < 30, array11 > 50], [array11 * 10, array11 // 10], default=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d7b8700-a431-4da4-99c6-54eda9f065dd", + "metadata": {}, + "outputs": [], + "source": [ + "# 给出一个条件和两个表达式,满足条件执行表达式1,不满足条件执行表达式2\n", + "np.where(array11 < 50, array11 * 10, array11 // 10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc672588-f36b-4951-902c-6b70ef83d1af", + "metadata": {}, + "outputs": [], + "source": [ + "array11" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa96b613-cef4-475e-b88c-f6ee8194ef4c", + "metadata": {}, + "outputs": [], + "source": [ + "# 滚动数组元素\n", + "np.roll(array11, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bfeed7b4-d835-4a1a-9858-d27d4bc363c3", + "metadata": {}, + "outputs": [], + "source": [ + "np.roll(array11, -2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52b9da9d-dd2b-4fea-984c-6d4b94efedab", + "metadata": {}, + "outputs": [], + "source": [ + "np.roll(array10, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53aa8382-51e2-4634-bb9f-4ff84ddaef60", + "metadata": {}, + "outputs": [], + "source": [ + "np.roll(array10, 2, axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f2de13b-2773-4a87-a854-ee9486dfcc0d", + "metadata": {}, + "outputs": [], + "source": [ + "array12 = np.arange(1, 10).reshape((3, 3))\n", + "array12" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3808483f-2811-45fd-adbb-a10bcc9d7dc6", + "metadata": {}, + "outputs": [], + "source": [ + "np.roll(array12, 2, axis=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f6d8d16-2e35-4926-ba13-d5e9cf77660d", + "metadata": {}, + "outputs": [], + "source": [ + "np.roll(array12, 1, axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29b76e3a-8414-4658-b0c6-7795f2186fbe", + "metadata": {}, + "outputs": [], + "source": [ + "# 替换数组元素\n", + "np.put(array11, [1, 3, 5, 7], [33, 88])\n", + "array11" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb4653ef-5c5a-4d3c-adb2-0f10d79ca6c6", + "metadata": {}, + "outputs": [], + "source": [ + "np.place(array11, array11 > 50, [44, 99])\n", + "array11" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03bcc628-1471-44ef-ad56-88468c08548d", + "metadata": {}, + "outputs": [], + "source": [ + "guido_image = plt.imread('res/guido.jpg')\n", + "guido_image.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16fa2543-195b-47d6-8e29-2682800f91aa", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(np.flip(guido_image, axis=0))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "259830fa-6cd3-43be-bffe-560050f795b9", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(np.flip(guido_image, axis=1))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6059087d-85ba-4151-9af0-8870876a6b1a", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(np.flip(guido_image, axis=2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70ebbf72-87c9-41d8-8c46-f1eccb531206", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(guido_image)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "015548b9-819c-49ba-a13d-7777662a7414", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(guido_image.swapaxes(0, 1))" + ] + }, + { + "cell_type": "markdown", + "id": "7c4c00de-f16f-4aac-ae37-e0b9df0eb6c2", + "metadata": {}, + "source": [ + "#### 普通函数矢量化" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa07c8bc-9558-4d2c-b12c-b0e3814cbb48", + "metadata": {}, + "outputs": [], + "source": [ + "# 面试官:讲一讲Python语言中的装饰器\n", + "# 用一个函数去装饰另一个函数或者一个类并为其提供额外的能力(横切关注功能)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02e4d3e6-9bc7-462e-8be4-800a1dcdc632", + "metadata": {}, + "outputs": [], + "source": [ + "# 面试题:写一个装饰器,如果原函数返回字符串,那么将字符串每个单词首字母大写\n", + "from functools import wraps\n", + "\n", + "\n", + "def titlize_str(func):\n", + "\n", + " @wraps(func)\n", + " def wrapper(*args, **kwargs):\n", + " result = func(*args, **kwargs)\n", + " if isinstance(result, str):\n", + " result = result.title()\n", + " return result\n", + "\n", + " return wrapper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e798956c-d543-4a50-a12b-f7314b98bf40", + "metadata": {}, + "outputs": [], + "source": [ + "@titlize_str\n", + "def say_hello(name):\n", + " return 'hello, ' + name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "82145cd0-bcb2-44b8-90ef-ac461e2120bd", + "metadata": {}, + "outputs": [], + "source": [ + "# 如果不使用@语法糖(便捷语法),也可以通过下面的方式应用装饰器\n", + "# say_hello = titlize_str(say_hello)\n", + "# say_hello('tom')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "108ca8a5-ec63-458b-b95b-df759b68cb51", + "metadata": {}, + "outputs": [], + "source": [ + "say_hello('tom')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d846bc7-718f-4344-b90e-c7803a104887", + "metadata": {}, + "outputs": [], + "source": [ + "# 获取原函数\n", + "say_hello = say_hello.__wrapped__\n", + "say_hello('tom')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea620684-3375-4876-b097-49e81bf225c9", + "metadata": {}, + "outputs": [], + "source": [ + "# 优化代码的执行性能:空间换时间\n", + "from functools import lru_cache\n", + "\n", + "\n", + "@lru_cache(maxsize=128)\n", + "def fib(n):\n", + " \"\"\"获取第n个斐波那契数\"\"\"\n", + " if n in (1, 2):\n", + " return 1\n", + " return fib(n - 1) + fib(n - 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f72372e-9cc9-4751-8ee7-e8d9f40727bd", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(1, 121):\n", + " print(i, fib(i))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "29d231a2-57cd-4786-85cd-1366f5378185", + "metadata": {}, + "outputs": [], + "source": [ + "# 通过vectorize装饰器将普通函数做矢量化处理\n", + "@np.vectorize\n", + "def fac(n):\n", + " if n == 0:\n", + " return 1\n", + " return n * fac(n - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "7c04cc06-ba86-4b1b-a4e1-75e4527f6dde", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 2, 3, 4, 5, 6, 7, 8])" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "temp = np.arange(1, 9)\n", + "temp" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "38e8026a-6e75-4e1b-a646-98c86736797f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 2, 6, 24, 120, 720, 5040, 40320])" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fac(temp)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "4ac8bb35-87f0-44d0-930e-3fc0c5fb63c3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([26, 68, 73, 33, 64, 54, 26, 40, 60, 36]),\n", + " array([37, 56, 65, 30, 57, 36, 61, 54, 34, 52]))" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x1 = np.random.randint(20, 80, 10)\n", + "x2 = np.random.randint(30, 70, 10)\n", + "x1, x2" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "4baced9b-fee2-4c2b-b7ca-dc5914b120b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 4, 1, 3, 1, 18, 1, 2, 2, 4])" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from math import gcd, lcm\n", + "\n", + "gcd = np.vectorize(gcd)\n", + "gcd(x1, x2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "08c6a075-8b61-4716-92c5-126cd78108c1", + "metadata": {}, + "outputs": [], + "source": [ + "lcm = np.vectorize(lcm)\n", + "lcm(x1, x2)" + ] + }, + { + "cell_type": "markdown", + "id": "859bba4b-a0cf-4140-a8de-b2f3fffcd355", + "metadata": {}, + "source": [ + "### 广播机制\n", + "\n", + "两个形状(shape属性)不一样的数组如果要做运算,要先通过广播机制使其形状一样才能运算。
\n", + "如果要执行广播机制使得两个数组形状一样,需要满足以下两个条件其中一个:\n", + "\n", + "1. 两个数组后缘维度(shape属性从后往前看对应的部分)相同。\n", + "2. 两个数组后缘维度不同,但是其中一方为1。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3339c56a-b68c-401e-a27c-134be60ccf14", + "metadata": {}, + "outputs": [], + "source": [ + "temp1 = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])\n", + "temp2 = np.array([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ecc9498-792c-4de1-a120-585003b19087", + "metadata": {}, + "outputs": [], + "source": [ + "temp1 + temp2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2cb20c46-3f87-4346-80ab-7e2786ca1475", + "metadata": {}, + "outputs": [], + "source": [ + "temp3 = np.array([[1], [2], [3], [4]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58932222-bb83-43b7-8cef-b9574898dbb5", + "metadata": {}, + "outputs": [], + "source": [ + "temp1 + temp3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74b6c376-05ac-4049-a4b9-5f0614de780e", + "metadata": {}, + "outputs": [], + "source": [ + "temp4 = np.array([1 ,2, 3])\n", + "temp5 = np.array([[3], [2], [1]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eefe6354-4614-4dfb-aa94-3d146b770b3b", + "metadata": {}, + "outputs": [], + "source": [ + "temp4.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04dd44a5-6bea-41eb-aad0-b0a18d64a512", + "metadata": {}, + "outputs": [], + "source": [ + "temp5.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "764eb0c3-991d-4a5f-a4eb-7da2c0b445bd", + "metadata": {}, + "outputs": [], + "source": [ + "temp4 + temp5" + ] + }, + { + "cell_type": "markdown", + "id": "8f1022cb-c07a-4149-aafd-9f53d235da4f", + "metadata": {}, + "source": [ + "### 矩阵" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a16c29a-088a-473b-b856-3dbd6660f9cd", + "metadata": {}, + "outputs": [], + "source": [ + "m1 = np.array([[1, 0, 2], [-1, 3, 1]])\n", + "m2 = np.array([[3, 1], [2, 1], [1, 0]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74a54196-ca95-4425-9212-62869795aed7", + "metadata": {}, + "outputs": [], + "source": [ + "m1.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1afbceea-782d-49c9-b6d4-d0848f3fdd99", + "metadata": {}, + "outputs": [], + "source": [ + "m2.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc65a37a-84eb-4aac-b63f-5625a0c15a3a", + "metadata": {}, + "outputs": [], + "source": [ + "m1 @ m2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67be1c96-a20c-4862-87cb-8579d3a303f5", + "metadata": {}, + "outputs": [], + "source": [ + "np.matmul(m1, m2)" + ] + }, + { + "cell_type": "markdown", + "id": "5fd602a8-61fc-4c5c-94a6-a930bcf6fb2f", + "metadata": {}, + "source": [ + "$$\n", + "\\begin{cases}\n", + "x_1 + 2x_2 + x_3 = 8 \\\\\n", + "3x_1 + 7x_2 + 2x_3 = 23 \\\\\n", + "2x_1 + 2x_2 + x_3 = 9\n", + "\\end{cases}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "id": "d41b4856-79c3-4f48-9d8e-58c8d6045884", + "metadata": {}, + "source": [ + "$$\n", + "\\boldsymbol{A} = \\begin{bmatrix}\n", + "1 & 2 & 1\\\\\n", + "3 & 7 & 2\\\\\n", + "2 & 2 & 1\n", + "\\end{bmatrix}, \\quad\n", + "\\boldsymbol{x} = \\begin{bmatrix}\n", + "x_1 \\\\\n", + "x_2\\\\\n", + "x_3\n", + "\\end{bmatrix}, \\quad\n", + "\\boldsymbol{b} = \\begin{bmatrix}\n", + "8 \\\\\n", + "23\\\\\n", + "9\n", + "\\end{bmatrix}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "edb85b44-50b3-4115-8539-5023a19bb2a1", + "metadata": {}, + "outputs": [], + "source": [ + "m3 = np.arange(1, 10, dtype='f8').reshape(3, 3)\n", + "m3[-1, -1] = 8\n", + "m3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b90dbf4-05f7-43ad-a37d-20d48d03dd3a", + "metadata": {}, + "outputs": [], + "source": [ + "# 计算矩阵的秩\n", + "np.linalg.matrix_rank(m3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a442ab40-97a9-4cf5-8d61-bfcaa3561655", + "metadata": {}, + "outputs": [], + "source": [ + "# 逆矩阵 - 奇异矩阵不能求逆矩阵\n", + "# LinAlgError: Singular matrix\n", + "np.linalg.inv(m3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19be7ff8-6a71-40ad-8e37-20008c56be7a", + "metadata": {}, + "outputs": [], + "source": [ + "# 有唯一解决的条件:系数矩阵的秩等于增广矩阵的秩,同时跟未知数的个数相同。\n", + "# 秩(rank):线性无关的行或者列的数量。\n", + "# 线性相关:一个向量可以通过其他向量做线性变换(数乘和加法)得到,那么它们就是线性相关的。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87043db3-e2bd-4a70-950a-d74163afc4d1", + "metadata": {}, + "outputs": [], + "source": [ + "A = np.array([[1, 2, 1], [3, 7, 2], [2, 2, 1]])\n", + "b = np.array([8, 23, 9]).reshape(-1, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "479d272b-2b46-4374-93f3-54e13af52d59", + "metadata": {}, + "outputs": [], + "source": [ + "# 系数矩阵的秩\n", + "np.linalg.matrix_rank(A)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "faa35591-09da-4232-9b38-72ee2fb824cc", + "metadata": {}, + "outputs": [], + "source": [ + "# 增广矩阵的秩\n", + "np.linalg.matrix_rank(np.hstack((A, b)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8d02dee-7d86-4f9d-8e33-77b5a76784a3", + "metadata": {}, + "outputs": [], + "source": [ + "# 解线性方程组\n", + "np.linalg.solve(A, b)" + ] + }, + { + "cell_type": "markdown", + "id": "336ee288-5be1-41e5-89cb-e22f465efdd2", + "metadata": {}, + "source": [ + "$$\n", + "A \\cdot x = b\n", + "$$\n", + "$$\n", + "A^{-1} \\cdot A \\cdot x = A^{-1} \\cdot b\n", + "$$\n", + "$$\n", + "I \\cdot x = A^{-1} \\cdot b\n", + "$$\n", + "$$\n", + "x = A^{-1} \\cdot b\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c2bd2fd-8867-4dad-8bbc-b5b175f690b7", + "metadata": {}, + "outputs": [], + "source": [ + "# 通过逆矩阵解线性方程组\n", + "np.linalg.inv(A) @ b" + ] + }, + { + "cell_type": "markdown", + "id": "b876a47b-a2ab-497a-b564-69750ddb8666", + "metadata": {}, + "source": [ + "#### 补充 - 用矩阵运算实现图像处理" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "95e138b5-ed1d-40c3-acbc-821b6ae8cf41", + "metadata": {}, + "outputs": [], + "source": [ + "# 安装opencv库\n", + "# %pip install opencv-python" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "669c59e5-a477-4bf3-b87b-7486e9d2e9ef", + "metadata": {}, + "outputs": [], + "source": [ + "def basic_matrix(translation):\n", + " \"\"\"基础变换矩阵\"\"\"\n", + " return np.array([[1, 0, translation[0]], [0, 1, translation[1]], [0, 0, 1]])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0338ad44-3142-428f-b1b6-45e691962900", + "metadata": {}, + "outputs": [], + "source": [ + "import copy\n", + "\n", + "def adjust_transform_for_image(img, trans_matrix):\n", + " \"\"\"根据图像调整变换矩阵\"\"\"\n", + " height, width, *_ = img.shape\n", + " center = np.array([0.5 * width, 0.5 * height])\n", + " return basic_matrix(center) @ trans_matrix @ basic_matrix(-center)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "368578be-3f94-4a37-be51-c6a5dbce1512", + "metadata": {}, + "outputs": [], + "source": [ + "import cv2\n", + "\n", + "def apply_transform(img, transform, border_value=(204, 204, 204)):\n", + " \"\"\"仿射变换\"\"\"\n", + " return cv2.warpAffine(\n", + " img,\n", + " transform[:2, :],\n", + " dsize=(img.shape[1], img.shape[0]),\n", + " flags=cv2.INTER_LINEAR,\n", + " borderMode=cv2.BORDER_CONSTANT,\n", + " borderValue=border_value\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28b22755-4aed-4cd2-a3a2-57bd73f8eb00", + "metadata": {}, + "outputs": [], + "source": [ + "def apply(img, trans_matrix):\n", + " \"\"\"应用变换\"\"\"\n", + " temp_matrix = adjust_transform_for_image(img, trans_matrix)\n", + " out_img = apply_transform(img, temp_matrix)\n", + " return out_img" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1526fbcd-ad60-4d7c-892c-5c0ae2b7ef3f", + "metadata": {}, + "outputs": [], + "source": [ + "def scale(img, x_ratio, y_ratio):\n", + " \"\"\"缩放\"\"\"\n", + " scale_matrix = np.array([\n", + " [x_ratio, 0, 0], \n", + " [0, y_ratio, 0], \n", + " [0, 0, 1]\n", + " ])\n", + " return apply(img, scale_matrix)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e443448-9b6c-488b-83a5-0de7ac4fca18", + "metadata": {}, + "outputs": [], + "source": [ + "def rotate(img, degree):\n", + " \"\"\"旋转\"\"\"\n", + " rad = np.deg2rad(degree)\n", + " rotate_matrix = np.array([\n", + " [np.cos(rad), -np.sin(rad), 0], \n", + " [np.sin(rad), np.cos(rad), 0], \n", + " [0, 0, 1]\n", + " ])\n", + " return apply(img, rotate_matrix)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a09187e-2707-49c2-8b8d-84ffa28b6f7e", + "metadata": {}, + "outputs": [], + "source": [ + "def transvect(img, ratio):\n", + " \"\"\"剪切影射\"\"\"\n", + " transvect_matrix = np.array([\n", + " [1, ratio, 0],\n", + " [0, 1, 0],\n", + " [0, 0, 1]\n", + " ])\n", + " return apply(img, transvect_matrix)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9561e506-3b64-49e6-9e9d-8731a2951646", + "metadata": {}, + "outputs": [], + "source": [ + "scaled_img = scale(guido_image, 1.25, 0.75)\n", + "plt.imshow(scaled_img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56cb960b-b3f9-4109-95ba-4388a2b1762e", + "metadata": {}, + "outputs": [], + "source": [ + "rotated_img = rotate(guido_image, -45)\n", + "plt.imshow(rotated_img)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc2ac604-0fd7-43d5-be18-407b78e46f58", + "metadata": {}, + "outputs": [], + "source": [ + "transvected_img = transvect(guido_image, -0.3)\n", + "plt.imshow(transvected_img)" + ] + }, + { + "cell_type": "markdown", + "id": "cd91252a-31d4-40d5-82ef-71f22f0bd39c", + "metadata": {}, + "source": [ + "#### 补充 - 用scipy处理图像" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9052864d-a32d-4cd3-9e2a-8df1654b8a67", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.ndimage import gaussian_filter, sobel\n", + "\n", + "# 获取灰度图\n", + "guido_image = plt.imread('res/guido.jpg')\n", + "gray_image = np.mean(guido_image, axis=2)\n", + "\n", + "plt.figure(figsize=(12, 4))\n", + "\n", + "# 灰度图\n", + "plt.subplot(1, 4, 1)\n", + "plt.imshow(gray_image, cmap=plt.cm.gray)\n", + "\n", + "# 模糊和锐化\n", + "plt.subplot(1, 4, 2)\n", + "blurred_image = gaussian_filter(gray_image, 3)\n", + "plt.imshow(blurred_image, cmap=plt.cm.gray)\n", + "\n", + "plt.subplot(1, 4, 3)\n", + "filtered_image = gaussian_filter(blurred_image, 1)\n", + "sharpen_image = blurred_image + 32 * (blurred_image - filtered_image)\n", + "plt.imshow(sharpen_image, cmap=plt.cm.gray)\n", + "\n", + "# 边缘图\n", + "plt.subplot(1, 4, 4)\n", + "# 使用索贝尔算子(邻点灰度加权差)进行边缘检测\n", + "edge_image = sobel(gray_image)\n", + "plt.imshow(edge_image, cmap=plt.cm.gray)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbbc6edf-c643-4cb4-a98b-05e806413478", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.ndimage import rotate, zoom\n", + "\n", + "plt.figure(figsize=(12, 4))\n", + "\n", + "# 旋转\n", + "plt.subplot(1, 3, 1)\n", + "rotated_image = rotate(guido_image, -16, reshape=True)\n", + "plt.imshow(rotated_image)\n", + "\n", + "# 旋转\n", + "plt.subplot(1, 3, 2)\n", + "rotated_image = rotate(guido_image, -16, reshape=False)\n", + "plt.imshow(rotated_image)\n", + "\n", + "# 缩放\n", + "plt.subplot(1, 3, 3)\n", + "scaled_image = zoom(guido_image, zoom=(0.8, 1.25, 1))\n", + "plt.imshow(scaled_image)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "331862d6-60cd-442f-97a4-bbbfbabfc3fa", + "metadata": {}, + "source": [ + "#### 补充 - 视频流人脸识别" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "383fa323-db82-4a8b-a13b-9cbe9b8c48d3", + "metadata": {}, + "outputs": [], + "source": [ + "# 安装face_recognition库\n", + "# %pip install face_recognition" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c1cf7b8-9d89-43d4-8954-616bce780183", + "metadata": {}, + "outputs": [], + "source": [ + "import cv2\n", + "import face_recognition\n", + "# from PIL import Image\n", + "\n", + "plt.figure(figsize=(12, 8))\n", + "\n", + "image = face_recognition.load_image_file('res/Solvay.jpg')\n", + "locations = face_recognition.face_locations(image)\n", + "for location in locations:\n", + " top, right, bottom, left = location\n", + " # Image.fromarray(image[top:bottom, left:right]).show()\n", + " cv2.rectangle(image, (left, top), (right, bottom), (255, 0, 0), 2)\n", + "plt.imshow(image)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa76d9c2-c8c3-4cec-8c91-b6b54fe6e32e", + "metadata": {}, + "outputs": [], + "source": [ + "# import cv2\n", + "# import face_recognition\n", + "# import numpy as np\n", + "\n", + "# # 获取摄像头\n", + "# video_capture = cv2.VideoCapture(0)\n", + "\n", + "# # 加载图片获取脸部特征\n", + "# obama_image = face_recognition.load_image_file(\"res/obama.jpg\")\n", + "# obama_face_encoding = face_recognition.face_encodings(obama_image)[0]\n", + "# luohao_image = face_recognition.load_image_file(\"res/luohao.png\")\n", + "# luohao_face_encoding = face_recognition.face_encodings(luohao_image)[0]\n", + "# guido_image = face_recognition.load_image_file(\"res/guido.jpg\")\n", + "# guido_face_encoding = face_recognition.face_encodings(guido_image)[0]\n", + "\n", + "# # 保存脸部特征和对应的名字\n", + "# known_face_encodings = [\n", + "# obama_face_encoding,\n", + "# luohao_face_encoding,\n", + "# guido_face_encoding\n", + "# ]\n", + "# known_face_names = [\n", + "# \"Barack\",\n", + "# \"Hao\",\n", + "# \"Guido\"\n", + "# ]\n", + "\n", + "# face_locations = []\n", + "# face_encodings = []\n", + "# face_names = []\n", + "# process_this_frame = True\n", + "\n", + "# while True:\n", + "# # 从视频中读取一帧数据\n", + "# ret, frame = video_capture.read()\n", + "\n", + "# # 调整为原始尺寸的四分之一(加速处理)\n", + "# small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)\n", + "\n", + "# # BGR转成RGB\n", + "# rgb_small_frame = small_frame[:, :, ::-1]\n", + "\n", + "# if process_this_frame:\n", + "# # 找到所有的人脸位置和脸部特征保存在列表中\n", + "# face_locations = face_recognition.face_locations(rgb_small_frame)\n", + "# face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)\n", + "\n", + "# face_names = []\n", + "# for face_encoding in face_encodings:\n", + "# # 比较脸部特征\n", + "# matches = face_recognition.compare_faces(known_face_encodings, face_encoding)\n", + "# name = \"Unknown\"\n", + "\n", + "# # 通过距离判定最佳匹配并获取对应的名字\n", + "# face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)\n", + "# best_match_index = np.argmin(face_distances)\n", + "# if matches[best_match_index]:\n", + "# name = known_face_names[best_match_index]\n", + "\n", + "# face_names.append(name)\n", + "\n", + "# process_this_frame = not process_this_frame\n", + "\n", + "# # 显示结果\n", + "# for (top, right, bottom, left), name in zip(face_locations, face_names):\n", + "# # 恢复正常的尺寸\n", + "# top, right, bottom, left = top * 4, right * 4, bottom * 4, left * 4\n", + "# # 绘制一个标识人脸的矩形框\n", + "# cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)\n", + "# # 绘制一个填写名字的矩形框\n", + "# cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)\n", + "# # 绘制识别出的人脸对应的名字\n", + "# cv2.putText(frame, name, (left + 6, bottom - 6), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 1)\n", + " \n", + "# cv2.imshow('Video', frame)\n", + " \n", + "# # 按键盘上的q键退出窗口 \n", + "# if cv2.waitKey(1) & 0xFF == ord('q'):\n", + "# break\n", + "\n", + "# video_capture.release()\n", + "# cv2.destroyAllWindows()" + ] + }, + { + "cell_type": "markdown", + "id": "b6989fb7-a3ab-4889-80be-f4ecce033e5c", + "metadata": {}, + "source": [ + "### 多项式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c719ca9-de74-4c2e-aaf2-d0a9c833f512", + "metadata": {}, + "outputs": [], + "source": [ + "# NumPy老版本用poly1d表示多项式\n", + "p1 = np.poly1d([3, 0, 2, 1])\n", + "p2 = np.poly1d([1, 2, 3])\n", + "print(p1)\n", + "print(p2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6aab279-fa9f-4d4e-b597-b3c51b478777", + "metadata": {}, + "outputs": [], + "source": [ + "# 多项式加法\n", + "print(p1 + p2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "375a1989-7231-432c-bf33-06fce490cacf", + "metadata": {}, + "outputs": [], + "source": [ + "# 多项式乘法\n", + "print(p1 * p2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4df5662a-c0bc-434d-b53f-623e09b51a76", + "metadata": {}, + "outputs": [], + "source": [ + "# 令x=2,计算多项式的值\n", + "p2(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df150df6-9904-45b7-8704-dbb033552dcb", + "metadata": {}, + "outputs": [], + "source": [ + "# 求导\n", + "print(p1.deriv())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2d3002c-59d6-4e00-b00f-69f3f6ac6609", + "metadata": {}, + "outputs": [], + "source": [ + "# 求不定积分\n", + "print(p1.integ())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0965b9e9-42fc-425c-90cb-d265e3f6e42f", + "metadata": {}, + "outputs": [], + "source": [ + "p3 = np.poly1d([1, 3, 2])\n", + "print(p3)\n", + "# 令多项式等于0,求解x\n", + "print(p3.roots)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5bda2d2-0d61-46db-ac48-0f7ec3438660", + "metadata": {}, + "outputs": [], + "source": [ + "type(p3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a71c4799-183e-4096-9903-7edf9464feb7", + "metadata": {}, + "outputs": [], + "source": [ + "from numpy.polynomial import Polynomial\n", + "\n", + "# NumPy新版本用Polynomial表示多项式\n", + "p1 = Polynomial([1, 2, 0, 3])\n", + "print(p1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0002c6e3-697b-425c-a17c-802e10f07981", + "metadata": {}, + "outputs": [], + "source": [ + "print(p1.deriv())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f9dde21-0854-4ae6-9703-9599a4204003", + "metadata": {}, + "outputs": [], + "source": [ + "print(p1.integ())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb191b28-fb2c-460b-9c87-0041b128ba16", + "metadata": {}, + "outputs": [], + "source": [ + "# 最高次项\n", + "p1.degree()" + ] + }, + { + "cell_type": "markdown", + "id": "2d6c3103-c5b0-413f-b2de-324b0394e3ae", + "metadata": {}, + "source": [ + "### 最小二乘解" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7163cb46-44bb-487c-8dd2-7c530574ecc0", + "metadata": {}, + "outputs": [], + "source": [ + "# 每月收入\n", + "x = np.array([3200, 4811, 5386, 5564, 6120, 6691, 6906, 7483, 7587, 7890,\n", + " 8090, 8300, 8650, 8835, 8975, 9070, 9100, 9184, 9247, 9313, \n", + " 9465, 9558, 9853, 9938, 10020, 10242, 10343, 10731, 10885, 10990, \n", + " 11100, 11227, 11313, 11414, 11630, 11806, 11999, 12038, 12400, 12547, \n", + " 12890, 13050, 13360, 13850, 14890, 14990, 15500, 16899, 17010, 19880])\n", + "# 每月网购支出\n", + "y = np.array([1761, 882, 1106, 182, 1532, 1978, 2174, 2117, 2134, 1924, \n", + " 2207, 2876, 2617, 2683, 3054, 3277, 3345, 3462, 3401, 3591,\n", + " 3596, 3671, 3829, 3907, 3852, 4288, 4359, 4099, 4300, 4367,\n", + " 5019, 4873, 4674, 5174, 4666, 5797, 5782, 5451, 5487, 5448,\n", + " 6002, 6439, 6309, 6045, 5935, 6928, 7356, 6682, 6672, 6582])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a68a1d0d-0dc9-4437-bed9-6e9aff589df1", + "metadata": {}, + "outputs": [], + "source": [ + "# 定性分析 - 散点图\n", + "plt.scatter(x, y)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89030441-eefd-4a02-ad41-ba154dbf87b2", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy import stats\n", + "\n", + "# 夏皮洛检验(正态性判定)\n", + "stats.shapiro(x), stats.shapiro(y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93573426-e1c0-4025-8b8f-2d83d69e103c", + "metadata": {}, + "outputs": [], + "source": [ + "# 定量分析 - 相关系数 - correlation coefficient\n", + "# 皮尔逊相关系数(标准化的协方差 - [-1, 1])\n", + "# 1. 连续值且成对出现\n", + "# 2. 没有异常值\n", + "# 3. 来自于正态总体\n", + "np.corrcoef(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b6e47de-8e3b-4be4-a5e4-f1d95649e6f9", + "metadata": {}, + "outputs": [], + "source": [ + "# 计算皮尔逊相关系数\n", + "stats.pearsonr(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8e53b1e-b2b1-4222-a69a-dcbca0041d0e", + "metadata": {}, + "outputs": [], + "source": [ + "history_data = {key: value for key, value in zip(x, y)}\n", + "len(history_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1530478e-ec70-4f87-828f-1d6798c53c5e", + "metadata": {}, + "outputs": [], + "source": [ + "data = np.random.randint(1, 100, 15).tolist()\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d246563-86fd-418f-b058-4aae08d9b9c1", + "metadata": {}, + "outputs": [], + "source": [ + "import heapq\n", + "\n", + "# 通过堆(heap)结构快速的找到TopN元素\n", + "print(heapq.nsmallest(3, data))\n", + "print(heapq.nlargest(5, data))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "866f9fd9-58aa-4c03-9415-99aa236958d4", + "metadata": {}, + "outputs": [], + "source": [ + "# 目标:因为月收入和网购支出之间有强相关关系,所以我们可以通过月收入预测网购支出\n", + "# 方法1:输入一个月收入,找到跟这个收入最接近的N条数据,用它们的平均值预测对应的网购支出\n", + "# KNN - k最近邻算法(找到k个最近的邻居,用这k个邻居的数据来做出预测)\n", + "import heapq\n", + "\n", + "\n", + "def predicate_by_knn(income, k=5):\n", + " \"\"\"KNN算法\"\"\"\n", + " keys = heapq.nsmallest(k, history_data, key=lambda x: (x - income) ** 2)\n", + " return np.mean([history_data[key] for key in keys]).round(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "399b9b6a-6625-4dc2-a09c-c0699c538a46", + "metadata": {}, + "outputs": [], + "source": [ + "predicate_by_knn(12800)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db0ee4bf-83f8-489f-a543-80854571da60", + "metadata": {}, + "outputs": [], + "source": [ + "predicate_by_knn(6800)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e514003-c143-4823-91eb-57f7608f6137", + "metadata": {}, + "outputs": [], + "source": [ + "predicate_by_knn(20000, k=3)" + ] + }, + { + "cell_type": "markdown", + "id": "aa44d3ad-aa3f-44db-889a-8a69b9a9be65", + "metadata": {}, + "source": [ + "回归模型:\n", + "$$ Y = aX + b $$\n", + "\n", + "损失函数:\n", + "$$ MSE = \\frac{1} {N} \\sum (\\hat{y_i} - y_i)^2 $$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "032eb740-ac9a-4526-9eb4-b6770907b07a", + "metadata": {}, + "outputs": [], + "source": [ + "# MSE - Mean Squared Error\n", + "def get_loss(a, b):\n", + " \"\"\"损失函数\"\"\"\n", + " y_hat = a * x + b\n", + " return np.mean((y_hat - y) ** 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d56ccc4-5a0d-4b3a-89ab-01cf690c4fba", + "metadata": {}, + "outputs": [], + "source": [ + "# 蒙特卡洛模拟(随机瞎蒙法)\n", + "import random\n", + "\n", + "min_loss = np.inf\n", + "ba, bb = None, None\n", + "\n", + "for _ in range(10000):\n", + " a = random.random() * 0.5 + 0.5\n", + " b = random.random() * 1000 - 2000\n", + " curr_loss = get_loss(a, b)\n", + " if curr_loss < min_loss:\n", + " min_loss = curr_loss\n", + " ba, bb = a, b\n", + " print(min_loss)\n", + "print(ba, bb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af5f7d02-f921-4712-82e3-1ce43c86c708", + "metadata": {}, + "outputs": [], + "source": [ + "plt.scatter(x, y)\n", + "plt.plot(x, ba * x + bb, color='r', linewidth=4)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "207c2da0-371f-40ea-b19f-f56cca8a7d30", + "metadata": {}, + "outputs": [], + "source": [ + "def predicate_by_regression(income):\n", + " return round(ba * income + bb, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9e3af32-654c-4704-9f32-cfe41f067d06", + "metadata": {}, + "outputs": [], + "source": [ + "predicate_by_regression(6800)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5133079a-59f8-4435-af1e-fb5bc13c457d", + "metadata": {}, + "outputs": [], + "source": [ + "predicate_by_regression(12800)" + ] + }, + { + "cell_type": "markdown", + "id": "200b9821-b8c8-41df-ae8e-d96bf5049415", + "metadata": {}, + "source": [ + "将回归模型带入损失函数:\n", + "$$ f(a, b) = \\frac {1} {N} \\sum_{i=1}^{N}(y_i - (ax_i + b))^2 $$\n", + "\n", + "如何让$f(a, b)$取到最小值???\n", + "\n", + "求偏导数,并令其等于0。\n", + "$$ \\frac {\\partial {f(a, b)}} {\\partial {a}} = \\frac {2} {N} \\sum_{i=1}^{N}(-x_iy_i + x_i^2a + x_ib) = 0 $$ \n", + "$$ \\frac {\\partial {f(a, b)}} {\\partial {b}} = \\frac {2} {N} \\sum_{i=1}^{N}(-y_i + x_ia + b) = 0 $$\n", + "\n", + "求解得到:\n", + "$$a = \\frac{\\sum(x_{i} - \\bar{x})(y_{i} - \\bar{y})}{\\sum(x_{i} - \\bar{x})^{2}}$$\n", + "$$b = \\bar{y} - a\\bar{x}$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ab79db5-85c7-44ad-8ffa-2684fb499f7a", + "metadata": {}, + "outputs": [], + "source": [ + "x_bar, y_bar = np.mean(x), np.mean(y)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "332777b5-bc3f-44d2-bd54-f8e9a19497aa", + "metadata": {}, + "outputs": [], + "source": [ + "ba = np.dot((x - x_bar), (y - y_bar)) / np.sum((x - x_bar) ** 2)\n", + "bb = y_bar - ba * x_bar\n", + "ba, bb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "795f6a40-136a-4772-9e2b-5798022e408d", + "metadata": {}, + "outputs": [], + "source": [ + "plt.scatter(x, y)\n", + "plt.plot(x, ba * x + bb, color='r', linewidth=4)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2bfac239-7bf0-4784-a124-bbfdb4cdafe8", + "metadata": {}, + "outputs": [], + "source": [ + "# 拟合出一个线性回归模型\n", + "np.polyfit(x, y, deg=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d5adc92-73e2-40df-843b-02efb28c6ae3", + "metadata": {}, + "outputs": [], + "source": [ + "# 拟合出一个多项式回归模型\n", + "a, b, c = np.polyfit(x, y, deg=2)\n", + "a, b, c" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c42ee939-e4b9-479e-ad73-66e806e29c6c", + "metadata": {}, + "outputs": [], + "source": [ + "plt.scatter(x, y)\n", + "plt.plot(x, a * x ** 2 + b * x + c, color='r', linewidth=4)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3363f359-3615-4ffa-a040-41e39c83b38f", + "metadata": {}, + "outputs": [], + "source": [ + "Polynomial.fit(x, y, deg=1).convert().coef" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Day66-80/code/day04.ipynb b/Day66-80/code/day04.ipynb new file mode 100644 index 0000000..604f0a3 --- /dev/null +++ b/Day66-80/code/day04.ipynb @@ -0,0 +1,1572 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "196a647a-6faa-4aee-a0bf-a345852251dd", + "metadata": {}, + "source": [ + "## 深入浅出pandas\n", + "\n", + "pandas是一个支持数据分析全流程的Python开源库,它的作者Wes McKinney于2008年开始开发这个库,其主要目标是提供一个大数据分析和处理的工具。pandas封装了从数据加载、数据重塑、数据清洗到数据透视、数据呈现等一系列操作,提供了三种核心的数据类型:\n", + "1. `Series`:数据系列,表示一维的数据。跟一维数组的区别在于每条数据都有对应的索引,处理数据的方法比`ndarray`更为丰富。\n", + "2. `DataFrame`:数据框、数据窗、数据表,表示二维的数据。跟二维数组相比,`DataFrame`有行索引和列索引,而且提供了100+方法来处理数据。\n", + "3. `Index`:为`Series`和`DataFrame`提供索引服务。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb84f909-921a-47da-87b1-61578c871422", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False\n", + "get_ipython().run_line_magic('config', \"InlineBackend.figure_format = 'svg'\")" + ] + }, + { + "cell_type": "markdown", + "id": "2102e83e-2a6d-47aa-b449-c058bea1a601", + "metadata": {}, + "source": [ + "### 创建DataFrame对象" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87dbde08-dcab-4ede-a791-b56e11dd9115", + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(20)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c5b2767-2074-4cdf-b1ba-beff6f425942", + "metadata": {}, + "outputs": [], + "source": [ + "stu_names = ['狄仁杰', '白起', '李元芳', '苏妲己', '孙尚香']\n", + "cou_names = ['语文', '数学', '英语']\n", + "scores_arr = np.random.randint(60, 101, (5, 3))\n", + "scores_arr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8c2a6bf-ca5e-479d-ab63-f5c3620186e3", + "metadata": {}, + "outputs": [], + "source": [ + "# 方法一:通过二维数组构造DataFrame对象\n", + "df1 = pd.DataFrame(data=scores_arr, columns=cou_names, index=stu_names)\n", + "df1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baad5381-fb7d-4cc9-9288-a05d750144af", + "metadata": {}, + "outputs": [], + "source": [ + "# 行索引\n", + "df1.index" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7f06b76-b60b-49cb-be72-adafb0978fca", + "metadata": {}, + "outputs": [], + "source": [ + "# 列索引\n", + "df1.columns" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13b1275d-77e5-4d5d-b227-19db3f4196fd", + "metadata": {}, + "outputs": [], + "source": [ + "# 值 - 二维数组\n", + "df1.values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dbf5bb11-1600-4ae4-bc95-369bc8189c20", + "metadata": {}, + "outputs": [], + "source": [ + "scores_dict = {\n", + " '语文': [95, 91, 69, 82, 92],\n", + " '数学': [86, 88, 80, 67, 100],\n", + " '英语': [75, 86, 71, 94, 81]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c300bbbd-329a-4852-bf76-78ce1de02b8f", + "metadata": {}, + "outputs": [], + "source": [ + "# 方法二:通过数据字典构造DataFrame对象\n", + "df2 = pd.DataFrame(data=scores_dict, index=stu_names)\n", + "df2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "705c0de6-43ff-46c6-85d5-301743d18d43", + "metadata": {}, + "outputs": [], + "source": [ + "# 查看DataFrame信息\n", + "df2.info(memory_usage='deep')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71417ac2-8f4b-4950-9336-de6fbc1f5da4", + "metadata": {}, + "outputs": [], + "source": [ + "# 方法三:从CSV文件加载数据创建DataFrame对象\n", + "df3 = pd.read_csv(\n", + " 'res/2023年北京积分落户数据.csv',\n", + " # encoding='utf-8', # 指定字符编码\n", + " # sep='', # 指定字段的分隔符(默认逗号)\n", + " # delimiter='#',\n", + " # header=0, # 表头所在的行\n", + " # quotechar='\"', # 包裹字符串的字符(默认双引号)\n", + " # index_col='公示编号', # 索引列\n", + " # usecols=['公示编号', '姓名', '积分分值'], # 指定加载的列\n", + " # nrows=10, # 加载的行数\n", + " # skiprows=np.arange(1, 101), # 跳过哪些行\n", + " # true_values=['是', 'Yes', 'YES'], # 哪些值会被视为布尔值True\n", + " # false_values=['否', 'No', 'NO'], # 哪些值会被视为布尔值False\n", + " # na_values=['---', 'N/A'], # 哪些值会被视为空值\n", + " # iterator=True, # 开启迭代器模式\n", + " # chunksize=1000, # 每次加载的数据体量\n", + ")\n", + "df3" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67b86b13-566b-4f97-86fd-3723ef21a87f", + "metadata": {}, + "outputs": [], + "source": [ + "df4 = pd.read_csv('res/big_data_file.csv.gz', low_memory=False)\n", + "df4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e52ff38d-8e40-4532-8df9-4d2807a3e2ec", + "metadata": {}, + "outputs": [], + "source": [ + "df4.info(memory_usage='deep')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65e871ff-e87c-4e6b-86cc-624af7ccbdc1", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install pyarrow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48fab84a-8b86-4405-966a-6bfb99582de5", + "metadata": {}, + "outputs": [], + "source": [ + "df5 = pd.read_csv('res/big_data_file.csv.gz', engine='pyarrow')\n", + "df5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea575de9-4398-46fe-b2f8-8fb37b93179b", + "metadata": {}, + "outputs": [], + "source": [ + "df5.info(memory_usage='deep')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5723cf03-b78f-4fc9-943c-f9b10036affa", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install xlrd xlwt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb3387b9-3402-4b25-a5d5-ff9690a1ac06", + "metadata": {}, + "outputs": [], + "source": [ + "# 方法四:从Excel文件加载数据创建DataFrame对象\n", + "df6 = pd.read_excel(\n", + " 'res/2020年销售数据.xlsx',\n", + " sheet_name='data',\n", + ")\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d06abbd8-9a34-4ab3-a75c-76e3ed8eb36c", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install -U pymysql cryptography sqlalchemy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5aa0e35f-2a13-4c8e-a9fd-87b0bf72307e", + "metadata": {}, + "outputs": [], + "source": [ + "# 方法五:从数据服务器加载数据创建DataFrame对象\n", + "from sqlalchemy import create_engine\n", + "\n", + "# URL \n", + "engine = create_engine('mysql+pymysql://guest:Guest.618@47.109.26.237:3306/hrs')\n", + "engine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b344f17-f5a1-4d7d-ad3c-ede4b122609c", + "metadata": {}, + "outputs": [], + "source": [ + "dept_df = pd.read_sql('tb_dept', engine, index_col='dno')\n", + "dept_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5d1ffa3-6962-4c26-ae92-a8d7bc7da0cb", + "metadata": {}, + "outputs": [], + "source": [ + "emp_df1 = pd.read_sql('tb_emp', engine, index_col='eno')\n", + "emp_df1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f84b6886-09d8-4f13-89cc-487574991dba", + "metadata": {}, + "outputs": [], + "source": [ + "emp_df2 = pd.read_sql('tb_emp2', engine, index_col='eno')\n", + "emp_df2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c60e96d2-9a0d-4901-b39c-c31760de47a0", + "metadata": {}, + "outputs": [], + "source": [ + "# 关闭连接释放资源\n", + "engine.connect().close()" + ] + }, + { + "cell_type": "markdown", + "id": "12086a7a-c161-4753-9a8e-180f9e8b2edf", + "metadata": {}, + "source": [ + "### 查看信息" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "785e58f9-b3f7-49a6-affc-8caaa66cebf1", + "metadata": {}, + "outputs": [], + "source": [ + "df6.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd8a9156-3939-430d-9738-60b3d8a95563", + "metadata": {}, + "outputs": [], + "source": [ + "# 获取前N行\n", + "df6.head(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b75ace23-9b92-4425-b58f-bcd81e8d72e7", + "metadata": {}, + "outputs": [], + "source": [ + "# 获取后N行\n", + "df6.tail(5)" + ] + }, + { + "cell_type": "markdown", + "id": "c2b2a909-0b40-473c-bb3f-85aca1925a19", + "metadata": {}, + "source": [ + "### 操作行、列、单元格" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe964b3b-7f51-4202-b528-f5102d9be9f0", + "metadata": {}, + "outputs": [], + "source": [ + "# 访问列\n", + "df6['销售日期']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2e5ccb3-4b97-4a02-8316-b1321390f286", + "metadata": {}, + "outputs": [], + "source": [ + "df6.销售渠道" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80ad78dc-4f47-4421-8478-ba7797350db4", + "metadata": {}, + "outputs": [], + "source": [ + "df6['销售渠道']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b970671-6f16-4e07-8666-715495de2832", + "metadata": {}, + "outputs": [], + "source": [ + "type(df6['销售日期'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c9cb56b-6a2b-479e-8c57-c61683858387", + "metadata": {}, + "outputs": [], + "source": [ + "df6[['销售渠道']]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75730cd3-0459-4a62-97ee-e037256cc98a", + "metadata": {}, + "outputs": [], + "source": [ + "type(df6[['销售渠道']])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e097e49-b762-4c9f-9d93-98abb1701d97", + "metadata": {}, + "outputs": [], + "source": [ + "# 访问多个列 - 花式索引\n", + "df6[['销售日期', '销售区域', '直接成本']]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf31a169-549e-4182-8206-789f97316115", + "metadata": {}, + "outputs": [], + "source": [ + "df6.columns[3:7]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "792713c0-13bc-4810-86cc-5f6f6ce78719", + "metadata": {}, + "outputs": [], + "source": [ + "df6[df6.columns[3:7]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02d43b17-15e3-44d5-844b-a50d365bf863", + "metadata": {}, + "outputs": [], + "source": [ + "# 访问行 - loc属性\n", + "df6.loc[1944]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "79da6932-f985-44dc-9f4b-e051e4749c65", + "metadata": {}, + "outputs": [], + "source": [ + "df6.iloc[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6246b39b-7229-4e0f-af7b-0915e707492a", + "metadata": {}, + "outputs": [], + "source": [ + "# 访问多行 - 花式索引\n", + "df6.loc[[0, 100, 58, 1000, 1000, 1000, 1099]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77321324-0ca9-4c2e-a792-3c717189cb27", + "metadata": {}, + "outputs": [], + "source": [ + "# 访问多行 - 切片索引\n", + "df6.loc[101:200]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5eb250eb-18e0-4181-a37a-dec55c633116", + "metadata": {}, + "outputs": [], + "source": [ + "# df6[101:200]\n", + "df6.iloc[101:200]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2daddd7-3635-40b1-9416-c1137315948c", + "metadata": {}, + "outputs": [], + "source": [ + "df6.iloc[-1:-101:-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9321811f-e62b-4db5-a478-cdc0934f097b", + "metadata": {}, + "outputs": [], + "source": [ + "# 访问单元格\n", + "df6.at[2, '售价']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd1670bc-0a13-457f-95f1-352a4d61b3a7", + "metadata": {}, + "outputs": [], + "source": [ + "df6.at[2, '售价'] = 999\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7460ef03-3f45-4cc0-99a3-85039c2606b0", + "metadata": {}, + "outputs": [], + "source": [ + "df6.iat[2, -3] = 888\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34c81da6-f58f-4c36-8596-004266e9374b", + "metadata": {}, + "outputs": [], + "source": [ + "# 添加列\n", + "df6['销售额'] = df6['售价'] * df6['销售数量']\n", + "df6['季度'] = df6['销售日期'].dt.quarter\n", + "df6['月份'] = df6['销售日期'].dt.month\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3c60210-202d-4bd8-8804-1d657746b29c", + "metadata": {}, + "outputs": [], + "source": [ + "# 添加行 - 实际工作中基本没有意义" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6bf78f3d-05a2-4c7a-a0f0-fb6659f1bd6f", + "metadata": {}, + "outputs": [], + "source": [ + "# 删除列\n", + "# inplace=False - 默认设定 - 不修改原对象返回修改后的新对象\n", + "# inplace=True - 直接修改DataFrame对象不返回新对象 - 方法没有返回值\n", + "df6.drop(columns=['季度'], inplace=True)\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cdf8cf10-5193-4c38-8fef-bc3d38a8a0a8", + "metadata": {}, + "outputs": [], + "source": [ + "# 删除行\n", + "# df6.drop(index=[0, 1, 2, 100, 1944, 1943])\n", + "df6.drop(index=[0, 1, 2, 100, 1944, 1943], inplace=True)\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ddfe77d-aa92-4d6a-b2db-8469b1222ed3", + "metadata": {}, + "outputs": [], + "source": [ + "df6.drop(index=df6.index[100:200], inplace=True)\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8020bbb0-740e-496a-9224-fe3495a19c92", + "metadata": {}, + "outputs": [], + "source": [ + "# 重命名\n", + "df6.rename(columns={'销售区域': '区域', '销售渠道': '渠道', '销售订单': '订单号'}, inplace=True)\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d028d2be-0944-4b70-a3ea-f7d06cdd458f", + "metadata": {}, + "outputs": [], + "source": [ + "# 重置索引\n", + "# drop=False - 默认值 - 原来的索引变成一个普通列\n", + "# drop=True - 原来的索引直接丢弃\n", + "df6.reset_index(drop=True, inplace=True)\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb55a518-f4bd-4fac-8554-4353c0798bc6", + "metadata": {}, + "outputs": [], + "source": [ + "# 设置索引\n", + "df6.set_index('订单号', inplace=True)\n", + "df6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "101bd804-5a90-4cd3-a545-613df6d9b8e5", + "metadata": {}, + "outputs": [], + "source": [ + "# 筛选数据 - 布尔索引\n", + "df6[df6['销售额'] > 100000]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64c83a43-fcb0-4ba1-9400-ae4a5b21715c", + "metadata": {}, + "outputs": [], + "source": [ + "df6[(df6['销售额'] > 100000) & (df6['月份'] == 6)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22c01e56-b188-40f7-9e53-3a3d2f0bcb29", + "metadata": {}, + "outputs": [], + "source": [ + "df6[(df6['销售额'] > 100000) | (df6['月份'] == 6)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5adb86b9-8b31-49cb-9292-94189f3714c5", + "metadata": {}, + "outputs": [], + "source": [ + "df6.query('销售额 > 100000')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b768afa0-7066-4a1d-8f10-b88386587388", + "metadata": {}, + "outputs": [], + "source": [ + "df6.query('月份 == 6 and 渠道 == \"实体\"')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e57b21c-0565-4352-8924-de169497bce0", + "metadata": {}, + "outputs": [], + "source": [ + "df6.query('销售额 > 100000 and 月份 == 6')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ef8ba56-5293-41b0-8208-85a0eed735e8", + "metadata": {}, + "outputs": [], + "source": [ + "# 随机抽样\n", + "df6.sample(n=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bfcd52d7-eac4-4776-b0e3-a37e67e349f3", + "metadata": {}, + "outputs": [], + "source": [ + "df6.sample(frac=0.05)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c654ca8-3179-4fa2-9213-7d7029357342", + "metadata": {}, + "outputs": [], + "source": [ + "# replace=False - 无放回抽样\n", + "ignore_rows = np.random.choice(np.arange(1, 1946), size=int(1945 * 0.9), replace=False)\n", + "pd.read_excel(\n", + " 'res/2020年销售数据.xlsx',\n", + " sheet_name='data',\n", + " skiprows=ignore_rows\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "2037ed6a-d616-4c67-9f5d-ea517d6e1c6b", + "metadata": {}, + "source": [ + "### 数据重塑\n", + "\n", + "1. 拼接(合并结构一致的数据)\n", + "2. 合并(事实表连接维度表)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2184fd4-bd44-459f-bda4-6dc11c09c219", + "metadata": {}, + "outputs": [], + "source": [ + "# 拼接两个DataFrame - union\n", + "all_emp_df = pd.concat([emp_df1, emp_df2])\n", + "all_emp_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05bc65a1-42ac-463c-a089-08fb8dc60855", + "metadata": {}, + "outputs": [], + "source": [ + "# 连表 - 连接事实表和维度表 - 用维度把数据分组然后再做聚合\n", + "# 连接两个DataFrame(内连接、左外连接、右外连接、全外连接)- join\n", + "# how - 连表方式 - inner、left、right、outer\n", + "# on - 基于哪个字段连表 - left_on、right_on\n", + "all_emp_df = pd.merge(all_emp_df, dept_df, how='inner', on='dno')\n", + "all_emp_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6a3d52d-a04c-494d-9ee9-2dad9805b1c1", + "metadata": {}, + "outputs": [], + "source": [ + "# 作业:在jobs目录下有若干个CVS文件,它们的数据结构是一样的,现在需要把所有CSV文件的数据拼接到一个DataFrame中\n", + "import os\n", + "\n", + "dfs = [pd.read_csv(os.path.join('res/jobs', filename))\n", + " for filename in os.listdir('res/jobs') \n", + " if filename.endswith('.csv')]\n", + "pd.concat(dfs, ignore_index=True).to_csv('res/all_jobs.csv', index=False)" + ] + }, + { + "cell_type": "markdown", + "id": "6b9ad1e1-fe5d-45a0-8755-ac6720a32ba0", + "metadata": {}, + "source": [ + "### 数据清洗\n", + "\n", + "1. 缺失值\n", + "2. 重复值\n", + "3. 异常值\n", + "4. 预处理" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45c835c4-559f-45f1-a501-70a8c12bbbb1", + "metadata": {}, + "outputs": [], + "source": [ + "# 甄别缺失值\n", + "all_emp_df.isna()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd7fbdf8-ebf2-463b-ac3b-cdb24560873a", + "metadata": {}, + "outputs": [], + "source": [ + "# all_emp_df['comm'].isna()\n", + "all_emp_df['comm'].isnull()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4f16d30-83e9-4761-92a1-780e85e721e1", + "metadata": {}, + "outputs": [], + "source": [ + "# all_emp_df['comm'].notna()\n", + "all_emp_df['comm'].notnull()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f2a153d-ab4a-475e-9ee3-0d623a289f7f", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df['comm'].notna().value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d388d57-fa1a-405b-880e-9316354a6f05", + "metadata": {}, + "outputs": [], + "source": [ + "# 删除空值 - 删除带有空值的行\n", + "all_emp_df.dropna()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b40fa037-3fab-454e-a300-2e9dcf4b2b60", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df.dropna(axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67ae21a1-7dc1-496b-85b5-013d79d25a63", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df.mgr.dropna()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66745379-9db7-42b0-ab6b-a55e870a515b", + "metadata": {}, + "outputs": [], + "source": [ + "# 填充空值\n", + "all_emp_df.fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1664115-30e0-4946-ae4b-c919bb319ddc", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df.comm.fillna(0).astype('i8')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1743531-66a2-42a4-8c28-ad268efc848c", + "metadata": {}, + "outputs": [], + "source": [ + "# 将空值下方的非空值向上填充 - backward fill\n", + "all_emp_df.comm.bfill()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fcef0a0-ff29-42bd-9955-5a97595390fd", + "metadata": {}, + "outputs": [], + "source": [ + "# 将空值上方的非空值向下填充 - forward fill\n", + "all_emp_df.comm.ffill()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eeeb9be3-802c-44e3-80a0-465aba1a485a", + "metadata": {}, + "outputs": [], + "source": [ + "# 通过插值算法填充空值 - interpolate\n", + "all_emp_df['comm'] = all_emp_df.comm.interpolate(method='linear')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1f094c3-1cc2-4826-a04a-24150ea9cef8", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df['comm'] = all_emp_df.comm.astype('i8')\n", + "all_emp_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a739242d-ebd2-42d2-9ec7-9a5939cbf74a", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df['mgr'] = all_emp_df.mgr.fillna(-1).astype('i8')\n", + "all_emp_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd376d13-2245-48b8-ba14-3315d4c48f9c", + "metadata": {}, + "outputs": [], + "source": [ + "# 甄别重复值\n", + "all_emp_df.ename.duplicated()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e107a38-c5e8-4e5e-9e42-71481c54e0d1", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df.duplicated(['ename', 'job'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "097eaaf2-1112-4e0f-b361-786bf91d6c1f", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计每个元素出现的频次\n", + "all_emp_df.ename.value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6494bb56-7ac7-47df-a9f1-960b02586e31", + "metadata": {}, + "outputs": [], + "source": [ + "all_emp_df.job.value_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "172e4d9a-63bd-44ca-98ea-e4614c8823ab", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计不重复的元素的个数\n", + "all_emp_df.ename.nunique()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6fa062c-d338-407f-8647-e84878a5642e", + "metadata": {}, + "outputs": [], + "source": [ + "# 删除重复值\n", + "# keep='first' - 默认值,重复元素保留第一项 - 'last' / False\n", + "all_emp_df.drop_duplicates(['ename', 'job'], keep='last', inplace=True)\n", + "all_emp_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "832a2ea2-6941-4364-b143-af7db9ff9701", + "metadata": {}, + "outputs": [], + "source": [ + "# 异常值的甄别\n", + "# 数值判定法(data < Q1 - 1.5 * IQR 或者 data > Q3 + 1.5 * IQR)\n", + "\n", + "\n", + "def find_outliers_by_iqr(data, whis=1.5):\n", + " q1, q3 = np.quantile(data, [0.25, 0.75])\n", + " iqr = q3 - q1\n", + " return data[(data < q1 - whis * iqr) | (data > q3 + whis * iqr)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1cd5d6aa-c60e-483e-995c-a627a0dfec15", + "metadata": {}, + "outputs": [], + "source": [ + "temp = np.random.normal(80, 8, 50).round(0)\n", + "temp = np.append(temp, [120, 160, 200, 40, 20, -50])\n", + "temp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2121dab4-0efc-4fcd-a5fe-67585552cb53", + "metadata": {}, + "outputs": [], + "source": [ + "find_outliers_by_iqr(temp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da048825-3f88-4009-9db5-159e8e883b10", + "metadata": {}, + "outputs": [], + "source": [ + "find_outliers_by_iqr(temp, whis=3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0da7034b-2350-43ff-a6eb-9e7f4361bdee", + "metadata": {}, + "outputs": [], + "source": [ + "# zscore判定法(三西格玛法则 ---> 68-95-99.7法则)\n", + "\n", + "\n", + "def find_outliers_by_zscore(data, mul=3):\n", + " mu, sigma = np.mean(data), np.std(data)\n", + " zscore = (data - mu) / sigma\n", + " return data[np.abs(zscore) > mul]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e88616c0-a4d8-4fd8-9ec2-e761cb5ba056", + "metadata": {}, + "outputs": [], + "source": [ + "find_outliers_by_zscore(temp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c902031c-2f78-4721-9734-5c5b0ca81650", + "metadata": {}, + "outputs": [], + "source": [ + "find_outliers_by_zscore(temp, mul=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e295014-d582-4e78-b5b9-6d9f0463ff8d", + "metadata": {}, + "outputs": [], + "source": [ + "find_outliers_by_zscore(df6.直接成本)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97b98c82-fd09-42a9-8a75-a3e71ae10fbc", + "metadata": {}, + "outputs": [], + "source": [ + "# 根据离群点的行索引删除行\n", + "df6.drop(index=find_outliers_by_zscore(df6.直接成本).index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0053ed12-c09f-4331-a6dd-487ff990c680", + "metadata": {}, + "outputs": [], + "source": [ + "med_value = np.median(temp)\n", + "med_value" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f02c2985-1b07-4b1c-b248-aa1de9e98451", + "metadata": {}, + "outputs": [], + "source": [ + "find_outliers_by_zscore(temp, mul=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "485adc15-f39d-419b-9869-2b366f5d88ec", + "metadata": {}, + "outputs": [], + "source": [ + "np.in1d(temp, find_outliers_by_zscore(temp, mul=2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ce92f242-1f0f-476e-ae85-91e1615783ef", + "metadata": {}, + "outputs": [], + "source": [ + "# 替换离群点\n", + "np.place(temp, np.in1d(temp, find_outliers_by_zscore(temp, mul=2)), med_value)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10b0b0bc-f98c-40fe-890f-976df9d9c52b", + "metadata": {}, + "outputs": [], + "source": [ + "temp" + ] + }, + { + "cell_type": "markdown", + "id": "d970e838-42f2-44d0-8f2d-07ebbf6de2b0", + "metadata": {}, + "source": [ + "#### 案例1:招聘数据清洗和预处理\n", + "\n", + "1. 数据加载\n", + "2. 去重\n", + "3. 数据抽取\n", + "4. 拆分列\n", + "5. 替换值\n", + "6. 数据筛选" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ec417a9-457f-434e-96a6-f4fd35d75987", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df = pd.read_csv('res/all_jobs.csv')\n", + "jobs_df.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74e0e4a5-3c03-4617-9661-8cfa03b88fd7", + "metadata": {}, + "outputs": [], + "source": [ + "# 根据URI列去重\n", + "jobs_df.drop_duplicates('uri', inplace=True)\n", + "jobs_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6cca7b8b-25f1-46b8-9946-34ba90f42116", + "metadata": {}, + "outputs": [], + "source": [ + "# 通过正则表达式从列中提取信息\n", + "jobs_df[['salary_lower', 'salary_upper']] = jobs_df.salary.str.extract(r'(\\d+)-(\\d+)').astype('i8')\n", + "jobs_df['salary'] = (jobs_df.salary_lower + jobs_df.salary_upper) / 2\n", + "jobs_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ffaea2af-09f6-4577-9c0d-024966d6854f", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df.drop(columns=['uri', 'city'], inplace=True)\n", + "jobs_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9ba5998-ca1d-44c8-87ca-363356074dd5", + "metadata": {}, + "outputs": [], + "source": [ + "# 拆分列\n", + "jobs_df['city'] = jobs_df.site.str.split(expand=True)[0]\n", + "jobs_df.drop(columns='site', inplace=True)\n", + "jobs_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "933e9006-4f5e-4238-b6d9-940dfeb6caf1", + "metadata": {}, + "outputs": [], + "source": [ + "# 字符串正则表达式替换\n", + "jobs_df['year'] = jobs_df.year.replace(r'5-10年|10年以上', '5年以上', regex=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d10a9c1c-a9d5-49e1-8fdf-a68b5bb3d59a", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df.year.unique()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d248e233-bac5-48d5-8a69-a1f04350867a", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df['edu'] = jobs_df.edu.replace(r'中专|高中', '学历不限', regex=True)\n", + "jobs_df['edu'] = jobs_df.edu.replace(r'硕士|博士', '研究生', regex=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eec6fbd5-2355-4674-9e5d-7f47a5a808a2", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df.edu.unique()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "352b1921-aa2b-4016-af3e-02032b2a3935", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df['job_name'] = jobs_df.job_name.str.lower()\n", + "jobs_df = jobs_df[jobs_df.job_name.str.contains('python|数据|产品|运营|data', regex=True)]\n", + "jobs_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df370013-1278-48d2-9891-8647df3c5e15", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df.to_csv('res/cleand_jobs.csv', index=False)" + ] + }, + { + "cell_type": "markdown", + "id": "8ee07676-737c-420e-b11a-235ff7f2c4c8", + "metadata": {}, + "source": [ + "#### 案例2:北京积分落户数据预处理\n", + "\n", + "1. 加载数据\n", + "2. 日期时间处理\n", + "3. 年龄段分箱\n", + "4. 落户积分归一化" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1232d023-7591-47b3-b67b-4920642dd28d", + "metadata": {}, + "outputs": [], + "source": [ + "settle_df = pd.read_csv('res/2023年北京积分落户数据.csv', index_col='公示编号')\n", + "settle_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "734eb268-3ad7-4e67-9661-08328075992b", + "metadata": {}, + "outputs": [], + "source": [ + "settle_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63698465-ddcd-430c-bd96-e78abaaebda3", + "metadata": {}, + "outputs": [], + "source": [ + "# 将字符串处理成日期\n", + "settle_df['出生年月'] = pd.to_datetime(settle_df['出生年月'])\n", + "settle_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "989c56c7-85fa-4180-9b86-5247a41cdbab", + "metadata": {}, + "outputs": [], + "source": [ + "# 将生日换算成年龄\n", + "settle_df['年龄'] = (pd.to_datetime('2023-01-01') - settle_df.出生年月).dt.days // 365\n", + "settle_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4191c7a2-19fd-4347-ac79-2371c8e59c10", + "metadata": {}, + "outputs": [], + "source": [ + "# 将年龄划分到年龄段 - 分箱 - 数据桶\n", + "settle_df['年龄段'] = pd.cut(\n", + " settle_df.年龄,\n", + " bins=np.arange(35, 61, 5),\n", + " labels=['35~39岁', '40~44岁', '45~49岁', '50~54岁', '55~59岁'],\n", + " right=False\n", + ")\n", + "settle_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea2e0c9b-0aa0-41d3-a52a-6926b797465c", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计每个元素出现的频次\n", + "temp = settle_df.年龄段.value_counts()\n", + "temp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30843274-b940-4527-92ed-97db86bb4ec7", + "metadata": {}, + "outputs": [], + "source": [ + "plt.cm.Greens(np.linspace(0.9, 0.1, 5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "375dd407-9d0a-4788-a38e-3a37efbb6d3b", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制柱状图\n", + "temp.plot(\n", + " kind='bar', # 图表类型\n", + " figsize=(8, 4), # 图表尺寸\n", + " xlabel='', # 横轴标签\n", + " ylabel='Count', # 纵轴标签\n", + " width=0.5, # 柱子宽度\n", + " hatch='//', # 柱子条纹\n", + " color=plt.cm.Greens(np.linspace(0.9, 0.3, temp.size)) # 颜色值\n", + ")\n", + "\n", + "for i in range(temp.size):\n", + " # plt.text(横坐标, 纵坐标, 标签内容)\n", + " plt.text(i, temp.iloc[i] + 30, temp.iloc[i], ha='center')\n", + "\n", + "# 定制横轴的刻度\n", + "plt.xticks(rotation=0)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e020ba6c-d16d-482f-ad3b-a9e855257b91", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制饼图\n", + "temp.plot(\n", + " kind='pie',\n", + " ylabel='',\n", + " autopct='%.1f%%', # 自动计算并显示百分比\n", + " wedgeprops={'width': 0.3}, # 环状结构部分的宽度\n", + " pctdistance=0.85, # 百分比到圆心的距离\n", + " labeldistance=1.1, # 标签到圆心的距离\n", + " # shadow=True, # 阴影效果\n", + " # startangle=0, # 起始角度\n", + " counterclock=True, # 是否反时针方向绘制\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e846eec2-6c95-409c-8b15-2b14cab3f57c", + "metadata": {}, + "outputs": [], + "source": [ + "# agg - aggregate - 聚合\n", + "settle_df.积分分值.agg(['mean', 'max', 'min', 'std', 'skew', 'kurt'])" + ] + }, + { + "cell_type": "markdown", + "id": "b1669102-1c03-4751-813c-b241a05718e3", + "metadata": {}, + "source": [ + "线性归一化:\n", + "$$\n", + "x^{\\prime} = \\frac{x - x_{min}}{x_{max} - x_{min}}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8d9dca7-b976-43ab-96b8-abefca66cc53", + "metadata": {}, + "outputs": [], + "source": [ + "# 将积分分值处理成0~1范围的值\n", + "max_score, min_score = settle_df.积分分值.agg(['max', 'min'])\n", + "max_score, min_score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10acd550-8422-4934-b38f-03554f86d305", + "metadata": {}, + "outputs": [], + "source": [ + "# map - 映射 - 将指定的函数作用到数据系列的每个元素上\n", + "# apply - 应用 - 将指定的函数应用到数据系列的每个元素上\n", + "settle_df['线性归一化积分'] = settle_df.积分分值.map(lambda x: (x - min_score) / (max_score - min_score)).round(2)\n", + "settle_df" + ] + }, + { + "cell_type": "markdown", + "id": "55e57b00-cb9e-4c9e-bc59-e99b738e2f5d", + "metadata": {}, + "source": [ + "zscore标准化:\n", + "$$\n", + "x^{\\prime} = \\frac{x - \\mu}{\\sigma}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5fc6260-5337-4161-99f0-d7be43d59361", + "metadata": {}, + "outputs": [], + "source": [ + "mu, sigma = settle_df.积分分值.agg(['mean', 'std'])\n", + "settle_df['zscore评分'] = settle_df.积分分值.apply(lambda x: (x - mu) / sigma)\n", + "settle_df" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Day66-80/code/day05.ipynb b/Day66-80/code/day05.ipynb new file mode 100644 index 0000000..0834e84 --- /dev/null +++ b/Day66-80/code/day05.ipynb @@ -0,0 +1,673 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "97982f4b-b3d0-47fe-b6e8-a1a327d03d93", + "metadata": {}, + "source": [ + "## 深入浅出pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "834d7d19-4015-4048-a1a6-2b8c756f0115", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56becc00-f1c7-4f81-86d1-7f7b23fd65d4", + "metadata": {}, + "outputs": [], + "source": [ + "%config InlineBackend.figure_format = 'svg'" + ] + }, + { + "cell_type": "markdown", + "id": "c81bfe94-906c-4e13-b321-0e6c397aea46", + "metadata": {}, + "source": [ + "### 数据透视\n", + "\n", + "1. 数据聚合(指标统计)\n", + "2. 排序和头部值\n", + "3. 透视表和交叉表" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7836ce8b-1e1d-40e5-b01c-c228ebf60928", + "metadata": {}, + "outputs": [], + "source": [ + "sales_df = pd.read_excel('res/2020年销售数据.xlsx', sheet_name='data')\n", + "sales_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca3b2020-9470-4e49-a3ca-066dc9a437b3", + "metadata": {}, + "outputs": [], + "source": [ + "sales_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "224ae45b-172a-48bc-8df0-7d6c7099529a", + "metadata": {}, + "outputs": [], + "source": [ + "# 添加销售额、毛利润、月份列\n", + "sales_df['销售额'] = sales_df.售价 * sales_df.销售数量\n", + "sales_df['毛利润'] = sales_df.销售额 - sales_df.直接成本\n", + "sales_df['月份'] = sales_df.销售日期.dt.month\n", + "sales_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8878bfd-64de-4a6a-9ae3-36e9af40d45e", + "metadata": {}, + "outputs": [], + "source": [ + "def make_tag(price):\n", + " if price < 300:\n", + " return '低端'\n", + " elif price < 800:\n", + " return '中端'\n", + " return '高端'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4cba4262-b952-4fce-9b07-fed9c5f2a2e3", + "metadata": {}, + "outputs": [], + "source": [ + "# 根据商品的价格添加价位标签\n", + "sales_df['价位'] = sales_df.售价.apply(make_tag)\n", + "sales_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2a2b472-55fc-4d35-8530-445e8ffbf800", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计北极星指标\n", + "GMV, profit, quantity = sales_df[['销售额', '毛利润', '销售数量']].sum()\n", + "print(f'销售额: {GMV}元')\n", + "print(f'毛利润: {profit}元')\n", + "print(f'销售数量: {quantity}件')\n", + "print(f'毛利率: {profit / GMV:.2%}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b6ac225-6834-462f-a5b7-8be8297b66d0", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计每个月的销售额和毛利润\n", + "temp1 = sales_df.groupby('月份')[['销售额', '毛利润']].agg('sum')\n", + "temp1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8f179a1-c270-458d-be84-b862ca2c143d", + "metadata": {}, + "outputs": [], + "source": [ + "# 使用透视表统计每个月的销售额和毛利润\n", + "pd.pivot_table(\n", + " sales_df,\n", + " index='月份',\n", + " values=['销售额', '毛利润'],\n", + " aggfunc='sum'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a3cb912-c47f-4d47-8bd4-ffc9eb171cba", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制折线图\n", + "temp1.plot(\n", + " kind='line',\n", + " figsize=(10, 5),\n", + " y=['销售额', '毛利润'], # 放到纵轴上的数据\n", + " xlabel='', # 横轴的标签\n", + " ylabel='销售额和毛利润', # 纵轴的标签\n", + " marker='^', # 标记点符号\n", + ")\n", + "# plt.fill_between(np.arange(1, 13), temp1.销售额, where=temp1.销售额 >= 3e6, facecolor='red', alpha=0.25)\n", + "# plt.fill_between(np.arange(1, 13), temp1.销售额, where=temp1.销售额 < 3e6, facecolor='green', alpha=0.25)\n", + "# 定制纵轴的取值范围\n", + "plt.ylim(0, 6e6)\n", + "# 定制横轴的刻度\n", + "plt.xticks(np.arange(1, 13), labels=[f'{x}月' for x in range(1, 13)])\n", + "# 定制标题\n", + "plt.title('2020年月度销售额和毛利润', fontdict={'fontsize': 22, 'color': 'navy'})\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "389db27d-2b11-47ea-a8c6-660fcc39b863", + "metadata": {}, + "outputs": [], + "source": [ + "plt.cm.RdYlBu_r" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "621a2487-2d15-450d-9f69-df8089616bd9", + "metadata": {}, + "outputs": [], + "source": [ + "# 计算月环比\n", + "temp1['销售额月环比'] = temp1.销售额.pct_change()\n", + "temp1['毛利润月环比'] = temp1.毛利润.pct_change()\n", + "# 索引重排序\n", + "temp1 = temp1.reindex(columns=['销售额', '销售额月环比', '毛利润', '毛利润月环比'])\n", + "# 渲染输出\n", + "temp1.style.format(\n", + " formatter={\n", + " '销售额月环比': '{:.2%}',\n", + " '毛利润月环比': '{:.2%}'\n", + " },\n", + " na_rep='-------'\n", + ").background_gradient(\n", + " 'RdYlBu_r',\n", + " subset=['销售额月环比', '毛利润月环比']\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b158aea-fe6e-4938-a4f8-4b880c6f23d0", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制横线图\n", + "mu = temp1.销售额.mean()\n", + "temp1['diff'] = temp1.销售额 - mu\n", + "temp1['colors'] = temp1.销售额.map(lambda x: 'green' if x > mu else 'red')\n", + "\n", + "plt.figure(figsize=(8, 6), dpi=200)\n", + "plt.hlines(y=temp1.index, xmin=0, xmax=temp1['diff'], color=temp1.colors, alpha=0.6, linewidth=6)\n", + "plt.yticks(np.arange(1, 13), labels=[f'{x}月' for x in np.arange(1, 13)])\n", + "# 定制网格线\n", + "plt.grid(linestyle='--', linewidth=0.4, alpha=0.5)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "810936a1-f6c5-43e9-9eda-b9fe8014ba9f", + "metadata": {}, + "outputs": [], + "source": [ + "# 各品牌对销售额贡献占比\n", + "temp2 = sales_df.groupby('品牌')['销售额'].sum()\n", + "temp2.plot(\n", + " kind='pie',\n", + " ylabel='',\n", + " autopct='%.2f%%', # 自动计算并显示百分比\n", + " pctdistance=0.82, # 百分比标签到圆心的距离\n", + " wedgeprops=dict(width=0.35, edgecolor='w'), # 定制环状饼图\n", + " explode=[0.1, 0, 0, 0, 0], # 分离饼图\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1465d545-14e8-435d-9b12-651df3bab9ea", + "metadata": {}, + "outputs": [], + "source": [ + "# 各销售区域每个月的销售额\n", + "temp3 = sales_df.groupby(['销售区域', '月份'], as_index=False)[['销售额']].sum()\n", + "# pivot - 将行旋转到列上(窄表 ----> 宽表)\n", + "# melt - 将列旋转到行上(宽表 ----> 窄表)\n", + "temp3.pivot(index='销售区域', columns='月份', values='销售额').fillna(0).astype('i8')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1adf23bb-7120-416c-846e-1098aac2e1f4", + "metadata": {}, + "outputs": [], + "source": [ + "# 创建透视表\n", + "pd.pivot_table(\n", + " sales_df,\n", + " index='销售区域',\n", + " columns='月份',\n", + " values='销售额',\n", + " aggfunc='sum',\n", + " fill_value=0,\n", + " margins=True,\n", + " margins_name='总计'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fdf54c9a-64c3-453d-bc5b-533fce7b25ed", + "metadata": {}, + "outputs": [], + "source": [ + "# 将价位字段处理成category类型并指定排序的顺序\n", + "sales_df['价位'] = sales_df.价位.astype('category').cat.reorder_categories(['高端', '中端', '低端'])\n", + "sales_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e706575-3260-4b37-99fe-696db4f6fe7b", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计每个月各种价位产品的销量\n", + "temp4 = sales_df.pivot_table(\n", + " index='价位',\n", + " columns='月份',\n", + " values='销售数量',\n", + " observed=False,\n", + " fill_value=0,\n", + " aggfunc='sum'\n", + ")\n", + "temp4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12527025-64f5-4a0a-9bf5-6bdbc4b6f93b", + "metadata": {}, + "outputs": [], + "source": [ + "# 交叉表\n", + "pd.crosstab(\n", + " index=sales_df.价位,\n", + " columns=sales_df.月份,\n", + " values=sales_df.销售数量,\n", + " aggfunc='sum'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d99ecd49-2669-493c-ae04-974afa71dc4d", + "metadata": {}, + "outputs": [], + "source": [ + "blood_types = np.array(['B', 'A', 'O', 'O', 'AB', 'B', 'O', 'B', 'AB', 'A', 'A', 'O', 'B', 'O', 'O', 'O', 'O', 'A', 'B', 'B'])\n", + "personality_types = np.array(['𝛃', '𝛂', '𝛂', '𝛂', '𝛃', '𝛂', '𝛄', '𝛄', '𝛂', '𝛄', '𝛃', '𝛂', '𝛂', '𝛂', '𝛄', '𝛄', '𝛂', '𝛂', '𝛂', '𝛂'])\n", + "\n", + "# 创建交叉表\n", + "pd.crosstab(\n", + " index=blood_types,\n", + " columns=personality_types,\n", + " rownames=['血型'],\n", + " colnames=['人格'],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3cba2e9e-8335-4081-a253-5ab6d08d8559", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制堆叠柱状图\n", + "temp4.T.plot(\n", + " figsize=(10, 4),\n", + " kind='bar',\n", + " width=0.6,\n", + " xlabel='',\n", + " ylabel='销售数量',\n", + " stacked=True\n", + ")\n", + "plt.xticks(rotation=0)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9eba1698-d8a5-4b66-b617-fc68072a670e", + "metadata": {}, + "outputs": [], + "source": [ + "# 让每一项数据除以对应月份的销售数量之和\n", + "temp5 = temp4.T.divide(temp4.sum(), axis=0)\n", + "temp5" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1ce2e80e-0af9-4162-a46d-b5d357a9362d", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制百分比堆叠柱状图\n", + "temp5.plot(\n", + " figsize=(10, 4),\n", + " kind='bar',\n", + " width=0.6,\n", + " xlabel='',\n", + " ylabel='销量占比',\n", + " stacked=True\n", + ")\n", + "plt.xticks(rotation=0)\n", + "plt.yticks(np.linspace(0, 1, 6), labels=[f'{x:.0%}' for x in np.linspace(0, 1, 6)])\n", + "plt.legend(loc='lower center')\n", + "\n", + "for i in temp5.index:\n", + " y1, y2, y3 = temp5.loc[i]\n", + " plt.text(i - 1, y2 / 2 + y1, f'{y2:.2%}', ha='center', va='center', fontdict={'size': 8})\n", + " plt.text(i - 1, y3 / 2 + y2 + y1, f'{y3:.2%}', ha='center', va='center', fontdict={'size': 8})\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "82ac6b37-302e-4913-a973-4a7f4e6daf1d", + "metadata": {}, + "source": [ + "### 作业:招聘岗位数据分析\n", + "\n", + "1. 统计出城市、招聘信息、招聘岗位的数量和平均月薪。\n", + "2. 统计每个城市的岗位数量从高到低排序。\n", + "3. 统计每个城市的平均薪资从高到低排序。\n", + "4. 统计招聘岗位对学历要求的占比。\n", + "5. 统计招聘岗位对工作年限的要求占比。\n", + "6. 分析薪资跟学历和工作年限的关系。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91e8b171-7568-4da1-8170-5d13fc8945bc", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df = pd.read_csv('res/cleaned_jobs.csv')\n", + "jobs_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cadf547-f7aa-494b-beeb-678f5a09e967", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计北极星指标\n", + "city_count = jobs_df['city'].nunique()\n", + "info_count = jobs_df['company_name'].count()\n", + "post_count = jobs_df['pos_count'].sum()\n", + "salary_avg = jobs_df['salary'].mean().round(1)\n", + "print(f'城市数量: {city_count}')\n", + "print(f'信息数量: {info_count}')\n", + "print(f'岗位数量: {post_count}')\n", + "print(f'平均薪资: {salary_avg}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb9fc057-df56-4f01-a08e-ab3427dca467", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计每个城市的岗位数量从高到低排序\n", + "jobs_df.groupby('city')[['pos_count']].sum().sort_values(by='pos_count', ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "430af08e-2ce2-4f4f-9d64-df2d09c7c9f3", + "metadata": {}, + "outputs": [], + "source": [ + "pd.pivot_table(\n", + " jobs_df,\n", + " index='city',\n", + " values='pos_count',\n", + " aggfunc='sum'\n", + ").sort_values(by='pos_count', ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcd4aeca-bb90-42e6-b45c-6cdea2c76271", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df.groupby('city')[['salary']].mean().round(1).sort_values(by='salary', ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e93f5c8-da3e-4064-ac77-748eb3167504", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计每个城市的平均薪资从高到低排序\n", + "pd.pivot_table(\n", + " jobs_df,\n", + " index='city',\n", + " values='salary',\n", + " aggfunc='mean'\n", + ").round(1).sort_values(by='salary', ascending=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ae067bf-2981-49f2-a224-28a5223dc21f", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df['edu'] = jobs_df.edu.astype('category').cat.reorder_categories(['学历不限', '大专', '本科', '研究生'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a87fab8f-18af-4c0b-9546-63d79778b4d8", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计招聘岗位对学历要求占比\n", + "pd.pivot_table(\n", + " jobs_df,\n", + " index='edu',\n", + " values='pos_count',\n", + " aggfunc='sum',\n", + " observed=True\n", + ").plot(\n", + " kind='pie',\n", + " ylabel='',\n", + " subplots=True,\n", + " legend=False,\n", + " autopct='%.2f%%',\n", + " pctdistance=0.85,\n", + " wedgeprops={'width': 0.35}\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15202818-48a8-40bb-8701-47bc1b481f80", + "metadata": {}, + "outputs": [], + "source": [ + "jobs_df['year'] = jobs_df.year.astype('category').cat.reorder_categories(['应届生', '1年以内', '经验不限', '1-3年', '3-5年', '5年以上'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6d965229-36d5-4c38-850c-3300726f208a", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计招聘岗位对工作年限要求绘制饼图\n", + "pd.pivot_table(\n", + " jobs_df,\n", + " index='year',\n", + " values='pos_count',\n", + " aggfunc='sum',\n", + " observed=True\n", + ").plot(\n", + " kind='pie',\n", + " y='pos_count',\n", + " ylabel='',\n", + " legend=False,\n", + " autopct='%.2f%%',\n", + " pctdistance=0.85,\n", + " wedgeprops={'width': 0.35}\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c7ab419-0bc4-428f-be9b-6d4d617b6663", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计不同学历和工作年限平均薪资\n", + "temp6 = pd.pivot_table(\n", + " jobs_df,\n", + " index='edu',\n", + " columns='year',\n", + " values='salary',\n", + " observed=False,\n", + " fill_value=0\n", + ").round(1)\n", + "temp6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8492b17-5ae8-47f0-a058-ab303a6087a9", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制热力图\n", + "plt.imshow(temp6, cmap='Reds')\n", + "plt.xticks(np.arange(6), labels=temp6.columns)\n", + "plt.yticks(np.arange(4), labels=temp6.index)\n", + "\n", + "for i in range(temp6.index.size):\n", + " for j in range(temp6.columns.size):\n", + " value = temp6.iat[i, j]\n", + " color = 'w' if value > salary_avg else 'k'\n", + " plt.text(j, i, value, ha='center', va='center', color=color)\n", + "\n", + "# 定制颜色条\n", + "plt.colorbar()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3eb8cd0-fe07-43a4-9514-8a38c08ca081", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install seaborn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6287994-0865-432a-9932-92b05b5bf7e8", + "metadata": {}, + "outputs": [], + "source": [ + "import seaborn as sns\n", + "\n", + "sns.heatmap(temp6, cmap='Reds', annot=True)\n", + "plt.xlabel('')\n", + "plt.ylabel('')\n", + "plt.yticks(rotation=0)\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Day66-80/code/day06.ipynb b/Day66-80/code/day06.ipynb new file mode 100644 index 0000000..0cdd91f --- /dev/null +++ b/Day66-80/code/day06.ipynb @@ -0,0 +1,809 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e4ad3216-d45c-4328-a509-3c01e0fec4d5", + "metadata": {}, + "source": [ + "## 深入浅出pandas" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5faacbe5-d44a-4e0e-a287-19270bc1a693", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False\n", + "get_ipython().run_line_magic('config', \"InlineBackend.figure_format = 'svg'\")" + ] + }, + { + "cell_type": "markdown", + "id": "9b6101ba-4d7b-408c-8f94-76cf789ab71a", + "metadata": {}, + "source": [ + "### 科比投篮数据分析\n", + "\n", + "1. 科比使用得最多的投篮动作\n", + "2. 科比交手次数最多的球队\n", + "3. 科比有出手的比赛有多少场\n", + "4. 科比职业生涯(常规赛+季后赛)总得分(不含罚篮)\n", + "5. 科比得分最高的五场比赛(对手、投篮次数、得分、命中率)\n", + "6. 科比得分最多的三个赛季(赛季、投篮次数、得分、命中率)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5008a34f-e108-4c35-a71e-0b28e2f4cbfc", + "metadata": {}, + "outputs": [], + "source": [ + "# 不限制最大显示的列数\n", + "pd.set_option('display.max_columns', None)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "526e6f01-7b18-487a-bd89-fdc5a5a494b0", + "metadata": {}, + "outputs": [], + "source": [ + "# 加载科比投篮数据\n", + "kobe_df = pd.read_csv('res/科比投篮数据.csv', index_col='shot_id')\n", + "kobe_df.tail(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "127b7ab9-568c-4982-8238-d5fcafe6e9b2", + "metadata": {}, + "outputs": [], + "source": [ + "kobe_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4cbe2e96-0bbe-43a7-bafc-f5f956090ea4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 科比使用得最多的投篮动作是什么\n", + "kobe_df.action_type.value_counts().index[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46bd394d-05f3-4adc-bc69-d8ef70e1f877", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "kobe_df.groupby('action_type')['action_type'].count().nlargest(1).index[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ebb1eab-a675-4f2f-833b-ff7c4207ec77", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 科比交手次数最多的球队是哪支队伍\n", + "kobe_df.drop_duplicates('game_id').opponent.value_counts().index[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b53491a-1e04-4684-94de-ddcf4eba0a9d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "kobe_df.drop_duplicates('game_id').groupby('opponent').opponent.count().idxmax()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf1b9f70-23df-4103-87db-7727329eb679", + "metadata": {}, + "outputs": [], + "source": [ + "# 科比有出手的比赛有多少场\n", + "kobe_df.game_id.nunique()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91dfb7c3-c2d8-4eff-9df9-8be558e054d1", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 统计科比常规赛和季后赛的投篮命中率\n", + "temp = kobe_df.dropna().pivot_table(index=['playoffs', 'shot_type'], columns=['shot_made_flag'], values='game_id', aggfunc='count')\n", + "temp = temp.divide(temp.sum(axis=1), axis=0)\n", + "temp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e998d2d7-1259-479d-b74e-6e9dccf8de36", + "metadata": {}, + "outputs": [], + "source": [ + "# 填充shot_made_flag字段的缺失值\n", + "def handle(x):\n", + " playoffs, shot_type, shot_made_flag = x\n", + " if np.isnan(shot_made_flag):\n", + " shot_made_flag = 1 if np.random.random() < temp.at[(playoffs, shot_type), 1.0] else 0\n", + " return shot_made_flag\n", + "\n", + "\n", + "kobe_df['shot_made_flag'] = kobe_df[['playoffs', 'shot_type', 'shot_made_flag']].apply(handle, axis=1).astype('?')\n", + "kobe_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1fd5dbf5-e89f-4f6c-9867-cca583d08527", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 处理得分字段\n", + "kobe_df['point'] = kobe_df.shot_type.str[0].astype('i8')\n", + "kobe_df['point'] = kobe_df[['shot_made_flag', 'point']].apply(lambda x: x.loc['point'] if x.loc['shot_made_flag'] else 0, axis=1)\n", + "kobe_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b431b8a2-600e-4ed2-8092-0ed8598043f4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 参考数据:投篮命中数11719\n", + "kobe_df.shot_made_flag.sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2fdf20e-54d2-43b4-a5b2-52b6f7e815c4", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 参考数据:不含罚篮的投篮得分25265\n", + "kobe_df.point.sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8929cfa5-2152-45ad-9ac4-ce9154ef8830", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 科比得分最多的赛季是哪个赛季和分数\n", + "kobe_df.groupby('season').point.sum().nlargest(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5bd3ccf-6654-4c84-a295-4142fd24cebe", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 获得得分最高的5场比赛的game_id\n", + "index = kobe_df.groupby('game_id').point.sum().nlargest(5).index.values\n", + "index" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e48b6dfb-c6f8-40c5-beda-a1e50ae742b4", + "metadata": {}, + "outputs": [], + "source": [ + "# 用布尔索引筛选数据\n", + "kobe_df[np.in1d(kobe_df.game_id, index)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6ba3500-a50f-400c-a2bc-7f04cf0e3393", + "metadata": {}, + "outputs": [], + "source": [ + "# 用query方法筛选数据\n", + "kobe_df.query('game_id in @index')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "171a9b52-e917-4b41-9472-80532cc1a72e", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# 科比得分最高的五场比赛(对手、投篮次数、得分、命中率)\n", + "# 参考数据(含罚篮):TOR - 81分 / POR - 65分 / DAL - 62分 / NYK - 61分 / MEM - 60分 / UTA - 60分\n", + "df1 = kobe_df[np.in1d(kobe_df.game_id, index)].groupby(\n", + " 'game_id'\n", + ")[['game_date', 'opponent', 'game_id', 'shot_made_flag', 'point']].agg({\n", + " 'game_date': 'max',\n", + " 'opponent': 'max',\n", + " 'game_id': 'count',\n", + " 'shot_made_flag': 'sum',\n", + " 'point': 'sum'\n", + "})\n", + "df1['rate'] = df1.shot_made_flag / df1.game_id\n", + "df1.drop(columns=['shot_made_flag'], inplace=True)\n", + "df1.reset_index(drop=True, inplace=True)\n", + "df1.set_index('game_date', inplace=True)\n", + "df1.rename(columns={'opponent': '对手', 'game_id': '出手次数', 'point': '得分', 'rate': '命中率'}, inplace=True)\n", + "df1.sort_values(by='得分', ascending=False).style.format(formatter={'命中率': '{:.2%}'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a129218e-4ceb-4004-9b91-5668b8d3940f", + "metadata": {}, + "outputs": [], + "source": [ + "df2 = kobe_df.query('game_id in @index').groupby(\n", + " 'game_id'\n", + ")[['game_date', 'opponent', 'game_id', 'shot_made_flag', 'point']].agg({\n", + " 'game_date': 'max',\n", + " 'opponent': 'max',\n", + " 'game_id': 'count',\n", + " 'shot_made_flag': 'sum',\n", + " 'point': 'sum'\n", + "})\n", + "df2['rate'] = df2.shot_made_flag / df2.game_id\n", + "df2.drop(columns=['shot_made_flag'], inplace=True)\n", + "df2.reset_index(drop=True, inplace=True)\n", + "df2.set_index('game_date', inplace=True)\n", + "df2.rename(columns={'opponent': '对手', 'game_id': '出手次数', 'point': '得分', 'rate': '命中率'}, inplace=True)\n", + "df2.sort_values(by='得分', ascending=False).style.format(formatter={'命中率': '{:.2%}'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "473883f8-acfa-4d32-806d-4201c50106e1", + "metadata": {}, + "outputs": [], + "source": [ + "# 科比得分最多的三个赛季(赛季、投篮次数、得分、命中率)\n" + ] + }, + { + "cell_type": "markdown", + "id": "c1ae87f9-1698-48b8-be11-0edd47cae298", + "metadata": {}, + "source": [ + "### 深圳二手房数据分析\n", + "\n", + "1. 统计深圳二手房单价分布规律\n", + "2. 统计深圳二手房总价分布规律\n", + "3. 统计每个区总价和均价的均值\n", + "4. 深圳每个区单价Top3的商圈\n", + "5. 哪种户型的二手房数量最多\n", + "6. 总价Top10的二手房分布在哪些区" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "041ac635-9598-4a10-9e04-664678f23448", + "metadata": {}, + "outputs": [], + "source": [ + "sz_df = pd.read_csv('res/深圳二手房数据.csv')\n", + "sz_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7298d0f3-d9ef-4cee-975d-8d720711d749", + "metadata": {}, + "outputs": [], + "source": [ + "sz_df.drop(columns='Unnamed: 0', inplace=True)\n", + "sz_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f140da58-6846-4595-b365-359cdc5b7dd1", + "metadata": {}, + "outputs": [], + "source": [ + "# 修正列名\n", + "sz_df.rename(columns={'hourseType': 'house_type', 'hourseSize': 'house_size'}, inplace=True)\n", + "sz_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "731f00a4-1db8-4aac-ba7c-7bf65ebc3b40", + "metadata": {}, + "outputs": [], + "source": [ + "# 将tax字段处理为bool类型\n", + "sz_df['tax'] = sz_df.tax.fillna('').astype('?')\n", + "sz_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b2bad9d-ae47-478d-8be1-139121742012", + "metadata": {}, + "outputs": [], + "source": [ + "sz_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a780b30-c8d8-4b40-b416-99b1d6572e26", + "metadata": {}, + "outputs": [], + "source": [ + "# 获取描述性统计信息\n", + "sz_df.total_price.agg(['mean', 'max', 'min', 'skew', 'kurt'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2b4a48c-9775-424f-bc10-4bc1d71b3813", + "metadata": {}, + "outputs": [], + "source": [ + "sz_df.unit_price.agg(['mean', 'max', 'min', 'skew', 'kurt'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c742fc0c-9b3c-4b8d-861a-879012677458", + "metadata": {}, + "outputs": [], + "source": [ + "sz_df.house_size.agg(['mean', 'max', 'min', 'skew', 'kurt'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c6a64d4-7bd8-4d04-b9a9-3d787ba9c732", + "metadata": {}, + "outputs": [], + "source": [ + "sz_df[sz_df.unit_price < 10000]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22989374-3017-48a5-9802-61459df9f8d6", + "metadata": {}, + "outputs": [], + "source": [ + "# 删除异常数据(单价小于10000)\n", + "sz_df.drop(index=sz_df[sz_df.unit_price < 10000].index, inplace=True)\n", + "sz_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02a1b493-6d66-4e31-8298-bbe50eae9676", + "metadata": {}, + "outputs": [], + "source": [ + "# 删除面积在10平米以下200平米以上的房屋信息\n", + "sz_df.drop(index=sz_df.query('house_size < 10 or house_size > 200').index, inplace=True)\n", + "sz_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf37b2b5-1493-4a2b-a16c-3df39a99fcf2", + "metadata": {}, + "outputs": [], + "source": [ + "# 添加一个总房间数字段\n", + "sz_df['rooms_num'] = sz_df.house_type.str.extract('(\\d+)室(\\d+)厅').astype('i8').sum(axis=1)\n", + "sz_df.rooms_num.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f605b5bd-9ccf-4946-b084-c05db4b1f78b", + "metadata": {}, + "outputs": [], + "source": [ + "sz_df.tail(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f197094-8843-4575-bf87-8e6599d4dce9", + "metadata": {}, + "outputs": [], + "source": [ + "# 删除房间总数大于8个的房屋信息\n", + "sz_df.drop(index=sz_df[sz_df.rooms_num > 8].index, inplace=True)\n", + "sz_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "920d0e09-fa58-456a-9d3d-6329ef49ae67", + "metadata": {}, + "outputs": [], + "source": [ + "# 单价分布\n", + "sz_df.unit_price.plot(kind='hist', figsize=(9, 5), bins=15, ylabel='')\n", + "plt.xticks(np.arange(10000, 210001, 20000))\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fd023d2-b7a9-456e-a58d-731512711484", + "metadata": {}, + "outputs": [], + "source": [ + "# 总价分布\n", + "sz_df.total_price.plot(kind='hist', figsize=(9, 5), bins=15, ylabel='')\n", + "plt.xticks(np.arange(100, 2901, 400))\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1d68635-d7fa-44dc-97dc-8938cba1de80", + "metadata": {}, + "outputs": [], + "source": [ + "# 统计每个区总价和均价的均值\n", + "sz_df.pivot_table(\n", + " index='area',\n", + " values=['title', 'unit_price', 'total_price'],\n", + " aggfunc={'title': 'count', 'unit_price': 'mean', 'total_price': 'mean'}\n", + ").round(1).sort_values(\n", + " 'unit_price', ascending=False\n", + ").style.format(\n", + " formatter={\n", + " 'total_price': '¥{:.0f}万元',\n", + " 'unit_price': '¥{:,.0f}元'\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "538ebe18-432e-4951-8667-ce90cb73ae5e", + "metadata": {}, + "outputs": [], + "source": [ + "# 深圳每个区房屋平均单价Top3商圈\n", + "temp_df = sz_df.groupby(['area', 'position'])[['unit_price']].mean().round(1)\n", + "temp_df['rank'] = temp_df.unit_price.groupby('area').rank(method='dense', ascending=False).astype('i8')\n", + "temp_df.query('rank <= 3')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04aaf617-240d-4fc2-b702-fa3e03978347", + "metadata": {}, + "outputs": [], + "source": [ + "# 深圳每个区房屋平均单价Top3商圈\n", + "temp_df = sz_df.groupby(['area', 'position'], as_index=False)[['unit_price']].mean().round(1)\n", + "temp_df = temp_df.groupby('area')[['position', 'unit_price']].apply(lambda x: x.nlargest(3, 'unit_price'))\n", + "temp_df.style.hide(level=1).format(formatter={'unit_price': '¥{:,.0f}元'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38f91365-6b40-40a1-b8bd-f0fd4cac9d50", + "metadata": {}, + "outputs": [], + "source": [ + "# 哪种户型的二手房数量最多\n", + "sz_df.groupby('house_type').house_type.count().nlargest(1).index[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4db07743-51cd-4007-8faf-7aa5714167d9", + "metadata": {}, + "outputs": [], + "source": [ + "# 总价Top10的二手房分布在哪些区\n", + "top10 = sz_df.total_price.nlargest(10).index.values\n", + "# 通过花式索引获取对应的行\n", + "sz_df.loc[top10].groupby('area').area.count()" + ] + }, + { + "cell_type": "markdown", + "id": "870a64c4-3164-4b96-b910-fc80b5e44853", + "metadata": {}, + "source": [ + "### 销售利润下滑诊断分析" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1e63fa6-018e-4cea-9899-05bc4a4aaae2", + "metadata": {}, + "outputs": [], + "source": [ + "detail_df = pd.read_excel('res/商品销售明细表.xlsx', sheet_name='Sheet1')\n", + "outlet_df = pd.read_excel('res/门店信息维度表.xlsx', sheet_name='Sheet1')\n", + "commod_df = pd.read_excel('res/商品信息维度表.xlsx', sheet_name='Sheet1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb430f84-5628-4dc5-a63a-36520cfcf66a", + "metadata": {}, + "outputs": [], + "source": [ + "detail_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41a2f93a-3799-4253-8acd-6f3498283010", + "metadata": {}, + "outputs": [], + "source": [ + "detail_df.rename(columns={'日期(年月日)': '销售日期'}, inplace=True)\n", + "detail_df['销售日期'] = pd.to_datetime(detail_df.销售日期)\n", + "detail_df['月份'] = detail_df.销售日期.dt.month\n", + "detail_df['利润额'] = detail_df.销售额 - detail_df.成本额\n", + "detail_df.head(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f94c5f3-c799-470f-a693-7d01afa4f88f", + "metadata": {}, + "outputs": [], + "source": [ + "temp1 = detail_df.groupby('月份')[['销售额', '利润额']].sum()\n", + "temp1['销售月环比'] = temp1.销售额.pct_change()\n", + "temp1['利润月环比'] = temp1.利润额.pct_change()\n", + "temp1[['销售额', '销售月环比', '利润额', '利润月环比']].style.format(\n", + " formatter={\n", + " '销售月环比': '{:.2%}',\n", + " '利润月环比': '{:.2%}',\n", + " },\n", + " na_rep='--------'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56b4ab4a-aa35-49c5-9387-80b545799cfe", + "metadata": {}, + "outputs": [], + "source": [ + "temp1.plot(kind='line', figsize=(9, 5), xlabel='', y=['销售额', '利润额'], color=['navy', 'coral'], marker='o')\n", + "plt.ylim(0, 1.4e7)\n", + "plt.grid(axis='y', linestyle=':', alpha=0.5)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00ca4494-b03d-4f42-890f-9d8088bb61e7", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.ticker as tkr\n", + "\n", + "ax = temp1.销售额.plot(kind='line', figsize=(9, 5), marker='o', color='navy', linestyle='--')\n", + "temp1.利润额.plot(ax=ax, kind='line', marker='*', color='darkgreen', linestyle='--', xlabel='')\n", + "plt.ylim(0, 14000000)\n", + "plt.legend(loc='lower right')\n", + "\n", + "# 基于ax构建双胞胎坐标系(共享横轴,自己定制纵轴)\n", + "ax2 = ax.twinx()\n", + "ax2.yaxis.set_major_formatter(tkr.PercentFormatter(xmax=1, decimals=0))\n", + "profs_rates = temp1.利润额 / temp1.销售额\n", + "profs_rates.plot(ax=ax2, kind='line', marker='^', color='r', linestyle=':', label='毛利率')\n", + "plt.ylim(0.45, 0.65)\n", + "plt.legend()\n", + "plt.grid(axis='y', linestyle=':', alpha=0.5)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "637679a6-f7e1-4de8-a17e-4541790aa99c", + "metadata": {}, + "outputs": [], + "source": [ + "# 事实表连接维度表\n", + "merged_df = pd.merge(detail_df, outlet_df, how='left', on='门店编码')\n", + "merged_df = pd.merge(merged_df, commod_df, how='left', on='商品编码')\n", + "august_df = merged_df.query('月份 == 8')\n", + "august_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e49793fb-6149-4366-94f6-2546fd14065e", + "metadata": {}, + "outputs": [], + "source": [ + "temp_df2 = august_df.groupby('省份')[['销售额', '成本额']].sum()\n", + "temp_df2.nlargest(10, '成本额')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6959bb8b-a2e5-4948-bfc1-c78be4a3d1d9", + "metadata": {}, + "outputs": [], + "source": [ + "temp_df2.nlargest(10, '成本额').plot(kind='bar', figsize=(9, 5), xlabel='')\n", + "plt.xticks(rotation=0)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e62162ff-af16-4a8f-af56-32b64712f0a5", + "metadata": {}, + "outputs": [], + "source": [ + "temp_df3 = august_df.query('省份 == \"湖南省\"').groupby('城市')[['销售额', '成本额']].sum()\n", + "temp_df3['利润率'] = (temp_df3.销售额 - temp_df3.成本额) / temp_df3.销售额\n", + "temp_df3.nsmallest(3, '利润率').style.format(formatter={'利润率': '{:.2%}'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5626ff61-f8dd-4871-9ad3-28c25910d36b", + "metadata": {}, + "outputs": [], + "source": [ + "temp_df4 = august_df.query('省份 == \"湖南省\" and 城市 == \"长沙市\"').groupby('门店名称')[['销售额', '成本额']].sum()\n", + "temp_df4['利润率'] = (temp_df4.销售额 - temp_df4.成本额) / temp_df4.销售额\n", + "temp_df4.sort_values(by='利润率').style.format(formatter={'利润率': '{:.2%}'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4993f987-37ad-450c-971a-d54ff4f6bc29", + "metadata": {}, + "outputs": [], + "source": [ + "august_df = august_df.query('省份 == \"湖南省\" and 城市 == \"长沙市\" and 门店名称 == \"长沙梅溪湖店\"')\n", + "august_df.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "04dfedf1-e7f1-4493-be54-a32b5e997e0d", + "metadata": {}, + "outputs": [], + "source": [ + "temp_df5 = august_df.groupby('商品类别')[['销售额', '成本额']].sum()\n", + "temp_df5['利润率'] = (temp_df5.销售额 - temp_df5.成本额) / temp_df5.销售额\n", + "temp_df5.sort_values(by='利润率').style.format(formatter={'利润率': '{:.2%}'})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fd3e504-3292-4bab-b3dd-f2f3629f6285", + "metadata": {}, + "outputs": [], + "source": [ + "temp_df6 = august_df.query('商品类别 == \"零食\"').groupby('商品名称')[['销售额', '成本额']].sum()\n", + "temp_df6['利润率'] = (temp_df6.销售额 - temp_df6.成本额) / temp_df6.销售额\n", + "temp_df6.sort_values(by='利润率').style.format(formatter={'利润率': '{:.2%}'})" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Day66-80/code/day07.ipynb b/Day66-80/code/day07.ipynb new file mode 100644 index 0000000..465b64d --- /dev/null +++ b/Day66-80/code/day07.ipynb @@ -0,0 +1,1246 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "89fbe897-855c-4208-848b-b411727c8eb9", + "metadata": {}, + "source": [ + "## 数据可视化详解" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5b31dba-6894-4c28-83d6-25bade7f28ed", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False\n", + "get_ipython().run_line_magic('config', \"InlineBackend.figure_format = 'svg'\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef91deaa-1007-465c-aa39-4b5198a57d84", + "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "\n", + "warnings.filterwarnings('ignore')" + ] + }, + { + "cell_type": "markdown", + "id": "cd7bef85-c7a9-4dad-822b-23bc36922deb", + "metadata": {}, + "source": [ + "### matplotlib\n", + "\n", + "整体架构:\n", + "1. 渲染层 - 底层的画布,图像的渲染,事件交互\n", + "2. 组件层 - 各种各样的统计图表\n", + "3. 脚本层 - 提供编程接口,通过调函数实现图表绘制\n", + "\n", + "绘图过程:\n", + "1. 创建画布 - plt.figure(figsize, dpi) --> Figure\n", + "2. 创建坐标系 - plt.subplot(nrows, ncols, index)\n", + "3. 绘制图表\n", + " - 折线图:plt.plot()\n", + " - 散点图:plt.scatter()\n", + " - 柱状图:plt.bar() / plt.barh()\n", + " - 饼图:plt.pie()\n", + " - 直方图:plt.hist()\n", + " - 箱线图:plt.boxplot()\n", + "4. 保存图表 - plt.savefig()\n", + "5. 显示图表 - plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73435be1-71e3-49e7-8703-7479be7f2b29", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.linspace(-2 * np.pi, 2 * np.pi, 120)\n", + "y1 = np.sin(x)\n", + "y2 = np.cos(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c57ddb9-5338-4008-8878-901f92cf94ba", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install PyQt5\n", + "# %pip install PyQt6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ba631ea-11ed-48d3-b06b-5efd93222f07", + "metadata": {}, + "outputs": [], + "source": [ + "# 魔法指令 - 将统计图表渲染到Qt窗口\n", + "# %matplotlib qt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c8fdb5f-18be-4ed5-826a-6b2466630154", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(8, 4), dpi=200)\n", + "plt.subplot(1, 1, 1)\n", + "plt.plot(x, y1, label='正弦', linewidth=0.5, linestyle='--', color='#D75281')\n", + "plt.plot(x, y2, label='余弦', marker='.', color='#0096FF')\n", + "plt.legend(loc='lower center')\n", + "# plt.savefig('aa.jpg')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80c166e9-0a24-4593-bfde-ceb592c5949c", + "metadata": {}, + "outputs": [], + "source": [ + "# 魔法指令 - 将统计图表渲染到浏览器\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39fbcb43-325c-4878-a23e-ae5d27ae08e2", + "metadata": {}, + "outputs": [], + "source": [ + "# 创建画布(Figure)\n", + "plt.figure(figsize=(8, 4), dpi=200)\n", + "# 创建坐标系(Axes)\n", + "ax = plt.subplot(1, 1, 1)\n", + "ax.spines['left'].set_position('center')\n", + "ax.spines['bottom'].set_position('center')\n", + "ax.spines['top'].set_visible(False)\n", + "ax.spines['right'].set_visible(False)\n", + "# 绘制折线图\n", + "plt.plot(x, y1, label='正弦', color='#D75281', linewidth=2, linestyle='-.')\n", + "plt.plot(x, y2, label='余弦', color='#0096FF', marker='.')\n", + "# 定制图表的标题\n", + "plt.title(r'$sin(\\alpha)$和$cos(\\alpha)$曲线图', fontdict=dict(fontsize=18, color='#FFFFFF', backgroundcolor='#0F3D3E'))\n", + "# 定制横轴的刻度\n", + "plt.xticks(\n", + " np.arange(-2 * np.pi, 2 * np.pi + 0.1, np.pi / 2),\n", + " labels=[r'$ -2\\pi $', r'$ -\\frac{3\\pi}{2} $', r'$ -\\pi $', r'$ -\\frac{\\pi}{2} $', \n", + " '0', r'$ \\frac{\\pi}{2} $', r'$ \\pi $', r'$ \\frac{3\\pi}{2} $', r'$ 2\\pi $']\n", + ")\n", + "# 定制纵轴的刻度\n", + "plt.yticks(np.arange(-1, 1.5, 0.5))\n", + "# 添加标注(文字和箭头)\n", + "plt.annotate(r'$ sin(\\alpha) $', xytext=(0.5, -0.5), xy=(0, 0), color='#EF5B0C', \n", + " arrowprops=dict(arrowstyle='fancy', connectionstyle='arc3, rad=0.25', color='darkgreen'))\n", + "# 定制图例\n", + "plt.legend(loc='lower right')\n", + "# 保存图表\n", + "# plt.savefig('aa.jpg')\n", + "# 显示图表\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e72a3b87-6cf9-4709-abe1-4c0a67d9fe00", + "metadata": {}, + "outputs": [], + "source": [ + "x2 = np.linspace(0.1, 10.1, 60)\n", + "y3 = np.log2(x2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf5cecf5-3316-4f60-ae1c-42ed31467827", + "metadata": {}, + "outputs": [], + "source": [ + "# 创建画布(Figure)\n", + "plt.figure(figsize=(8, 6), dpi=200)\n", + "# 创建坐标系(Axes)\n", + "plt.subplot(2, 2, 1)\n", + "# 绘制折线图\n", + "plt.plot(x, y1, label='正弦', color='#D75281', linewidth=2, linestyle='-.')\n", + "# 创建坐标系\n", + "plt.subplot(2, 2, 2)\n", + "plt.plot(x, y2, label='余弦', color='#0096FF', marker='.')\n", + "# 创建坐标系\n", + "# plt.subplot(2, 2, (3, 4))\n", + "plt.subplot(2, 1, 2)\n", + "# 绘制散点图\n", + "plt.plot(x2, y3, color='darkgreen', marker='*')\n", + "# 显示图表\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eaf4e484-6821-4e8c-839f-fae932b68224", + "metadata": {}, + "outputs": [], + "source": [ + "# 创建画布(Figure)\n", + "plt.figure(figsize=(8, 6), dpi=200)\n", + "# 创建网格对象(GridSpec)\n", + "grid = plt.GridSpec(2, 3)\n", + "plt.subplot(grid[:, 0])\n", + "plt.subplot(grid[0, 1:])\n", + "plt.subplot(grid[1, 1])\n", + "plt.subplot(grid[1, 2])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1d3b736-da33-479c-9d0c-a84145dd59e1", + "metadata": {}, + "outputs": [], + "source": [ + "# 月收入\n", + "income = np.fromstring('5550, 7500, 10500, 15000, 20000, 25000, 30000, 40000', sep=',')\n", + "income" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cedebe7e-58eb-456a-bb64-4e75d1729c92", + "metadata": {}, + "outputs": [], + "source": [ + "# 月网购支出\n", + "outcome = np.fromstring('800, 1800, 1250, 2000, 1800, 2100, 2500, 3500', sep=',')\n", + "outcome" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eab6ea54-8c49-46a8-918c-8ce7aebd521e", + "metadata": {}, + "outputs": [], + "source": [ + "plt.scatter(income, outcome)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef2ee591-6fa9-4b02-9f10-6f2dc776b8fe", + "metadata": {}, + "outputs": [], + "source": [ + "# 网购次数\n", + "nums = np.array([5, 3, 10, 5, 12, 20, 8, 10])\n", + "nums" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "239a9331-c6a4-450c-bc13-f4e3c2969f2e", + "metadata": {}, + "outputs": [], + "source": [ + "plt.get_cmap('rainbow_r')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b9abf01-b4a8-4b3e-8de4-8284f6fc3efe", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制散点图 ---> 气泡图(引入第三个变量)\n", + "plt.scatter(income, outcome, s=nums * 30, c=nums, cmap='rainbow_r')\n", + "plt.colorbar()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a36fd15-7779-4dd5-a205-478f1ae1506c", + "metadata": {}, + "outputs": [], + "source": [ + "data1 = np.random.randint(100, 500, 4)\n", + "data2 = np.random.randint(200, 600, 4)\n", + "data3 = np.random.randint(300, 500, 4)\n", + "quarter = np.arange(4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc760daf-de0c-478d-a84b-d5ed0fd5a5e9", + "metadata": {}, + "outputs": [], + "source": [ + "errs = np.random.randint(10, 30, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "143a0b7b-a3ff-4a76-9032-fd236826966c", + "metadata": {}, + "outputs": [], + "source": [ + "# 柱状图\n", + "plt.bar(quarter-0.2, data1, label='A组', hatch='//', width=0.2)\n", + "plt.bar(quarter, data2, label='B组', hatch='xxx', width=0.2)\n", + "plt.bar(quarter+0.2, data3, label='C组', width=0.2, yerr=errs)\n", + "plt.xticks(quarter, labels=[f'Q{i}' for i in range(1, 5)])\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a802edea-f2f7-4265-809f-78318d1c1e57", + "metadata": {}, + "outputs": [], + "source": [ + "# 堆叠柱状图\n", + "plt.bar(quarter, data1, label='A组', width=0.4)\n", + "plt.bar(quarter, data2, label='B组', width=0.4, bottom=data1)\n", + "plt.bar(quarter, data3, label='C组', width=0.4, bottom=data1 + data2)\n", + "plt.xticks(quarter, labels=[f'Q{i}' for i in range(1, 5)])\n", + "for i in range(quarter.size):\n", + " plt.text(i, data1[i] // 2, data1[i], ha='center', va='center', color='w')\n", + " plt.text(i, data1[i] + data2[i] // 2, data2[i], ha='center', color='w')\n", + " plt.text(i, data1[i] + data2[i] + data3[i] // 2, data3[i], ha='center', color='w')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5f01c63f-f1da-42af-a04a-e45d95c2262b", + "metadata": {}, + "outputs": [], + "source": [ + "# 计算每组数据的占比\n", + "temp_df = pd.DataFrame(data={\n", + " 'A组': data1,\n", + " 'B组': data2,\n", + " 'C组': data3,\n", + "})\n", + "temp_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ccb6a3f1-ae06-467d-9cc0-00b3eb1b9b84", + "metadata": {}, + "outputs": [], + "source": [ + "pct_data = temp_df.apply(lambda x: x / temp_df.sum(axis=1))\n", + "pct_data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ec0bf97-62ed-4ec7-8998-b1277d752098", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.ticker as tkr\n", + "\n", + "# 绘制百分比堆叠柱状图\n", + "data1, data2, data3 = pct_data.A组, pct_data.B组, pct_data.C组\n", + "plt.bar(quarter, data1, label='A组', width=0.4)\n", + "plt.bar(quarter, data2, label='B组', width=0.4, bottom=data1)\n", + "plt.bar(quarter, data3, label='C组', width=0.4, bottom=data1 + data2)\n", + "plt.xticks(quarter, labels=[f'Q{i}' for i in range(1, 5)])\n", + "# plt.yticks(np.arange(0, 1.1, 0.2), labels=[f'{i}%' for i in range(0, 101, 20)])\n", + "plt.gca().yaxis.set_major_formatter(tkr.PercentFormatter(xmax=1, decimals=0))\n", + "for i in range(quarter.size):\n", + " plt.text(i, data1[i] / 2, f'{data1[i] * 100:.1f}%', ha='center', color='w')\n", + " plt.text(i, data1[i] + data2[i] / 2, f'{data2[i] * 100:.1f}%', ha='center', color='w')\n", + " plt.text(i, data1[i] + data2[i] + data3[i] / 2, f'{data3[i] * 100:.1f}%', ha='center', color='w')\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98d80c5c-2f1a-41e2-9222-d396bb4a580e", + "metadata": {}, + "outputs": [], + "source": [ + "labels = ['苹果', '香蕉', '桃子', '荔枝', '石榴', '山竹', '榴莲']\n", + "data = np.random.randint(100, 500, 7)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33086285-51e7-40af-b7fe-c775b63beff0", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制饼图\n", + "plt.figure(figsize=(5, 5), dpi=120)\n", + "plt.pie(\n", + " data, \n", + " labels=labels, # 每块饼对应的标签\n", + " labeldistance=1.1, # 标签到圆心的距离\n", + " autopct='%.1f%%', # 自动计算和显示百分比\n", + " pctdistance=0.88, # 百分比到圆心的距离\n", + " explode=[0, 0, 0.05, 0, 0, 0, 0.1], # 分离距离(分离饼图)\n", + " shadow=True, # 阴影效果\n", + " wedgeprops={'width': 0.25, 'edgecolor': 'w'}, # 楔子属性(环状饼图)\n", + " textprops={'fontsize': 9, 'color': 'k'} # 文本属性\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14813133-5f35-4324-b001-17708872d80c", + "metadata": {}, + "outputs": [], + "source": [ + "x1 = np.random.normal(0, 1, 5000)\n", + "x2 = np.random.random(100000)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2607c7d9-77e6-4b01-8d15-1e8b61dc6dba", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制直方图(概率密度)\n", + "plt.hist(x1, bins=20, density=True, color='darkcyan')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c5b6ed0-0c80-4040-8cdd-51c00083d252", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制直方图(累积分布)\n", + "plt.hist(x2, bins=10, density=True, cumulative=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80db5ad6-30e8-47e3-a092-5b2a934f35c8", + "metadata": {}, + "outputs": [], + "source": [ + "data = x1[::5]\n", + "data.size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a14acac1-6515-4b23-befc-0f28f3193b88", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制箱线图\n", + "plt.boxplot(data, showmeans=True, notch=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8c5c940-7f83-4fc2-803f-b302d78cd30d", + "metadata": {}, + "outputs": [], + "source": [ + "# 堆叠折线图(面积图)\n", + "plt.figure(figsize=(8, 4))\n", + "days = np.arange(7)\n", + "sleeping = [7, 8, 6, 6, 7, 8, 10]\n", + "eating = [2, 3, 2, 1, 2, 3, 2]\n", + "working = [7, 8, 7, 8, 6, 2, 3]\n", + "playing = [8, 5, 9, 9, 9, 11, 9]\n", + "plt.stackplot(days, sleeping, eating, working, playing)\n", + "plt.legend(['睡觉', '吃饭', '工作', '玩耍'], fontsize=10)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66c79bd0-8253-4fa2-b303-696598e952d1", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "category_names = ['Strongly disagree', 'Disagree',\n", + " 'Neither agree nor disagree', 'Agree', 'Strongly agree']\n", + "results = {\n", + " 'Question 1': [10, 15, 17, 32, 26],\n", + " 'Question 2': [26, 22, 29, 10, 13],\n", + " 'Question 3': [35, 37, 7, 2, 19],\n", + " 'Question 4': [32, 11, 9, 15, 33],\n", + " 'Question 5': [21, 29, 5, 5, 40],\n", + " 'Question 6': [8, 19, 5, 30, 38]\n", + "}\n", + "\n", + "\n", + "def survey(results, category_names):\n", + " \"\"\"\n", + " Parameters\n", + " ----------\n", + " results : dict\n", + " A mapping from question labels to a list of answers per category.\n", + " It is assumed all lists contain the same number of entries and that\n", + " it matches the length of *category_names*.\n", + " category_names : list of str\n", + " The category labels.\n", + " \"\"\"\n", + " labels = list(results.keys())\n", + " data = np.array(list(results.values()))\n", + " data_cum = data.cumsum(axis=1)\n", + " category_colors = plt.colormaps['RdYlGn'](\n", + " np.linspace(0.15, 0.85, data.shape[1]))\n", + "\n", + " fig, ax = plt.subplots(figsize=(9.2, 5))\n", + " ax.invert_yaxis()\n", + " ax.xaxis.set_visible(False)\n", + " ax.set_xlim(0, np.sum(data, axis=1).max())\n", + "\n", + " for i, (colname, color) in enumerate(zip(category_names, category_colors)):\n", + " widths = data[:, i]\n", + " starts = data_cum[:, i] - widths\n", + " rects = ax.barh(labels, widths, left=starts, height=0.5,\n", + " label=colname, color=color)\n", + "\n", + " r, g, b, _ = color\n", + " text_color = 'white' if r * g * b < 0.5 else 'darkgrey'\n", + " ax.bar_label(rects, label_type='center', color=text_color)\n", + " ax.legend(ncols=len(category_names), bbox_to_anchor=(0, 1),\n", + " loc='lower left', fontsize='small')\n", + "\n", + " return fig, ax\n", + "\n", + "\n", + "survey(results, category_names)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36499ccc-a243-4f96-9759-454d3267bb9a", + "metadata": {}, + "outputs": [], + "source": [ + "# 雷达图(极坐标折线图)\n", + "labels = np.array(['速度', '力量', '经验', '防守', '发球', '技术'])\n", + "malong_values = np.array([93, 95, 98, 92, 96, 97])\n", + "shuigu_values = np.array([30, 40, 65, 80, 45, 60])\n", + "angles = np.linspace(0, 2 * np.pi, labels.size, endpoint=False)\n", + "# 加一条数据让图形闭合\n", + "malong_values = np.append(malong_values, malong_values[0])\n", + "shuigu_values = np.append(shuigu_values, shuigu_values[0])\n", + "angles = np.append(angles, angles[0])\n", + "\n", + "# 创建画布\n", + "plt.figure(figsize=(4, 4), dpi=120)\n", + "# 创建坐标系\n", + "ax = plt.subplot(projection='polar')\n", + "# 绘图和填充\n", + "plt.plot(angles, malong_values, color='r', linewidth=2, label='马龙')\n", + "plt.fill(angles, malong_values, color='r', alpha=0.25)\n", + "plt.plot(angles, shuigu_values, color='g', linewidth=2, label='水谷隼')\n", + "plt.fill(angles, shuigu_values, color='g', alpha=0.25)\n", + "# 设置文字和网格线\n", + "# ax.set_thetagrids(angles[:-1] * 180 / np.pi, labels, fontsize=10)\n", + "# ax.set_rgrids([0, 20, 40, 60, 80, 100], fontsize=10)\n", + "ax.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e3939fab-09a5-4e21-9bee-0081acddb36e", + "metadata": {}, + "outputs": [], + "source": [ + "# 玫瑰图(圆形柱状图)\n", + "group1 = np.random.randint(20, 50, 4)\n", + "group2 = np.random.randint(10, 60, 4)\n", + "x = np.array([f'A组-Q{i}' for i in range(1, 5)] + [f'B组-Q{i}' for i in range(1, 5)])\n", + "y = np.array(group1.tolist() + group2.tolist())\n", + "theta = np.linspace(0, 2 * np.pi, x.size, endpoint=False)\n", + "width = 2 * np.pi / x.size\n", + "\n", + "# 产生随机颜色\n", + "colors = np.random.rand(8, 3)\n", + "# 将柱状图投影到极坐标\n", + "ax = plt.subplot(projection='polar')\n", + "plt.bar(theta, y, width=width, color=colors, bottom=0)\n", + "ax.set_thetagrids(theta * 180 / np.pi, x, fontsize=10)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0390d2a-a0a9-483a-a4bc-ce1c5059f30b", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib qt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d338e6e3-bf98-428b-bac2-693bb1a2a760", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制3D曲面\n", + "from mpl_toolkits.mplot3d import Axes3D\n", + "\n", + "fig = plt.figure(figsize=(8, 4), dpi=120)\n", + "ax = Axes3D(fig, auto_add_to_figure=False)\n", + "fig.add_axes(ax)\n", + "x = np.arange(-2, 2, 0.1)\n", + "y = np.arange(-2, 2, 0.1)\n", + "x, y = np.meshgrid(x, y)\n", + "z = (1 - y ** 5 + x ** 5) * np.exp(-x ** 2 - y ** 2)\n", + "ax.plot_surface(x, y, z)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6dc5a3e1-d5a0-4f83-b8a6-7f69015a1ba9", + "metadata": {}, + "outputs": [], + "source": [ + "# %matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "id": "6f1235e1-f300-4ae2-ad7a-b97523bedfa1", + "metadata": {}, + "source": [ + "### seaborn\n", + "\n", + "对matplotlib进行了封装,定制了默认的样式,简化了调用matplotlib函数时需要传入的参数。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25a64a4e-bacc-43da-ad92-ec7ae2191b38", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install seaborn" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa94138e-a642-45d5-957e-b094bdabed91", + "metadata": {}, + "outputs": [], + "source": [ + "import seaborn as sns\n", + "\n", + "# 设置使用默认主题(样式、配色方案、字体方案、……)\n", + "sns.set_theme()\n", + "# sns.set_theme(font_scale=1.2, style='darkgrid', palette='Dark2')\n", + "\n", + "plt.rcParams['font.sans-serif'].insert(0, 'SimHei')\n", + "plt.rcParams['axes.unicode_minus'] = False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dabdf3ae-f8dc-42ad-a929-909b0325ca3e", + "metadata": {}, + "outputs": [], + "source": [ + "# import ssl\n", + "\n", + "# ssl._create_default_https_context = ssl._create_unverified_context" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ddf6e866-1115-404d-b548-a6070e4e065e", + "metadata": {}, + "outputs": [], + "source": [ + "# tips_df = sns.load_dataset('tips')\n", + "# tips_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "500326a6-ff54-41e0-8c93-8307107ce52d", + "metadata": {}, + "outputs": [], + "source": [ + "tips_df = pd.read_excel('res/tips.xlsx')\n", + "tips_df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de1ae073-3fe9-45a1-9db8-21ed75de45ff", + "metadata": {}, + "outputs": [], + "source": [ + "tips_df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5028d1d5-7d7e-4e34-a17b-80bae21f6324", + "metadata": {}, + "outputs": [], + "source": [ + "sns.histplot(data=tips_df, x='total_bill')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11b422dd-68ff-4881-9164-2343b7be0375", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制直方图\n", + "# kde - kernel density estimation - 拟合概率密度曲线\n", + "sns.histplot(data=tips_df, x='total_bill', kde=True, stat='density')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b05e0b86-5296-4f05-a64c-34395e9f565a", + "metadata": {}, + "outputs": [], + "source": [ + "# tips_df[['total_bill', 'tip']].corr(method='pearson')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8eb7d2be-9981-4750-999f-2335824ff619", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制点对图\n", + "# sns.pairplot(data=tips_df)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c9f97b00-ecab-4602-8a9a-16cb30db687e", + "metadata": {}, + "outputs": [], + "source": [ + "# tips_df.query('sex == \"Female\"')[['total_bill', 'tip']].corr()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d8e63c7-97b6-41b4-bf97-10fc1f14617a", + "metadata": {}, + "outputs": [], + "source": [ + "# tips_df.query('sex == \"Male\"')[['total_bill', 'tip']].corr()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17ac77e3-adac-4ac9-8e7a-f911b014b598", + "metadata": {}, + "outputs": [], + "source": [ + "sns.color_palette('tab10')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d31d89fd-f0c3-4698-8a2f-85dfb7e852ea", + "metadata": {}, + "outputs": [], + "source": [ + "sns.pairplot(data=tips_df, hue='sex', palette='tab10')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1d78fd7-64d7-4c37-a738-379ca63ab7ac", + "metadata": {}, + "outputs": [], + "source": [ + "sns.color_palette('winter')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5097260c-30e6-48b7-997c-a2b815b3eb0a", + "metadata": {}, + "outputs": [], + "source": [ + "sns.pairplot(data=tips_df, hue='sex', palette='winter')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "145a9efe-30b9-447d-bcf6-c74c2233b572", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制联合分布图\n", + "sns.jointplot(data=tips_df, x='total_bill', y='tip', hue='sex')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bdcb5a1a-d27f-41d6-9d91-97fad47c31d6", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制联合分布图\n", + "sns.jointplot(data=tips_df, x='total_bill', y='tip', hue='smoker')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f83e9d23-93b3-4d1b-81cd-d7b89613aefa", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制联合分布图\n", + "sns.jointplot(data=tips_df, x='total_bill', y='tip', hue='time')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bfc55394-74e6-45d6-9a7a-68d26221a8f2", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制线性回归模型图\n", + "# lm - linear regression model\n", + "sns.lmplot(data=tips_df, x='total_bill', y='tip', hue='smoker')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef565a16-3847-466b-a22f-9ecc42265c31", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制箱线图\n", + "sns.boxplot(data=tips_df, x='day', y='total_bill')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a36af19-38af-4f53-9c9a-f0a6ef8f02d3", + "metadata": {}, + "outputs": [], + "source": [ + "# 绘制小提琴图\n", + "sns.violinplot(data=tips_df, x='day', y='total_bill')" + ] + }, + { + "cell_type": "markdown", + "id": "27067b07-bf54-4905-a7a3-0fc9f91885f8", + "metadata": {}, + "source": [ + "### pyecharts\n", + "\n", + "对Apache的echarts库用Python语言进行了封装,可以绘制美观且交互性极佳的统计图表。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "afecad24-897c-4729-9034-640f4d38ef7e", + "metadata": {}, + "outputs": [], + "source": [ + "# %pip install -U pyecharts" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7fab295e-0e60-4fd4-9852-77f23bea19fc", + "metadata": {}, + "outputs": [], + "source": [ + "# 配置Notebook的类型是JupyterLab\n", + "from pyecharts.globals import CurrentConfig, NotebookType\n", + "\n", + "CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e12a132-50d5-40a9-9b3f-e67b224ef7cf", + "metadata": {}, + "outputs": [], + "source": [ + "from pyecharts.charts import Bar\n", + "\n", + "a = np.random.randint(10, 50, 6)\n", + "b = np.random.randint(20, 40, 6)\n", + "\n", + "bar = Bar()\n", + "bar.add_xaxis([\"衬衫\", \"羊毛衫\", \"雪纺衫\", \"裤子\", \"高跟鞋\", \"袜子\"])\n", + "bar.add_yaxis(\"商家A\", a.tolist())\n", + "bar.add_yaxis(\"商家B\", b.tolist())\n", + "bar.load_javascript()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baaaab27-4cc2-4584-904f-9e7bc6181d95", + "metadata": {}, + "outputs": [], + "source": [ + "bar.render_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "617bd760-447e-4b6f-b6a4-41dc126be5d8", + "metadata": {}, + "outputs": [], + "source": [ + "from pyecharts.charts import Bar\n", + "from pyecharts import options as opts\n", + "from pyecharts.globals import ThemeType\n", + "\n", + "# 创建柱状图对象\n", + "bar = Bar(init_opts=opts.InitOpts(width='640px', height='480px', theme=ThemeType.LIGHT))\n", + "# 修改全局配置项\n", + "bar.set_global_opts(\n", + " # 定制标题\n", + " title_opts=opts.TitleOpts(\n", + " title='2022年各品类销售额',\n", + " title_link='http://www.qfedu.com',\n", + " pos_left='center'\n", + " ),\n", + " # 定制图例\n", + " legend_opts=opts.LegendOpts(\n", + " is_show=True,\n", + " pos_top='bottom'\n", + " ),\n", + " # 定制工具箱\n", + " toolbox_opts=opts.ToolboxOpts(\n", + " is_show=True,\n", + " pos_left='right',\n", + " pos_top='center',\n", + " orient='vertical'\n", + " )\n", + ")\n", + "# 添加横轴的数据\n", + "bar.add_xaxis([\"衬衫\", \"羊毛衫\", \"雪纺衫\", \"裤子\", \"高跟鞋\", \"袜子\"])\n", + "# 添加纵轴的数据\n", + "bar.add_yaxis(\"商家A\", [5, 20, 36, 10, 45, 20])\n", + "bar.add_yaxis(\"商家B\", [15, 22, 23, 18, 37, 40])\n", + "# 让浏览器加载JS文件\n", + "bar.load_javascript()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5734c4da-1a6d-418a-a2d4-80dfb196da8b", + "metadata": {}, + "outputs": [], + "source": [ + "# 渲染图表\n", + "bar.render_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6ce15768-7810-4699-9795-51188525ca83", + "metadata": {}, + "outputs": [], + "source": [ + "from pyecharts import options as opts\n", + "from pyecharts.charts import Funnel\n", + "\n", + "steps = ['曝光', '点击', '加购', '下单', '支付']\n", + "vdata = [10000, 5000, 2000, 1200, 880]\n", + "\n", + "f = Funnel()\n", + "f.add('转化', [(step, data) for step, data in zip(steps, vdata)])\n", + "f.set_global_opts(\n", + " title_opts=opts.TitleOpts(\n", + " title='转化漏斗',\n", + " pos_left='10%',\n", + " title_textstyle_opts=opts.TextStyleOpts(\n", + " font_family='微软雅黑',\n", + " font_size=28\n", + " )\n", + " )\n", + ")\n", + "f.load_javascript()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78f6113c-ad73-4a25-a20b-b88b0b8fbd58", + "metadata": {}, + "outputs": [], + "source": [ + "f.render_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2add1c4-9ab9-493e-b312-a972795c6f3c", + "metadata": {}, + "outputs": [], + "source": [ + "# 注意:pyecharts库只接受原生Python数据类型(不支持numpy的数组,pandas的Series或DataFrame)\n", + "import pyecharts.options as opts\n", + "from pyecharts.charts import Pie\n", + "\n", + "x_data = [\"直接访问\", \"邮件营销\", \"联盟广告\", \"视频广告\", \"搜索引擎\"]\n", + "y_data = [335, 310, 234, 135, 1548]\n", + "\n", + "pie_chart = Pie(init_opts=opts.InitOpts(\n", + " width='640px',\n", + " height='480px'\n", + "))\n", + "pie_chart.add(\n", + " series_name=\"访问来源\",\n", + " data_pair=[list(z) for z in zip(x_data, y_data)],\n", + " radius=[\"50%\", \"70%\"],\n", + " label_opts=opts.LabelOpts(is_show=False, position=\"center\"),\n", + ")\n", + "pie_chart.set_global_opts(legend_opts=opts.LegendOpts(pos_left=\"legft\", orient=\"vertical\"))\n", + "pie_chart.set_series_opts(\n", + " label_opts=opts.LabelOpts(formatter=\"{b}: {d}%\")\n", + ")\n", + "pie_chart.load_javascript()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1b478655-7774-43ad-a0ca-1fd7082d5a4d", + "metadata": {}, + "outputs": [], + "source": [ + "pie_chart.render_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be870fdd-71c9-4b98-82bf-dc5f7c81503a", + "metadata": {}, + "outputs": [], + "source": [ + "baidu = pd.read_excel('res/2022年股票数据.xlsx', sheet_name='BIDU', index_col='Date')\n", + "baidu = baidu[::-1][['Open', 'Close', 'Low', 'High']].round(2)\n", + "baidu" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e5eec52d-7ba1-4c30-8fcf-582971528ac1", + "metadata": {}, + "outputs": [], + "source": [ + "from pyecharts import options as opts\n", + "from pyecharts.charts import Kline\n", + "\n", + "x_data = baidu.index.strftime('%m月%d日').values.tolist()\n", + "y_data = baidu.values.tolist()\n", + "\n", + "kline_chart = Kline()\n", + "kline_chart.add_xaxis(x_data)\n", + "kline_chart.add_yaxis('', y_data)\n", + "kline_chart.set_global_opts(\n", + " xaxis_opts=opts.AxisOpts(is_scale=True),\n", + " yaxis_opts=opts.AxisOpts(\n", + " is_scale=True,\n", + " splitarea_opts=opts.SplitAreaOpts(\n", + " is_show=True,\n", + " areastyle_opts=opts.AreaStyleOpts(opacity=1)\n", + " ),\n", + " ),\n", + " datazoom_opts=[\n", + " opts.DataZoomOpts(\n", + " pos_bottom='2%',\n", + " range_start=40,\n", + " range_end=60,\n", + " )\n", + " ],\n", + ")\n", + "kline_chart.load_javascript()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26dfb364-9edd-4a0a-8ac1-4b7146971e44", + "metadata": {}, + "outputs": [], + "source": [ + "kline_chart.render_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ce7fde5-2f11-48f3-a4fc-2d9f3303a13b", + "metadata": {}, + "outputs": [], + "source": [ + "# 安装地图数据\n", + "# %pip uninstall -y echarts-countries-pypkg\n", + "# %pip uninstall -y echarts-china-provinces-pypkg\n", + "# %pip uninstall -y echarts-china-cities-pypkg\n", + "# %pip uninstall -y echarts-china-counties-pypkg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97d8b5ca-6c66-4500-a74a-8fa521a51549", + "metadata": {}, + "outputs": [], + "source": [ + "from pyecharts import options as opts\n", + "from pyecharts.charts import Map\n", + "\n", + "data = [\n", + " ('广东省', 594), ('浙江省', 438), ('四川省', 316), ('北京市', 269), ('山东省', 248),\n", + " ('江苏省', 234), ('湖南省', 196), ('福建省', 166), ('河南省', 153), ('辽宁省', 152),\n", + " ('上海市', 138), ('河北省', 86), ('安徽省', 79), ('湖北省', 75), ('黑龙江省', 70), \n", + " ('陕西省', 63), ('吉林省', 59), ('江西省', 56), ('重庆市', 46), ('贵州省', 39),\n", + " ('山西省', 37), ('云南省', 33), ('广西壮族自治区', 28), ('天津市', 22), ('新疆维吾尔自治区', 24),\n", + " ('海南省', 18), ('台湾省', 11), ('甘肃省', 7), ('青海省', 3), ('内蒙古自治区', 17), \n", + " ('宁夏回族自治区', 1), ('西藏自治区', 1), ('香港特别行政区', 12), ('澳门特别行政区', 2)\n", + "]\n", + "\n", + "map_chart = Map(init_opts=opts.InitOpts(width='1000px', height='1000px'))\n", + "map_chart.add('', data, 'china', is_roam=False)\n", + "map_chart.load_javascript()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb71a3f2-f08b-435a-ad75-a4b6a6be6f91", + "metadata": {}, + "outputs": [], + "source": [ + "map_chart.render_notebook()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Day66-80/res/contents_of_data_analysis.png b/Day66-80/res/contents_of_data_analysis.png new file mode 100644 index 0000000000000000000000000000000000000000..3599354d568c12a6f80cd937b98d64ee8ab0b1dc GIT binary patch literal 268289 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Μ0bbZo1l67|d42v*Lrj8#Iu-PJXuI6P_l2 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 z diff --git a/Day81-90/88.神经网络模型.md b/Day81-90/88.神经网络模型.md index 545b3ca..7a199ee 100755 --- a/Day81-90/88.神经网络模型.md +++ b/Day81-90/88.神经网络模型.md @@ -148,7 +148,7 @@ weighted avg 0.45 0.37 0.23 30 > **注意**:由于创建`MLPClassifier`时没有指定`random_state`参数,所以代码每次执行的结果可能并不相同。 -模型的预测准确率只有`0.37`,大家对这个结果是不是感觉到非常失望,我们煞费苦心构建的模型预测效果竟然如此拉胯。别紧张,上面代码中我们创建神经网络模型时,`hidden_layer_sizes`参数设置的是`(1, )`,它表示我们的网络只有 1 个隐藏层,而且隐藏层只有 1 个神经元。我们只要增加隐藏层和神经元的数量,让模型可以更好的学习特征和目标之间的映射关系,预测的效果就会好起来。下面,我们将`hidden_layer_sizes`参数调整为`(32, 32, 32)`,即模型有三个隐藏层,每层有 32 个神经元,再来看看代码执行的结果。 +模型的预测准确率只有`0.37`,大家对这个结果是不是感觉到非常失望,我们煞费苦心构建的模型预测效果竟然如此拉胯。别紧张,上面代码中我们创建神经网络模型时,`hidden_layer_sizes`参数设置的是`(1, )`,它表示我们的网络只有 1 个隐藏层,而且隐藏层只有 1 个神经元,这个神经元承受了太多(它真的,我哭死)。接下俩,我们需要增加隐藏层和神经元的数量,让模型可以更好的学习特征和目标之间的映射关系,这样预测的效果就会好起来。下面,我们将`hidden_layer_sizes`参数调整为`(32, 32, 32)`,即模型有三个隐藏层,每层有 32 个神经元,再来看看代码执行的结果。 ```python from sklearn.datasets import load_iris @@ -176,7 +176,7 @@ y_pred = model.predict(X_test) print(classification_report(y_test, y_pred)) ``` -> **说明**:大家可以试着运行上面的代码,看看有没有获得更好的结果。当然,大家也可以重新设置`hidden_layer_sizes`参数,看看会得到怎样的结果。 +> **说明**:大家可以试着运行上面的代码,看看有没有获得更好的结果。当然,模型准确率为 1 也未必就值得高兴,因为你可能训练了一个过拟合的模型。无论如何,大家可以试着重新设置`hidden_layer_sizes`参数,看看会得到怎样的结果。 下面,我们还是对`MLPClassifier`几个比较重要的超参数做一个说明。 @@ -396,7 +396,7 @@ Test MSE: 8.7226 Test R2: 0.8569 ``` -通过上面的输出可以看到,随着神经网络模型不断的前向传播和反向传播,损失变得越来越小,模型的拟合变得越来越好。在预测的时候,我们利用训练得到的模型参数进行一次正向传播,就完成了从特征到目标值的映射,模型评估的两个指标看起来还不错。目前,神经网络被广泛应用于模式识别、图像处理、语音识别等领域,是深度学习中最核心的技术。对深度学习有兴趣的读者,可以关注我的另一个项目[“深度学习就是大力出奇迹”](https://github.com/jackfrued/Deep-Learning-Is-Nothing),目前该项目仍然在创造更新中。 +通过上面的输出可以看到,随着神经网络模型不断的前向传播和反向传播,损失变得越来越小,模型的拟合变得越来越好。在预测的时候,我们利用训练得到的模型参数进行一次正向传播,就完成了从特征到目标值的映射,评估回归模型的两个指标 MSE 和 $\small{R^{2}}$ 看起来还不错哟。目前,神经网络被广泛应用于模式识别、图像处理、语音识别等领域,是深度学习中最核心的技术。对深度学习有兴趣的读者,可以关注我的另一个项目[“深度学习就是大力出奇迹”](https://github.com/jackfrued/Deep-Learning-Is-Nothing),目前该项目仍然在创作更新中。 ### 模型优缺点 diff --git a/Day81-90/90.机器学习实战.md b/Day81-90/90.机器学习实战.md index 0746d13..36c0d14 100755 --- a/Day81-90/90.机器学习实战.md +++ b/Day81-90/90.机器学习实战.md @@ -379,9 +379,9 @@ test['FamilySize'] = test['SibSp'] + test['Parch'] + 1 # 删除多余特征 test.drop(columns=['Name', 'Ticket', 'SibSp', 'Parch'], inplace=True) -# 逻辑回归模型 +# 使用逻辑回归模型 passenger_id, X_test = test.index, test -# XGBoost模型 +# 使用XGBoost模型 # passenger_id, X_test = test.index, xgb.DMatrix(test) y_test_pred = model.predict(X_test) diff --git a/Python学习资源汇总.md b/Python学习资源汇总.md new file mode 100755 index 0000000..f913353 --- /dev/null +++ b/Python学习资源汇总.md @@ -0,0 +1,108 @@ +## Python学习资源汇总 + +最近有很多小伙伴在找 Python 的相关学习资源,给大家做一个汇总吧,大家就不需要到处打听了,而且网上的资源良莠不齐,给大家整理一些优质的资源,让大家少走弯路。温馨提示一下,下面的资源选一些适合自己的就行了,并非每个都值得学习和研究。 + +### Python学习教程 + +#### 图文教程 + +1. [《从零开始学Python》](https://www.zhihu.com/column/c_1216656665569013760)- 我自己在知乎创作的专栏,欢迎大家打卡学习 +2. [《基于Python的数据分析》](https://www.zhihu.com/column/c_1217746527315496960)- 我自己在知乎创作的专栏,欢迎大家学习交流 +3. [《说走就走的AI之旅》](https://www.zhihu.com/column/c_1628900668109946880)- 我自己在知乎创作的专栏,欢迎大家学习交流 +4. [《Python - 100天从新手到大师》](https://github.com/jackfrued/Python-100-Days) - 我自己在 GitHub 分享的 Python 学习项目 +5. [《Python 3教程》](https://www.runoob.com/python3/python3-tutorial.html)- 菜鸟教程上的 Python 课程,上面还有很多其他学习资源 +6. [《Python教程》](https://liaoxuefeng.com/books/python/introduction/index.html)- 廖雪峰个人网站上的 Python 课程,上面还有一些其他学习资源 + +#### 视频教程 + +1. [《从零开始学Python》](https://space.bilibili.com/1177252794/lists/1222205)- 我自己分享在 Bilibili 的 Python 入门视频 +2. [《快速上手Python语言》](https://www.zhihu.com/education/video-course/1491848366791700480)- 在知乎知学堂上传的一套之前讲课的随堂视频 +3. [《Python进阶课程》](https://space.bilibili.com/1177252794/lists/4128173)- 我自己分享在 Bilibili 的 Python 进阶随堂视频 +4. [《Python数据分析三剑客》](https://space.bilibili.com/1177252794/lists/502289)- 我自己分享在 Bilibili 的 Python 数据分析随堂视频 +5. [《AI Python for Beginners》](https://www.deeplearning.ai/short-courses/ai-python-for-beginners/)- 吴恩达(Andrew Ng)老师的 Python 入门课 +6. [《AI for Everyone》](https://www.deeplearning.ai/courses/ai-for-everyone/)- 吴恩达(Andrew Ng)老师的 AI 通识课 +7. [《Deep Learning Specilizaiton》](https://www.deeplearning.ai/courses/deep-learning-specialization/)- 吴恩达(Andrew Ng)老师的深度学习专项课程 +8. [《100 Days of Code: The Complete Python Pro Bootcamp》](https://www.udemy.com/course/100-days-of-code/) - Udemy 上很受欢迎的一整套 Python 课程(付费) +9. [《Python for Data Science and Machine Learning Bootcamp》](https://www.udemy.com/course/python-for-data-science-and-machine-learning-bootcamp/) - Udemy 上一套评分很高的数据科学课程(付费) +10. [《PyTorch: Deep Learning and Artificial Intelligence》](https://www.udemy.com/course/pytorch-deep-learning/) - Udemy 好评课程(付费) + +> **说明**:吴恩达老师的课程在 YouTube 和 Bilibili 上也有很多人分享,YouTube 上面也有很多免费的 Python 课程曾经让我觉得受益匪浅。这些课程很多都是言简意赅、直击问题的,不像国内很多培训机构,动不动就分享七百集的课程或者八百G的学习资料,让很多人误以为点赞收藏就是学会。国内外各种学习平台也很多,有人喜欢 Udemy,有人喜欢 Coursera,我只是把我自己看过觉得不错的课程分享出来,大家可以根据需要自己去对应的平台查找,当然更重要的是有计划的学习起来。 + +#### 资源网站 + +1. [Python 官方网站](https://python.org) - 下载 Python 解释器、查看官方文档、了解社区动态等 +2. [Online Python](