提交 778bfbbf 编写于 作者: 骆昊的技术专栏's avatar 骆昊的技术专栏

更新了部分资源

上级 301bb830
......@@ -30,7 +30,7 @@
4. 1970年~1971年:Ken Tompson和Dennis Ritchie用B语言在PDP-11上重写了Unics,并在Brian Kernighan的建议下将其更名为Unix。
![](./res/ken-and-dennis-pdp-11.png)
<img src="https://gitee.com/jackfrued/mypic/raw/master/20220516090404.png" style="zoom:50%;">
5. 1972年~1973年:Dennis Ritchie发明了C语言来取代可移植性较差的B语言,并开启了用C语言重写Unix的工作。
......@@ -40,7 +40,7 @@
8. 1987年:Andrew S. Tanenbaum教授为了能在课堂上为学生讲解操作系统运作的细节,决定在不使用任何AT&T的源代码前提下,自行开发与Unix兼容的操作系统以避免版权上的争议,该系统被命名为Minix。
![](./res/andrew.jpg)
<img src="https://gitee.com/jackfrued/mypic/raw/master/20220516090432.jpg" style="zoom:50%;">
9. 1991年:Linus Torvalds就读于芬兰赫尔辛基大学期间,尝试在Minix上做一些开发工作,但因为Minix只是作为教学用途的操作系统,功能并不强大,为了方便在学校的新闻组和邮件系统中读写和下载文件,Linus编写了磁盘驱动程序和文件系统,这些东西形成了Linux系统内核的雏形。
......
drop database if exists `app_store`;
create database `app_store` default character set utf8mb4;
use `app_store`;
create table `app_info` (
`id` bigint(20) not null auto_increment comment '自增id, app的id',
`app_name` varchar(255) default '' comment '名称',
......@@ -14,10 +20,8 @@ create table `app_info` (
`version_desc` varchar(4096) default '' comment '',
`create_time` datetime not null default '0000-00-00 00:00:00' comment '创建时间',
`update_time` datetime not null default '0000-00-00 00:00:00' comment '更新时间',
primary key (`id`),
key `idx_app_name` (`app_name`),
key `idx_developer` (`user_id`)
) engine=innodb auto_increment=100000 default charset=utf8 comment='app基本信息表';
primary key (`id`)
) engine=innodb auto_increment=100000 default charset=utf8mb4 comment='app基本信息表';
create table `app_ext_info` (
`id` bigint(20) not null auto_increment comment '自增id',
......@@ -27,9 +31,8 @@ create table `app_ext_info` (
`comment_count` int(10) unsigned not null default '0' comment '评论量',
`create_time` int(10) not null default 0 comment '创建时间',
`update_time` int(10) not null default 0 comment '更新时间',
primary key (`id`),
unique key `idx_app_id` (`app_id`)
) engine=innodb default charset=utf8 comment='App扩展信息表';
primary key (`id`)
) engine=innodb default charset=utf8mb4 comment='App扩展信息表';
create table `app_category` (
`id` bigint(20) not null auto_increment comment '自增id',
......@@ -43,7 +46,7 @@ create table `app_category` (
`create_time` int(10) not null default 0 comment '创建时间',
`update_time` int(10) not null default 0 comment '更新时间',
primary key (`id`)
) engine=innodb default charset=utf8 comment='分类信息表';
) engine=innodb default charset=utf8mb4 comment='分类信息表';
create table `app_category_rel` (
`id` bigint(20) not null auto_increment comment '自增id',
......@@ -51,8 +54,7 @@ create table `app_category_rel` (
`category_id` bigint(20) unsigned not null default '0' comment '最低层分类id',
primary key (`id`),
unique key `idx_category_app` (`category_id`,`app_record_id`),
key `idx_app` (`app_id`)
) engine=innodb default charset=utf8 comment='App和分类关联表';
) engine=innodb default charset=utf8mb4 comment='App和分类关联表';
create table `app_comment` (
`id` bigint(20) not null auto_increment comment '自增id',
......@@ -70,7 +72,7 @@ create table `app_comment` (
`update_time` int(10) not null default 0 comment '更新时间',
primary key (`id`),
key `idx_app_status` (`app_id`, `status`, `top_flag`)
) engine=innodb default charset=utf8 comment='评论信息表';
) engine=innodb default charset=utf8mb4 comment='评论信息表';
create table `user_app_relation` (
`id` bigint(20) not null auto_increment comment '自增id',
......@@ -81,7 +83,7 @@ create table `user_app_relation` (
`is_del` tinyint(4) not null default '0' comment '1:删除 0:未删除',
primary key (`id`),
key `idx_user_app` (`user_id`,`app_id`)
) engine=innodb auto_increment=8063 default charset=utf8 comment='用户购买关系表';
) engine=innodb auto_increment=8063 default charset=utf8mb4 comment='用户购买关系表';
create table `bot_score` (
`id` bigint(20) not null auto_increment comment '自增id',
......@@ -93,4 +95,4 @@ create table `bot_score` (
`update_time` int(10) not null default 0 comment '更新时间',
primary key (`id`),
unique key `idx_uid_score` (`app_id`,`commenter_uid`)
) engine=innodb default charset=utf8 comment='App评分表';
\ No newline at end of file
) engine=innodb default charset=utf8mb4 comment='App评分表';
\ No newline at end of file
......@@ -2,23 +2,25 @@
当今世界对信息技术的依赖程度在不断加深,每天都会有大量的数据产生,我们经常会感到数据越来越多,但是要从中发现有价值的信息却越来越难。这里所说的信息,可以理解为对数据集处理之后的结果,是从数据集中提炼出的可用于其他场合的结论性的东西,而**从原始数据中抽取出有价值的信息**的这个过程我们就称之为**数据分析**,它是数据科学工作的一部分。
> 定义:**数据分析是有针对性的收集、加工、整理数据并采用统计、挖掘等技术对数据进行分析和解释的科学和艺术**。
### 数据分析师的职责和技能栈
我们通常将从事数据分析、数据挖掘和数据产品的岗位都统称为数据分析岗位,但是根据工作性质的不同,又可以分为偏业务的**数据分析方向**、偏算法的**数据挖掘方向**、偏产品的**数据产品方向**和偏开发的**数据工程方向**。我们通常所说的数据分析师主要是指**业务数据分析师**,很多数据分析师的职业生涯都是从这个岗位开始的,而且这个岗位也是招聘数量最多的岗位。业务数据分析师在公司通常不属于研发部门而**属于运营部门**,所以这个岗位也称为**数据运营****商业分析**通常招聘信息对这个岗位的描述(JD)是:
HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘等岗位都统称为数据分析岗位,但是根据工作性质的不同,又可以分为偏工程的**数据治理方向**、偏业务的**数据分析方向**、偏算法的**数据挖掘方向**、偏开发的**数据开发方向**、偏产品的**数据产品经理**。我们通常所说的数据分析师主要是指**业务数据分析师**,很多数据分析师的职业生涯都是从这个岗位开始的,而且这个岗位也是招聘数量最多的岗位。业务数据分析师在公司通常不属于研发部门而属于运营部门,所以这个岗位也称为**数据运营****商业分析**,这类人员通常也被称为“BI工程师”。通常招聘信息对这个岗位的描述(JD)是:
1. 负责各部门相关的报表
1. 负责相关报表的输出
2. 建立和优化指标体系。
3. 监控数据波动和异常,找出问题。
4. 优化和驱动业务,推动数字化运营。
5. 找出潜在的市场和产品的上升空间。
根据上面的描述,作为业务数据分析师,我们的工作不是给领导一个简单浅显的结论,而是结合公司的业务,完成**监控数据****揪出异常****找到原因****探索趋势**的工作。所以作为数据分析师,不管是用 Python 语言、Excel、SPSS或其他的商业智能工具,工具只是达成目标的手段,**数据思维是核心技能**,而从实际业务问题出发到最终**发现数据中的商业价值**是终极目标。数据分析师在很多公司只是一个基础岗位,精于业务的数据分析师可以向**数据分析经理****数据运营总监**等管理岗位发展;对于熟悉机器学习算法的数据分析师来说,可以向**数据挖掘工程师****算法专家**方向发展,而这些岗位除了需要相应的数学和统计学知识,在编程能力方面也比数据分析师有更高的要求,可能还需要有大数据存储和处理的相关经验;作为数据产品经理,除了传统产品经理的技能栈之外,也需要较强的技术能力,例如要了解常用的推荐算法、机器学习模型,能够为算法的改进提供依据,能够制定相关埋点的规范和口径,虽然不需要精通各种算法,但是要站在产品的角度去考虑数据模型、指标、算法等的落地;数据工程师是一个偏技术的岗位,基本上的成长道路都是从 SQL 开始,逐步向 Hadoop 生态圈迁移,需要有 Java 语言的编程经验。
根据上面的描述,作为业务数据分析师,我们的工作不是给领导一个简单浅显的结论,而是结合公司的业务,完成**监控数据****揪出异常****找到原因****探索趋势**等工作。作为数据分析师,不管是用 Python 语言、Excel、SPSS或其他的商业智能工具,工具只是达成目标的手段,**数据思维是核心技能**,从实际业务问题出发到最终**发现数据中的商业价值**是终极目标。数据分析师在很多公司只是一个基础岗位,精于业务的数据分析师可以向**数据分析经理****数据运营总监**等管理岗位发展;对于熟悉机器学习算法的数据分析师来说,可以向**数据挖掘工程师****算法专家**方向发展,而这些岗位除了需要相应的数学和统计学知识,在编程能力方面也比数据分析师有更高的要求,可能还需要有大数据存储和处理的相关经验。数据治理岗位主要是帮助公司建设数据仓库或数据湖,实现数据从业务系统、埋点系统、日志系统到分析库的转移,为后续的数据分析和挖掘提供基础设施。数据治理岗位对 SQL 和 HiveSQL 有着较高的要求,要求能够熟练的使用 ETL 工具,此外可能还需要对 Hadoop 生态圈有一个很好的认知。作为数据产品经理,除了传统产品经理的技能栈之外,也需要较强的技术能力,例如要了解常用的推荐算法、机器学习模型,能够为算法的改进提供依据,能够制定相关埋点的规范和口径,虽然不需要精通各种算法,但是要站在产品的角度去考虑数据模型、指标、算法等的落地
以下是我总结的数据分析师的技能栈,仅供参考。
1. 计算机科学(数据分析工具、编程语言、数据库)
2. 数学和统计学(数据思维、统计思维)
3. 人工智能(机器学习算法)
3. 人工智能(机器学习中的数据挖掘算法)
4. 业务理解能力(沟通、表达、经验)
5. 总结和表述能力(商业PPT、文字总结)
......@@ -30,8 +32,8 @@
1. 确定目标(输入):理解业务,确定指标口径
2. 获取数据:数据仓库、电子表格、三方接口、网络爬虫、开放数据集等
3. 清洗数据:缺失值/重复值/异常值处理、数据变换(格式化、规范化)、数据归约、离散化等
4. 探索数据:运算、统计、分组、聚合、可视化
3. 清洗数据:缺失值/重复值/异常值处理、数据变换(格式化、规范化)、离散化等
4. 数据透视:运算、统计、分组、聚合、可视化
5. 数据报告(输出):数据发布,工作成果总结汇报
6. 分析洞察(后续):解释数据的变化,提出对应的方案
......@@ -51,7 +53,7 @@
#### 三大神器
1. [NumPy](https://numpy.org/):支持常见的数组和矩阵操作,通过`ndarray`类实现了对多维数组的封装,提供了操作这些数组的方法和函数集。由于 NumPy 内置了并行运算功能,当使用多核 CPU 时,Numpy会自动做并行计算。
2. [Pandas](https://pandas.pydata.org/):pandas的核心是其特有的数据结构`DataFrame``Series`,这使得 pandas 可以处理包含不同类型的数据的负责表格和时间序列,这一点是NumPy的`ndarray`做不到的。使用 pandas,可以轻松顺利的加载各种形式的数据,然后对数据进行切片、切块、处理缺失值、聚合、重塑和可视化等操作。
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) 一样的绘图组件。
#### 其他相关库
......
## 环境准备
如果希望快速开始使用 Python 处理数据科学相关的工作,建议大家直接安装 Anaconda,然后使用 Anaconda 中集成的 Notebook 或 JupyterLab 工具来编写代码。因为对于新手来说,先安装官方的 Python 解释器,再逐个安装工作中会使用到的三方库文件会比较麻烦,尤其是在 Windows 环境下,经常会因为构建工具或 DLL 文件的缺失导致安装失败,而一般新手也很难根据错误提示信息采取正确的解决措施,容易产生严重的挫败感。
如果希望快速开始使用 Python 处理数据科学相关的工作,建议大家直接安装 Anaconda,然后使用 Anaconda 中集成的 Notebook 或 JupyterLab 工具来编写代码。因为对于新手来说,先安装官方的 Python 解释器,再逐个安装工作中会使用到的三方库文件会比较麻烦,尤其是在 Windows 环境下,经常会因为构建工具或 DLL 文件的缺失导致安装失败,而一般新手也很难根据错误提示信息采取正确的解决措施,容易产生严重的挫败感。如果计算机上已经有 Python 解释器环境了,也可以直接使用 Python 的包管理工具 pip 安装 Jupyter,再根据实际工作的需要安装三方库,这种方式适合有一定经验的用户。
### 安装和使用 Anaconda
对于个人用户来说,可以从 Anaconda 的[官方网站](https://www.anaconda.com/)下载它的“个人版(Individual Edition)”安装程序,安装完成后,你的计算机上不仅拥有了 Python 环境和 Spyder(类似于PyCharm的集成开发工具),还拥有了与数据科学工作相关的近200个工具包,包括我们上面提到 Python 数据分析三大神器。除此之外,Anaconda 还提供了一个名为 conda 的包管理工具,通过这个工具不仅可以管理 Python 的工具包,还可以用于创建运行 Python 程序的虚拟环境。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211005111417.png" width="100%">
<img src="https://github.com/jackfrued/mypic/raw/master/20211005111417.png">
如上图所示,可以通过 Anaconda 官网提供的下载链接选择适合自己操作系统的安装程序,建议大家选择图形化的安装程序,下载完成后双击安装程序开始安装。安装过程基本使用默认设置即可,完成安装后,macOS 用户可以在“应用程序”或“Launchpad”中找到名为“Anaconda-Navigator”的应用程序,运行该程序可以看到如下所示的界面,我们可以在这里选择需要执行的操作。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211005111729.png" width="85%">
<img src="https://github.com/jackfrued/mypic/raw/master/20211005111729.png">
对于 Windows 用户,建议按照安装向导的提示和推荐的选项来安装 Anaconda(除了安装路径,基本也没有什么需要选择的),安装完成后可以在“开始菜单”中找到“Anaconda3”。
> **温馨提示**:可以选择 Miniconda 作为 Anaconda 的替代品,Miniconda 只会安装 Python 解释器环境和一些必要的工具,其他的三方库由用户自行选择安装。其实我个人并不喜欢 Anaconda,因为它是给小白用户使用的,我们有了 Python 以后完全可以按照自己的意愿来安装需要的三方库。
#### conda命令
如果希望使用 conda 工具来管理依赖项或者创建项目的虚拟环境,可以在终端或命令行提示符中使用 conda 命令。Windows 用户可以在“开始菜单”中找到“Anaconda3”,然后点击“Anaconda Prompt”来启动支持 conda 的命令行提示符。macOS 用户建议直接使用“Anaconda-Navigator”中的“Environments”,通过可视化的方式对虚拟环境和依赖项进行管理。
......@@ -47,7 +49,7 @@
- 更新指定的包:`conda update matplotlib`
- 移除指定的包:`conda remove matplotlib`
> **说明**:在搜索、安装和更新软件包时,默认会连接到官方网站进行操作,如果觉得速度不给力,可以将默认的官方网站替换为国内的镜像网站,推荐使用清华大学的开源镜像网站。将默认源更换为国内镜像的命令是:`conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ `。如果需要换回默认源,可以使用命令`conda config --remove-key channels`。
> **说明**:在搜索、安装和更新软件包时,默认会连接到官方网站进行操作,如果觉得速度不给力,可以将默认的官方网站替换为国内的镜像网站,推荐使用清华大学的开源镜像网站。将默认源更换为国内镜像的命令是:`conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/`和`conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main`。如果需要换回默认源,可以使用命令`conda config --remove-key channels`。
### 使用Notebook
......@@ -79,11 +81,11 @@ Notebook 是基于网页的用于交互计算的应用程序,可以用于代
首先,我们可以创建一个用于书写 Python 代码的 Notebook,如下图所示。
![](https://gitee.com/jackfrued/mypic/raw/master/20211005113911.png)
![](https://github.com/jackfrued/mypic/raw/master/20211129131353.png)
接下来,我们就可以编写代码、撰写文档和运行程序啦,如下图所示。
![](https://gitee.com/jackfrued/mypic/raw/master/20211005113900.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005113900.png)
#### Notebook使用技巧
......@@ -97,17 +99,17 @@ Notebook 是基于网页的用于交互计算的应用程序,可以用于代
2. 获得帮助。在使用 Notebook 时,如果希望了解一个对象(如变量、类、函数等)的相关信息或使用方式,可以在对象后面使用`?`并运行代码, 窗口下方会显示出对应的信息,帮助我们了解该对象,如下所示。
![](https://gitee.com/jackfrued/mypic/raw/master/20211005113848.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005113848.png)
3. 搜索命名。如果只记得一个类或一个函数名字的一部分,可以使用通配符`*`并配合`?`进行搜索,如下所示。
![](https://gitee.com/jackfrued/mypic/raw/master/20211005113836.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005113836.png)
4. 调用命令。可以在 Notebook 中使用`!`后面跟系统命令的方式来执行系统命令。
5. 魔法指令。Notebook 中有很多非常有趣且有用的魔法指令,例如可以使用`%timeit`测试语句的执行时间,可以使用`%pwd`查看当前工作目录等。如果想查看所有的魔法指令,可以使用`%lsmagic`,如果了解魔法指令的用法,可以使用`%magic`来查看,如下图所示。
![](https://gitee.com/jackfrued/mypic/raw/master/20211005113825.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005113825.png)
常用的魔法指令有:
......@@ -131,34 +133,34 @@ Notebook 是基于网页的用于交互计算的应用程序,可以用于代
命令模式下的快捷键:
| 快捷键 | 功能说明 |
| ------------------------------- | -------------------------------------------- |
| Alt + Enter(Option + Enter) | 运行当前单元格并在下面插入新的单元格 |
| Shift + Enter | 运行当前单元格并选中下方的单元格 |
| Ctrl + Enter(Command + Enter) | 运行当前单元格 |
| j / k、Shift + j / Shift + k | 选中下方/上方单元格、连续选中下方/上方单元格 |
| a / b | 在下方/上方插入新的单元格 |
| c / x | 复制单元格 / 剪切单元格 |
| v / Shift + v | 在下方/上方粘贴单元格 |
| dd / z | 删除单元格 / 恢复删除的单元格 |
| l / Shift + l | 显示或隐藏当前/所有单元格行号 |
| ii / 00 | 中断/重启Notebook内核 |
| Space / Shift + Space | 向下/向上滚动页面 |
| 快捷键 | 功能说明 |
| ---------------------------------------- | -------------------------------------------- |
| `Alt` + `Enter`(`Option` + `Enter`) | 运行当前单元格并在下面插入新的单元格 |
| `Shift` + `Enter` | 运行当前单元格并选中下方的单元格 |
| `Ctrl` + `Enter`(`Command` + `Enter`) | 运行当前单元格 |
| `j` / `k`、`Shift` + `j` / `Shift` + `k` | 选中下方/上方单元格、连续选中下方/上方单元格 |
| `a` / `b` | 在下方/上方插入新的单元格 |
| `c` / `x` | 复制单元格 / 剪切单元格 |
| `v` / `Shift` + `v` | 在下方/上方粘贴单元格 |
| `dd` / `z` | 删除单元格 / 恢复删除的单元格 |
| `l` / `Shift` + `l` | 显示或隐藏当前/所有单元格行号 |
| `ii` / `00` | 中断/重启Notebook内核 |
| `Space` / `Shift` + `Space` | 向下/向上滚动页面 |
编辑模式下的快捷键:
| 快捷键 | 功能说明 |
| ------------------------------------------------ | -------------------------------------- |
| Shift + Tab | 获得提示信息 |
| Ctrl + ](Command + ])/ Ctrl + [(Command + [) | 增加/减少缩进 |
| Alt + Enter(Option + Enter) | 运行当前单元格并在下面插入新的单元格 |
| Shift + Enter | 运行当前单元格并选中下方的单元格 |
| Ctrl + Enter(Command + Enter) | 运行当前单元格 |
| Ctrl + Left / Right(Command + Left / Right) | 光标移到行首/行尾 |
| Ctrl + Up / Down(Command + Up / Down) | 光标移动代码开头/结尾处 |
| Up / Down | 光标上移/下移一行或移到上/下一个单元格 |
| 快捷键 | 功能说明 |
| ------------------------------------------------------------ | -------------------------------------- |
| `Shift` + `Tab` | 获得提示信息 |
| `Ctrl` + `]`(`Command` + `]`)/ `Ctrl` + `[`(`Command` + `[`) | 增加/减少缩进 |
| `Alt` + `Enter`(`Option` + `Enter`) | 运行当前单元格并在下面插入新的单元格 |
| `Shift` + `Enter` | 运行当前单元格并选中下方的单元格 |
| `Ctrl` + `Enter`(`Command` + `Enter`) | 运行当前单元格 |
| `Ctrl` + `Left` / `Right`(`Command` + `Left` / `Right`) | 光标移到行首/行尾 |
| `Ctrl` + `Up` / `Down`(`Command` + `Up` / `Down`) | 光标移动代码开头/结尾处 |
| `Up` / `Down` | 光标上移/下移一行或移到上/下一个单元格 |
> **温馨提示**:如果记不住这些快捷键也没有关系,在命令模式下按`h`键可以打开 Notebook 的帮助系统,马上就可以看到快捷键的设置,而且可以根据实际的需要重新编辑快捷键,如下图所示。
>
> ![](https://gitee.com/jackfrued/mypic/raw/master/20211005113812.png)
> ![](https://github.com/jackfrued/mypic/raw/master/20211005113812.png)
......@@ -2,7 +2,7 @@
Numpy 是一个开源的 Python 科学计算库,**用于快速处理任意维度的数组**。Numpy **支持常见的数组和矩阵操作**,对于同样的数值计算任务,使用 NumPy 不仅代码要简洁的多,而且 NumPy 的性能远远优于原生 Python,基本是一个到两个数量级的差距,而且数据量越大,NumPy 的优势就越明显。
Numpy 最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一维、二维和多维数组,该对象相当于是一个快速而灵活的大数据容器。NumPy 底层代码使用 C 语言编写,解决了 GIL 的限制,`ndarray`在存储数据的时候,数据与数据的地址都是连续的,这样就给使得批量操作速度很快,远远优于 Python 中的`list`;另一方面`ndarray`对象提供了更多的方法来处理数据,尤其是和统计相关的方法,这些方法也是 Python 原生的`list`没有的。
Numpy 最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一维、二维和多维数组,该对象相当于是一个快速而灵活的大数据容器。NumPy 底层代码使用 C 语言编写,解决了 GIL 的限制,`ndarray`在存取数据的时候,数据与数据的地址都是连续的,这确保了可以进行高效率的批量操作,远远优于 Python 中的`list`;另一方面`ndarray`对象提供了更多的方法来处理数据,尤其是和统计相关的方法,这些方法也是 Python 原生的`list`没有的。
### 准备工作
......@@ -12,7 +12,7 @@ Numpy 最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一
jupyter notebook
```
> **提示**:在启动Notebook之前,建议先安装好数据分析相关依赖项,包括之前提到的三大神器以及相关依赖项,包括:`numpy`、`pandas`、`matplotlib`、`openpyxl`、`xlrd`、`xlwt`等。如果使用Anaconda,则无需单独安装。
> **提示**:在启动Notebook之前,建议先安装好数据分析相关依赖项,包括之前提到的三大神器以及相关依赖项,包括:`numpy`、`pandas`、`matplotlib`、`openpyxl`等。如果使用Anaconda,则无需单独安装。
2. 导入
......@@ -22,7 +22,7 @@ Numpy 最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一
import matplotlib.pyplot as plt
```
> **说明**:如果已经启动了 Notebook 但尚未安装相关依赖库,例如尚未安装`numpy`,可以在 Notebook 的单元格中输入`!pip install numpy`并运行该单元格来安装 NumPy,也可以一次性安装多个三方库,需要在单元格中输入!pip install numpy pandas matplotlib`。注意上面的代码,我们不仅导入了NumPy,还将 pandas 和 matplotlib 库一并导入了。
> **说明**:如果已经启动了 Notebook 但尚未安装相关依赖库,例如尚未安装`numpy`,可以在 Notebook 的单元格中输入`!pip install numpy`并运行该单元格来安装 NumPy,也可以一次性安装多个三方库,需要在单元格中输入`%pip install numpy pandas matplotlib`。注意上面的代码,我们不仅导入了NumPy,还将 pandas 和 matplotlib 库一并导入了。
### 创建数组对象
......@@ -455,7 +455,7 @@ Numpy 最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一
`ndarray`对象元素的数据类型可以参考如下所示的表格。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211005114813.png" width="85%">
<img src="https://github.com/mypic/20211005114813.png" width="85%">
4. `ndim`属性:数组的维度
......@@ -695,9 +695,9 @@ Numpy 最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一
关于数组的索引和切片运算,大家可以通过下面的两张图来增强印象,这两张图来自[《利用Python进行数据分析》](https://item.jd.com/12398725.html)一书,它是`pandas`库的作者 Wes McKinney 撰写的 Python 数据分析领域的经典教科书,有兴趣的读者可以购买和阅读原书。
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115005.png)
<img src="https://github.com/jackfrued/mypic/raw/master/20211005115005.png" style="zoom: 65%">
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115041.png)
<img src="https://github.com/jackfrued/mypic/raw/master/20211005115041.png" style="zoom:65%">
3. 花式索引(fancy index)
......@@ -830,7 +830,7 @@ plt.imshow(guido_image)
plt.imshow(guido_image[::-1])
```
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115228.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005115228.png)
对数组的1轴进行反向切片,实现图像的水平翻转。
......@@ -838,7 +838,7 @@ plt.imshow(guido_image[::-1])
plt.imshow(guido_image[:,::-1])
```
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115242.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005115242.png)
将 Guido 的头切出来。
......@@ -846,7 +846,7 @@ plt.imshow(guido_image[:,::-1])
plt.imshow(guido_image[30:350, 90:300])
```
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115305.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005115305.png)
### 数组对象的方法
......@@ -877,50 +877,13 @@ print(array28.cumsum())
[ 1 3 6 10 15 20 24 27 29 30]
```
####其他方法
#### 其他方法
1. `all()` / `any()`方法:判断数组是否所有元素都是`True` / 判断数组是否有为`True`的元素。
2. `astype()`方法:拷贝数组,并将数组中的元素转换为指定的类型。
3. `dot()`方法:实现一个数组和另一个数组的点积运算。
在数学上,**点积**(dot product)又称**数量积**或**标量积**,是一种接受两个等长的数字序列,返回单个数字的代数运算。从代数角度看,先对两个数字序列中的每组对应元素求积,再对所有积求和,结果即为点积,即:$\boldsymbol{A} \cdot \boldsymbol{B} = \sum_{i=1}^{n}a_ib_i$。从几何角度看,点积则是两个向量的长度与它们夹角余弦的积,即:$\boldsymbol{A} \cdot \boldsymbol{B}=|\boldsymbol{A}||\boldsymbol{B}|\cos{\theta}$。
在欧几里得几何中,两个笛卡尔坐标向量的点积也称为**内积**(inner product),NumPy 中也提供了实现内积的函数,但是内积的含义要高于点积,点积相当于是内积在欧几里得空间$\mathbb{R}^n$的特例,而内积可以推广到赋范向量空间。
一维数组的点积运算,代码:
```Python
array29 = np.array([3, 4])
array30 = np.array([5, 6])
array29.dot(array30)
```
输出:
```
39
```
二维数组的点积运算,代码:
```Python
array31 = np.array([[1, 2, 3], [4, 5, 6]])
array32 = np.array([[1, 2], [3, 4], [5, 6]])
array31.dot(array32)
```
输出:
```
array([[22, 28],
[49, 64]])
```
> **说明**:可以看出,二维数组的点积就是矩阵乘法运算。
4. `dump()`方法:保存数组到文件中,可以通过 NumPy 中的`load()`函数从保存的文件中加载数据创建数组。
3. `dump()`方法:保存数组到文件中,可以通过 NumPy 中的`load()`函数从保存的文件中加载数据创建数组。
代码:
......@@ -938,9 +901,9 @@ print(array28.cumsum())
[5, 6]])
```
5. `fill()`方法:向数组中填充指定的元素。
4. `fill()`方法:向数组中填充指定的元素。
6. `flatten()`方法:将多维数组扁平化为一维数组。
5. `flatten()`方法:将多维数组扁平化为一维数组。
代码:
......@@ -954,11 +917,11 @@ print(array28.cumsum())
array([1, 2, 3, 4, 5, 6])
```
7. `nonzero()`方法:返回非0元素的索引。
6. `nonzero()`方法:返回非0元素的索引。
8. `round()`方法:对数组中的元素做四舍五入操作。
7. `round()`方法:对数组中的元素做四舍五入操作。
9. `sort()`方法:对数组进行就地排序。
8. `sort()`方法:对数组进行就地排序。
代码:
......@@ -974,7 +937,7 @@ print(array28.cumsum())
array([12, 35, 40, 54, 66, 78, 82, 96])
```
10. `swapaxes()``transpose()`方法:交换数组指定的轴。
9. `swapaxes()``transpose()`方法:交换数组指定的轴。
代码:
......@@ -987,7 +950,7 @@ print(array28.cumsum())
```
array([[1, 3, 5],
[2, 4, 6]])
[2, 4, 6]])
```
代码:
......@@ -1001,23 +964,8 @@ print(array28.cumsum())
```
array([[1, 3, 5],
[2, 4, 6]])
[2, 4, 6]])
```
11. `take()`方法:从数组中取指定索引的元素,类似于花式索引。
代码:
```Python
array34 = array33.take([0, 2, -3, -1])
array34
```
输出:
```
array([12, 40, 78, 96])
```
12. `tolist()`方法:将数组转成Python中的`list`
11. `tolist()`方法:将数组转成Python中的`list`
......@@ -2,7 +2,7 @@
### 数组的运算
使用 NumPy 最为方便的是当需要对数组元素进行运算时,不用编写循环代码遍历每个元素,所有的运算都会自动的**矢量化**(使用高效的提前编译的底层语言代码来对数据序列进行数学操作)。简单的说就是,NumPy 中的数学运算和数学函数会自动作用于数组中的每个成员。
使用 NumPy 最为方便的是当需要对数组元素进行运算时,不用编写循环代码遍历每个元素,所有的运算都会自动的**矢量化**(使用高效的、提前编译的底层代码来对数据序列进行数学操作)。简单的说就是,NumPy 中的数学运算和数学函数会自动作用于数组中的每个成员。
#### 数组跟标量的运算
......@@ -102,7 +102,7 @@ print(np.power(array37, array38))
**表2:通用二元函数**
| 函数 | 说明 |
| --------------------------------- | ---- |
| ---------------------------------- | ---- |
| `add(x, y)` / `substract(x, y)` | 加法函数 / 减法函数 |
|`multiply(x, y)` / `divide(x, y)`|乘法函数 / 除法函数|
| `floor_divide(x, y)` / `mod(x, y)` | 整除函数 / 求模函数 |
......@@ -110,8 +110,10 @@ print(np.power(array37, array38))
| `power(x, y)` | 数组$x$的元素$x_i$和数组$y$的元素$y_i$,计算$x_i^{y_i}$ |
| `maximum(x, y)` / `fmax(x, y)` | 两两比较元素获取最大值 / 获取最大值(忽略NaN) |
| `minimum(x, y)` / `fmin(x, y)` | 两两比较元素获取最小值 / 获取最小值(忽略NaN) |
| `inner(x, y)` | 内积运算 |
| `cross(x, y) `/ `outer(x, y)` | 叉积运算 / 外积运算 |
| `dot(x, y)` | 点积运算(数量积,通常记为$\cdots$,用于欧几里得空间(Euclidean space)) |
| `inner(x, y)` | 内积运算(内积的含义要高于点积,点积相当于是内积在欧几里得空间$$的特例,而内积可以推广到**赋范向量空间**,只要它满足平行四边形法则即可) |
| `cross(x, y) ` | 叉积运算(向量积,通常记为$\times$,运算结果是一个向量) |
| `outer(x, y)` | 外积运算(张量积,通常记为$\bigotimes$,运算结果通常是一个矩阵) |
| `intersect1d(x, y)` | 计算`x``y`的交集,返回这些元素构成的有序数组 |
| `union1d(x, y)` | 计算`x``y`的并集,返回这些元素构成的有序数组 |
| `in1d(x, y)` | 返回由判断`x` 的元素是否在`y`中得到的布尔值构成的数组 |
......@@ -159,11 +161,11 @@ array([[1, 1, 1],
通过上面的例子,我们发现形状不同的数组仍然有机会进行二元运算,但也绝对不是任意的数组都可以进行二元运算。简单的说,只有两个数组后缘维度相同或者其中一个数组后缘维度为1时,广播机制会被触发,而通过广播机制如果能够使两个数组的形状一致,才能进行二元运算。所谓后缘维度,指的是数组`shape`属性对应的元组中最后一个元素的值(从后往前数最后一个维度的值),例如,我们之前打开的图像对应的数组后缘维度为3,3行4列的二维数组后缘维度为4,而有5个元素的一维数组后缘维度为5。简单的说就是,后缘维度相同或者其中一个数组的后缘维度为1,就可以应用广播机制;而广播机制如果能够使得数组的形状一致,就满足了两个数组对应元素做运算的需求,如下图所示。
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115640.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005115640.png)
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115658.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005115658.png)
![](https://gitee.com/jackfrued/mypic/raw/master/20211005115800.png)
![](https://github.com/jackfrued/mypic/raw/master/20211005115800.png)
### 其他常用函数
......@@ -189,8 +191,6 @@ array([[1, 1, 1],
| `roll` | 沿指定轴对数组元素进行移位 |
| `resize` | 重新调整数组的大小 |
| `place` / `put` | 将数组中满足条件的元素/指定的元素替换为指定的值 |
| `ptp` | 沿指定的轴计算极差(最大值与最小值的差) |
| `median` | 沿指定轴计算中位数 |
| `partition` | 用选定的元素对数组进行一次划分并返回划分后的数组 |
> **提示**:上面的`resize`函数和`ndarray`对象的`resize`方法是有区别的,`resize`函数在调整数组大小时会重复数组中的元素作为填补多出来的元素的值,而`ndarry`对象的`resize`方法是用0来填补多出来的元素。这些小细节不清楚暂时也不要紧,但是如果用到对应的功能了就要引起注意。
......@@ -399,7 +399,7 @@ np.linalg.det(m4)
```Python
# 解线性方程组ax=b
# 3x + y = 9,x + 2y = 8
# 3*x1 + x2= 9,x1 + 2*x2 = 8
a = np.array([[3,1], [1,2]])
b = np.array([9, 8])
np.linalg.solve(a, b)
......
......@@ -671,7 +671,7 @@ for i in range(4):
plt.show()
```
![](res/series-bar-graph.png)
![](https://github.com/jackfrued/mypic/raw/master/20220619171513.png)
绘制反映每个季度占比的饼图。
......@@ -683,4 +683,4 @@ plt.ylabel('各季度占比')
plt.show()
```
![](res/series-pie-graph.png)
\ No newline at end of file
![](https://github.com/jackfrued/mypic/raw/master/20220619171503.png)
\ No newline at end of file
......@@ -199,7 +199,7 @@ dno
异常值的检测有Z-score 方法、IQR 方法、DBScan 聚类、孤立森林等,这里我们对前两种方法做一个简单的介绍。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211004192858.png" style="zoom:50%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20211004192858.png" style="zoom:50%;">
如果数据服从正态分布,依据3σ法则,异常值被定义与平均值的偏差超过三倍标准差的值。在正态分布下,距离平均值3σ之外的值出现的概率为$ P(|x-\mu|>3\sigma)<0.003 $,属于小概率事件。如果数据不服从正态分布,那么可以用远离平均值的多少倍的标准差来描述,这里的倍数就是Z-score。Z-score以标准差为单位去度量某一原始分数偏离平均值的距离,公式如下所示。
$$
......
......@@ -161,7 +161,7 @@ df = pd.read_excel('2020年销售数据.xlsx')
df.head()
```
> **说明**:如果需要上面例子中的 Excel 文件,可以通过下面的阿里云盘地址进行获取,该文件在“我的分享”下面的“数据集”目录中。地址:https://www.aliyundrive.com/s/oPi7DRAVKRm
> **说明**:如果需要上面例子中的 Excel 文件,可以通过百度云盘进行获取。链接:https://pan.baidu.com/s/1NhWtYcpFzF72cxcsoDoXjQ?pwd=swg1,提取码:swg1
输出:
......@@ -375,7 +375,7 @@ pd.pivot_table(df, index='销售区域', values='销售额', aggfunc='sum')
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106180912.png" style="zoom:50%">
<img src="https://github.com/jackfrued/mypic/raw/master/20211106180912.png" style="zoom:50%">
> **注意**:上面的结果操作跟之前用`groupby`的方式得到的结果有一些区别,`groupby`操作后,如果对单个列进行聚合,得到的结果是一个`Series`对象,而上面的结果是一个`DataFrame` 对象。
......@@ -398,7 +398,7 @@ pd.pivot_table(
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106104551.png" style="zoom:50%">
<img src="https://github.com/jackfrued/mypic/raw/master/20211106104551.png" style="zoom:50%">
使用`pivot_table`函数时,还可以通过添加`margins`和`margins_name`参数对分组聚合的结果做一个汇总,具体的操作和效果如下所示。
......@@ -413,7 +413,7 @@ pd.pivot_table(
输出:
![image-20211106181707655](https://gitee.com/jackfrued/mypic/raw/master/20211106181707.png)
![image-20211106181707655](https://github.com/jackfrued/mypic/raw/master/20211106181707.png)
交叉表就是一种特殊的透视表,它不需要先构造一个`DataFrame`对象,而是直接通过数组或`Series`对象指定两个或多个因素进行运算得到统计结果。例如,我们要统计每个销售区域的销售总额,也可以按照如下所示的方式来完成,我们先准备三组数据。
......@@ -464,7 +464,7 @@ plt.show()
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106195040.png" style="zoom:50%">
<img src="https://github.com/jackfrued/mypic/raw/master/20211106195040.png" style="zoom:50%">
如果要绘制饼图,可以修改`plot`方法的`kind`参数为`pie`,然后使用定制饼图的参数对图表加以定制,代码如下所示。
......@@ -480,5 +480,5 @@ plt.show()
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211106201550.png" style="zoom:50%">
<img src="https://github.com/jackfrued/mypic/raw/master/20211106201550.png" style="zoom:50%">
......@@ -24,7 +24,7 @@ baidu_df
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208205710.png" style="zoom:38%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208205710.png" style="zoom:38%;">
上面的`DataFrame``Open``High``Low``Close``Volume`五个列,分别代码股票的开盘价、最高价、最低价、收盘价和成交量,接下来我们对百度的股票数据进行窗口计算。
......@@ -34,7 +34,7 @@ baidu_df.rolling(5).mean()
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208205932.png" style="zoom:38%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208205932.png" style="zoom:38%;">
上面的`Close` 列的数据就是我们需要的5日均线,当然,我们也可以用下面的方法,直接在`Close`列对应的`Series`对象上计算5日均线。
......@@ -116,7 +116,7 @@ boston_df.corr()
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208213325.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208213325.png">
斯皮尔曼相关系数对数据条件的要求没有皮尔逊相关系数严格,只要两个变量的观测值是成对的等级评定资料,或者是由连续变量观测资料转化得到的等级资料,不论两个变量的总体分布形态、样本容量的大小如何,都可以用斯皮尔曼等级相关系数来进行研究。我们通过下面的方式来计算斯皮尔曼相关系数。
......@@ -126,7 +126,7 @@ boston_df.corr('spearman')
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208213518.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208213518.png">
在 Notebook 或 JupyterLab 中,我们可以为`PRICE`列添加渐变色,用颜色直观的展示出跟房价负相关、正相关、不相关的列,`DataFrame`对象`style`属性的`background_gradient`方法可以完成这个操作,代码如下所示。
......@@ -134,7 +134,7 @@ boston_df.corr('spearman')
boston_df.corr('spearman').style.background_gradient('RdYlBu', subset=['PRICE'])
```
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208215228.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208215228.png">
上面代码中的`RdYlBu`代表的颜色如下所示,相关系数的数据值越接近`1`,颜色越接近红色;数据值越接近`1`,颜色越接近蓝色;数据值在`0`附件则是黄色。
......@@ -142,7 +142,7 @@ boston_df.corr('spearman').style.background_gradient('RdYlBu', subset=['PRICE'])
plt.get_cmap('RdYlBu')
```
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208215057.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208215057.png">
### Index的应用
......@@ -360,7 +360,7 @@ df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208220551.png" style="zoom:150%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208220551.png" style="zoom:150%;">
代码:
......@@ -370,7 +370,7 @@ df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208220713.png" style="zoom:150%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208220713.png" style="zoom:150%;">
- `asfreq()`方法:指定一个时间频率抽取对应的数据,代码如下所示。
......@@ -382,7 +382,7 @@ df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221202.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221202.png">
代码:
......@@ -392,7 +392,7 @@ df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221249.png" style="zoom:150%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221249.png" style="zoom:150%;">
- `resample()`方法:基于时间对数据进行重采样,相当于根据时间周期对数据进行了分组操作,代码如下所示。
......@@ -404,7 +404,7 @@ df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221429.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221429.png">
> **说明**:上面的代码中,`W`表示一周,`5D`表示`5`天,`1M`表示`1`个月。
......@@ -429,7 +429,7 @@ df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221947.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221947.png">
- `tz_convert()`方法:转换时区。
......@@ -441,7 +441,7 @@ df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208222404.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20211208222404.png">
......
## Pandas的应用-5
### DataFrame的应用
#### 窗口计算
`DataFrame`对象的`rolling`方法允许我们将数据置于窗口中,然后就可以使用函数对窗口中的数据进行运算和处理。例如,我们获取了某只股票近期的数据,想制作5日均线和10日均线,那么就需要先设置窗口再进行运算。我们可以使用三方库`pandas-datareader`来获取指定的股票在某个时间段内的数据,具体的操作如下所示。
安装`pandas-datareader`三方库。
```Bash
pip install pandas-datareader
```
通过`pandas-datareader` 提供的`get_data_stooq`从 Stooq 网站获取百度(股票代码:BIDU)近期股票数据。
```Python
import pandas_datareader as pdr
baidu_df = pdr.get_data_stooq('BIDU', start='2021-11-22', end='2021-12-7')
baidu_df.sort_index(inplace=True)
baidu_df
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208205710.png" style="zoom:38%;">
上面的`DataFrame``Open``High``Low``Close``Volume`五个列,分别代表股票的开盘价、最高价、最低价、收盘价和成交量,接下来我们对百度的股票数据进行窗口计算。
```Python
baidu_df.rolling(5).mean()
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208205932.png" style="zoom:38%;">
上面的`Close` 列的数据就是我们需要的5日均线,当然,我们也可以用下面的方法,直接在`Close`列对应的`Series`对象上计算5日均线。
```Python
baidu_df.Close.rolling(5).mean()
```
输出:
```
Date
2021-11-22 NaN
2021-11-23 NaN
2021-11-24 NaN
2021-11-26 NaN
2021-11-29 150.608
2021-11-30 151.014
2021-12-01 150.682
2021-12-02 150.196
2021-12-03 147.062
2021-12-06 146.534
2021-12-07 146.544
Name: Close, dtype: float64
```
#### 相关性判定
在统计学中,我们通常使用协方差(covariance)来衡量两个随机变量的联合变化程度。如果变量 $X$ 的较大值主要与另一个变量 $Y$ 的较大值相对应,而两者较小值也相对应,那么两个变量倾向于表现出相似的行为,协方差为正。如果一个变量的较大值主要对应于另一个变量的较小值,则两个变量倾向于表现出相反的行为,协方差为负。简单的说,协方差的正负号显示着两个变量的相关性。方差是协方差的一种特殊情况,即变量与自身的协方差。
$$
cov(X,Y) = E((X - \mu)(Y - \upsilon)) = E(X \cdot Y) - \mu\upsilon
$$
如果 $X$ 和 $Y$ 是统计独立的,那么二者的协方差为0,这是因为在 $X$ 和 $Y$ 独立的情况下:
$$
E(X \cdot Y) = E(X) \cdot E(Y) = \mu\upsilon
$$
协方差的数值大小取决于变量的大小,通常是不容易解释的,但是正态形式的协方差可以显示两变量线性关系的强弱。在统计学中,皮尔逊积矩相关系数就是正态形式的协方差,它用于度量两个变量 $X$ 和 $Y$ 之间的相关程度(线性相关),其值介于`-1``1`之间。
$$
\rho{X,Y} = \frac {cov(X, Y)} {\sigma_{X}\sigma_{Y}}
$$
估算样本的协方差和标准差,可以得到样本皮尔逊系数,通常用希腊字母 $\rho$ 表示。
$$
\rho = \frac {\sum_{i=1}^{n}(X_i - \bar{X})(Y_i - \bar{Y})} {\sqrt{\sum_{i=1}^{n}(X_i - \bar{X})^2} \sqrt{\sum_{i=1}^{n}(Y_i - \bar{Y})^2}}
$$
我们用 $\rho$ 值判断指标的相关性时遵循以下两个步骤。
1. 判断指标间是正相关、负相关,还是不相关。
- 当 $ \rho \gt 0 $,认为变量之间是正相关,也就是两者的趋势一致。
- 当 $ \rho \lt 0 $,认为变量之间是负相关,也就是两者的趋势相反。
- 当 $ \rho = 0 $,认为变量之间是不相关的,但并不代表两个指标是统计独立的。
2. 判断指标间的相关程度。
- 当 $ \rho $ 的绝对值在 $ [0.6,1] $ 之间,认为变量之间是强相关的。
- 当 $ \rho $ 的绝对值在 $ [0.1,0.6) $ 之间,认为变量之间是弱相关的。
- 当 $ \rho $ 的绝对值在 $ [0,0.1) $ 之间,认为变量之间没有相关性。
皮尔逊相关系数适用于:
1. 两个变量之间是线性关系,都是连续数据。
2. 两个变量的总体是正态分布,或接近正态的单峰分布。
3. 两个变量的观测值是成对的,每对观测值之间相互独立。
`DataFrame`对象的`cov`方法和`corr`方法分别用于计算协方差和相关系数,`corr`方法的第一个参数`method`的默认值是`pearson`,表示计算皮尔逊相关系数;除此之外,还可以指定`kendall`或`spearman`来获得肯德尔系数或斯皮尔曼等级相关系数。
接下来,我们从名为`boston_house_price.csv`的文件中获取著名的[波士顿房价数据集](https://www.heywhale.com/mw/dataset/590bd595812ede32b73f55f2)来创建一个`DataFrame`,我们通过`corr`方法计算可能影响房价的`13`个因素中,哪些跟房价是正相关或负相关的,代码如下所示。
```Python
boston_df = pd.read_csv('data/csv/boston_house_price.csv')
boston_df.corr()
```
> **说明**:如果需要上面例子中的 CSV 文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:<https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g>,提取码:e7b4。
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208213325.png">
斯皮尔曼相关系数对数据条件的要求没有皮尔逊相关系数严格,只要两个变量的观测值是成对的等级评定资料,或者是由连续变量观测资料转化得到的等级资料,不论两个变量的总体分布形态、样本容量的大小如何,都可以用斯皮尔曼等级相关系数来进行研究。我们通过下面的方式来计算斯皮尔曼相关系数。
```Python
boston_df.corr('spearman')
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208213518.png">
在 Notebook 或 JupyterLab 中,我们可以为`PRICE`列添加渐变色,用颜色直观的展示出跟房价负相关、正相关、不相关的列,`DataFrame`对象`style`属性的`background_gradient`方法可以完成这个操作,代码如下所示。
```Python
boston_df.corr('spearman').style.background_gradient('RdYlBu', subset=['PRICE'])
```
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208215228.png">
上面代码中的`RdYlBu`代表的颜色如下所示,相关系数的数据值越接近`1`,颜色越接近红色;数据值越接近`1`,颜色越接近蓝色;数据值在`0`附件则是黄色。
```Python
plt.get_cmap('RdYlBu')
```
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208215057.png">
### Index的应用
我们再来看看`Index`类型,它为`Series``DataFrame`对象提供了索引服务,常用的`Index`有以下几种,我们直接上代码。
1. 范围索引(`RangeIndex`
代码:
```Python
sales_data = np.random.randint(400, 1000, 12)
month_index = pd.RangeIndex(1, 13, name='月份')
ser = pd.Series(data=sales_data, index=month_index)
ser
```
输出:
```
月份
1 703
2 705
3 557
4 943
5 961
6 615
7 788
8 985
9 921
10 951
11 874
12 609
dtype: int64
```
2. 分类索引(`CategoricalIndex`
代码:
```Python
cate_index = pd.CategoricalIndex(
['苹果', '香蕉', '苹果', '苹果', '桃子', '香蕉'],
ordered=True,
categories=['苹果', '香蕉', '桃子']
)
ser = pd.Series(data=amount, index=cate_index)
ser
```
输出:
```
苹果 6
香蕉 6
苹果 7
苹果 6
桃子 8
香蕉 6
dtype: int64
```
基于索引分组数据,然后使用`sum`进行求和。
```Python
ser.groupby(level=0).sum()
```
输出:
```
苹果 19
香蕉 12
桃子 8
dtype: int64
```
3. 多级索引(`MultiIndex`
代码:
```Python
ids = np.arange(1001, 1006)
sms = ['期中', '期末']
index = pd.MultiIndex.from_product((ids, sms), names=['学号', '学期'])
courses = ['语文', '数学', '英语']
scores = np.random.randint(60, 101, (10, 3))
df = pd.DataFrame(data=scores, columns=courses, index=index)
df
```
> **说明**:上面的代码使用了`MultiIndex`的类方法`from_product`,该方法通过`ids`和`sms`两组数据的笛卡尔积构造了多级索引。
输出:
```
语文 数学 英语
学号 学期
1001 期中 93 77 60
期末 93 98 84
1002 期中 64 78 71
期末 70 71 97
1003 期中 72 88 97
期末 99 100 63
1004 期中 80 71 61
期末 91 62 72
1005 期中 82 95 67
期末 84 78 86
```
根据第一级索引分组数据,按照期中成绩占`25%`,期末成绩占`75%` 的方式计算每个学生每门课的成绩。
```Python
# 计算每个学生的成绩,期中占25%,期末占75%
df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75)
```
输出:
```
语文 数学 英语
学号
1001 93.00 92.75 78.00
1002 68.50 72.75 90.50
1003 92.25 97.00 71.50
1004 88.25 64.25 69.25
1005 83.50 82.25 81.25
```
4. 日期时间索引(`DatetimeIndex`
通过`date_range()`函数,我们可以创建日期时间索引,代码如下所示。
代码:
```Python
pd.date_range('2021-1-1', '2021-6-1', periods=10)
```
输出:
```
DatetimeIndex(['2021-01-01 00:00:00', '2021-01-17 18:40:00',
'2021-02-03 13:20:00', '2021-02-20 08:00:00',
'2021-03-09 02:40:00', '2021-03-25 21:20:00',
'2021-04-11 16:00:00', '2021-04-28 10:40:00',
'2021-05-15 05:20:00', '2021-06-01 00:00:00'],
dtype='datetime64[ns]', freq=None)
```
代码:
```Python
pd.date_range('2021-1-1', '2021-6-1', freq='W')
```
输出:
```
DatetimeIndex(['2021-01-03', '2021-01-10', '2021-01-17', '2021-01-24',
'2021-01-31', '2021-02-07', '2021-02-14', '2021-02-21',
'2021-02-28', '2021-03-07', '2021-03-14', '2021-03-21',
'2021-03-28', '2021-04-04', '2021-04-11', '2021-04-18',
'2021-04-25', '2021-05-02', '2021-05-09', '2021-05-16',
'2021-05-23', '2021-05-30'],
dtype='datetime64[ns]', freq='W-SUN')
```
通过`DateOffset`类型,我们可以设置时间差并和`DatetimeIndex`进行运算,具体的操作如下所示。
代码:
```Python
index = pd.date_range('2021-1-1', '2021-6-1', freq='W')
index - pd.DateOffset(days=2)
```
输出:
```
DatetimeIndex(['2021-01-01', '2021-01-08', '2021-01-15', '2021-01-22',
'2021-01-29', '2021-02-05', '2021-02-12', '2021-02-19',
'2021-02-26', '2021-03-05', '2021-03-12', '2021-03-19',
'2021-03-26', '2021-04-02', '2021-04-09', '2021-04-16',
'2021-04-23', '2021-04-30', '2021-05-07', '2021-05-14',
'2021-05-21', '2021-05-28'],
dtype='datetime64[ns]', freq=None)
```
代码:
```Python
index + pd.DateOffset(days=2)
```
输出:
```
DatetimeIndex(['2021-01-05', '2021-01-12', '2021-01-19', '2021-01-26',
'2021-02-02', '2021-02-09', '2021-02-16', '2021-02-23',
'2021-03-02', '2021-03-09', '2021-03-16', '2021-03-23',
'2021-03-30', '2021-04-06', '2021-04-13', '2021-04-20',
'2021-04-27', '2021-05-04', '2021-05-11', '2021-05-18',
'2021-05-25', '2021-06-01'],
dtype='datetime64[ns]', freq=None)
```
可以使用`DatatimeIndex`类型的相关方法来处理数据,例如`shift()`方法可以通过时间前移或后移数据。我们仍然以上面百度股票数据为例,来演示`shift()`方法的使用,代码如下所示。
代码:
```Python
baidu_df.shift(3, fill_value=0)
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208220551.png" style="zoom:150%;">
代码:
```Python
baidu_df.shift(-1, fill_value=0)
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208220713.png" style="zoom:150%;">
通过`asfreq()`方法,我们可以指定一个时间频率抽取对应的数据,代码如下所示。
代码:
```Python
baidu_df.asfreq('5D')
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221202.png">
代码:
```Python
baidu_df.asfreq('5D', method='ffill')
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221249.png" style="zoom:150%;">
通过`resample()`方法,我们可以基于时间对数据进行重采样,相当于根据时间周期对数据进行了分组操作,代码如下所示。
代码:
```Python
baidu_df.resample('1M').mean()
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221429.png">
> **说明**:上面的代码中,`W`表示一周,`5D`表示`5`天,`1M`表示`1`个月。
如果要实现日期时间的时区转换,我们首先用`tz_localize()`方法将日期时间本地化,代码如下所示。
代码:
```Python
baidu_df = baidu_df.tz_localize('Asia/Chongqing')
baidu_df
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208221947.png">
在对时间本地化以后,我们使用`tz_convert()`方法就可以实现转换时区,代码如下所示。
代码:
```Python
baidu_df.tz_convert('America/New_York')
```
输出:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20211208222404.png">
## 数据可视化-1
在完成了对数据的透视之后,我们可以将数据透视的结果通过可视化的方式呈现出来,简单的说,就是将数据变成漂亮的统计图表,然后进一步发现和解读数据背后隐藏的商业价值。在之前的课程中,我们已经为大家展示过用使用`Series``DataFrame`对象的`plot`方法生成可视化图表的操作,本章我们为大家讲解`plot`方法的基石,它就是大名鼎鼎的`matplotlib`库。
### 常用的图表类型
常用的图表类型及其应用场景如下图所示。
<img src="https://github.com/jackfrued/mypic/raw/master/20220315193326.png" style="zoom:65%">
### Matplotlib 的安装和导入
如果还没有安装`matplotlib`库,可以使用 Python 的包管理工具 pip 来安装,命令如下所示。
```Shell
pip install matplotlib
```
在 Notebook 中,我们可以用下面的方式导入`matplotlib`。为了解决图表中文显示的问题,我们可以通过`pyplot`模块的`rcParams`属性修改配置参数,具体的操作如下所示。
```Python
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei', 'Songti SC']
plt.rcParams['axes.unicode_minus'] = False
```
> **说明**:上面代码中的`SimHei`是字体名称,大家可以通过百度云盘下载并安装该字体,链接地址:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4;`Songti SC`是我的 macOS 上自带的字体,对于 macOS 或 Windows 系统,字体的名字都可以在用户主目录下的`.matplotlib`文件夹下的`fontlist-v330.json`文件中找到。值得注意的是,使用中文字体后坐标轴上的负号会显示不出来,所以需要将`axes.unicode_minus`参数设置为`False`,这样才能让坐标轴上的负号正常显示。
通过下面的魔法指令,我们可以在绘图时生成[矢量图](https://zh.wikipedia.org/wiki/%E7%9F%A2%E9%87%8F%E5%9B%BE%E5%BD%A2)(SVG - Scalable Vector Graphics)。
```Python
%config InlineBackend.figure_format='svg'
```
### 绘图的流程
#### 创建画布
`pyplot`模块的`figure`函数可以用来创建画布,创建画布时,可以通过`figsize`参数指定画布的尺寸(默认值是`[6.4, 4.8]`);可以通过`dpi`参数设置绘图的分辨率,因为`dpi`代表了每英寸的像素点数量。除此之外,还可以通过`facecolor`参数设置画布的背景色。`figure`函数的返回值是一个`Figure`对象,它代表了绘图使用的画布,我们可以基于画布来创建绘图使用的坐标系。
```Python
plt.figure(figsize=(8, 4), dpi=120, facecolor='darkgray')
```
#### 创建坐标系
可以直接使用`pyplot`模块的`subplot`函数来创建坐标系,该函数会返回`Axes`对象。`subplot`的前三个参数分别用来指定整个画布分成几行几列以及当前坐标系的索引,这三个参数的默认值都是`1`。如果需要在画布上创建多个坐标系,就需要使用该函数,否则就直接使用默认的也是唯一的坐标系。当然,也可以通过上面创建的`Figure`对象的`add_subplot`方法或`add_axes`方法来创建坐标系,前者跟`subplot`函数的作用一致,后者会产生嵌套的坐标系。
```Python
plt.subplot(2, 2, 1)
```
#### 绘制图像
##### 折线图
在绘图时,如果没有先调用`figure`函数和`subplot`函数,我们将使用默认的画布和坐标系,如果要绘制折线图,可以使用`pyplot`模块的`plot`函数,并指定横轴和纵轴的数据。折线图最适合用来观察数据的趋势,尤其是当横坐标代表时间的情况下。我们可以使用`plot`函数的`color`参数来定制折线的颜色,可以使用`marker`参数来定制数据点的标记(例如:`*`表示五角星,`^`表示三角形,`o`表示小圆圈等),可以使用`linestyle`参数来定制折线的样式(例如:`-`表示实线,`--`表示虚线,`:`表示点线等),可以使用`linewidth`参数来定制折线的粗细。 下面的代码绘制了一条正弦曲线,其中`marker='*'`会将数据点的标记设置为五角星形状,而`color='red'`会将折线绘制为红色。
```Python
import numpy as np
x = np.linspace(-2 * np.pi, 2 * np.pi, 120)
y = np.sin(x)
# 创建画布
plt.figure(figsize=(8, 4), dpi=120)
# 绘制折线图
plt.plot(x, y, linewidth=2, marker='*', color='red')
# 显示绘图
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220501173344.png" style="zoom:50%;">
如果要在一个坐标系上同时绘制正弦和余弦曲线,可以对上面的代码稍作修改。
```Python
x = np.linspace(-2 * np.pi, 2 * np.pi, 120)
y1, y2 = np.sin(x), np.cos(x)
plt.figure(figsize=(8, 4), dpi=120)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
plt.plot(x, y2, linewidth=2, marker='^', color='blue')
# 定制图表的标注,其中的arrowprops是定制箭头样式的参数
plt.annotate('sin(x)', xytext=(0.5, -0.75), xy=(0, -0.25), fontsize=12, arrowprops={
'arrowstyle': '->', 'color': 'darkgreen', 'connectionstyle': 'angle3, angleA=90, angleB=0'
})
plt.annotate('cos(x)', xytext=(-3, 0.75), xy=(-1.25, 0.5), fontsize=12, arrowprops={
'arrowstyle': '->', 'color': 'darkgreen', 'connectionstyle': 'arc3, rad=0.35'
})
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502095949.png" style="zoom:50%;">
如果要使用两个坐标系分别绘制正弦和余弦,可以用上面提到的`subplot`函数来创建坐标系,然后再绘图。
```Python
plt.figure(figsize=(8, 4), dpi=120)
# 创建坐标系(第1个图)
plt.subplot(2, 1, 1)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
# 创建坐标系(第2个图)
plt.subplot(2, 1, 2)
plt.plot(x, y2, linewidth=2, marker='^', color='blue')
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220501173446.png" style="zoom:50%;">
当然也可以像下面这么做,大家可以运行代码看看跟上面的图有什么区别。
```Python
plt.figure(figsize=(8, 4), dpi=120)
plt.subplot(1, 2, 1)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
plt.subplot(1, 2, 2)
plt.plot(x, y2, linewidth=2, marker='^', color='blue')
plt.show()
```
然后,再试一试下面这个代码,看看运行效果如何。
```Python
fig = plt.figure(figsize=(10, 4), dpi=120)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
# 用Figure对象的add_axes方法在现有坐标系中嵌套一个新的坐标系
# 该方法的参数是一个四元组,代表了新坐标系在原坐标系中的位置
# 前两个值是左下角的位置,后两个值是坐标系的宽度和高度
ax = fig.add_axes((0.595, 0.6, 0.3,0.25))
ax.plot(x, y2, marker='^', color='blue')
ax = fig.add_axes((0.155, 0.2, 0.3,0.25))
ax.plot(x, y2, marker='^', color='green')
plt.show()
```
##### 散点图
散点图可以帮助我们了解两个变量的关系,如果需要了解三个变量的关系,可以将散点图升级为气泡图。下面的代码中,`x``y`两个数组分别表示每个月的收入和每个月网购的支出,如果我们想了解`x``y`是否存在相关关系,就可以绘制如下所示的散点图。
```Python
x = np.array([5550,7500,10500,15000,20000,25000,30000,40000])
y = np.array([800,1800,1250,2000,1800,2100,2500,3500])
plt.figure(figsize=(6, 4), dpi=120)
plt.scatter(x, y)
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220501173034.png" style="zoom:50%;">
##### 柱状图
在对比数据的差异时,柱状图是非常棒的选择,我们可以使用`pyplot`模块的`bar`函数来生成柱状图,也可以使用`barh`函数来生成水平柱状图。我们先为柱状图准备一些数据,代码如下所示。
```Python
x = np.arange(4)
y1 = np.random.randint(20, 50, 4)
y2 = np.random.randint(10, 60, 4)
```
绘制柱状图的代码。
```Python
plt.figure(figsize=(6, 4), dpi=120)
# 通过横坐标的偏移,让两组数据对应的柱子分开
# width参数控制柱子的粗细,label参数为柱子添加标签
plt.bar(x - 0.1, y1, width=0.2, label='销售A组')
plt.bar(x + 0.1, y2, width=0.2, label='销售B组')
# 定制横轴的刻度
plt.xticks(x, labels=['Q1', 'Q2', 'Q3', 'Q4'])
# 定制显示图例
plt.legend()
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220501173557.png" style="zoom:50%;">
如果想绘制堆叠柱状图,可以对上面的代码稍作修改,如下所示。
```Python
labels = ['Q1', 'Q2', 'Q3', 'Q4']
plt.figure(figsize=(6, 4), dpi=120)
plt.bar(labels, y1, width=0.4, label='销售A组')
# 注意:堆叠柱状图的关键是将之前的柱子作为新柱子的底部
# 可以通过bottom参数指定底部数据,新柱子绘制在底部数据之上
plt.bar(labels, y2, width=0.4, bottom=y1, label='销售B组')
plt.legend(loc='lower right')
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220501173645.png" style="zoom:50%;">
##### 饼状图
饼状图通常简称为饼图,是一个将数据划分为几个扇形区域的统计图表,它主要用于描述数量、频率等之间的相对关系。在饼图中,每个扇形区域的大小就是其所表示的数量的比例,这些扇形区域合在一起刚好是一个完整的饼。在需要展示数据构成的场景下,饼状图、树状图和瀑布图是不错的选择,我们可以使用`pyplot`模块的`pie`函数来绘制饼图,代码如下所示。
```Python
data = np.random.randint(100, 500, 7)
labels = ['苹果', '香蕉', '桃子', '荔枝', '石榴', '山竹', '榴莲']
plt.figure(figsize=(5, 5), dpi=120)
plt.pie(
data,
# 自动显示百分比
autopct='%.1f%%',
# 饼图的半径
radius=1,
# 百分比到圆心的距离
pctdistance=0.8,
# 颜色(随机生成)
colors=np.random.rand(7, 3),
# 分离距离
# explode=[0.05, 0, 0.1, 0, 0, 0, 0],
# 阴影效果
# shadow=True,
# 字体属性
textprops=dict(fontsize=8, color='black'),
# 楔子属性(生成环状饼图的关键)
wedgeprops=dict(linewidth=1, width=0.35),
# 标签
labels=labels
)
# 定制图表的标题
plt.title('水果销售额占比')
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502094128.png" style="zoom:50%;">
>**说明**:大家可以试一试将上面代码中被注释的部分恢复,看看有什么样的效果。
##### 直方图
在统计学中,直方图是一种展示数据分布情况的图形,是一种二维统计图表,它的两个坐标分别是统计样本和该样本对应的某个属性的度量。下面的数据是某学校100名男学生的身高,如果我们想知道数据的分布,就可以使用直方图。
```Python
heights = np.array([
170, 163, 174, 164, 159, 168, 165, 171, 171, 167,
165, 161, 175, 170, 174, 170, 174, 170, 173, 173,
167, 169, 173, 153, 165, 169, 158, 166, 164, 173,
162, 171, 173, 171, 165, 152, 163, 170, 171, 163,
165, 166, 155, 155, 171, 161, 167, 172, 164, 155,
168, 171, 173, 169, 165, 162, 168, 177, 174, 178,
161, 180, 155, 155, 166, 175, 159, 169, 165, 174,
175, 160, 152, 168, 164, 175, 168, 183, 166, 166,
182, 174, 167, 168, 176, 170, 169, 173, 177, 168,
172, 159, 173, 185, 161, 170, 170, 184, 171, 172
])
```
可以使用`pyplot`模块的`hist`函数来绘制直方图,代码如下所示。
```Python
# 将身高数据分到以下8个组中
bins = np.array([150, 155, 160, 165, 170, 175, 180, 185, 190])
plt.figure(figsize=(6, 4), dpi=120)
# density参数默认值为False,表示纵轴显示频数
# 将density参数设置为True,纵轴会显示概率密度
plt.hist(heights, bins, density=True)
# 定制横轴标签
plt.xlabel('身高')
# 定制纵轴标签
plt.ylabel('概率密度')
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502093924.png" style="zoom:50%;">
##### 箱线图
箱线图又叫箱型图或盒须图,是一种用于展示一组数据分散情况的统计图表,如下所示。因图形如箱子,而且在上下四分位数之外有线条像胡须延伸出去而得名。在箱线图中,箱子的上边界是上四分位数($Q_3$)的位置,箱子的下边界是下四分位数($Q_1$)的位置,箱子中间的线条是中位数($Q_2$)的位置,而箱子的长度就是四分位距离(IQR)。除此之外,箱子上方线条的边界是最大值,箱子下方线条的边界是最小值,这两条线之外的点就是离群值(outlier)。所谓离群值,是指数据小于$Q_1 - 1.5 \times IQR$或数据大于$Q_3 + 1.5 \times IQR$的值,公式中的`1.5`还可以替换为`3`来发现极端离群值(extreme outlier),而介于`1.5``3`之间的离群值通常称之为适度离群值(mild outlier)。
可以使用`pyplot`模块的`boxplot`函数来绘制箱线图,代码如下所示。
```Python
# 数组中有47个[0, 100)范围的随机数
data = np.random.randint(0, 100, 47)
# 向数组中添加三个可能是离群点的数据
data = np.append(data, 160)
data = np.append(data, 200)
data = np.append(data, -50)
plt.figure(figsize=(6, 4), dpi=120)
# whis参数的默认值是1.5,将其设置为3可以检测极端离群值
# showmeans=True表示在图中标记均值的位置
plt.boxplot(data, whis=1.5, showmeans=True, notch=True)
# 定制纵轴的取值范围
plt.ylim([-100, 250])
# 定制横轴的刻度
plt.xticks([1], labels=['data'])
plt.show()
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220501172802.png" style="zoom:50%;" />
> **说明**:由于数据是随机生成的,所以大家运行上面的代码生成的图可能跟我这里并不相同。
#### 显示或保存图像
可以使用`pyplot`模块的`show`函数来显示绘制的图表,我们在上面的代码中使用过这个函数。如果希望保存图表,可以使用`savefig`函数。需要注意的是,如果要同时显示和保存图表,应该先执行`savefig`函数,再执行`show`函数,因为在调用`show`函数时,图表已经被释放,位于`show`函数之后的`savefig`保存的只是一个空白的区域。
```Python
plt.savefig('chart.png')
plt.show()
```
### 其他图表
使用 matplotlib,我们还可以绘制出其他的统计图表(如:雷达图、玫瑰图、热力图等),但实际工作中,使用频率最高的几类图表我们在上面已经为大家完整的展示出来了。此外,matplotlib 还有很多对统计图表进行定制的细节,例如定制坐标轴、定制图表上的文字和标签等。如果想了解如何用 matplotlib 绘制和定制更多的统计图表,可以直接查看 matplotlib 官方网站上的[文档](https://matplotlib.org/stable/tutorials/index.html)[示例](https://matplotlib.org/stable/gallery/index.html)
## 数据可视化-2
通过前面的学习,我们已经对数据可视化工具 matplotlib 有一个初步的认知。大家可能也会发现了,matplotlib 提供的函数虽然强大,但是参数太多,要想对图表进行深度的定制就需要修改一系列的参数,这一点对新手并不友好。另一方面,使用 matplotlib 定制的统计图是静态图表,可能在某些需要交互效果的场景下并不合适。为了解决这两个问题,我们为大家介绍两个新的可视化工具,一个是 seaborn,一个是 pyecharts。
### Seaborn
Seaborn 是建立在 matplotlib 之上的数据可视化工具,它相当于是对 matplotlib 进行了更高级的封装,而且 seaborn 也能跟 pandas 无缝整合,让我们可以用更少的代码构建出更好的统计图表,帮助我们探索和理解数据。Seaborn 包含但不局限于以下描述的功能:
1. 面向数据集的 API,可用于检查多个变量之间的关系。
1. 支持使用分类变量来显示观察结果或汇总统计数据。
1. 能够可视化单变量或双变量分布以及在数据子集之间进行比较的选项
1. 各类因变量线性回归模型的自动估计与作图。
1. 集成调色板和主题,轻松定制统计图表的视觉效果。
可以使用 Python 的包管理工具 pip 来安装 seaborn。
```Bash
pip install seaborn
```
在 Jupyter 中,可以直接使用魔法指令进行安装,如下所示。
```Bash
%pip install seaborn
```
下面,我们用 seaborn 自带的数据集为例,为大家简单的展示 seaborn 的用法和强大之处,想要深入研究 seaborn 的读者可以自行阅读官方[文档](https://seaborn.pydata.org/tutorial.html)和并查看官方作品集中的[示例。](https://seaborn.pydata.org/examples/index.html)根据官方示例来编写自己的代码是一个不错的选择,简单的说就是保留官方代码,将数据换成自己的数据即可。下图展示了 seaborn 绘制图表的函数,可以看出,seaborn 的这些函数主要支持我们通过绘制图表来探索数据的关系、分布和分类。
<img src="https://github.com/jackfrued/mypic/raw/master/20220502115005.png" style="zoom:75%;">
使用 seaborn,首先需要导入该库并设置主题,代码如下所示。
```Python
import seaborn as sns
sns.set_theme()
```
如果需要在图表上显示中文,还需要用之前讲过的方法修改 matplotlib 的配置参数,代码如下所示。
```Python
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei', ]
plt.rcParams['axes.unicode_minus'] = False
```
> **注意**:上面的代码必须放在调用 set_theme 函数之后,否则调用 set_theme 函数时又会重新修改 matplotlib 配置参数中的字体设置。
加载官方的 Tips 数据集(就餐小费数据)。
```Python
tips_df = sns.load_dataset('tips')
tips_df.info()
```
运行结果如下所示,其中 total_bill 表示账单总金额,tip 表示小费的金额,sex 是顾客的性别,smoker 表示顾客是否抽样,day 代表星期几,time 代表是午餐还是晚餐,size 是就餐人数。
```
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 total_bill 244 non-null float64
1 tip 244 non-null float64
2 sex 244 non-null category
3 smoker 244 non-null category
4 day 244 non-null category
5 time 244 non-null category
6 size 244 non-null int64
dtypes: category(4), float64(2), int64(1)
memory usage: 7.4 KB
```
由于数据集是联网加载的,上述代码可能因为 SSL 的原因无法获取到数据,可以尝试先运行下面的代码,然后再加载数据集。
```Python
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
```
如果我们希望了解账单金额的分布,可以使用下面的代码来绘制分布图。
```Python
sns.histplot(data=tips_df, x='total_bill', kde=True)
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502115531.png" style="zoom:50%;">
如果想了解变量之间的两两关系,我们可以绘制点对图,代码和效果如下所示。
```Python
sns.pairplot(data=tips_df, hue='sex')
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502120236.png" style="zoom:50%;">
如果对上面图表的颜色不满意,还可以通过 palette 参数选择 seaborn 自带的“调色板”来修改颜色,这种方式相比于自行指定颜色或使用随机颜色方便和靠谱了很多,下图为大家展示了部分 seaborn 自带的“调色板”。
<img src="https://github.com/jackfrued/mypic/raw/master/20220502120749.png" style="zoom:50%;">
我们可以将上面的代码稍作修改,看看运行结果有什么差别。
```Python
sns.pairplot(data=tips_df, hue='sex', palette='Dark2')
```
接下来,我们为 total_bill 和 tip 两组数据绘制联合分布图,代码如下所示。
```Python
sns.jointplot(data=tips_df, x='total_bill', y='tip', hue='sex')
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502121226.png" style="zoom:50%;">
上面清晰的展示了,total_bill 和 tip 之间存在正相关关系,这一点我们也可以通过 DataFrame 对象的 corr 方法进行验证。接下来,我们可以建立回归模型来拟合这些数据点,而 seaborn 的线性回归模型图已经帮我们实现了这项功能,代码如下所示。
```Python
sns.lmplot(data=tips_df, x='total_bill', y='tip', hue='sex')
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502121656.png" style="zoom:50%;">
如果我们希望了解账单金额的集中和离散趋势,可以绘制箱线图或小提琴图,代码如下所示,我们将数据按星期四、星期五、星期六和星期天分别进行展示。
```Python
sns.boxplot(data=tips_df, x='day', y='total_bill')
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502122106.png" style="zoom:50%;">
```Python
sns.violinplot(data=tips_df, x='day', y='total_bill')
```
<img src="https://github.com/jackfrued/mypic/raw/master/20220502122144.png" style="zoom:50%;">
> **说明**:相较于箱线图,小提琴图没有标注异常点而是显示了数据的整个范围,另一方面,小提琴图很好的展示了数据的分布(密度轨迹)。
### Pyecharts
Echarts 原来是百度开发的一个前端图表库,2018年1月16日,ECharts 进入 Apache Incubator 进行孵化,目前已经是 Apache 软件基金会的顶级项目。凭借着良好的交互性和精巧的图表设计,ECharts 得到了众多开发者的认可,而 pyecharts 就是基于 Python 语言对 ECharts 进行了包装,让 Python 开发者也可以使用 ECharts 绘制外观精美且交互性强的统计图表。
可以使用 Python 的包管理工具 pip 来安装 pyecharts。
```Bash
pip install pyecharts
```
在 Jupyter 中,可以直接使用魔法指令进行安装,如下所示。
```Bash
%pip install pyecharts
```
接下来,我们通过来自于 pyecharts 官方网站新手教程中的一个例子,来认识 pyecharts。当然,我们对官网的例子进行一些调整,代码如下所示。
```Python
from pyecharts.charts import Bar
from pyecharts import options
from pyecharts.globals import ThemeType
# 创建柱状图对象并设置初始参数(宽度、高度、主题)
bar = Bar(init_opts=options.InitOpts(
width='600px',
height='450px',
theme=ThemeType.CHALK
))
# 设置横轴数据
bar.add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])
# 设置纵轴数据(第一组)
bar.add_yaxis(
"商家A",
[25, 20, 36, 10, 75, 90],
)
# 设置纵轴数据(第二组)
bar.add_yaxis(
"商家B",
[15, 12, 30, 20, 45, 60]
)
# 设置纵轴数据(第三组)
bar.add_yaxis(
"商家C",
[12, 32, 40, 52, 35, 26]
)
# 添加全局配置参数
bar.set_global_opts(
# 横轴相关的参数
xaxis_opts=options.AxisOpts(
axislabel_opts=options.LabelOpts(
color='white'
)
),
# 纵轴相关的参数(标签、最小值、最大值、间隔)
yaxis_opts=options.AxisOpts(
axislabel_opts=options.LabelOpts(
color='white'
),
min_=0,
max_=100,
interval=10
),
# 标题相关的参数(内容、链接、位置、文本样式)
title_opts=options.TitleOpts(
title='2021年销售数据展示',
title_link='http://www.qfedu.com',
pos_left='2%',
title_textstyle_opts=options.TextStyleOpts(
color='white',
font_size=16,
font_family='SimHei',
font_weight='bold'
)
),
# 工具箱相关的参数
toolbox_opts=options.ToolboxOpts(
orient='vertical',
pos_left='right'
)
)
# 在Jupyter Notebook中渲染图表
bar.render_notebook()
```
上面代码的运行效果如下图所示。值得一提的是,下图中的标题、图例、右侧的工具箱都是可以点击的,大家可以点击它们看看会有什么样的效果,ECharts 的魅力就在于它的交互效果,大家一定要试一试。如果要将下面的统计图表保存成一个网页,可以将上面最后一行代码修改为`bar.render('index.html')`即可。
<img src="https://github.com/jackfrued/mypic/raw/master/20220502185650.png" style="zoom:50%;">
接下来,我们也是通过一个官方示例,看看如何绘制饼图。
```Python
import pyecharts.options as opts
from pyecharts.charts import Pie
# 准备饼图需要的数据
x_data = ["直接访问", "邮件营销", "联盟广告", "视频广告", "搜索引擎"]
y_data = [335, 310, 234, 135, 1548]
data = [(x, y) for x, y in zip(x_data, y_data)]
# 创建饼图对象并设置初始化参数
pie = Pie(init_opts=opts.InitOpts(width="800px", height="400px"))
# 向饼图添加数据
pie.add(
'',
data_pair=data,
radius=["50%", "75%"],
label_opts=opts.LabelOpts(is_show=False),
)
# 设置全局配置项
pie.set_global_opts(
# 配置图例相关的参数
legend_opts=opts.LegendOpts(
pos_left="legft",
orient="vertical"
)
)
# 设置数据系列配置参数
pie.set_series_opts(
# 设置不显示工具提示
tooltip_opts=opts.TooltipOpts(is_show=False),
# 设置饼图标签的样式
label_opts=opts.LabelOpts(formatter="{b}({c}): {d}%")
)
pie.render_notebook()
```
运行上面的代码,效果如下图所示。
<img src="https://github.com/jackfrued/mypic/raw/master/20220502190558.png" style="zoom:50%;">
需要提醒大家注意的是,pyecharts 并不能直接使用 NumPy 的 ndarray 和 Pandas 的 Series、DataFrame 为其提供数据,它需要的是 Python 原生的数据类型。可能大家也注意到了,上面的代码中,我们使用的都是列表、元组这样的数据类型。
最后,我们来看看如何绘制地图,绘制地图首先需要安装额外的依赖库来获取地图相关信息,命令如下所示。
```Bash
pip install echarts-countries-pypkg echarts-china-provinces-pypkg echarts-china-cities-pypkg echarts-china-counties-pypkg
```
在 Jupyter 中,可以直接使用魔法指令进行安装,如下所示。
```Bash
%pip install echarts-countries-pypkg
%pip install echarts-china-provinces-pypkg
%pip install echarts-china-cities-pypkg
%pip install echarts-china-counties-pypkg
```
> **说明**:上面的四个库分别包含了世界各国、中国省级行政区域、中国市级行政区域、中国区/县级行政区域的数据。
然后,我们将全国各省抖音大V的数据放在一个列表中,代码如下所示。
```Python
data = [
('广东', 594), ('浙江', 438), ('四川', 316), ('北京', 269), ('山东', 248),
('江苏', 234), ('湖南', 196), ('福建', 166), ('河南', 153), ('辽宁', 152),
('上海', 138), ('河北', 86), ('安徽', 79), ('湖北', 75), ('黑龙江', 70),
('陕西', 63), ('吉林', 59), ('江西', 56), ('重庆', 46), ('贵州', 39),
('山西', 37), ('云南', 33), ('广西', 24), ('天津', 22), ('新疆', 21),
('海南', 18), ('内蒙古', 14), ('台湾', 11), ('甘肃', 7), ('广西壮族自治区', 4),
('香港', 4), ('青海', 3), ('新疆维吾尔自治区', 3), ('内蒙古自治区', 3), ('宁夏', 1)
]
```
接下来,我们使用 pyecharts 在地图上标记各省抖音大V人数。
```Python
from pyecharts.charts import Map
map_chart = Map()
map_chart.add('', data, 'china', is_roam=False)
map_chart.render_notebook()
```
代码的运行效果如下图所示,将鼠标置于地图上时,会高亮对应的省并看到相关的信息。
<img src="https://github.com/jackfrued/mypic/raw/master/20220502192142.png" style="zoom:50%;">
和 seaborn 一样,我们建议大家参考官方提供的示例来使用 pyecharts,我们可以在 pyecharts [官方网站](https://pyecharts.org/#/zh-cn/)的左侧导航栏中找到“图表类型”选项,下面每种类型的图表都有对应的官方示例,很多代码是可以直接使用的,我们需要做的就是将数据换成自己的数据。
\ No newline at end of file
## 概率基础
### 数据的集中趋势
我们经常会使用以下几个指标来描述一组数据的集中趋势:
1. 均值 - 均值代表某个数据集的整体水平,我们经常提到的客单价、平均访问时长、平均配送时长等指标都是均值。均值的缺点是容易受极值的影响,虽然可以使用加权平均值来消除极值的影响,但是可能事先并不清楚数据的权重;对于正数可以用几何平均值来替代算术平均值。
- 算术平均值:$$\bar{x}=\frac{\sum_{i=1}^{n}x_{i}}{n}=\frac{x_{1}+x_{2}+\cdots +x_{n}}{n}$$,例如计算最近30天日均DAU、日均新增访客等,都可以使用算术平均值。
- 几何平均值:$$\left(\prod_{i=1}^{n}x_{i}\right)^{\frac{1}{n}}={\sqrt[{n}]{x_{1}x_{2} \cdots x_{n}}}$$,例如计算不同渠道的平均转化率、不同客群的平均留存率、不同品类的平均付费率等,就可以使用几何平均值。
2. 中位数 - 将数据按照升序或降序排列后位于中间的数,它描述了数据的中等水平。
3. 众数 - 数据集合中出现频次最多的数据,它代表了数据的一般水平。数据的趋势越集中,众数的代表性就越好。众数不受极值的影响,但是无法保证唯一性和存在性。
例子:有A和B两组数据。
```
A组:5, 6, 6, 6, 6, 8, 10
B组:3, 5, 5, 6, 6, 9, 12
```
A组的均值:6.74,中位数:6,众数:6。
B组的均值:6.57,中位数:6,众数:5, 6。
> **说明**:在Excel中,可以使用AVERAGE、MEDIAN、MODE函数分别计算均值、中位数和众数。求中位数也可以使用QUARTILE.EXC或QUARTILE.INC函数,将第二个参数设置为2即可。
对A组的数据进行一些调整。
```
A组:5, 6, 6, 6, 6, 8, 10, 200
B组:3, 5, 5, 6, 6, 9, 12
```
A组的均值会大幅度提升,但中位数和众数却没有变化。
> **思考**:怎样判断上面的200到底是不是一个异常值?
| | 优点 | 缺点 |
| ------ | -------------------------------- | ------------------------------------ |
| 均值 | 充分利用了所有数据,适应性强 | 容易收到极端值(异常值)的影响 |
| 中位数 | 能够避免被极端值(异常值)的影响 | 不敏感 |
| 众数 | 能够很好的反映数据的集中趋势 | 有可能不存在(数据没有明显集中趋势) |
> **练习1**:在“概率基础练习.xlsx”文件的表单“练习1”中,有一组用户订单支付金额的数据,计算订单的均值、中位数、众数。
>
> **练习2:**在“概率基础练习.xlsx”文件的表单“练习2”中,有一组商品销售量的数据,现计划设定一个阈值,对阈值以下的商品对应的分销商进行优化,应该选择什么作为阈值比较合适?
### 数据的离散趋势
如果说数据的集中趋势,说明了数据最主要的特征是什么;那么数据的离散趋势,则体现了这个特征的稳定性。例如 A 地区冬季平均气温`0`摄氏度,最低气温`-10`摄氏度;B 地区冬季平均气温`-2`摄氏度,最低气温`-4`摄氏度;如果你是一个特别怕冷的人,在选择 A 和 B 两个区域作为工作和生活的城市时,你会做出怎样的选择?
1. 极值:就是最大值(maximum)、最小值(minimum),代表着数据集的上限和下限。
> **说明**:在Excel中,计算极值的函数是MAX和MIN。
2. 极差:又称“全距”,是一组数据中的最大观测值和最小观测值之差,记作$R$。一般情况下,极差越大,离散程度越大,数据受极值的影响越严重。
3. 四分位距离:$ IQR = Q_3 - Q_1 $。
4. 方差:将每个值与均值的偏差进行平方,然后除以总数据量得到的值。简单来说就是表示数据与期望值的偏离程度。方差越大,就意味着数据越不稳定、波动越剧烈,因此代表着数据整体比较分散,呈现出离散的趋势;而方差越小,意味着数据越稳定、波动越平滑,因此代表着数据整体比较集中。
- 总体方差:$$ \sigma^2 = \frac {\sum_{i=1}^{N}(X_i - \mu)^2} {N} $$。
- 样本方差:$$ S^2 = \frac {\sum_{i=1}^{N}(X_i - \bar{X})^2} {N-1} $$。
> **说明**:在Excel中,计算总体方差和样本方差的函数分别是VAR.P和VAR.S。
5. 标准差:将方差进行平方根运算后的结果,与方差一样都是表示数据与期望值的偏离程度。
- 总体标准差:$$ \sigma = \sqrt{\frac{\sum_{i=1}^{N}(X_i - \mu)^2}{N}} $$。
- 样本标准差:$$ S = \sqrt{\frac{\sum_{i=1}^{N}(X_i - \bar{X})^2}{N-1}} $$。
> **说明**:在Excel中,计算标准差的函数分别是STDEV.P和STDEV.S。
> **练习3**:复制“概率基础练习.xlsx”文件的表单“练习1”,将复制的表单命名为“练习3”,计算订单支付金额的最大值、最小值、极差、方差和标准差。
### 数据的频数分析
频数分析是指用一定的方式将数据分组,然后统计每个分组中样本的数量,再辅以图表(如直方图)就可以更直观的展示数据分布趋势的一种方法。
频数分析的意义:
1. 大问题变小问题,迅速聚焦到需要关注的群体。
2. 找到合理的分类机制,有利于长期的数据分析(维度拆解)。
例如:一个班有40个学生,考试成绩如下所示:
```
73, 87, 88, 65, 73, 76, 80, 95, 83, 69, 55, 67, 70, 94, 86, 81, 87, 95, 84, 92, 92, 76, 69, 97, 72, 90, 72, 85, 80, 83, 97, 95, 62, 92, 67, 73, 91, 95, 86, 77
```
用上面学过的知识,先解读学生考试成绩的数据。
均值:81.275,中位数:83,众数:95。
最高分:97,最低分:55,极差:42,方差:118.15,标准差:10.87。
但是,仅仅依靠上面的数据是很难对一个数据集做出全面的解读,我们可以把学生按照考试成绩进行分组,如下所示,大家可以自行尝试在Excel或用Python来完成这个操作。
| 分数段 | 学生人数 |
| -------- | -------- |
| <60 | 1 |
| [60, 65) | 1 |
| [65, 69) | 5 |
| [70, 75) | 6 |
| [75, 80) | 3 |
| [80, 85) | 6 |
| [85, 90) | 6 |
| [90, 95) | 6 |
| >=95 | 6 |
> **练习4**:在“概率基础练习.xlsx”文件的表单“练习4”中,有某App首页版本迭代上线后的A/B测试数据,数据代表了参与测试的用户7日的活跃天数,请分析A组和B组的数据并判定哪组表现更优。
>
> **练习5**:在“概率基础练习.xlsx”文件的表单“练习5”中,有某App某个功能迭代上线后的A/B测试数据,数据代表了参与测试的用户30日的产品使用时长,请分析A组和B组的数据并判定哪组表现更优。
### 数据的概率分布
#### 基本概念
1. 随机试验:在相同条件下对某种随机现象进行观测的试验。随机试验满足三个特点:
- 可以在相同条件下重复的进行。
- 每次试验的结果不止一个,事先可以明确指出全部可能的结果。
- 重复试验的结果以随机的方式出现(事先不确定会出现哪个结果)。
2. 随机变量:如果$X$指定给概率空间$S$中每一个事件$e$有一个实数$X(e)$,同时针对每一个实数$r$都有一个事件集合$A_r$与其相对应,其中$A_r=\{e: X(e) \le r\}$,那么$X$被称作随机变量。从这个定义看出,$X$的本质是一个实值函数,以给定事件为自变量的实值函数,因为函数在给定自变量时会产生因变量,所以将$X$称为随机变量。
- 离散型随机变量:数据可以一一列出。
- 连续型随机变量:数据不可以一一列出。
如果离散型随机变量的取值非常庞大时,可以近似看做连续型随机变量。
3. 概率质量函数/概率密度函数:概率质量函数是描述离散型随机变量为特定取值的概率的函数,通常缩写为**PMF**。概率密度函数是描述连续型随机变量在某个确定的取值点可能性的函数,通常缩写为**PDF**。二者的区别在于,概率密度函数本身不是概率,只有对概率密度函数在某区间内进行积分后才是概率。
#### 离散型分布
1. 伯努利分布(*Bernoulli distribution*):又名**两点分布**或者**0-1分布**,是一个离散型概率分布。若伯努利试验成功,则随机变量取值为1。若伯努利试验失败,则随机变量取值为0。记其成功概率为$p (0 \le p \le 1)$,失败概率为$q=1-p$,则概率质量函数为:
$$ {\displaystyle f_{X}(x)=p^{x}(1-p)^{1-x}=\left\{{\begin{matrix}p&{\mbox{if }}x=1,\\q\ &{\mbox{if }}x=0.\\\end{matrix}}\right.} $$
2. 二项分布(*Binomial distribution*):$n$个独立的是/非试验中成功的次数的离散概率分布,其中每次试验的成功概率为$p$。一般地,如果随机变量$X$服从参数为$n$和$p$的二项分布,记为$X\sim B(n,p)$。$n$次试验中正好得到$k$次成功的概率由概率质量函数给出,$\displaystyle f(k,n,p)=\Pr(X=k)={n \choose k}p^{k}(1-p)^{n-k}$,对于$k= 0, 1, 2, ..., n$,其中${n \choose k}={\frac {n!}{k!(n-k)!}}$。
3. 泊松分布(*Poisson distribution*):适合于描述单位时间内随机事件发生的次数的概率分布。如某一服务设施在一定时间内受到的服务请求的次数、汽车站台的候客人数、机器出现的故障数、自然灾害发生的次数、DNA序列的变异数、放射性原子核的衰变数等等。泊松分布的概率质量函数为:$P(X=k)=\frac{e^{-\lambda}\lambda^k}{k!}$,泊松分布的参数$\lambda$是单位时间(或单位面积)内随机事件的平均发生率。
> **说明**:泊松分布是在没有计算机的年代,由于二项分布的运算量太大运算比较困难,为了减少运算量,数学家为二项分布提供的一种近似。
#### 分布函数和密度函数
对于连续型随机变量,我们不可能去罗列每一个值出现的概率,因此要引入分布函数的概念。
$$
F(x) = P\{X \le x\}
$$
如果将$ X $看成是数轴上的随机坐标,上面的分布函数表示了$ x $落在区间$ (-\infty, x) $中的概率。分布函数有以下性质:
1. $ F(x) $是一个单调不减的函数;
2. $ 0 \le F(x) \le 1$,且$ F(-\infty) = \lim_{x \to -\infty} F(x) = 0 $, $F(\infty) = \lim_{x \to \infty} F(x) = 1$;
3. $ F(x) $是右连续的。
概率密度函数就是给分布函数求导的结果,简单的说就是:
$$
F(x) = \int_{- \infty}^{x} f(t)dt
$$
#### 连续型分布
1. 均匀分布(*Uniform distribution*):如果连续型随机变量$X$具有概率密度函数$f(x)=\begin{cases}{\frac{1}{b-a}} \quad &{a \leq x \leq b} \\ {0} \quad &{\mbox{other}}\end{cases}$,则称$X$服从$[a,b]$上的均匀分布,记作$X\sim U[a,b]$。
2. 指数分布(*Exponential distribution*):如果连续型随机变量$X$具有概率密度函数$f(x)=\begin{cases} \lambda e^{- \lambda x} \quad &{x \ge 0} \\ {0} \quad &{x \lt 0} \end{cases}$,则称$X$服从参数为$\lambda$的指数分布,记为$X \sim Exp(\lambda)$。指数分布可以用来表示独立随机事件发生的时间间隔,比如旅客进入机场的时间间隔、客服中心接入电话的时间间隔、知乎上出现新问题的时间间隔等等。指数分布的一个重要特征是无记忆性(无后效性),这表示如果一个随机变量呈指数分布,它的条件概率遵循:$P(T \gt s+t\ |\ T \gt t)=P(T \gt s), \forall s,t \ge 0$。
3. 正态分布(*Normal distribution*):又名**高斯分布***Gaussian distribution*),是一个非常常见的连续概率分布,经常用自然科学和社会科学中来代表一个不明的随机变量。若随机变量$X$服从一个位置参数为$\mu$、尺度参数为$\sigma$的正态分布,记为$X \sim N(\mu,\sigma^2)$,其概率密度函数为:$\displaystyle f(x)={\frac {1}{\sigma {\sqrt {2\pi }}}}e^{-{\frac {\left(x-\mu \right)^{2}}{2\sigma ^{2}}}}$。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210716155507.png" width="80%">
“3$\sigma$法则”:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210716155542.png" width="75%">
正态分布有一个非常重要的性质,**大量统计独立的随机变量的平均值的分布趋于正态分布**,这就是**中心极限定理**。中心极限定理的重要意义在于,我们可以用正态分布作为其他概率分布的近似。
一个例子:假设某校入学新生的智力测验平均分数与标准差分别为 100 与 12。那么随机抽取 50 个学生,他们智力测验平均分数大于 105 的概率是多少?小于 90 的概率是多少?
本例没有正态分布的假设,还好中心极限定理提供一个可行解,那就是当随机样本数量超过30,样本平均数 近似于一个正态变量,标准正态变量$ Z = \frac {\bar{X} - \mu} {\sigma / \sqrt{n}} $。
平均分数大于 105 的概率为:$ P(Z \gt \frac{105 - 100}{12 / \sqrt{50}}) = P(Z \gt 5/1.7) = P(Z \gt 2.94) = 0.0016$。
平均分数小于 90 的概率为:$ P(Z \lt \frac{90-100}{12/\sqrt{50}}) = P(Z < -5.88) = 0.0000 $。
> **说明**:上面标准正态分布的概率值可以查表得到。
4. 伽马分布(*Gamma distribution*):假设$X_1, X_2, ... X_n$为连续发生事件的等候时间,且这$n$次等候时间为独立的,那么这$n$次等候时间之和$Y$($Y=X_1+X_2+...+X_n$)服从伽玛分布,即$Y \sim \Gamma(\alpha,\beta)$,其中$\alpha=n, \beta=\lambda$,这里的$\lambda$是连续发生事件的平均发生频率。
5. 卡方分布(*Chi-square distribution*):若$k$个随机变量$Z_1,Z_2,...,Z_k$是相互独立且符合标准正态分布(数学期望为0,方差为1)的随机变量,则随机变量$Z$的平方和$X=\sum_{i=1}^{k}Z_i^2$被称为服从自由度为$k$的卡方分布,记为$X \sim \chi^2(k)$。
### 其他内容
#### 条件概率和贝叶斯定理
**条件概率**是指事件A在事件B发生的条件下发生的概率,通常记为$P(A|B)$。设A与B为样本空间$\Omega$中的两个事件,其中$P(B) \gt 0$。那么在事件B发生的条件下,事件A发生的条件概率为:$P(A|B)=\frac{P(A \cap B)}{P(B)}$,其中$P(A \cap B)$是联合概率,即A和B两个事件共同发生的概率。
事件A在事件B已发生的条件下发生的概率,与事件B在事件A已发生的条件下发生的概率是不一样的。然而,这两者是有确定的关系的,**贝叶斯定理**就是对这种关系的陈述,即:$P(A|B)=\frac{P(A)P(B|A)}{P(B)}$,其中:
- $P(A|B)$是已知B发生后,A的条件概率,也称为A的后验概率。
- $P(A)$是A的先验概率(也称为边缘概率),是不考虑B时A发生的概率。
- $P(B|A)$是已知A发生后,B的条件概率,称为B的似然性。
- $P(B)$是B的先验概率。
按照上面的描述,贝叶斯定理可以表述为:`后验概率 = (似然性 * 先验概率) / 标准化常量`​,简单的说就是后验概率与先验概率和相似度的乘积成正比。
#### 大数定理
样本数量越多,则其算术平均值就有越高的概率接近期望值。
1. 弱大数定律(辛钦定理):样本均值依概率收敛于期望值,即对于任意正数$\epsilon$,有:$\lim_{n \to \infty}P(|\bar{X_n}-\mu|>\epsilon)=0$。
2. 强大数定律:样本均值以概率1收敛于期望值,即:$P(\lim_{n \to \infty}\bar{X_n}=\mu)=1$。
#### 假设检验
假设检验就是通过抽取样本数据,并且通过**小概率反证法**去验证整体情况的方法。假设检验的核心思想是小概率反证法(首先假设想推翻的命题是成立的,然后试图找出矛盾,找出不合理的地方来证明命题为假命题),即在**零假设**(null hypothesis)的前提下,估算某事件发生的可能性,如果该事件是小概率事件,在一次研究中本来是不可能发生的,但现在却发生了,这时候就可以推翻零假设,接受**备择假设**(alternative hypothesis)。如果该事件不是小概率事件,我们就找不到理由来拒绝之前的假设,实际中可引申为接受所做的无效假设。
假设检验会存在两种错误情况,一种称为“拒真”,一种称为“取伪”。如果原假设是对的,但你拒绝了原假设,这种错误就叫作“拒真”,这个错误的概率也叫作显著性水平$\alpha$,或称为容忍度;如果原假设是错的,但你承认了原假设,这种错误就叫作“取伪”,这个错误的概率我们记为$\beta$。
### 总结
描述性统计通常用于研究表象,将现象用数据的方式描述出来(用整体的数据来描述整体的特征);推理性统计通常用于推测本质(通过样本数据特征去推理总体数据特征),也就是你看到的表象的东西有多大概率符合你对隐藏在表象后的本质的猜测。
此差异已折叠。
......@@ -8,7 +8,7 @@
举一个例子,如果我们需要分析优惠券的金额对用户的购买转化率是否能起到有效作用,我们可以将数据分成以下三个组:
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085210.png" width="60%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085210.png" width="60%">
用户购买行为是随机的,购买率很高的不会很多,购买率极低的也不会很多,绝大部分用户的购买率都集中在某个值附近,这个值我们叫作整体购买率的平均值。如果每个客群分组自身的购买率均值与这个整体购买率平均值不一致,就会出现以下两种情况。
......@@ -16,17 +16,17 @@
蓝色分组的购买率平均值(蓝色线)比整体平均值(黑色线)要高,有可能是最右边那个很高的购买率把分组的均值抬升的,同时蓝色分组的数据分布很散(方差大),此时不能有十足把握说明该组用户的购买转化率很高。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085506.png" width="50%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085506.png" width="50%">
2. 第二种情况
绿色分组的购买率平均值(绿色线)比整体平均值(黑色线)要高,但是绿色分组的数据非常集中,都集中在分组的平均值(绿色线)附近,此时我们可以认为该组的转化率平均值与整体有明显区别。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085608.png" width="50%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085608.png" width="50%">
为了更好表述上面的问题,我们可以引入“组内方差”的概念,即描述每个分组内部数据分布的离散情况。如下图所示,对于上面蓝色和绿色分组的“组内方差”,显然蓝色的组内方差更大,绿色的组内方差更小。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085808.png" width="75%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085808.png" width="75%">
综上所述,如果上面三个分组的用户购买率平均值不在中线(整体购买率)左右,而是有明显的偏高或偏低,并且该组内的每个转化率都紧紧围绕在该组购买率平均值的附近(即组内方差很小)。那么我们就可以断定:该组的购买率与整体不一致,是该组对应优惠金额的影响造成的。
......@@ -37,7 +37,7 @@
- 当 F > F crit,这几组之间的差异超过判断基准了,认为不同优惠金额的分组间的购买率是不一样的,优惠金额这个因素会对购买率产生影响,也就是说通过运营优惠金额这个抓手,是可以提升用户购买转化率的;
- 当 F < F crit,则认为不同优惠金额的分组间的购买率是一样的,优惠金额这个因素不会对购买率产生影响,也就是说需要继续寻找其他与购买转化率有关的抓手。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713090505.png">
<img src="https://github.com/jackfrued/mypic/raw/master/20210713090505.png">
> **说明**:图中 SS 代表方差、df 代表指标自由度、MS 是均方差、P-value 是差异的显著性水平。
......@@ -72,9 +72,9 @@
上面的案例是针对一种策略来分析效果。我们把这种形式的方差分析叫作单因素方差分析,实际工作中,我们可能需要研究多种策略(例如运营中的渠道、活动、客群等)对结果的影响,我们称之为多因素方差分析。例如我们会在多个运营渠道上安排多种运营活动,评价各个渠道的转化率。此时,影响转化率的因素有渠道和活动两个因素,我们可以使用“无重复双因素方差分析”来检查数据。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210714125251.png" width="75%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210714125251.png" width="75%">
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210714130853.png" width="75%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210714130853.png" width="75%">
#### 应用场景
......@@ -83,7 +83,7 @@
1. 同一个客群在实施某个策略前后的指标对比。
2. 两个或多个客群对比同一指标,评估同一指标在不同客群上的差异。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210714131318.png" width="85%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210714131318.png" width="85%">
### 参数估计
......
......@@ -33,7 +33,7 @@ $$
事实上,相关性分析的应用场景非常多,基本上当问到“这两个东西有什么关系”、“哪个指标的作用(贡献或价值)更大”、“我们应该重点解决哪个问题”这类问题时,都可以用相关性分析给出比较准确的回答,非常便于产品运营找到解决问题的核心抓手。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713095938.png" width="80%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210713095938.png" width="80%">
在使用相关分析时,应注意以下几个方面:
......@@ -47,7 +47,7 @@ $$
1. 方法一:使用 CORREL 函数。
2. 方法二:使用“数据分析”模块的“相关系数”功能。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713164021.png" width="75%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210713164021.png" width="75%">
### 相关分析案例
......@@ -55,11 +55,11 @@ $$
留存的运营中我们最常看的就是新客的留存和活跃客群的留存,用来评估哪个客群的留存与整体的留存联系更紧密,以便制定后续运营的策略。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210928214403.png" style="zoom:65%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20210928214403.png" style="zoom:65%;">
利用Excel进行相关分析的结果如下所示。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210928214522.png" style="zoom:65%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20210928214522.png" style="zoom:65%;">
可以看出,活跃访客的留存率与整体留存率的相关是强相关;而新增访客的留存率与整体留存率的相关是弱相关,所以如果要提升整体留存率,我们的产品运营资源应当更多地投放给活跃用户,以提升整体的留存率;而新增访客,虽然不会拿到很多运营资源,但是我们也要去深入分析为什么新增访客的留存的贡献比较小,适时做一些提升这部分客群与整体留存的策略。
......@@ -67,13 +67,13 @@ $$
基本上电商运营会同时部署多个渠道,包括线上电商平台以及线下的门店。由于现有某产品从各个渠道获客的用户在产品上的购买转化率,需要评估哪些渠道的用户对整体购买转化率贡献最大,后续将重点营销此渠道。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210928214725.png" style="zoom:65%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20210928214725.png" style="zoom:65%;">
#### 案例3:分析哪些因素对 DAU 的影响更大
我们分析 DAU 时常会将它拆解为各种维度来分析,这里我们分析与 DAU 联系最紧密的维度到底是哪些,以帮助我们制定针对性的运营策略,如下图所示。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210928215043.png" style="zoom:65%;">
<img src="https://github.com/jackfrued/mypic/raw/master/20210928215043.png" style="zoom:65%;">
对于这样的报表,我们需要找出到底是哪几个城市、哪个操作系统,以及哪个年龄段的用户对于 DAU 的影响最大。如果能找出来这个关系,那么后续要提升 DAU,就有非常清晰的方向。
......@@ -104,7 +104,7 @@ $$
在Excel中,可以使用“数据分析”模块的“”来实现线性回归。
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210714073655.png" width="75%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210714073655.png" width="75%">
对于回归分析,最为重要的是评价回归的结果是否理想,这关系到能否通过回归方程去预测将来,我们先看看决定系数(Multiple R-Squared,通常称之为$ R^2 $)。在统计学习中,决定系数用于度量因变量的变化中可由自变量解释部分所占的比例,也就是你的回归模型的解释力是否良好,$ R^2 $ 的值越接近`1`越好。
$$
......@@ -113,7 +113,7 @@ SS_{res} = \sum_{i}(y_{i} - \hat {y_i})^2 \\
R^2 = 1 - \frac {SS_{res}} {SS_{tot}}
$$
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210714074159.png" width="60%">
<img src="https://github.com/jackfrued/mypic/raw/master/20210714074159.png" width="60%">
接下来我们还要对回归方程的显著性进行检验,主要包括 t 检验(回归系数的检验)和F检验(回归方程的检验)。对于F检验(F-statistic)的结果,主要关注其 p-value ,如果 p-value 小于0.05,那么说明拟合效果是不错的。
......@@ -368,15 +368,15 @@ Python在以下领域都有用武之地。
#### Day74 - [Pandas的应用-5](./Day66-80/74.Pandas的应用-5.md)
#### Day75 - [数据可视化](./Day66-80/75.数据可视化.md)
#### Day75 - [数据可视化-1](./Day66-80/75.数据可视化-1.md)
#### Day76 - [概率基础](./Day66-80/76.概率基础.md)
#### Day76 - [数据可视化-2](./Day66-80/76.数据可视化-2.md)
#### Day77 - [相关和回归](./Day66-80/77.相关和回归.md)
#### Day77 - [概率统计基础](./Day66-80/77.概率统计基础.md)
#### Day78 - [方差分析和参数估计](./Day66-80/78.方差分析和参数估计.md)
#### Day79 - [聚类和降维](./Day66-80/79.聚类和降维.md)
#### Day79 - [相关和回归](./Day66-80/79.相关和回归.md)
#### Day80 - [数据分析方法论](./Day66-80/80.数据分析方法论.md)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册