更新了Python数据分析部分的文档和资源文件
|
@ -2,7 +2,9 @@
|
|||
|
||||
当今世界对信息技术的依赖程度在不断加深,每天都会有大量的数据产生,我们经常会感到数据越来越多,但是要从中发现有价值的信息却越来越难。这里所说的信息,可以理解为对数据集处理之后的结果,是从数据集中提炼出的可用于其他场合的结论性的东西,而**从原始数据中抽取出有价值的信息**的这个过程我们就称之为**数据分析**,它是数据科学工作的一部分。
|
||||
|
||||
> 定义:**数据分析是有针对性的收集、加工、整理数据并采用统计、挖掘等技术对数据进行分析和解释的科学和艺术**。
|
||||
<img src="res/data_science.png" style="zoom:50%;">
|
||||
|
||||
> 定义:**数据分析是有针对性的收集、加工、整理数据并采用统计、挖掘等技术对数据进行探索、分析、呈现和解释的科学**。
|
||||
|
||||
### 数据分析师的职责和技能栈
|
||||
|
||||
|
@ -14,7 +16,7 @@ HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘
|
|||
4. 优化和驱动业务,推动数字化运营。
|
||||
5. 找出潜在的市场和产品的上升空间。
|
||||
|
||||
根据上面的描述,作为业务数据分析师,我们的工作不是给领导一个简单浅显的结论,而是结合公司的业务,完成**监控数据**、**揪出异常**、**找到原因**、**探索趋势**等工作。作为数据分析师,不管是用 Python 语言、Excel、SPSS或其他的商业智能工具,工具只是达成目标的手段,**数据思维是核心技能**,从实际业务问题出发到最终**发现数据中的商业价值**是终极目标。数据分析师在很多公司只是一个基础岗位,精于业务的数据分析师可以向**数据分析经理**或**数据运营总监**等管理岗位发展;对于熟悉机器学习算法的数据分析师来说,可以向**数据挖掘工程师**或**算法专家**方向发展,而这些岗位除了需要相应的数学和统计学知识,在编程能力方面也比数据分析师有更高的要求,可能还需要有大数据存储和处理的相关经验。数据治理岗位主要是帮助公司建设数据仓库或数据湖,实现数据从业务系统、埋点系统、日志系统到分析库的转移,为后续的数据分析和挖掘提供基础设施。数据治理岗位对 SQL 和 HiveSQL 有着较高的要求,要求能够熟练的使用 ETL 工具,此外可能还需要对 Hadoop 生态圈有一个很好的认知。作为数据产品经理,除了传统产品经理的技能栈之外,也需要较强的技术能力,例如要了解常用的推荐算法、机器学习模型,能够为算法的改进提供依据,能够制定相关埋点的规范和口径,虽然不需要精通各种算法,但是要站在产品的角度去考虑数据模型、指标、算法等的落地
|
||||
根据上面的描述,作为业务数据分析师,我们的工作不是给领导一个简单浅显的结论,而是结合公司的业务,完成**监控数据**、**揪出异常**、**找到原因**、**探索趋势**等工作。作为数据分析师,不管是用 Python 语言、Excel、SPSS或其他的商业智能工具,工具只是达成目标的手段,**数据思维是核心技能**,从实际业务问题出发到最终**发现数据中的商业价值**是终极目标。数据分析师在很多公司只是一个基础岗位,精于业务的数据分析师可以向**数据分析经理**或**数据运营总监**等管理岗位发展;对于熟悉机器学习算法的数据分析师来说,可以向**数据挖掘工程师**或**算法专家**方向发展,而这些岗位除了需要相应的数学和统计学知识,在编程能力方面也比数据分析师有更高的要求,可能还需要有大数据存储和处理的相关经验。数据治理岗位主要是帮助公司建设数据仓库或数据湖,实现数据从业务系统、埋点系统、日志系统到分析库的转移,为后续的数据分析和挖掘提供基础设施。数据治理岗位对 SQL 和 HiveSQL 有着较高的要求,需要熟练的使用 ETL 工具,此外还需要对 Hadoop 生态圈有一个较好的认知。作为数据产品经理,除了传统产品经理的技能栈之外,也需要较强的技术能力,例如要了解常用的推荐算法、机器学习模型,能够为算法的改进提供依据,能够制定相关埋点的规范和口径,虽然不需要精通各种算法,但是要站在产品的角度去考虑数据模型、指标、算法等的落地。
|
||||
|
||||
以下是我总结的数据分析师的技能栈,仅供参考。
|
||||
|
||||
|
@ -26,15 +28,15 @@ HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘
|
|||
|
||||
### 数据分析的流程
|
||||
|
||||
我们提到数分析这个词很多时候可能指的都是**狭义的数据分析**,这类数据分析主要目标就是生成可视化报表并通过这些报表来洞察业务中的问题。**广义的数据分析**还包含了数据挖掘的部分,不仅要通过数据实现对业务的监控和分析,还要利用机器学习算法,找出隐藏在数据背后的知识,并利用这些知识为将来的决策提供支撑。简单的说,**一个完整的数据分析应该包括基本的数据分析和深入的数据挖掘两个部分**。
|
||||
我们提到数分析这个词很多时候可能指的都是**狭义的数据分析**,这类数据分析主要目标就是生成可视化报表并通过这些报表来洞察业务中的问题,这类工作一般都是具有滞后性的。**广义的数据分析**还包含了数据挖掘的部分,不仅要通过数据实现对业务的监控和分析,还要利用机器学习算法,找出隐藏在数据背后的知识,并利用这些知识为将来的决策提供支撑,具备一定的前瞻性。
|
||||
|
||||
基本的数据分析工作一般包含以下几个方面的内容,当然因为行业和工作内容的不同会略有差异。
|
||||
|
||||
1. 确定目标(输入):理解业务,确定指标口径
|
||||
2. 获取数据:数据仓库、电子表格、三方接口、网络爬虫、开放数据集等
|
||||
3. 清洗数据:缺失值/重复值/异常值处理、数据变换(格式化、规范化)、离散化等
|
||||
4. 数据透视:运算、统计、分组、聚合、可视化
|
||||
5. 数据报告(输出):数据发布,工作成果总结汇报
|
||||
2. 获取数据:数据仓库(SQL提数)、电子表格、三方接口、网络爬虫、开放数据集等
|
||||
3. 清洗数据:包括对缺失值、重复值、异常值的处理以及相关的预处理(格式化、离散化、二值化等)
|
||||
4. 数据透视:排序、统计、分组聚合、交叉表、透视表等
|
||||
5. 数据呈现(输出):数据可视化,发布工作成果(数据分析报告)
|
||||
6. 分析洞察(后续):解释数据的变化,提出对应的方案
|
||||
|
||||
深入的数据挖掘工作通常包含以下几个方面的内容,当然因为行业和工作内容的不同会略有差异。
|
||||
|
@ -48,17 +50,17 @@ HR在发布招聘需求时,通常将数据工程、数据分析、数据挖掘
|
|||
|
||||
### 数据分析相关库
|
||||
|
||||
使用 Python 从事数据科学相关的工作是一个非常棒的选择,因为 Python 整个生态圈中,有大量的成熟的用于数据科学的软件包(工具库)。而且不同于其他的用于数据科学的编程语言(如:Julia、R),Python 除了可以用于数据科学,能做的事情还很多,可以说 Python 语言几乎是无所不能的。
|
||||
使用 Python 从事数据科学相关的工作是一个非常棒的选择,因为 Python 整个生态圈中,有大量的成熟的用于数据科学的软件包(工具库)。而且不同于其他的用于数据科学的编程语言(如:Julia、R),Python 除了可以用于数据科学,还能做很多其他的事情,可以说 Python 语言几乎是无所不能的。
|
||||
|
||||
#### 三大神器
|
||||
|
||||
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) 一样的绘图组件。
|
||||
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 的功能,封装了大量科学计算的算法,包括线性代数、稀疏矩阵、信号和图像处理、最优化问题、快速傅里叶变换等。
|
||||
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 的一部分,它是 Python 数据科学运算的核心,提供了大量机器学习可能用到的工具,包括:数据预处理、监督学习(分类、回归)、无监督学习(聚类)、模式选择、交叉检验等。
|
||||
3. [Scikit-learn](https://scikit-learn.org/):scikit-learn 最初是 SciPy 的一部分,提供了大量机器学习可能用到的工具,包括数据预处理、监督学习(分类、回归)、无监督学习(聚类)、模式选择、交叉检验等。
|
||||
4. [Statsmodels](https://www.statsmodels.org/stable/index.html):包含了经典统计学和经济计量学算法的库。
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
|
||||
### 安装和使用 Anaconda
|
||||
|
||||
对于个人用户来说,可以从 Anaconda 的[官方网站](https://www.anaconda.com/)下载它的“个人版(Individual Edition)”安装程序,安装完成后,你的计算机上不仅拥有了 Python 环境和 Spyder(类似于PyCharm的集成开发工具),还拥有了与数据科学工作相关的近200个工具包,包括我们上面提到 Python 数据分析三大神器。除此之外,Anaconda 还提供了一个名为 conda 的包管理工具,通过这个工具不仅可以管理 Python 的工具包,还可以用于创建运行 Python 程序的虚拟环境。
|
||||
对于个人用户来说,可以从 Anaconda 的[官方网站](https://www.anaconda.com/)下载它的“个人版(Individual Edition)”安装程序,安装完成后,你的计算机上不仅拥有了 Python 环境和 Spyder(类似于 PyCharm 的集成开发工具),还拥有了与数据科学工作相关的近200个工具包,包括我们上面提到 Python 数据分析三大神器。除此之外,Anaconda 还提供了一个名为 conda 的包管理工具,通过这个工具不仅可以管理 Python 的工具包,还可以用于创建运行 Python 程序的虚拟环境。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211005111417.png">
|
||||
<img src="res/download-anaconda.png" style="zoom:50%;">
|
||||
|
||||
如上图所示,可以通过 Anaconda 官网提供的下载链接选择适合自己操作系统的安装程序,建议大家选择图形化的安装程序,下载完成后双击安装程序开始安装。安装过程基本使用默认设置即可,完成安装后,macOS 用户可以在“应用程序”或“Launchpad”中找到名为“Anaconda-Navigator”的应用程序,运行该程序可以看到如下所示的界面,我们可以在这里选择需要执行的操作。
|
||||
如上图所示,可以通过 Anaconda 官网提供的下载链接选择适合自己操作系统的安装程序,建议大家选择图形化的安装程序,下载完成后双击安装程序开始安装。安装过程基本使用默认设置即可,完成安装后,macOS 用户可以在“应用程序”或“启动台”中找到名为“Anaconda-Navigator”的应用程序,运行该程序可以看到如下所示的界面,我们可以在这里选择需要执行的操作。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211005111729.png">
|
||||
<img src="res/anaconda_navigator.png" style="zoom:50%;">
|
||||
|
||||
对于 Windows 用户,建议按照安装向导的提示和推荐的选项来安装 Anaconda(除了安装路径,基本也没有什么需要选择的),安装完成后可以在“开始菜单”中找到“Anaconda3”。
|
||||
|
||||
> **温馨提示**:可以选择 Miniconda 作为 Anaconda 的替代品,Miniconda 只会安装 Python 解释器环境和一些必要的工具,其他的三方库由用户自行选择安装。其实我个人并不喜欢 Anaconda,因为它是给小白用户使用的,我们有了 Python 以后完全可以按照自己的意愿来安装需要的三方库。
|
||||
> **提示**:可以选择 Miniconda 作为 Anaconda 的替代品,Miniconda 只会安装 Python 解释器环境和一些必要的工具,其他的三方库由用户自行选择安装。**其实我个人并不喜欢 Anaconda,因为它是给小白用户使用的,我们有了 Python 环境以后完全可以按照自己的意愿来安装需要的三方库**。
|
||||
|
||||
#### conda命令
|
||||
|
||||
如果希望使用 conda 工具来管理依赖项或者创建项目的虚拟环境,可以在终端或命令行提示符中使用 conda 命令。Windows 用户可以在“开始菜单”中找到“Anaconda3”,然后点击“Anaconda Prompt”来启动支持 conda 的命令行提示符。macOS 用户建议直接使用“Anaconda-Navigator”中的“Environments”,通过可视化的方式对虚拟环境和依赖项进行管理。
|
||||
对于非新手用户,如果希望使用 conda 工具来管理依赖项或者创建项目的虚拟环境,可以在终端或命令行提示符中使用 conda 命令。Windows 用户可以在“开始菜单”中找到“Anaconda3”,然后点击“Anaconda Prompt”或“Anaconda PowerShell”来启动支持 conda 的命令行提示符。新手用户如果想创建新的虚拟环境或管理三方库(依赖项),建议直接使用“Anaconda-Navigator”中的“Environments”,通过可视化的方式对虚拟环境和依赖项进行管理。
|
||||
|
||||
1. 版本和帮助信息。
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
|||
- 退出虚拟环境:`conda deactivate`
|
||||
- 删除虚拟环境:`conda remove --name venv --all`
|
||||
|
||||
> **说明**:上面的命令中,`venv`和`venv2`是虚拟环境文件夹的名字,可以将其替换为自己喜欢的名字,但是**强烈建议**使用英文且不要出现空格或其他特殊字符。
|
||||
> **说明**:上面的命令中,`venv`和`venv2`是虚拟环境文件夹的名字,可以将其替换为自己喜欢的名字,但是**强烈建议**使用英文且不要有特殊字符。
|
||||
|
||||
3. 包(三方库或工具)管理。
|
||||
|
||||
|
@ -51,65 +51,61 @@
|
|||
|
||||
> **说明**:在搜索、安装和更新软件包时,默认会连接到官方网站进行操作,如果觉得速度不给力,可以将默认的官方网站替换为国内的镜像网站,推荐使用清华大学的开源镜像网站。将默认源更换为国内镜像的命令是:`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
|
||||
### 安装和使用JupyterLab
|
||||
|
||||
#### 安装和启动Notebook
|
||||
#### 安装和启动
|
||||
|
||||
如果已经安装了 Anaconda,macOS 用户可以按照上面所说的方式在“Anaconda-Navigator”中直接启动“Jupyter Notebook”(以下统一简称为 Notebook)。Windows 用户可以在“开始菜单”中找到 Anaconda 文件夹,接下来选择运行文件夹中的“Jupyter Notebook”就可以开始数据科学的探索之旅。
|
||||
如果已经安装了 Anaconda,可以按照上面所说的方式在“Anaconda-Navigator”中直接启动 Notebook 或 JupyterLab。按照官方的说法,JupyterLab 是下一代的 Notebook,提供了更友好的界面和更强大的功能,我们也推荐大家使用 JupyterLab。Windows 用户也可以在开始菜单中打开“Anaconda Prompt”或“Anaconda PowerShell”,由于已经激活了 Anaconda 默认的虚拟环境,只需要输入`jupyter lab`命令来启动JupyterLab。macOS 系统在安装 Anaconda以后,每次打开终端时会自动激活 Anaconda 默认的虚拟环境,也是通过输入`jupyter lab`命令就可以启动JupyterLab。
|
||||
|
||||
对于安装了 Python 环境但是没有安装 Anaconda 的用户,可以用 Python 的包管理工具`pip`来安装`jupyter`,然后在终端(Windows 系统为命令行提示符)中运行`jupyter notebook`命令来启动 Notebook,如下所示。
|
||||
对于安装了 Python 环境但是没有安装 Anaconda 的用户,可以用 Python 的包管理工具`pip`来安装 JupyterLab,安装成功后在终端或命令行提示符中执行`jupyter lab`命令来启动 JupyterLab,如下所示。
|
||||
|
||||
安装 Notebook:
|
||||
安装 JupyterLab:
|
||||
|
||||
```Bash
|
||||
pip install jupyter
|
||||
pip install jupyterlab
|
||||
```
|
||||
|
||||
安装三大神器:
|
||||
安装 Python 数据分析三大神器:
|
||||
|
||||
```Bash
|
||||
pip install numpy pandas matplotlib
|
||||
```
|
||||
|
||||
运行 Notebook:
|
||||
启动 JupyterLab:
|
||||
|
||||
```Bash
|
||||
jupyter notebook
|
||||
jupyter lab
|
||||
```
|
||||
|
||||
Notebook 是基于网页的用于交互计算的应用程序,可以用于代码开发、文档撰写、代码运行和结果展示。简单的说,你可以在网页中直接**编写代码**和**运行代码**,代码的运行结果也会直接在代码块下方进行展示。如在编写代码的过程中需要编写说明文档,可在同一个页面中使用 Markdown 格式进行编写,而且可以直接看到渲染后的效果。此外,Notebook 的设计初衷是提供一个能够支持多种编程语言的工作环境,目前它能够支持超过40种编程语言,包括 Python、R、Julia、Scala 等。
|
||||
JupyterLab 是基于网页的用于交互计算的应用程序,可以用于代码开发、文档撰写、代码运行和结果展示。简单的说,你可以在网页中直接**编写代码**和**运行代码**,代码的运行结果也会直接在代码块下方进行展示。如在编写代码的过程中需要编写说明文档,可在同一个页面中使用 Markdown 格式进行编写,而且可以直接看到渲染后的效果。此外,Notebook 的设计初衷是提供一个能够支持多种编程语言的工作环境,目前它能够支持超过40种编程语言,包括 Python、R、Julia、Scala 等。
|
||||
|
||||
首先,我们可以创建一个用于书写 Python 代码的 Notebook,如下图所示。
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211129131353.png)
|
||||
<img src="res/JupyterLab_1.png" style="zoom:50%;">
|
||||
|
||||
接下来,我们就可以编写代码、撰写文档和运行程序啦,如下图所示。
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211005113900.png)
|
||||
<img src="res/JupyterLab_2.png" style="zoom:50%;">
|
||||
|
||||
#### Notebook使用技巧
|
||||
#### 使用技巧
|
||||
|
||||
如果使用 Python 做工程化的项目开发,PyCharm 肯定是最好的选择,它提供了一个集成开发环境应该具有的所有功能,尤其是智能提示、代码补全、自动纠错这类功能会让开发人员感到非常舒服。如果使用 Python 做数据科学相关的工作,Notebook 并不比 PyCharm 逊色,在数据和图表展示方面 Notebook 更加优秀。这个工具的使用非常简单,大家可以看看 Notebook 菜单栏,相信理解起来不会有太多困难,在知乎上有一篇名为[《最详尽使用指南:超快上手Jupyter Notebook》](https://zhuanlan.zhihu.com/p/32320214)的文章,也可以帮助大家快速认识 Notebook。
|
||||
如果使用 Python 做工程化的项目开发,PyCharm 肯定是最好的选择,它提供了一个集成开发环境应该具有的所有功能,尤其是智能提示、代码补全、自动纠错这类功能会让开发人员感到非常舒服。如果使用 Python 做数据科学相关的工作,JupyterLab 并不比 PyCharm 逊色,在数据和图表展示方面 JupyterLab 更加优秀。为此,JetBrains 公司还专门开发了一个对标 JupyterLab 的新工具 DataSpell,有兴趣的读者可以自行了解。下面我们为大家介绍一些 JupyterLab 的使用技巧,希望能够帮助大家提升工作效率。
|
||||
|
||||
> **说明**:[Jupyter 官网](https://jupyter.org/)上还有一个名为 JupyterLab 的工具,被称之为“Next-Generation Notebook”,用户界面较之 Notebook 更加友好,有兴趣的读者可以使用`pip install jupyterlab`命令来安装这个工具,然后通过`jupyter lab`来启动它。
|
||||
1. 自动补全。在使用 JupyterLab 编写代码时,按`Tab`键会获得代码提示和补全功能。
|
||||
|
||||
下面我为大家介绍一些 Notebook 的使用技巧,希望能够帮助大家提升工作效率。
|
||||
2. 获得帮助。如果希望了解一个对象(如变量、类、函数等)的相关信息或使用方式,可以在对象后面使用`?`并运行代码, 窗口下方会显示出对应的信息,帮助我们了解该对象,如下所示。
|
||||
|
||||
1. 自动补全。在使用 Notebook 编写代码时,按`Tab`键会获得代码提示。
|
||||
|
||||
2. 获得帮助。在使用 Notebook 时,如果希望了解一个对象(如变量、类、函数等)的相关信息或使用方式,可以在对象后面使用`?`并运行代码, 窗口下方会显示出对应的信息,帮助我们了解该对象,如下所示。
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211005113848.png)
|
||||
<img src="res/JupyterLab_3.png" style="zoom:100%;">
|
||||
|
||||
3. 搜索命名。如果只记得一个类或一个函数名字的一部分,可以使用通配符`*`并配合`?`进行搜索,如下所示。
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211005113836.png)
|
||||
<img src="res/JupyterLab_4.png" style="zoom:100%;">
|
||||
|
||||
4. 调用命令。可以在 Notebook 中使用`!`后面跟系统命令的方式来执行系统命令。
|
||||
4. 调用命令。可以在 JupyterLab 中使用`!`后面跟系统命令的方式来执行系统命令。
|
||||
|
||||
5. 魔法指令。Notebook 中有很多非常有趣且有用的魔法指令,例如可以使用`%timeit`测试语句的执行时间,可以使用`%pwd`查看当前工作目录等。如果想查看所有的魔法指令,可以使用`%lsmagic`,如果了解魔法指令的用法,可以使用`%magic`来查看,如下图所示。
|
||||
5. 魔法指令。JupyterLab 中有很多非常有趣且有用的魔法指令,例如可以使用`%timeit`测试语句的执行时间,可以使用`%pwd`查看当前工作目录等。如果想查看所有的魔法指令,可以使用`%lsmagic`,如果了解魔法指令的用法,可以使用`%magic`来查看,如下图所示。
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211005113825.png)
|
||||
<img src="res/JupyterLab_5.png" style="zoom:100%;">
|
||||
|
||||
常用的魔法指令有:
|
||||
|
||||
|
@ -129,38 +125,35 @@ Notebook 是基于网页的用于交互计算的应用程序,可以用于代
|
|||
| `%who` / `%whos` | 显示命名空间中的变量 |
|
||||
| `%xdel` | 删除一个对象并清理所有对它的引用 |
|
||||
|
||||
6. 快捷键。Notebook 中的很多操作可以通过快捷键来实现,使用快捷键可以提升工作效率。Notebook 的快捷键又可以分为命令模式下的快捷键和编辑模式下的快捷键,所谓编辑模式就是处于输入代码或撰写文档状态的模式,在编辑模式下按`Esc`可以回到命令模式,在命令模式下按`Enter`可以进入编辑模式。
|
||||
6. 快捷键。JupyterLab 中的很多操作可以通过快捷键来实现,使用快捷键可以提升工作效率。JupyterLab 的快捷键可以分为命令模式下的快捷键和编辑模式下的快捷键,所谓编辑模式就是处于输入代码或撰写文档状态的模式,在编辑模式下按`Esc`可以回到命令模式,在命令模式下按`Enter`可以进入编辑模式。
|
||||
|
||||
命令模式下的快捷键:
|
||||
|
||||
| 快捷键 | 功能说明 |
|
||||
| ---------------------------------------- | -------------------------------------------- |
|
||||
| `Alt` + `Enter`(`Option` + `Enter`) | 运行当前单元格并在下面插入新的单元格 |
|
||||
| `Alt` + `Enter` | 运行当前单元格并在下面插入新的单元格 |
|
||||
| `Shift` + `Enter` | 运行当前单元格并选中下方的单元格 |
|
||||
| `Ctrl` + `Enter`(`Command` + `Enter`) | 运行当前单元格 |
|
||||
| `Ctrl` + `Enter` | 运行当前单元格 |
|
||||
| `j` / `k`、`Shift` + `j` / `Shift` + `k` | 选中下方/上方单元格、连续选中下方/上方单元格 |
|
||||
| `a` / `b` | 在下方/上方插入新的单元格 |
|
||||
| `c` / `x` | 复制单元格 / 剪切单元格 |
|
||||
| `v` / `Shift` + `v` | 在下方/上方粘贴单元格 |
|
||||
| `dd` / `z` | 删除单元格 / 恢复删除的单元格 |
|
||||
| `l` / `Shift` + `l` | 显示或隐藏当前/所有单元格行号 |
|
||||
| `ii` / `00` | 中断/重启Notebook内核 |
|
||||
| `Shift` + `l` | 显示或隐藏当前/所有单元格行号 |
|
||||
| `Space` / `Shift` + `Space` | 向下/向上滚动页面 |
|
||||
|
||||
编辑模式下的快捷键:
|
||||
|
||||
| 快捷键 | 功能说明 |
|
||||
| ------------------------------------------------------------ | -------------------------------------- |
|
||||
| -------------------------- | -------------------------------------- |
|
||||
| `Shift` + `Tab` | 获得提示信息 |
|
||||
| `Ctrl` + `]`(`Command` + `]`)/ `Ctrl` + `[`(`Command` + `[`) | 增加/减少缩进 |
|
||||
| `Alt` + `Enter`(`Option` + `Enter`) | 运行当前单元格并在下面插入新的单元格 |
|
||||
| `Ctrl` + `]`/ `Ctrl` + `[` | 增加/减少缩进 |
|
||||
| `Alt` + `Enter` | 运行当前单元格并在下面插入新的单元格 |
|
||||
| `Shift` + `Enter` | 运行当前单元格并选中下方的单元格 |
|
||||
| `Ctrl` + `Enter`(`Command` + `Enter`) | 运行当前单元格 |
|
||||
| `Ctrl` + `Left` / `Right`(`Command` + `Left` / `Right`) | 光标移到行首/行尾 |
|
||||
| `Ctrl` + `Up` / `Down`(`Command` + `Up` / `Down`) | 光标移动代码开头/结尾处 |
|
||||
| `Ctrl` + `Enter` | 运行当前单元格 |
|
||||
| `Ctrl` + `Left` / `Right` | 光标移到行首/行尾 |
|
||||
| `Ctrl` + `Up` / `Down` | 光标移动代码开头/结尾处 |
|
||||
| `Up` / `Down` | 光标上移/下移一行或移到上/下一个单元格 |
|
||||
|
||||
> **温馨提示**:如果记不住这些快捷键也没有关系,在命令模式下按`h`键可以打开 Notebook 的帮助系统,马上就可以看到快捷键的设置,而且可以根据实际的需要重新编辑快捷键,如下图所示。
|
||||
>
|
||||
> ![](https://github.com/jackfrued/mypic/raw/master/20211005113812.png)
|
||||
> **说明**:对于 macOS 系统可以将`Alt`键替换成`Option`键,将`Ctrl`键替换成`Command`键。
|
||||
|
||||
|
|
|
@ -1,413 +1,343 @@
|
|||
## NumPy的应用-2
|
||||
|
||||
### 数组的运算
|
||||
### 数组对象的方法
|
||||
|
||||
使用 NumPy 最为方便的是当需要对数组元素进行运算时,不用编写循环代码遍历每个元素,所有的运算都会自动的**矢量化**(使用高效的、提前编译的底层代码来对数据序列进行数学操作)。简单的说就是,NumPy 中的数学运算和数学函数会自动作用于数组中的每个成员。
|
||||
#### 获取描述统计信息
|
||||
|
||||
#### 数组跟标量的运算
|
||||
|
||||
代码:
|
||||
描述统计信息主要包括数据的集中趋势、离散程度和频数分析等,其中集中趋势主要看均值和中位数,离散程度可以看极值、方差、标准差等,详细的内容大家可以阅读[《统计思维系列课程01:解读数据》](https://zhuanlan.zhihu.com/p/595273755)。
|
||||
|
||||
```Python
|
||||
array35 = np.arange(1, 10)
|
||||
print(array35 + 10)
|
||||
print(array35 * 10)
|
||||
array1 = np.random.randint(1, 100, 10)
|
||||
array1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[11 12 13 14 15 16 17 18 19]
|
||||
[10 20 30 40 50 60 70 80 90]
|
||||
array([46, 51, 15, 42, 53, 71, 20, 62, 6, 94])
|
||||
```
|
||||
|
||||
#### 数组跟数组的运算
|
||||
**计算总和、均值和中位数。**
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
array36 = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
|
||||
print(array35 + array36)
|
||||
print(array35 * array36)
|
||||
print(array35 ** array36)
|
||||
print(array1.sum())
|
||||
print(np.sum(array1))
|
||||
print(array1.mean())
|
||||
print(np.mean(array1))
|
||||
print(np.median(array1))
|
||||
print(np.quantile(array1, 0.5))
|
||||
```
|
||||
|
||||
> **说明**:上面代码中的`mean`、`median`和`quantile`分别是 NumPy 中计算算术平均值、中位数和分位数的函数,其中`quantitle`函数的第二个参数设置为0.5表示计算50%分位数,也就是中位数。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
460
|
||||
460
|
||||
46.0
|
||||
46.0
|
||||
48.5
|
||||
48.5
|
||||
```
|
||||
|
||||
**极值、全距和四分位距离。**
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
print(array1.max())
|
||||
print(np.amax(array1))
|
||||
print(array1.min())
|
||||
print(np.amin(array1))
|
||||
print(array1.ptp())
|
||||
print(np.ptp(array1))
|
||||
q1, q3 = np.quantile(array1, [0.25, 0.75])
|
||||
print(q3 - q1)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[ 2 3 4 6 7 8 10 11 12]
|
||||
[ 1 2 3 8 10 12 21 24 27]
|
||||
[ 1 2 3 16 25 36 343 512 729]
|
||||
94
|
||||
94
|
||||
6
|
||||
6
|
||||
88
|
||||
88
|
||||
34.25
|
||||
```
|
||||
|
||||
#### 通用一元函数
|
||||
|
||||
通用函数是对`ndarray`中的数据执行元素级运算的函数。你可以将其看做普通函数(接收一个标量值作为参数,返回一个标量值)的矢量化包装器,如下所示。
|
||||
**方差、标准差和变异系数。**
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
print(np.sqrt(array35))
|
||||
print(np.log2(array35))
|
||||
print(array1.var())
|
||||
print(np.var(array1))
|
||||
print(array1.std())
|
||||
print(np.std(array1))
|
||||
print(array1.std() / array1.mean())
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[1. 1.41421356 1.73205081 2. 2.23606798 2.44948974
|
||||
2.64575131 2.82842712 3. ]
|
||||
[0. 1. 1.5849625 2. 2.32192809 2.5849625
|
||||
2.80735492 3. 3.169925 ]
|
||||
651.2
|
||||
651.2
|
||||
25.51862065237853
|
||||
25.51862065237853
|
||||
0.5547526228777941
|
||||
```
|
||||
|
||||
**表1:通用一元函数**
|
||||
**绘制箱线图。**
|
||||
|
||||
| 函数 | 说明 |
|
||||
| -------------------------------- | --------------------------------------------- |
|
||||
| `abs` / `fabs` | 求绝对值的函数 |
|
||||
| `sqrt` | 求平方根的函数,相当于`array ** 0.5 ` |
|
||||
| `square` | 求平方的函数,相当于`array ** 2` |
|
||||
| `exp` | 计算$e^x$的函数 |
|
||||
| `log` / `log10` / `log2` | 对数函数(`e`为底 / `10`为底 / `2`为底) |
|
||||
| `sign` | 符号函数(`1` - 正数;`0` - 零;`-1` - 负数) |
|
||||
| `ceil` / `floor` | 上取整 / 下取整 |
|
||||
| `isnan` | 返回布尔数组,NaN对应`True`,非NaN对应`False` |
|
||||
| `isfinite` / `isinf` | 判断数值是否为无穷大的函数 |
|
||||
| `cos` / `cosh` / `sin` | 三角函数 |
|
||||
| `sinh` / `tan` / `tanh` | 三角函数 |
|
||||
| `arccos` / `arccosh` / `arcsin` | 反三角函数 |
|
||||
| `arcsinh` / `arctan` / `arctanh` | 反三角函数 |
|
||||
| `rint` / `round` | 四舍五入函数 |
|
||||
|
||||
#### 通用二元函数
|
||||
箱线图又称为盒须图,是显示一组数据分散情况的统计图,因形状如箱子而得名。 它主要用于反映原始数据分布的特征,还可以进行多组数据分布特征的比较。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
array37 = np.array([[4, 5, 6], [7, 8, 9]])
|
||||
array38 = np.array([[1, 2, 3], [3, 2, 1]])
|
||||
print(array37 ** array38)
|
||||
print(np.power(array37, array38))
|
||||
plt.boxplot(array1, showmeans=True)
|
||||
plt.ylim([-20, 120])
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/box_plot_1.png" style="zoom:40%;">
|
||||
|
||||
值得注意的是,对于二维或更高维的数组,在获取描述统计信息时,可以通过名为`axis`的参数指定均值、方差等运算是沿着哪一个轴来执行,`axis`参数不同,执行的结果可能是大相径庭的,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
array2 = np.random.randint(60, 101, (5, 3))
|
||||
array2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[[ 4 25 216]
|
||||
[343 64 9]]
|
||||
[[ 4 25 216]
|
||||
[343 64 9]]
|
||||
```
|
||||
|
||||
**表2:通用二元函数**
|
||||
|
||||
| 函数 | 说明 |
|
||||
| ---------------------------------- | ---- |
|
||||
| `add(x, y)` / `substract(x, y)` | 加法函数 / 减法函数 |
|
||||
|`multiply(x, y)` / `divide(x, y)`|乘法函数 / 除法函数|
|
||||
| `floor_divide(x, y)` / `mod(x, y)` | 整除函数 / 求模函数 |
|
||||
|`allclose(x, y)`|检查数组`x`和`y`元素是否几乎相等|
|
||||
| `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) |
|
||||
| `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`中得到的布尔值构成的数组 |
|
||||
| `setdiff1d(x, y)` | 计算`x`和`y`的差集,返回这些元素构成的数组 |
|
||||
| `setxor1d(x, y)` | 计算`x`和`y`的对称差,返回这些元素构成的数组 |
|
||||
|
||||
>**补充说明**:在二维空间内,两个向量$\boldsymbol{A}=\begin{bmatrix} a_1 \\ a_2 \end{bmatrix}$和$\boldsymbol{B}=\begin{bmatrix} b_1 \\ b_2 \end{bmatrix}$的叉积是这样定义的:$\boldsymbol{A}\times \boldsymbol{B}=\begin{vmatrix} a_1 \quad a_2 \\ b_1 \quad b_2 \end{vmatrix}=a_1b_2 - a_2b_1$,其中$\begin{vmatrix} a_1 \quad a_2 \\ b_1 \quad b_2 \end{vmatrix}$称为行列式。但是一定要注意,叉积并不等同于行列式,行列式的运算结果是一个标量,而叉积运算的结果是一个向量。如果不明白,我们可以看看三维空间两个向量,$\boldsymbol{A}=\begin{bmatrix} a_1 \\ a_2 \\ a_3 \end{bmatrix}$和$\boldsymbol{B}=\begin{bmatrix} b_1 \\ b_2 \\ b_3 \end{bmatrix}$的叉积是$\left< \hat{i} \begin{vmatrix} a_2 \quad a_3 \\ b_2 \quad b_3 \end{vmatrix}, -\hat{j} \begin{vmatrix} a_1 \quad a_3 \\ b_1 \quad b_3 \end{vmatrix}, \hat{k} \begin{vmatrix} a_1 \quad a_2 \\ b_1 \quad b_2 \end{vmatrix} \right>$,其中$\hat{i}, \hat{j}, \hat{k}$代表每个维度的单位向量。
|
||||
|
||||
#### 广播机制
|
||||
|
||||
上面的例子中,两个二元运算的数组形状是完全相同的,我们再来研究一下,两个形状不同的数组是否可以直接做二元运算或使用二元函数进行运算,请看下面的例子。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
array39 = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])
|
||||
array40 = np.array([1, 2, 3])
|
||||
array39 + array40
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 2, 3],
|
||||
[2, 3, 4],
|
||||
[3, 4, 5],
|
||||
[4, 5, 6]])
|
||||
array([[72, 64, 73],
|
||||
[61, 73, 61],
|
||||
[76, 85, 77],
|
||||
[97, 88, 90],
|
||||
[63, 93, 82]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
array41 = np.array([[1], [2], [3], [4]])
|
||||
array39 + array41
|
||||
array2.mean()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1],
|
||||
[3, 3, 3],
|
||||
[5, 5, 5],
|
||||
[7, 7, 7]])
|
||||
```
|
||||
|
||||
通过上面的例子,我们发现形状不同的数组仍然有机会进行二元运算,但也绝对不是任意的数组都可以进行二元运算。简单的说,只有两个数组后缘维度相同或者其中一个数组后缘维度为1时,广播机制会被触发,而通过广播机制如果能够使两个数组的形状一致,才能进行二元运算。所谓后缘维度,指的是数组`shape`属性对应的元组中最后一个元素的值(从后往前数最后一个维度的值),例如,我们之前打开的图像对应的数组后缘维度为3,3行4列的二维数组后缘维度为4,而有5个元素的一维数组后缘维度为5。简单的说就是,后缘维度相同或者其中一个数组的后缘维度为1,就可以应用广播机制;而广播机制如果能够使得数组的形状一致,就满足了两个数组对应元素做运算的需求,如下图所示。
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211005115640.png)
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211005115658.png)
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20211005115800.png)
|
||||
|
||||
### 其他常用函数
|
||||
|
||||
除了上面讲到的函数外,NumPy 中还提供了很多用于处理数组的函数,`ndarray`对象的很多方法也可以通过直接调用函数来实现,下表给出了一些常用的函数。
|
||||
|
||||
**表3:NumPy其他常用函数**
|
||||
|
||||
| 函数 | 说明 |
|
||||
| ------------------- | ------------------------------------------------ |
|
||||
| `unique` | 去除数组重复元素,返回唯一元素构成的有序数组 |
|
||||
| `copy` | 返回拷贝数组得到的数组 |
|
||||
| `sort` | 返回数组元素排序后的拷贝 |
|
||||
| `split` / `hsplit` / `vsplit` | 将数组拆成若干个子数组 |
|
||||
| `stack` / `hstack` / `vstack` | 将多个数组堆叠成新数组 |
|
||||
| `concatenate` | 沿着指定的轴连接多个数组构成新数组 |
|
||||
| `append` / `insert` | 向数组末尾追加元素 / 在数组指定位置插入元素 |
|
||||
| `argwhere` | 找出数组中非0元素的位置 |
|
||||
| `extract` / `select` / `where` | 按照指定的条件从数组中抽取或处理数组元素 |
|
||||
| `flip` | 沿指定的轴翻转数组中的元素 |
|
||||
| `fromiter` | 通过迭代器创建数组对象 |
|
||||
| `fromregex` | 通过读取文件和正则表达式解析获取数据创建数组对象 |
|
||||
| `repeat` / `tile` | 通过对元素的重复来创建新数组 |
|
||||
| `roll` | 沿指定轴对数组元素进行移位 |
|
||||
| `resize` | 重新调整数组的大小 |
|
||||
| `place` / `put` | 将数组中满足条件的元素/指定的元素替换为指定的值 |
|
||||
| `partition` | 用选定的元素对数组进行一次划分并返回划分后的数组 |
|
||||
|
||||
> **提示**:上面的`resize`函数和`ndarray`对象的`resize`方法是有区别的,`resize`函数在调整数组大小时会重复数组中的元素作为填补多出来的元素的值,而`ndarry`对象的`resize`方法是用0来填补多出来的元素。这些小细节不清楚暂时也不要紧,但是如果用到对应的功能了就要引起注意。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
array42 = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
|
||||
array43 = np.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])
|
||||
np.hstack((array42, array43))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1, 4, 4, 4],
|
||||
[2, 2, 2, 5, 5, 5],
|
||||
[3, 3, 3, 6, 6, 6]])
|
||||
77.0
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
np.vstack((array42, array43))
|
||||
array2.mean(axis=0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1],
|
||||
[2, 2, 2],
|
||||
[3, 3, 3],
|
||||
[4, 4, 4],
|
||||
[5, 5, 5],
|
||||
[6, 6, 6]])
|
||||
array([73.8, 80.6, 76.6])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
np.concatenate((array42, array43))
|
||||
array2.mean(axis=1)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1],
|
||||
[2, 2, 2],
|
||||
[3, 3, 3],
|
||||
[4, 4, 4],
|
||||
[5, 5, 5],
|
||||
[6, 6, 6]])
|
||||
array([69.66666667, 65. , 79.33333333, 91.66666667, 79.33333333])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
np.concatenate((array42, array43), axis=1)
|
||||
array2.max(axis=0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1, 4, 4, 4],
|
||||
[2, 2, 2, 5, 5, 5],
|
||||
[3, 3, 3, 6, 6, 6]])
|
||||
array([97, 93, 90])
|
||||
```
|
||||
|
||||
### 矩阵运算
|
||||
代码:
|
||||
|
||||
NumPy 中提供了专门用于线性代数(linear algebra)的模块和表示矩阵的类型`matrix`,当然我们通过二维数组也可以表示一个矩阵,官方并不推荐使用`matrix`类而是建议使用二维数组,而且有可能在将来的版本中会移除`matrix`类。无论如何,利用这些已经封装好的类和函数,我们可以轻松愉快的实现线性代数中很多的操作。
|
||||
```Python
|
||||
array2.max(axis=1)
|
||||
```
|
||||
|
||||
#### 线性代数快速回顾
|
||||
输出:
|
||||
|
||||
1. **向量**也叫**矢量**,是一个同时具有大小和方向,且满足平行四边形法则的几何对象。与向量相对的概念叫**标量**或**数量**,标量只有大小、绝大多数情况下没有方向。
|
||||
2. 向量可以进行**加**、**减**、**数乘**、**点积**、**叉积**等运算。
|
||||
3. **行列式**由向量组成,它的性质可以由向量解释。
|
||||
4. 行列式可以使用**行列式公式**计算:$det(\boldsymbol{A})=\sum_{n!} \pm {a_{1\alpha}a_{2\beta} \cdots a_{n\omega}}$。
|
||||
5. 高阶行列式可以用**代数余子式**展开成多个低阶行列式,如:$det(\boldsymbol{A})=a_{11}C_{11}+a_{12}C_{12}+ \cdots +a_{1n}C_{1n}$。
|
||||
6. **矩阵**是由一系列元素排成的矩形阵列,矩阵里的元素可以是数字、符号或数学公式。
|
||||
7. 矩阵可以进行**加法**、**减法**、**数乘**、**乘法**、**转置**等运算。
|
||||
8. **逆矩阵**用$\boldsymbol{A^{-1}}$表示,$\boldsymbol{A}\boldsymbol{A^{-1}}=\boldsymbol{A^{-1}}\boldsymbol{A}=\boldsymbol{I}$;没有逆矩阵的方阵是**奇异矩阵**。
|
||||
9. 如果一个方阵是**满秩矩阵**(矩阵的秩等于矩阵的阶数),该方阵对应的线性方程有唯一解。
|
||||
```
|
||||
array([73, 73, 85, 97, 93])
|
||||
```
|
||||
|
||||
> **说明**:**矩阵的秩**是指矩阵中线性无关的行/列向量的最大个数,同时也是矩阵对应的线性变换的像空间的维度。
|
||||
再看看绘制箱线图,对于二维数组每一列都会产生一个统计图形,如下所示。
|
||||
|
||||
#### NumPy中矩阵相关函数
|
||||
代码:
|
||||
|
||||
1. 创建矩阵对象。
|
||||
```Python
|
||||
plt.boxplot(array2, showmeans=True)
|
||||
plt.ylim([-20, 120])
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/box_plot_2.png" style="zoom:75%;">
|
||||
|
||||
> **说明**:箱线图中的小圆圈用来表示离群点,也就是大于$\small{Q_3 + 1.5 \times IQR}$或小于$\small{Q_1 - 1.5 \times IQR}$的值。公式中的常量`1.5`可以通过绘制箱线图的`boxplot`函数的`whis`参数进行修改,常用的值是`1.5`和`3`,修改为`3`通常是为了标识出极度离群点。
|
||||
|
||||
需要说明的是,NumPy 的数组对象并没有提供计算几何平均值、调和平均值、去尾平均值等的方法,如果有这方面的需求,可以使用名为 scipy 的三方库,它的`stats`模块中提供了这些函数。此外,该模块还提供了计算众数、变异系数、偏态、峰度的函数,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
from scipy import stats
|
||||
|
||||
print(np.mean(array1)) # 算术平均值
|
||||
print(stats.gmean(array1)) # 几何平均值
|
||||
print(stats.hmean(array1)) # 调和平均值
|
||||
print(stats.tmean(array1, [10, 90])) # 去尾平均值
|
||||
print(stats.variation(array1)) # 变异系数
|
||||
print(stats.skew(array1)) # 偏态系数
|
||||
print(stats.kurtosis(array1)) # 峰度系数
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
46.0
|
||||
36.22349548825599
|
||||
24.497219530825497
|
||||
45.0
|
||||
0.5547526228777941
|
||||
0.11644192634527782
|
||||
-0.7106251396024126
|
||||
```
|
||||
|
||||
#### 其他相关方法概述
|
||||
|
||||
1. `all()` / `any()`方法:判断数组是否所有元素都是`True` / 判断数组是否有为`True`的元素。
|
||||
|
||||
2. `astype()`方法:拷贝数组,并将数组中的元素转换为指定的类型。
|
||||
|
||||
3. `reshape()`方法:调整数组对象的形状。
|
||||
|
||||
4. `dump()`方法:保存数组到二进制文件中,可以通过 NumPy 中的`load()`函数从保存的文件中加载数据创建数组。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# matrix构造函数可以传入类数组对象也可以传入字符串
|
||||
m1 = np.matrix('1 2 3; 4 5 6')
|
||||
m1
|
||||
array.dump('array1-data')
|
||||
array3 = np.load('array1-data', allow_pickle=True)
|
||||
array3
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
matrix([[1, 2, 3],
|
||||
[4, 5, 6]])
|
||||
array([46, 51, 15, 42, 53, 71, 20, 62, 6, 94])
|
||||
```
|
||||
|
||||
5. `tofile()`方法:将数组对象写入文件中。
|
||||
|
||||
```Python
|
||||
array1.tofile('res/array.txt', sep=',')
|
||||
```
|
||||
|
||||
6. `fill()`方法:向数组中填充指定的元素。
|
||||
|
||||
7. `flatten()`方法:将多维数组扁平化为一维数组。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# asmatrix函数也可以写成mat函数,它们其实是同一个函数
|
||||
m2 = np.asmatrix(np.array([[1, 1], [2, 2], [3, 3]]))
|
||||
m2
|
||||
array2.flatten()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
matrix([[1, 1],
|
||||
[2, 2],
|
||||
[3, 3]])
|
||||
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
```
|
||||
|
||||
8. `nonzero()`方法:返回非0元素的索引。
|
||||
|
||||
9. `round()`方法:对数组中的元素做四舍五入操作。
|
||||
|
||||
10. `sort()`方法:对数组进行就地排序。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
m1 * m2
|
||||
array1.sort()
|
||||
array1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
matrix([[14, 14],
|
||||
[32, 32]])
|
||||
array([ 6, 15, 20, 42, 46, 51, 53, 62, 71, 94])
|
||||
```
|
||||
|
||||
> **说明**:注意`matrix`对象和`ndarray`对象乘法运算的差别,如果两个二维数组要做矩阵乘法运算,应该使用`@`运算符或`matmul`函数,而不是`*`运算符。
|
||||
11. `swapaxes()`和`transpose()`方法:交换数组指定的轴和转置。
|
||||
|
||||
2. 矩阵对象的属性。
|
||||
代码:
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ------- | ----------------------------------------- |
|
||||
| `A` | 获取矩阵对象对应的`ndarray`对象 |
|
||||
| `A1` | 获取矩阵对象对应的扁平化后的`ndarray`对象 |
|
||||
| `I` | 可逆矩阵的逆矩阵 |
|
||||
| `T` | 矩阵的转置 |
|
||||
| `H` | 矩阵的共轭转置 |
|
||||
| `shape` | 矩阵的形状 |
|
||||
| `size` | 矩阵元素的个数 |
|
||||
```Python
|
||||
array2.swapaxes(0, 1)
|
||||
```
|
||||
|
||||
3. 矩阵对象的方法。
|
||||
输出:
|
||||
|
||||
矩阵对象的方法跟之前讲过的`ndarray`数组对象的方法基本差不多,此处不再进行赘述。
|
||||
```
|
||||
array([[1, 4, 7],
|
||||
[2, 5, 8],
|
||||
[3, 6, 9]])
|
||||
```
|
||||
|
||||
#### NumPy的线性代数模块
|
||||
代码:
|
||||
|
||||
NumPy 的`linalg`模块中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的函数,它们跟 MATLAB 和 R 等语言所使用的是相同的行业标准线性代数库,下面的表格列出了`numpy`以及`linalg`模块中常用的跟线性代数相关的函数。
|
||||
```Python
|
||||
array2.transpose()
|
||||
```
|
||||
|
||||
| 函数 | 说明 |
|
||||
| --------------- | ------------------------------------------------------------ |
|
||||
| `diag` | 以一维数组的形式返回方阵的对角线元素或将一维数组转换为方阵(非对角元素元素为0) |
|
||||
| `vdot` | 向量的点积 |
|
||||
| `dot` | 数组的点积 |
|
||||
| `inner` | 数组的内积 |
|
||||
| `outer` | 数组的叉积 |
|
||||
| `trace` | 计算对角线元素的和 |
|
||||
| `norm` | 求模(范数)运算 |
|
||||
| `det` | 计算行列式的值(在方阵上计算会得到一个标量) |
|
||||
| `matrix_rank` | 计算矩阵的秩 |
|
||||
| `eig` | 计算矩阵的特征值(eigenvalue)和特征向量(eigenvector) |
|
||||
| `inv` | 计算非奇异矩阵($n$阶方阵)的逆矩阵 |
|
||||
| `pinv` | 计算矩阵的摩尔-彭若斯(Moore-Penrose)广义逆 |
|
||||
| `qr` | QR分解(把矩阵分解成一个正交矩阵与一个上三角矩阵的积) |
|
||||
| `svd` | 计算奇异值分解(singular value decomposition) |
|
||||
| `solve` | 解线性方程组$\boldsymbol{A}\boldsymbol{x}=\boldsymbol{b}$,其中$\boldsymbol{A}$是一个方阵 |
|
||||
| `lstsq` | 计算$\boldsymbol{A}\boldsymbol{x}=\boldsymbol{b}$的最小二乘解 |
|
||||
输出:
|
||||
|
||||
大家如果有兴趣可以用下面的代码验证上面的函数。
|
||||
```
|
||||
array([[1, 4, 7],
|
||||
[2, 5, 8],
|
||||
[3, 6, 9]])
|
||||
```
|
||||
|
||||
代码:
|
||||
12. `tolist()`方法:将数组转成 Python 中的`list`。
|
||||
|
||||
```Python
|
||||
m3 = np.array([[1., 2.], [3., 4.]])
|
||||
np.linalg.inv(m3)
|
||||
```
|
||||
代码:
|
||||
|
||||
输出:
|
||||
```Python
|
||||
print(array2.tolist())
|
||||
print(type(array2.tolist()))
|
||||
```
|
||||
|
||||
```
|
||||
array([[-2. , 1. ],
|
||||
[ 1.5, -0.5]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
m4 = np.array([[1, 3, 5], [2, 4, 6], [4, 7, 9]])
|
||||
np.linalg.det(m4)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
2
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# 解线性方程组ax=b
|
||||
# 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)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([2., 3.])
|
||||
```
|
||||
输出:
|
||||
|
||||
```
|
||||
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
<class 'list'>
|
||||
```
|
||||
|
|
|
@ -0,0 +1,478 @@
|
|||
## NumPy的应用-3
|
||||
|
||||
### 数组的运算
|
||||
|
||||
使用 NumPy 最为方便的是当需要对数组元素进行运算时,不用编写循环代码遍历每个元素,所有的运算都会自动的**矢量化**。简单的说就是,NumPy 中的数学运算和数学函数会自动作用于数组中的每个成员。
|
||||
|
||||
#### 数组跟标量的运算
|
||||
|
||||
NumPy 的数组可以跟一个数值进行加、减、乘、除、求模、求幂等运算,对应的运算会作用到数组的每一个元素上,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
array1 = np.arange(1, 10)
|
||||
print(array1 + 10)
|
||||
print(array1 * 10)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[11 12 13 14 15 16 17 18 19]
|
||||
[10 20 30 40 50 60 70 80 90]
|
||||
```
|
||||
|
||||
除了上述的运算,关系运算也是没有问题的,之前讲布尔索引的时候已经遇到过了。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(array1 > 5)
|
||||
print(array1 % 2 == 0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[False False False False False True True True True]
|
||||
[False True False True False True False True False]
|
||||
```
|
||||
|
||||
#### 数组跟数组的运算
|
||||
|
||||
NumPy 的数组跟数组也可以执行算术运算和关系运算,运算会作用于两个数组对应的元素上,这就要求两个数组的形状(`shape`属性)要相同,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
array2 = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
|
||||
print(array1 + array2)
|
||||
print(array1 * array2)
|
||||
print(array1 ** array2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[ 2 3 4 6 7 8 10 11 12]
|
||||
[ 1 2 3 8 10 12 21 24 27]
|
||||
[ 1 2 3 16 25 36 343 512 729]
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(array1 > array2)
|
||||
print(array1 % array2 == 0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[False True True True True True True True True]
|
||||
[ True True True True False True False False True]
|
||||
```
|
||||
|
||||
#### 通用一元函数
|
||||
|
||||
NumPy 中通用一元函数的参数是一个数组对象,函数会对数组进行元素级的处理,例如:`sqrt`函数会对数组中的每个元素计算平方根,而`log2`函数会对数组中的每个元素计算以2为底的对数,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(np.sqrt(array1))
|
||||
print(np.log2(array1))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[1. 1.41421356 1.73205081 2. 2.23606798 2.44948974
|
||||
2.64575131 2.82842712 3. ]
|
||||
[0. 1. 1.5849625 2. 2.32192809 2.5849625
|
||||
2.80735492 3. 3.169925 ]
|
||||
```
|
||||
|
||||
**表1:通用一元函数**
|
||||
|
||||
| 函数 | 说明 |
|
||||
| -------------------------------- | --------------------------------------------- |
|
||||
| `abs` / `fabs` | 求绝对值的函数 |
|
||||
| `sqrt` | 求平方根的函数,相当于`array ** 0.5 ` |
|
||||
| `square` | 求平方的函数,相当于`array ** 2` |
|
||||
| `exp` | 计算$e^x$的函数 |
|
||||
| `log` / `log10` / `log2` | 对数函数(`e`为底 / `10`为底 / `2`为底) |
|
||||
| `sign` | 符号函数(`1` - 正数;`0` - 零;`-1` - 负数) |
|
||||
| `ceil` / `floor` | 上取整 / 下取整 |
|
||||
| `isnan` | 返回布尔数组,NaN对应`True`,非NaN对应`False` |
|
||||
| `isfinite` / `isinf` | 判断数值是否为无穷大的函数 |
|
||||
| `cos` / `cosh` / `sin` | 三角函数 |
|
||||
| `sinh` / `tan` / `tanh` | 三角函数 |
|
||||
| `arccos` / `arccosh` / `arcsin` | 反三角函数 |
|
||||
| `arcsinh` / `arctan` / `arctanh` | 反三角函数 |
|
||||
| `rint` / `round` | 四舍五入函数 |
|
||||
|
||||
#### 通用二元函数
|
||||
|
||||
NumPy 中通用二元函数的参数是两个数组对象,函数会对两个数组中的对应元素进行运算,例如:`maximum`函数会对两个数组中对应的元素找最大值,而`power`函数会对两个数组中对应的元素进行求幂操作,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
array3 = np.array([[4, 5, 6], [7, 8, 9]])
|
||||
array4 = np.array([[1, 2, 3], [3, 2, 1]])
|
||||
print(np.maximum(array3, array4))
|
||||
print(np.power(array3, array4))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[[4 5 6]
|
||||
[7 8 9]]
|
||||
[[ 4 25 216]
|
||||
[343 64 9]]
|
||||
```
|
||||
|
||||
**表2:通用二元函数**
|
||||
|
||||
| 函数 | 说明 |
|
||||
| ---------------------------------- | ------------------------------------------------------------ |
|
||||
| `add(x, y)` / `substract(x, y)` | 加法函数 / 减法函数 |
|
||||
| `multiply(x, y)` / `divide(x, y)` | 乘法函数 / 除法函数 |
|
||||
| `floor_divide(x, y)` / `mod(x, y)` | 整除函数 / 求模函数 |
|
||||
| `allclose(x, y)` | 检查数组`x`和`y`元素是否几乎相等 |
|
||||
| `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) |
|
||||
| `dot(x, y)` | 点积运算(数量积,通常记为$\cdot$,用于欧几里得空间(Euclidean space)) |
|
||||
| `inner(x, y)` | 内积运算(内积的含义要高于点积,点积相当于是内积在欧几里得空间$\mathbb{R}^n$的特例,而内积可以推广到赋范向量空间,只要它满足平行四边形法则即可) |
|
||||
| `cross(x, y) ` | 叉积运算(向量积,通常记为$\times$,运算结果是一个向量) |
|
||||
| `outer(x, y)` | 外积运算(张量积,通常记为$\bigotimes$,运算结果通常是一个矩阵) |
|
||||
| `intersect1d(x, y)` | 计算`x`和`y`的交集,返回这些元素构成的有序数组 |
|
||||
| `union1d(x, y)` | 计算`x`和`y`的并集,返回这些元素构成的有序数组 |
|
||||
| `in1d(x, y)` | 返回由判断`x` 的元素是否在`y`中得到的布尔值构成的数组 |
|
||||
| `setdiff1d(x, y)` | 计算`x`和`y`的差集,返回这些元素构成的数组 |
|
||||
| `setxor1d(x, y)` | 计算`x`和`y`的对称差,返回这些元素构成的数组 |
|
||||
|
||||
>**说明**:关于向量和矩阵的运算,我们在下一个章节加以说明。
|
||||
|
||||
#### 广播机制
|
||||
|
||||
上面数组运算的例子中,两个数组的形状(`shape`属性)是完全相同的,我们再来研究一下,两个形状不同的数组是否可以直接做二元运算或使用通用二元函数进行运算,请看下面的例子。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
array5 = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])
|
||||
array6 = np.array([1, 2, 3])
|
||||
array5 + array6
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 2, 3],
|
||||
[2, 3, 4],
|
||||
[3, 4, 5],
|
||||
[4, 5, 6]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
array7 = np.array([[1], [2], [3], [4]])
|
||||
array5 + array7
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1],
|
||||
[3, 3, 3],
|
||||
[5, 5, 5],
|
||||
[7, 7, 7]])
|
||||
```
|
||||
|
||||
通过上面的例子,我们发现形状不同的数组仍然有机会进行二元运算,但这不代表任意形状的数组都可以进行二元运算。简单的说,只有两个数组后缘维度相同或者后缘维度不同但其中一个数组后缘维度为1时,广播机制才会被触发。通过广播机制,NumPy 将两个原本形状不相同的数组变成形状相同,才能进行二元运算。所谓后缘维度,指的是数组形状(`shape`属性)从后往前看对应的部分,我们举例说明。
|
||||
|
||||
<img src="res/broadcast-1.png" style="zoom:100%;">
|
||||
|
||||
上图中,一个数组的形状是`(4, 3)`,另一个数组的形状是`(3, )`,从后往前看对应的部分都是`3`,属于后缘维度相同,可以应用广播机制,第二个数组会沿着缺失元素那个轴的方向去广播自己,最终让两个数组形状达成一致。
|
||||
|
||||
<img src="res/broadcast-3.png" style="zoom:100%;">
|
||||
|
||||
上图中,一个数组的形状是`(3, 4, 2)`,另一个数组的形状是`(4, 2)`,从后往前看对应的部分都是`(4, 2)`,属于后缘维度相同,可以应用广播机制,第二个数组会沿着缺失元素那个轴的方向去广播自己,最终让两个数组形状达成一致。
|
||||
|
||||
<img src="res/broadcast-2.png" style="zoom:100%;">
|
||||
|
||||
上图中,一个数组的形状是`(4, 3)`,另一个数组的形状是`(4, 1)`,这是后缘维度不相同的情况,但是第二个数组跟第一个数组不同的地方为`1`,第二个数组可以沿着为`1` 的那个轴广播自己,最终让两个数组形状达成一致。
|
||||
|
||||
> **思考**:一个3行1列的二维数组和一个1行3列的二维数组能够执行加法运算吗?
|
||||
|
||||
### 其他常用函数
|
||||
|
||||
除了上面讲到的函数外,NumPy 中还提供了很多用于处理数组的函数,`ndarray`对象的很多方法也可以通过调用函数来实现,下表给出了一些常用的函数。
|
||||
|
||||
**表3:NumPy其他常用函数**
|
||||
|
||||
| 函数 | 说明 |
|
||||
| ------------------------------ | ------------------------------------------------ |
|
||||
| `unique` | 去除数组重复元素,返回唯一元素构成的有序数组 |
|
||||
| `copy` | 返回拷贝数组得到的数组 |
|
||||
| `sort` | 返回数组元素排序后的拷贝 |
|
||||
| `split` / `hsplit` / `vsplit` | 将数组拆成若干个子数组 |
|
||||
| `stack` / `hstack` / `vstack` | 将多个数组堆叠成新数组 |
|
||||
| `concatenate` | 沿着指定的轴连接多个数组构成新数组 |
|
||||
| `append` / `insert` | 向数组末尾追加元素 / 在数组指定位置插入元素 |
|
||||
| `argwhere` | 找出数组中非0元素的位置 |
|
||||
| `extract` / `select` / `where` | 按照指定的条件从数组中抽取或处理数组元素 |
|
||||
| `flip` | 沿指定的轴翻转数组中的元素 |
|
||||
| `fromregex` | 通过读取文件和正则表达式解析获取数据创建数组对象 |
|
||||
| `repeat` / `tile` | 通过对元素的重复来创建新数组 |
|
||||
| `roll` | 沿指定轴对数组元素进行移位 |
|
||||
| `resize` | 重新调整数组的大小 |
|
||||
| `place` / `put` | 将数组中满足条件的元素/指定的元素替换为指定的值 |
|
||||
| `partition` | 用选定的元素对数组进行一次划分并返回划分后的数组 |
|
||||
|
||||
**去重(重复元素只保留一项)**。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.unique(array5)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([0, 1, 2, 3])
|
||||
```
|
||||
|
||||
**堆叠和拼接**。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
array8 = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])
|
||||
array9 = np.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])
|
||||
np.hstack((array8, array9))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1, 4, 4, 4],
|
||||
[2, 2, 2, 5, 5, 5],
|
||||
[3, 3, 3, 6, 6, 6]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.vstack((array8, array9))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1],
|
||||
[2, 2, 2],
|
||||
[3, 3, 3],
|
||||
[4, 4, 4],
|
||||
[5, 5, 5],
|
||||
[6, 6, 6]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.concatenate((array8, array9))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1],
|
||||
[2, 2, 2],
|
||||
[3, 3, 3],
|
||||
[4, 4, 4],
|
||||
[5, 5, 5],
|
||||
[6, 6, 6]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.concatenate((array8, array9), axis=1)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 1, 1, 4, 4, 4],
|
||||
[2, 2, 2, 5, 5, 5],
|
||||
[3, 3, 3, 6, 6, 6]])
|
||||
```
|
||||
|
||||
**追加和插入元素**。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.append(array1, [10, 100])
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.insert(array1, 1, [98, 99, 100])
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([ 1, 98, 99, 100, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
```
|
||||
|
||||
**抽取和处理元素**。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.extract(array1 % 2 != 0, array1)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([1, 3, 5, 7, 9])
|
||||
```
|
||||
|
||||
> **说明**:上面`extract`函数的操作相当于我们之前讲的布尔索引。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.select([array1 <= 3, array1 >= 7], [array1 * 10, array1 ** 2])
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([10, 20, 30, 0, 0, 0, 49, 64, 81])
|
||||
```
|
||||
|
||||
> **说明**:上面`select`函数的第一个参数设置了两个条件,满足第一个条件的元素执行了乘以10的操作,满足第二个条件的元素执行了求平方的操作,两个条件都不能满足的数组元素会被处理为0。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.where(array1 <= 5, array1 * 10, array1 ** 2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([10, 20, 30, 40, 50, 36, 49, 64, 81])
|
||||
```
|
||||
|
||||
> **说明**:上面`where`函数的第一个参数给出了条件,满足条件的元素执行了乘以10的操作,不能满足条件的元素执行了求平方的操作。
|
||||
|
||||
**重复数组元素创建新数组**。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.repeat(array1, 3)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.tile(array1, 2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||
```
|
||||
|
||||
**调整数组大小**。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.resize(array1, (5, 3))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1, 2, 3],
|
||||
[4, 5, 6],
|
||||
[7, 8, 9],
|
||||
[1, 2, 3],
|
||||
[4, 5, 6]])
|
||||
```
|
||||
|
||||
> **提示**:`array1`原本是一个有9个元素的一维数组,通过`resize`函数调整成为5行3列共15个元素的二维数组,缺少的元素通过复用原数组中的元素来补充。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.resize(array5, (2, 4))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[0, 0, 0, 1],
|
||||
[1, 1, 2, 2]])
|
||||
```
|
||||
|
||||
**替换数组元素**。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.put(array1, [0, 1, -1, 3, 5], [100, 200])
|
||||
array1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([100, 200, 3, 200, 5, 100, 7, 8, 100])
|
||||
```
|
||||
|
||||
> **说明**:上面`put`函的第二个参数给出了要被替换的元素的索引,但是用来作为替换值的元素只有`100`和`200`,所以这两个值会被循环使用,因此索引为`0`、`1`、`-1`、`3`、`5`的元素被依次替换成了`100`、`200`、`100`、`200`、`100`。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.place(array1, array1 > 5, [1, 2, 3])
|
||||
array1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([1, 2, 3, 3, 5, 1, 2, 3, 1])
|
||||
```
|
||||
|
||||
> **注意**:`put`函数和`place`函数都没有返回新的数组对象,而是在原来的数组上直接进行替换。
|
|
@ -1,686 +0,0 @@
|
|||
## Pandas的应用-1
|
||||
|
||||
Pandas是Wes McKinney在2008年开发的一个强大的**分析结构化数据**的工具集。Pandas以NumPy为基础(数据表示和运算),提供了用于数据处理的函数和方法,对数据分析和数据挖掘提供了很好的支持;同时Pandas还可以跟数据可视化工具Matplotlib很好的整合在一起,非常轻松愉快的实现数据的可视化展示。
|
||||
|
||||
Pandas核心的数据类型是`Series`(数据系列)、`DataFrame`(数据表/数据框),分别用于处理一维和二维的数据,除此之外还有一个名为`Index`的类型及其子类型,它为`Series`和`DataFrame`提供了索引功能。日常工作中以`DataFrame`使用最为广泛,因为二维的数据本质就是一个有行有列的表格(想一想Excel电子表格和关系型数据库中的二维表)。上述这些类型都提供了大量的处理数据的方法,数据分析师可以以此为基础实现对数据的各种常规处理。
|
||||
|
||||
### Series的应用
|
||||
|
||||
Pandas库中的`Series`对象可以用来表示一维数据结构,跟数组非常类似,但是多了一些额外的功能。`Series`的内部结构包含了两个数组,其中一个用来保存数据,另一个用来保存数据的索引。
|
||||
|
||||
#### 创建Series对象
|
||||
|
||||
> **提示**:在执行下面的代码之前,请先导入`pandas`以及相关的库文件,具体的做法可以参考上一章。
|
||||
|
||||
##### 方法1:通过列表或数组创建Series对象
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# data参数表示数据,index参数表示数据的索引(标签)
|
||||
# 如果没有指定index属性,默认使用数字索引
|
||||
ser1 = pd.Series(data=[320, 180, 300, 405], index=['一季度', '二季度', '三季度', '四季度'])
|
||||
ser1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 320
|
||||
二季度 180
|
||||
三季度 300
|
||||
四季度 405
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 方法2:通过字典创建Series对象。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# 字典中的键就是数据的索引(标签),字典中的值就是数据
|
||||
ser2 = pd.Series({'一季度': 320, '二季度': 180, '三季度': 300, '四季度': 405})
|
||||
ser2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 320
|
||||
二季度 180
|
||||
三季度 300
|
||||
四季度 405
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
#### 索引和切片
|
||||
|
||||
跟数组一样,Series对象也可以进行索引和切片操作,不同的是Series对象因为内部维护了一个保存索引的数组,所以除了可以使用整数索引通过位置检索数据外,还可以通过自己设置的索引标签获取对应的数据。
|
||||
|
||||
##### 使用整数索引
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
print(ser2[0], ser[1], ser[2], ser[3])
|
||||
ser2[0], ser2[3] = 350, 360
|
||||
print(ser2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
320 180 300 405
|
||||
一季度 350
|
||||
二季度 180
|
||||
三季度 300
|
||||
四季度 360
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
> **提示**:如果要使用负向索引,必须在创建`Series`对象时通过`index`属性指定非数值类型的标签。
|
||||
|
||||
##### 使用自定义的标签索引
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
print(ser2['一季度'], ser2['三季度'])
|
||||
ser2['一季度'] = 380
|
||||
print(ser2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
350 300
|
||||
一季度 380
|
||||
二季度 180
|
||||
三季度 300
|
||||
四季度 360
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 切片操作
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
print(ser2[1:3])
|
||||
print(ser2['二季度':'四季度'])
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
二季度 180
|
||||
三季度 300
|
||||
dtype: int64
|
||||
二季度 500
|
||||
三季度 500
|
||||
四季度 520
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2[1:3] = 400, 500
|
||||
ser2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 380
|
||||
二季度 400
|
||||
三季度 500
|
||||
四季度 360
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 花式索引
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
print(ser2[['二季度', '四季度']])
|
||||
ser2[['二季度', '四季度']] = 500, 520
|
||||
print(ser2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
二季度 400
|
||||
四季度 360
|
||||
dtype: int64
|
||||
一季度 380
|
||||
二季度 500
|
||||
三季度 500
|
||||
四季度 520
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 布尔索引
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2[ser2 >= 500]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
二季度 500
|
||||
三季度 500
|
||||
四季度 520
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
####属性和方法
|
||||
|
||||
Series对象的常用属性如下表所示。
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ------------------------- | --------------------------------------- |
|
||||
| `dtype` / `dtypes` | 返回`Series`对象的数据类型 |
|
||||
| `hasnans` | 判断`Series`对象中有没有空值 |
|
||||
| `at` / `iat` | 通过索引访问`Series`对象中的单个值 |
|
||||
| `loc` / `iloc` | 通过一组索引访问`Series`对象中的一组值 |
|
||||
| `index` | 返回`Series`对象的索引 |
|
||||
| `is_monotonic` | 判断`Series`对象中的数据是否单调 |
|
||||
| `is_monotonic_increasing` | 判断`Series`对象中的数据是否单调递增 |
|
||||
| `is_monotonic_decreasing` | 判断`Series`对象中的数据是否单调递减 |
|
||||
| `is_unique` | 判断`Series`对象中的数据是否独一无二 |
|
||||
| `size` | 返回`Series`对象中元素的个数 |
|
||||
| `values` | 以`ndarray`的方式返回`Series`对象中的值 |
|
||||
|
||||
`Series`对象的方法很多,我们通过下面的代码为大家介绍一些常用的方法。
|
||||
|
||||
##### 统计相关的方法
|
||||
|
||||
`Series`对象支持各种获取描述性统计信息的方法。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# 求和
|
||||
print(ser2.sum())
|
||||
# 求均值
|
||||
print(ser2.mean())
|
||||
# 求最大
|
||||
print(ser2.max())
|
||||
# 求最小
|
||||
print(ser2.min())
|
||||
# 计数
|
||||
print(ser2.count())
|
||||
# 求标准差
|
||||
print(ser2.std())
|
||||
# 求方差
|
||||
print(ser2.var())
|
||||
# 求中位数
|
||||
print(ser2.median())
|
||||
```
|
||||
|
||||
`Series`对象还有一个名为`describe()`的方法,可以获得上述所有的描述性统计信息,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2.describe()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
count 4.000000
|
||||
mean 475.000000
|
||||
std 64.031242
|
||||
min 380.000000
|
||||
25% 470.000000
|
||||
50% 500.000000
|
||||
75% 505.000000
|
||||
max 520.000000
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
> **提示**:因为`describe()`返回的也是一个`Series`对象,所以也可以用`ser2.describe()['mean']`来获取平均值。
|
||||
|
||||
如果`Series`对象有重复的值,我们可以使用`unique()`方法获得去重之后的`Series`对象;可以使用`nunique()`方法统计不重复值的数量;如果想要统计每个值重复的次数,可以使用`value_counts()`方法,这个方法会返回一个`Series`对象,它的索引就是原来的`Series`对象中的值,而每个值出现的次数就是返回的`Series`对象中的数据,在默认情况下会按照出现次数做降序排列。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3 = pd.Series(data=['apple', 'banana', 'apple', 'pitaya', 'apple', 'pitaya', 'durian'])
|
||||
ser3.value_counts()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
apple 3
|
||||
pitaya 2
|
||||
durian 1
|
||||
banana 1
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3.nunique()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
4
|
||||
```
|
||||
|
||||
##### 数据处理的方法
|
||||
|
||||
`Series`对象的`isnull()`和`notnull()`方法可以用于空值的判断,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4 = pd.Series(data=[10, 20, np.NaN, 30, np.NaN])
|
||||
ser4.isnull()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 False
|
||||
1 False
|
||||
2 True
|
||||
3 False
|
||||
4 True
|
||||
dtype: bool
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4.notnull()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 True
|
||||
1 True
|
||||
2 False
|
||||
3 True
|
||||
4 False
|
||||
dtype: bool
|
||||
```
|
||||
|
||||
`Series`对象的`dropna()`和`fillna()`方法分别用来删除空值和填充空值,具体的用法如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4.dropna()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10.0
|
||||
1 20.0
|
||||
3 30.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# 将空值填充为40
|
||||
ser4.fillna(value=40)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10.0
|
||||
1 20.0
|
||||
2 40.0
|
||||
3 30.0
|
||||
4 40.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# backfill或bfill表示用后一个元素的值填充空值
|
||||
# ffill或pad表示用前一个元素的值填充空值
|
||||
ser4.fillna(method='ffill')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10.0
|
||||
1 20.0
|
||||
2 20.0
|
||||
3 30.0
|
||||
4 30.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
需要提醒大家注意的是,`dropna()`和`fillna()`方法都有一个名为`inplace`的参数,它的默认值是`False`,表示删除空值或填充空值不会修改原来的`Series`对象,而是返回一个新的`Series`对象来表示删除或填充空值后的数据系列,如果将`inplace`参数的值修改为`True`,那么删除或填充空值会就地操作,直接修改原来的`Series`对象,那么方法的返回值是`None`。后面我们会接触到的很多方法,包括`DataFrame`对象的很多方法都会有这个参数,它们的意义跟这里是一样的。
|
||||
|
||||
`Series`对象的`mask()`和`where()`方法可以将满足或不满足条件的值进行替换,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser5 = pd.Series(range(5))
|
||||
ser5.where(ser5 > 0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 NaN
|
||||
1 1.0
|
||||
2 2.0
|
||||
3 3.0
|
||||
4 4.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser5.where(ser5 > 1, 10)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10
|
||||
1 10
|
||||
2 2
|
||||
3 3
|
||||
4 4
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser5.mask(ser5 > 1, 10)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 0
|
||||
1 1
|
||||
2 10
|
||||
3 10
|
||||
4 10
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
`Series`对象的`duplicated()`方法可以帮助我们找出重复的数据,而`drop_duplicates()`方法可以帮我们删除重复数据。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3.duplicated()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 False
|
||||
1 False
|
||||
2 True
|
||||
3 False
|
||||
4 True
|
||||
5 True
|
||||
6 False
|
||||
dtype: bool
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3.drop_duplicates()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 apple
|
||||
1 banana
|
||||
3 pitaya
|
||||
6 durian
|
||||
dtype: object
|
||||
```
|
||||
|
||||
`Series`对象的`apply()`和`map()`方法非常重要,它们可以用于数据处理,把数据映射或转换成我们期望的样子,这个操作在数据分析的数据准备阶段非常重要。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser6 = pd.Series(['cat', 'dog', np.nan, 'rabbit'])
|
||||
ser6
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 cat
|
||||
1 dog
|
||||
2 NaN
|
||||
3 rabbit
|
||||
dtype: object
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser6.map({'cat': 'kitten', 'dog': 'puppy'})
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 kitten
|
||||
1 puppy
|
||||
2 NaN
|
||||
3 NaN
|
||||
dtype: object
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser6.map('I am a {}'.format, na_action='ignore')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 I am a cat
|
||||
1 I am a dog
|
||||
2 NaN
|
||||
3 I am a rabbit
|
||||
dtype: object
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser7 = pd.Series([20, 21, 12], index=['London', 'New York', 'Helsinki'])
|
||||
ser7
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
London 20
|
||||
New York 21
|
||||
Helsinki 12
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser7.apply(np.square)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
London 400
|
||||
New York 441
|
||||
Helsinki 144
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser7.apply(lambda x, value: x - value, args=(5, ))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
London 15
|
||||
New York 16
|
||||
Helsinki 7
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 排序和取头部值的方法
|
||||
|
||||
`Series`对象的`sort_index()`和`sort_values()`方法可以用于对索引和数据的排序,排序方法有一个名为`ascending`的布尔类型参数,该参数用于控制排序的结果是升序还是降序;而名为`kind`的参数则用来控制排序使用的算法,默认使用了`quicksort`,也可以选择`mergesort`或`heapsort`;如果存在空值,那么可以用`na_position`参数空值放在最前还是最后,默认是`last`,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser8 = pd.Series(
|
||||
data=[35, 96, 12, 57, 25, 89],
|
||||
index=['grape', 'banana', 'pitaya', 'apple', 'peach', 'orange']
|
||||
)
|
||||
# 按值从小到大排序
|
||||
ser8.sort_values()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
pitaya 12
|
||||
peach 25
|
||||
grape 35
|
||||
apple 57
|
||||
orange 89
|
||||
banana 96
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# 按索引从大到小排序
|
||||
ser8.sort_index(ascending=False)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
pitaya 12
|
||||
peach 25
|
||||
orange 89
|
||||
grape 35
|
||||
banana 96
|
||||
apple 57
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
如果要从`Series`对象中找出元素中最大或最小的“Top-N”,实际上是不需要对所有的值进行排序的,可以使用`nlargest()`和`nsmallest()`方法来完成,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# 值最大的3个
|
||||
ser8.nlargest(3)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
banana 96
|
||||
orange 89
|
||||
apple 57
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# 值最小的2个
|
||||
ser8.nsmallest(2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
pitaya 12
|
||||
peach 25
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
#### 绘制图表
|
||||
|
||||
Series对象有一个名为`plot`的方法可以用来生成图表,如果选择生成折线图、饼图、柱状图等,默认会使用Series对象的索引作为横坐标,使用Series对象的数据作为纵坐标。
|
||||
|
||||
首先导入`matplotlib`中`pyplot`模块并进行必要的配置。
|
||||
|
||||
```Python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# 配置支持中文的非衬线字体(默认的字体无法显示中文)
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei', ]
|
||||
# 使用指定的中文字体时需要下面的配置来避免负号无法显示
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
```
|
||||
|
||||
创建`Series`对象并绘制对应的柱状图。
|
||||
|
||||
```Python
|
||||
ser9 = pd.Series({'一季度': 400, '二季度': 520, '三季度': 180, '四季度': 380})
|
||||
# 通过Series对象的plot方法绘图(kind='bar'表示绘制柱状图)
|
||||
ser9.plot(kind='bar', color=['r', 'g', 'b', 'y'])
|
||||
# x轴的坐标旋转到0度(中文水平显示)
|
||||
plt.xticks(rotation=0)
|
||||
# 在柱状图的柱子上绘制数字
|
||||
for i in range(4):
|
||||
plt.text(i, ser9[i] + 5, ser9[i], ha='center')
|
||||
# 显示图像
|
||||
plt.show()
|
||||
```
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20220619171513.png)
|
||||
|
||||
绘制反映每个季度占比的饼图。
|
||||
|
||||
```Python
|
||||
# autopct参数可以配置在饼图上显示每块饼的占比
|
||||
ser9.plot(kind='pie', autopct='%.1f%%')
|
||||
# 设置y轴的标签(显示在饼图左侧的文字)
|
||||
plt.ylabel('各季度占比')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
![](https://github.com/jackfrued/mypic/raw/master/20220619171503.png)
|
|
@ -0,0 +1,686 @@
|
|||
## NumPy的应用-4
|
||||
|
||||
### 向量
|
||||
|
||||
**向量**(*vector*)也叫**矢量**,是一个同时具有大小和方向,且满足平行四边形法则的几何对象。与向量相对的概念叫**标量**或**数量**,标量只有大小,绝大多数情况下没有方向。我们通常用带箭头的线段来表示向量,在平面直角坐标系中的向量如下图所示。需要注意的是,向量是表达大小和方向的量,并没有规定起点和终点,所以相同的向量可以画在任意位置,例如下图中$\boldsymbol{w}$和$\boldsymbol{v}$两个向量并没有什么区别。
|
||||
|
||||
<img src="res/vector_1.png" style="zoom:40%;">
|
||||
|
||||
向量有很多种代数表示法,对于二维空间的向量,下面几种写法都是可以的。
|
||||
$$
|
||||
\boldsymbol{a} = \langle a_1, a_2 \rangle = (a_1, a_2) = \begin{pmatrix} a_1 \\ a_2 \end{pmatrix} = \begin{bmatrix} a_1 \\ a_2 \end{bmatrix}
|
||||
$$
|
||||
向量的大小称为向量的模,它是一个标量,对于二维空间的向量,模可以通过下面的公式计算。
|
||||
$$
|
||||
\lvert \boldsymbol{a} \rvert = \sqrt{a_{1}^{2} + a_{2}^{2}}
|
||||
$$
|
||||
注意,这里的$\lvert \boldsymbol{a} \rvert$并不是绝对值,你可以将其称为向量$\boldsymbol{a}$的二范数,这是数学中的符号重用现象。上面的写法和概念也可以推广到$n$维空间,我们通常用$\boldsymbol{R^n}$表示$n$维空间,我们刚才说的二维空间可以记为$\boldsymbol{R^2}$,三维空间可以记为$\boldsymbol{R^3}$。虽然生活在三维空间的我们很难想象四维空间、五维空间是什么样子,但是这并不影响我们探讨高维空间,机器学习中,我们经常把有$n$个特征的训练样本称为一个$n$维向量。
|
||||
|
||||
#### 向量的加法
|
||||
|
||||
相同维度的向量可以相加得到一个新的向量,运算的方法是将向量的每个分量相加,如下所示。
|
||||
$$
|
||||
\boldsymbol{u} = \begin{bmatrix} u_1 \\ u_2 \\ \vdots \\ u_n \end{bmatrix}, \quad
|
||||
\boldsymbol{v} = \begin{bmatrix} v_1 \\ v_2 \\ \vdots \\ v_n \end{bmatrix}, \quad
|
||||
\boldsymbol{u} + \boldsymbol{v} = \begin{bmatrix} u_1 + v_1 \\ u_2 + v_2 \\ \vdots \\ u_n + v_n \end{bmatrix}
|
||||
$$
|
||||
向量的加法满足“平行四边形法则”,即两个向量$\boldsymbol{u}$和$\boldsymbol{v}$构成了平行四边形的两条邻边,相加的结果是平行四边形的对角线,如下图所示。
|
||||
|
||||
<img src="res/vector_2.png" style="zoom:58%;">
|
||||
|
||||
#### 向量的数乘
|
||||
|
||||
一个向量$\boldsymbol{v}$可以和一个标量$k$相乘,运算的方法是将向量中的每个分量与该标量相乘即可,如下所示。
|
||||
$$
|
||||
\boldsymbol{v} = \begin{bmatrix} v_1 \\ v_2 \\ \vdots \\ v_n \end{bmatrix}, \quad
|
||||
k \cdot \boldsymbol{v} = \begin{bmatrix} k \cdot v_1 \\ k \cdot v_2 \\ \vdots \\ k \cdot v_n \end{bmatrix}
|
||||
$$
|
||||
我们可以用 NumPy 的数组来表示向量,向量的加法可以通过两个数组的加法来实现,向量的数乘可以通过数组和标量的乘法来实现,此处不再进行赘述。
|
||||
|
||||
#### 向量的点积
|
||||
|
||||
点积(*dot product*)是两个向量之间最为重要的运算之一,运算的方法是将两个向量对应分量的乘积求和,所以点积的结果是一个标量,其几何意义是两个向量的模乘以二者夹角的余弦如下所示。
|
||||
$$
|
||||
\boldsymbol{u} = \begin{bmatrix} u_1 \\ u_2 \\ \vdots \\ u_n \end{bmatrix}, \quad
|
||||
\boldsymbol{v} = \begin{bmatrix} v_1 \\ v_2 \\ \vdots \\ v_n \end{bmatrix} \quad \\
|
||||
\boldsymbol{u} \cdot \boldsymbol{v} = \sum_{i=1}^{n}{u_iv_i} = \lvert \boldsymbol{u} \rvert \lvert \boldsymbol{v} \rvert cos\theta
|
||||
$$
|
||||
假如我们用3维向量来表示用户对喜剧片、言情片和动作片这三类电影的偏好,我们用1到5的数字来表示喜欢的程度,其中5表示非常喜欢,4表示比较喜欢,3表示无感,2表示比较反感,1表示特别反感。那么,下面的向量表示用户非常喜欢喜剧片,特别反感言情片,对动作片不喜欢也不反感。
|
||||
$$
|
||||
\boldsymbol{u} = \begin{pmatrix} 5 \\ 1 \\ 3 \end{pmatrix}
|
||||
$$
|
||||
现在有两部电影上映了,一部属于言情喜剧片,一部属于喜剧动作片,我们把两部电影也通过3维向量的方式进行表示,如下所示。
|
||||
$$
|
||||
\boldsymbol{m_1} = \begin{pmatrix} 4 \\ 5 \\ 1 \end{pmatrix}, \quad \boldsymbol{m_2} = \begin{pmatrix} 5 \\ 1 \\ 5 \end{pmatrix}
|
||||
$$
|
||||
如果现在我们需要向刚才的用户推荐一部电影,我们应该给他推荐哪一部呢?我们可以将代表用户的向量$\boldsymbol{u}$和代表电影的向量$\boldsymbol{m_1}$和$\boldsymbol{m_2}$分别进行点积运算,再除以向量的模长,得到向量夹角的余弦值,余弦值越接近1,说明向量的夹角越接近0度,也就是两个向量的相似度越高。很显然,我们应该向用户推荐跟他观影喜好相似度更高的电影。
|
||||
$$
|
||||
cos\theta_1 = \frac{\boldsymbol{u} \cdot \boldsymbol{m1}}{|\boldsymbol{u}||\boldsymbol{m1}|} \approx \frac{4 \times 5 + 5 \times 1 + 3 \times 1}{5.92 \times 6.48} \approx 0.73 \\
|
||||
cos\theta_2 = \frac{\boldsymbol{u} \cdot \boldsymbol{m2}}{|\boldsymbol{u}||\boldsymbol{m2}|} \approx \frac{5 \times 5 + 1 \times 1 + 3 \times 5}{5.92 \times 7.14} \approx 0.97
|
||||
$$
|
||||
大家可能会说,向量$\boldsymbol{m_2}$代表的电影肉眼可见跟用户是更加匹配的。的确,对于一个三维向量我们凭借直觉也能够给出正确的答案,但是对于一个$n$维向量,当$n$的值非常大时,你还有信心凭借肉眼的观察和本能的直觉给出准确的答案吗?向量的点积可以通过`dot`函数来计算,而向量的模长可以通过 NumPy 的`linalg`模块中的`norm`函数来计算,代码如下所示。
|
||||
|
||||
```python
|
||||
u = np.array([5, 1, 3])
|
||||
m1 = np.array([4, 5, 1])
|
||||
m2 = np.array([5, 1, 5])
|
||||
print(np.dot(u, m1) / (np.linalg.norm(u) * np.linalg.norm(m1))) # 0.7302967433402214
|
||||
print(np.dot(u, m2) / (np.linalg.norm(u) * np.linalg.norm(m2))) # 0.9704311900788593
|
||||
```
|
||||
|
||||
#### 向量的叉积
|
||||
|
||||
在二维空间,两个向量的叉积是这样定义的:
|
||||
$$
|
||||
\boldsymbol{A} = \begin{pmatrix} a_{1} \\ a_{2} \end{pmatrix}, \quad \boldsymbol{B} = \begin{pmatrix} b_{1} \\ b_{2} \end{pmatrix} \\
|
||||
\boldsymbol{A} \times \boldsymbol{B} = \begin{vmatrix} a_{1} \quad a_{2} \\ b_{1} \quad b_{2} \end{vmatrix} = a_{1}b_{2} - a_{2}b_{1}
|
||||
$$
|
||||
对于三维空间,两个向量的叉积结果是一个向量,如下所示:
|
||||
$$
|
||||
\boldsymbol{A} = \begin{pmatrix} a_{1} \\ a_{2} \\ a_{3} \end{pmatrix}, \quad \boldsymbol{B} = \begin{pmatrix} b_{1} \\ b_{2} \\ b_{3} \end{pmatrix} \\
|
||||
\boldsymbol{A} \times \boldsymbol{B} = \begin{vmatrix} \boldsymbol{\hat{i}} \quad \boldsymbol{\hat{j}} \quad \boldsymbol{\hat{k}} \\ a_{1} \quad a_{2} \quad a_{3} \\ b_{1} \quad b_{2} \quad b_{3} \end{vmatrix} = \langle \boldsymbol{\hat{i}}\begin{vmatrix} a_{2} \quad a_{3} \\ b_{2} \quad b_{3} \end{vmatrix}, -\boldsymbol{\hat{j}}\begin{vmatrix} a_{1} \quad a_{3} \\ b_{1} \quad b_{3} \end{vmatrix}, \boldsymbol{\hat{k}}\begin{vmatrix} a_{1} \quad a_{2} \\ b_{1} \quad b_{2} \end{vmatrix} \rangle
|
||||
$$
|
||||
因为叉积的结果是向量,所以$\boldsymbol{A} \times \boldsymbol{B}$和$\boldsymbol{B} \times \boldsymbol{A}$的结果并不相同,事实上:
|
||||
$$
|
||||
\boldsymbol{A} \times \boldsymbol{B} = -(\boldsymbol{B} \times \boldsymbol{A})
|
||||
$$
|
||||
NumPy 中可以通过`cross`函数来计算向量的叉积,代码如下所示。
|
||||
|
||||
```python
|
||||
print(np.cross(u, m1)) # [-14 7 21]
|
||||
print(np.cross(m1, u)) # [ 14 -7 -21]
|
||||
```
|
||||
|
||||
### 行列式
|
||||
|
||||
**行列式**(*determinant*)通常记作$det(\boldsymbol{A})$或$|\boldsymbol{A}|$,其中$\boldsymbol{A}$是一个$n$阶方阵。行列式可以看做是有向面积或体积的概念在一般欧几里得空间的推广,或者说行列式描述的是一个线性变换对“体积”所造成的影响。行列式的概念最早出现在解线性方程组的过程中,十七世纪晚期,关孝和(日本江户时代的数学家)与莱布尼茨的著作中已经使用行列式来确定线性方程组解的个数以及形式;十八世纪开始,行列式开始作为独立的数学概念被研究;十九世纪以后,行列式理论进一步得到发展和完善。
|
||||
|
||||
<img src="res/Parallelogramme.jpeg" style="zoom:125%;">
|
||||
|
||||
#### 行列式的性质
|
||||
|
||||
行列式是由向量引出的,所以行列式解释的其实是向量的性质。
|
||||
|
||||
**性质1**:如果$det(\boldsymbol{A})$中某行(或某列)的元素全部为0,那么$det(\boldsymbol{A}) = 0$。
|
||||
|
||||
**性质2**:如果$det(\boldsymbol{A})$中某行(或某列)有公共因子$k$,则可以提出$k$,得到行列式$det(\boldsymbol{A^{'}})$,且$det(\boldsymbol{A}) = k \cdot det(\boldsymbol{A^{'}})$。
|
||||
$$
|
||||
det(\boldsymbol{A})={\begin{vmatrix}a_{11}&a_{12}&\dots &a_{1n}\\\vdots &\vdots &\ddots &\vdots \\{\color {blue}k}a_{i1}&{\color {blue}k}a_{i2}&\dots &{\color {blue}k}a_{in}\\\vdots &\vdots &\ddots &\vdots \\a_{n1}&a_{n2}&\dots &a_{nn}\end{vmatrix}}={\color {blue}k}{\begin{vmatrix}a_{11}&a_{12}&\dots &a_{1n}\\\vdots &\vdots &\ddots &\vdots \\a_{i1}&a_{i2}&\dots &a_{in}\\\vdots &\vdots &\ddots &\vdots \\a_{n1}&a_{n2}&\dots &a_{nn}\end{vmatrix}}={\color {blue}k} \cdot det(\boldsymbol{A^{'}})
|
||||
$$
|
||||
|
||||
**性质3**:如果$det(\boldsymbol{A})$中某行(或某列)的每个元素是两数之和,则此行列式可拆分为两个行列式相加,如下所示。
|
||||
$$
|
||||
{\begin{vmatrix}a_{11}&a_{12}&\dots &a_{1n}\\\vdots &\vdots &\ddots &\vdots \\{\color {blue}a_{i1}}+{\color {OliveGreen}b_{i1}}&{\color {blue}a_{i2}}+{\color {OliveGreen}b_{i2}}&\dots &{\color {blue}a_{in}}+{\color {OliveGreen}b_{in}}\\\vdots &\vdots &\ddots &\vdots \\a_{n1}&a_{n2}&\dots &a_{nn}\end{vmatrix}}={\begin{vmatrix}a_{11}&a_{12}&\dots &a_{1n}\\\vdots &\vdots &\ddots &\vdots \\{\color {blue}a_{i1}}&{\color {blue}a_{i2}}&\dots &{\color {blue}a_{in}}\\\vdots &\vdots &\ddots &\vdots \\a_{n1}&a_{n2}&\dots &a_{nn}\end{vmatrix}}+{\begin{vmatrix}a_{11}&a_{12}&\dots &a_{1n}\\\vdots &\vdots &\ddots &\vdots \\{\color {OliveGreen}b_{i1}}&{\color {OliveGreen}b_{i2}}&\dots &{\color {OliveGreen}b_{in}}\\\vdots &\vdots &\ddots &\vdots \\a_{n1}&a_{n2}&\dots &a_{nn}\end{vmatrix}}
|
||||
$$
|
||||
**性质4**:如果$det(\boldsymbol{A})$中两行(或两列)元素对应成比例,那么$det(\boldsymbol{A}) = 0$。
|
||||
|
||||
**性质5**:如果$det(\boldsymbol{A})$中两行(或两列)互换得到$det(\boldsymbol{A^{'}})$,那么$det(\boldsymbol{A}) = -det(\boldsymbol{A^{'}})$。
|
||||
|
||||
**性质6**:将$det(\boldsymbol{A})$中某行(或某列)的$k$倍加进另一行(或另一列)里,行列式的值不变,如下所示。
|
||||
$$
|
||||
{\begin{vmatrix}\vdots &\vdots &\vdots &\vdots \\a_{i1}&a_{i2}&\dots &a_{in}\\a_{j1}&a_{j2}&\dots &a_{jn}\\\vdots &\vdots &\vdots &\vdots \\\end{vmatrix}}={\begin{vmatrix}\vdots &\vdots &\vdots &\vdots \\a_{i1}&a_{i2}&\dots &a_{in}\\a_{j1}{\color {blue}+ka_{i1}}&a_{j2}{\color {blue}+ka_{i2}}&\dots &a_{jn}{\color {blue}+ka_{in}}\\\vdots &\vdots &\vdots &\vdots \\\end{vmatrix}}
|
||||
$$
|
||||
**性质7**:将行列式的行列互换,行列式的值不变,如下所示。
|
||||
$$
|
||||
{\begin{vmatrix}a_{11}&a_{12}&\dots &a_{1n}\\a_{21}&a_{22}&\dots &a_{2n}\\\vdots &\vdots &\ddots &\vdots \\a_{n1}&a_{n2}&\dots &a_{nn}\end{vmatrix}}={\begin{vmatrix}a_{11}&a_{21}&\dots &a_{n1}\\a_{12}&a_{22}&\dots &a_{n2}\\\vdots &\vdots &\ddots &\vdots \\a_{1n}&a_{2n}&\dots &a_{nn}\end{vmatrix}}
|
||||
$$
|
||||
**性质8**:方块矩阵$\boldsymbol{A}$和$\boldsymbol{B}$的乘积的行列式等于其行列式的乘积,即$det(\boldsymbol{A}\boldsymbol{B}) = det(\boldsymbol{A})det(\boldsymbol{B})$。特别的,若将矩阵中的每一行都乘以常数$r$,那么行列式的值将是原来的$r^{n}$倍,即$det(r\boldsymbol{A}) = det(r\boldsymbol{I_{n}} \cdot \boldsymbol{A}) = r^{n}det(\boldsymbol{A})$,其中$\boldsymbol{I_{n}}$是$n$阶单位矩阵。
|
||||
|
||||
**性质9**:若$\boldsymbol{A}$是可逆矩阵,那么$det(\boldsymbol{A}^{-1}) = (det(\boldsymbol{A}))^{-1}$。
|
||||
|
||||
#### 行列式的计算
|
||||
|
||||
$n$阶行列式的计算公式如下所示:
|
||||
$$
|
||||
det(\boldsymbol{A})=\sum_{n!} \pm {a_{1\alpha}a_{2\beta} \cdots a_{n\omega}}
|
||||
$$
|
||||
|
||||
对于二阶行列式,上面的公式相当于:
|
||||
$$
|
||||
\begin{vmatrix} a_{11} \quad a_{12} \\ a_{21} \quad a_{22} \end{vmatrix} = a_{11}a_{22} - a_{12}a_{21}
|
||||
$$
|
||||
对于三阶行列式,上面的计算公式相当于:
|
||||
$$
|
||||
\begin{vmatrix} a_{11} \quad a_{12} \quad a_{13} \\ a_{21} \quad a_{22} \quad a_{23} \\ a_{31} \quad a_{32} \quad a_{33} \end{vmatrix} = a_{11}a_{22}a_{33} + a_{12}a_{23}a_{31} + a_{13}a_{21}a_{32} - a_{11}a_{23}a_{32} - a_{12}a_{21}a_{33} - a_{13}a_{22}a_{31}
|
||||
$$
|
||||
高阶行列式可以用**代数余子式**(*cofactor*)展开成多个低阶行列式,如下所示:
|
||||
$$
|
||||
det(\boldsymbol{A})=a_{11}C_{11}+a_{12}C_{12}+ \cdots +a_{1n}C_{1n} = \sum_{i=1}^{n}{a_{1i}C_{1i}}
|
||||
$$
|
||||
其中,$C_{11}$是原行列式去掉$a_{11}$所在行和列之后剩余的部分构成的行列式,以此类推。
|
||||
|
||||
### 矩阵
|
||||
|
||||
**矩阵**(*matrix*)是由一系列元素排成的矩形阵列,矩阵里的元素可以是数字、符号或数学公式。矩阵可以进行**加法**、**减法**、**数乘**、**转置**、**矩阵乘法**等运算,如下图所示。
|
||||
|
||||
<img src="res/matrix_operation.png" style="zoom:62%;">
|
||||
|
||||
值得一提的是矩阵乘法运算,该运算仅当第一个矩阵$\boldsymbol{A}$的列数和另一个矩阵$\boldsymbol{B}$的行数相等时才能定义。如果$\boldsymbol{A}$是一个$m \times n$的矩阵,$\boldsymbol{B}$是一个$n \times k$矩阵,它们的乘积是一个$m \times k$的矩阵,其中元素的计算公式如下所示:
|
||||
$$
|
||||
[\mathbf{AB}]_{i,j} = A_{i,1}B_{1,j} + A_{i,2}B_{2,j} + \cdots + A_{i,n}B_{n,j} = \sum_{r=1}^n A_{i,r}B_{r,j}
|
||||
$$
|
||||
<img src="res/matrix_multiply.png" style="zoom:35%;">
|
||||
|
||||
例如:
|
||||
$$
|
||||
\begin{bmatrix}
|
||||
1 & 0 & 2 \\
|
||||
-1 & 3 & 1 \\
|
||||
\end{bmatrix}
|
||||
\times
|
||||
\begin{bmatrix}
|
||||
3 & 1 \\
|
||||
2 & 1 \\
|
||||
1 & 0
|
||||
\end{bmatrix}
|
||||
=
|
||||
\begin{bmatrix}
|
||||
(1 \times 3 + 0 \times 2 + 2 \times 1) & (1 \times 1 + 0 \times 1 + 2 \times 0) \\
|
||||
(-1 \times 3 + 3 \times 2 + 1 \times 1) & (-1 \times 1 + 3 \times 1 + 1 \times 0) \\
|
||||
\end{bmatrix}
|
||||
=
|
||||
\begin{bmatrix}
|
||||
5 & 1 \\
|
||||
4 & 2 \\
|
||||
\end{bmatrix}
|
||||
$$
|
||||
矩阵的乘法满足结合律和对矩阵加法的分配律:
|
||||
|
||||
结合律: $(\boldsymbol{AB})\boldsymbol{C} = \boldsymbol{A}(\boldsymbol{BC})$。
|
||||
|
||||
左分配律:$(\boldsymbol{A} + \boldsymbol{B})\boldsymbol{C} = \boldsymbol{AC} + \boldsymbol{BC}$。
|
||||
|
||||
右分配律:$\boldsymbol{C}(\boldsymbol{A} + \boldsymbol{B}) = \boldsymbol{CA} + \boldsymbol{CB}$。
|
||||
|
||||
**矩阵乘法不满足交换律**。一般情况下,矩阵$\boldsymbol{A}$和$\boldsymbol{B}$的乘积$\boldsymbol{AB}$存在,但$\boldsymbol{BA}$不一定存在,即便$\boldsymbol{BA}$存在,大多数时候$\boldsymbol{AB} \neq \boldsymbol{BA}$。
|
||||
|
||||
矩阵乘法的一个基本应用是在线性方程组上。线性方程组是方程组的一种,它符合以下的形式:
|
||||
$$
|
||||
\begin{cases}
|
||||
a_{1,1}x_{1} + a_{1,2}x_{2} + \cdots + a_{1,n}x_{n}= b_{1} \\
|
||||
a_{2,1}x_{1} + a_{2,2}x_{2} + \cdots + a_{2,n}x_{n}= b_{2} \\
|
||||
\vdots \quad \quad \quad \vdots \\
|
||||
a_{m,1}x_{1} + a_{m,2}x_{2} + \cdots + a_{m,n}x_{n}= b_{m}
|
||||
\end{cases}
|
||||
$$
|
||||
运用矩阵的方式,可以将线性方程组写成一个向量方程:
|
||||
$$
|
||||
\boldsymbol{Ax} = \boldsymbol{b}
|
||||
$$
|
||||
其中,$\boldsymbol{A}$是由方程组里未知数的系数排成的$m \times n$矩阵,$\boldsymbol{x}$是含有$n$个元素的行向量,$\boldsymbol{b}$是含有$m$个元素的行向量。
|
||||
|
||||
矩阵是线性变换(保持向量加法和标量乘法的函数)的便利表达法。矩阵乘法的本质在联系到线性变换的时候最能体现,因为矩阵乘法和线性变换的合成有以下的联系,即每个$m \times n$的矩阵$\boldsymbol{A}$都代表了一个从$\boldsymbol{R}^{n}$射到$\boldsymbol{R}^{m}$的线性变换。如果无法理解上面这些内容,推荐大家看看B站上名为[《线性代数的本质》](https://www.bilibili.com/video/BV1ib411t7YR/)的视频,相信这套视频会让你对线性代数有一个更好的认知。
|
||||
|
||||
下图是一个来自于维基百科的例子,图中展示了一些典型的二维实平面上的线性变换对平面向量(图形)造成的效果以及它们对应的二维矩阵,其中每个线性变换将蓝色图形映射成绿色图形;平面的原点$(0, 0)$用黑点表示。
|
||||
|
||||
<img src="res/linear_transformation.png" style="zoom:45%;">
|
||||
|
||||
#### 矩阵对象
|
||||
|
||||
NumPy 中提供了专门用于线性代数(*linear algebra*)的模块和表示矩阵的类型`matrix`,当然我们通过二维数组也可以表示一个矩阵,官方并不推荐使用`matrix`类而是建议使用二维数组,而且有可能在将来的版本中会移除`matrix`类。无论如何,利用这些已经封装好的类和函数,我们可以轻松愉快的实现很多对矩阵的操作。
|
||||
|
||||
我们可以通过下面的代码来创建矩阵(`matrix`)对象。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
m1 = np.matrix('1 2 3; 4 5 6')
|
||||
m1
|
||||
```
|
||||
|
||||
> **说明**:`matrix`构造器可以传入类数组对象也可以传入字符串来构造矩阵对象。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
matrix([[1, 2, 3],
|
||||
[4, 5, 6]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
m2 = np.asmatrix(np.array([[1, 1], [2, 2], [3, 3]]))
|
||||
m2
|
||||
```
|
||||
|
||||
> **说明**:`asmatrix`函数也可以用`mat`函数代替,这两个函数其实是同一个函数。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
matrix([[1, 1],
|
||||
[2, 2],
|
||||
[3, 3]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
m1 * m2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
matrix([[14, 14],
|
||||
[32, 32]])
|
||||
```
|
||||
|
||||
> **说明**:注意`matrix`对象和`ndarray`对象乘法运算的差别,`matrix`对象的`*`运算是矩阵乘法运算。如果两个二维数组要做矩阵乘法运算,应该使用`@`运算符或`matmul`函数,而不是`*`运算符。
|
||||
|
||||
矩阵对象的属性如下表所示。
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ------- | ----------------------------------------- |
|
||||
| `A` | 获取矩阵对象对应的`ndarray`对象 |
|
||||
| `A1` | 获取矩阵对象对应的扁平化后的`ndarray`对象 |
|
||||
| `I` | 可逆矩阵的逆矩阵 |
|
||||
| `T` | 矩阵的转置 |
|
||||
| `H` | 矩阵的共轭转置 |
|
||||
| `shape` | 矩阵的形状 |
|
||||
| `size` | 矩阵元素的个数 |
|
||||
|
||||
矩阵对象的方法跟之前讲过的`ndarray`数组对象的方法基本差不多,此处不再进行赘述。
|
||||
|
||||
#### 线性代数模块
|
||||
|
||||
NumPy 的`linalg`模块中有一组标准的矩阵分解运算以及诸如求逆和行列式之类的函数,它们跟 MATLAB 和 R 等语言所使用的是相同的行业标准线性代数库,下面的表格列出了`numpy`以及`linalg`模块中一些常用的线性代数相关函数。
|
||||
|
||||
| 函数 | 说明 |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| `diag` | 以一维数组的形式返回方阵的对角线元素或将一维数组转换为方阵(非对角元素元素为0) |
|
||||
| `matmul` | 矩阵乘法运算 |
|
||||
| `trace` | 计算对角线元素的和 |
|
||||
| `norm` | 求矩阵或向量的范数 |
|
||||
| `det` | 计算行列式的值 |
|
||||
| `matrix_rank` | 计算矩阵的秩 |
|
||||
| `eig` | 计算矩阵的特征值(*eigenvalue*)和特征向量(*eigenvector*) |
|
||||
| `inv` | 计算非奇异矩阵($n$阶方阵)的逆矩阵 |
|
||||
| `pinv` | 计算矩阵的摩尔-彭若斯(*Moore-Penrose*)广义逆 |
|
||||
| `qr` | QR分解(把矩阵分解成一个正交矩阵与一个上三角矩阵的积) |
|
||||
| `svd` | 计算奇异值分解(*singular value decomposition*) |
|
||||
| `solve` | 解线性方程组$\boldsymbol{Ax}=\boldsymbol{b}$,其中$\boldsymbol{A}$是一个方阵 |
|
||||
| `lstsq` | 计算$\boldsymbol{Ax}=\boldsymbol{b}$的最小二乘解 |
|
||||
|
||||
下面我们简单尝试一下上面的函数,先试一试求逆矩阵。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
m3 = np.array([[1., 2.], [3., 4.]])
|
||||
m4 = np.linalg.inv(m3)
|
||||
m4
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[-2. , 1. ],
|
||||
[ 1.5, -0.5]])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
np.around(m3 @ m4)
|
||||
```
|
||||
|
||||
> **说明**:`around`函数对数组元素进行四舍五入操作,默认小数点后面的位数为0。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1., 0.],
|
||||
[0., 1.]])
|
||||
```
|
||||
|
||||
> **说明**:矩阵和它的逆矩阵做矩阵乘法会得到单位矩阵。
|
||||
|
||||
计算行列式的值。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
m5 = np.array([[1, 3, 5], [2, 4, 6], [4, 7, 9]])
|
||||
np.linalg.det(m5)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
2
|
||||
```
|
||||
|
||||
计算矩阵的秩。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
np.linalg.matrix_rank(m5)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
3
|
||||
```
|
||||
|
||||
求解线性方程组。
|
||||
$$
|
||||
\begin{cases}
|
||||
x_1 + 2x_2 + x_3 = 8 \\
|
||||
3x_1 + 7x_2 + 2x_3 = 23 \\
|
||||
2x_1 + 2x_2 + x_3 = 9
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
对于上面的线性方程组,我们可以用矩阵的形式来表示它,如下所示。
|
||||
$$
|
||||
\boldsymbol{A} = \begin{bmatrix}
|
||||
1 & 2 & 1\\
|
||||
3 & 7 & 2\\
|
||||
2 & 2 & 1
|
||||
\end{bmatrix}, \quad
|
||||
\boldsymbol{x} = \begin{bmatrix}
|
||||
x_1 \\
|
||||
x_2\\
|
||||
x_3
|
||||
\end{bmatrix}, \quad
|
||||
\boldsymbol{b} = \begin{bmatrix}
|
||||
8 \\
|
||||
23\\
|
||||
9
|
||||
\end{bmatrix}
|
||||
$$
|
||||
|
||||
$$
|
||||
\boldsymbol{Ax} = \boldsymbol{b}
|
||||
$$
|
||||
|
||||
线性方程组有唯一解的条件:系数矩阵$\boldsymbol{A}$的秩等于增广矩阵$\boldsymbol{Ab}$的秩,而且跟未知数的个数相同。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
A = np.array([[1, 2, 1], [3, 7, 2], [2, 2, 1]])
|
||||
b = np.array([8, 23, 9]).reshape(-1, 1)
|
||||
print(np.linalg.matrix_rank(A))
|
||||
print(np.linalg.matrix_rank(np.hstack((A, b))))
|
||||
```
|
||||
|
||||
> **说明**:使用数组对象的`reshape`方法调形时,如果其中一个参数为-1,那么该维度有多少个元素是通过数组元素个数(`size`属性)和其他维度的元素个数自动计算出来的。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
3
|
||||
3
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
np.linalg.solve(A, b)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1.],
|
||||
[2.],
|
||||
[3.]])
|
||||
```
|
||||
|
||||
> **说明**:上面的结果表示,线性方程组的解为:$x_1 = 1, x_2 = 2, x_3 = 3$。
|
||||
|
||||
下面是另一种求解线性方程组的方法,大家可以停下来思考下为什么。
|
||||
$$
|
||||
\boldsymbol{x} = \boldsymbol{A}^{-1} \cdot \boldsymbol{b}
|
||||
$$
|
||||
代码:
|
||||
|
||||
```Python
|
||||
np.linalg.inv(A) @ b
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1.],
|
||||
[2.],
|
||||
[3.]])
|
||||
```
|
||||
|
||||
### 多项式
|
||||
|
||||
除了数组,NumPy 中还封装了用于**多项式**(*polynomial*)运算的数据类型。多项式是变量的整数次幂与系数的乘积之和,形如:
|
||||
$$
|
||||
f(x)=a_nx^n + a_{n-1}x^{n-1} + \cdots + a_1x^{1} + a_0x^{0}
|
||||
$$
|
||||
在 NumPy 1.4版本之前,我们可以用`poly1d`类型来表示多项式,目前它仍然可用,但是官方提供了新的模块`numpy.polynomial`,它除了支持基本的幂级数多项式外,还可以支持切比雪夫多项式、拉盖尔多项式等。
|
||||
|
||||
#### 创建多项式对象
|
||||
|
||||
创建`poly1d`对象,例如:$\small{f(x)=3x^{2}+2x+1}$。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
p1 = np.poly1d([3, 2, 1])
|
||||
p2 = np.poly1d([1, 2, 3])
|
||||
print(p1)
|
||||
print(p2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
2
|
||||
3 x + 2 x + 1
|
||||
2
|
||||
1 x + 2 x + 3
|
||||
```
|
||||
|
||||
#### 多项式的操作
|
||||
|
||||
**获取多项式的系数**
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(p1.coefficients)
|
||||
print(p2.coeffs)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[3 2 1]
|
||||
[1 2 3]
|
||||
```
|
||||
|
||||
**两个多项式的四则运算**
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(p1 + p2)
|
||||
print(p1 * p2)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
2
|
||||
4 x + 4 x + 4
|
||||
4 3 2
|
||||
3 x + 8 x + 14 x + 8 x + 3
|
||||
```
|
||||
|
||||
**带入$\small{x}$求多项式的值**
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(p1(3))
|
||||
print(p2(3))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
34
|
||||
18
|
||||
```
|
||||
|
||||
**多项式求导和不定积分**
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(p1.deriv())
|
||||
print(p1.integ())
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
|
||||
6 x + 2
|
||||
3 2
|
||||
1 x + 1 x + 1 x
|
||||
```
|
||||
|
||||
**求多项式的根**
|
||||
|
||||
例如有多项式$\small{f(x)=x^2+3x+2}$,多项式的根即一元二次方程$\small{x^2+3x+2=0}$的解。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
p3 = np.poly1d([1, 3, 2])
|
||||
print(p3.roots)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[-2. -1.]
|
||||
```
|
||||
|
||||
如果使用`numpy.polynomial`模块的`Polynomial`类来表示多项式对象,那么对应的操作如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
from numpy.polynomial import Polynomial
|
||||
|
||||
p3 = Polynomial((2, 3, 1))
|
||||
print(p3) # 输出多项式
|
||||
print(p3(3)) # 令x=3,计算多项式的值
|
||||
print(p3.roots()) # 计算多项式的根
|
||||
print(p3.degree()) # 获得多项式的次数
|
||||
print(p3.deriv()) # 求导
|
||||
print(p3.integ()) # 求不定积分
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
2.0 + 3.0·x + 1.0·x²
|
||||
20.0
|
||||
[-2. -1.]
|
||||
2
|
||||
3.0 + 2.0·x
|
||||
0.0 + 2.0·x + 1.5·x² + 0.33333333·x³
|
||||
```
|
||||
|
||||
#### 最小二乘解
|
||||
|
||||
`Polynomial`类还有一个名为`fit`的类方法,它可以给多项式求最小二乘解。所谓最小二乘解(least-squares solution),是用最小二乘法通过最小化误差的平方和来寻找数据的最佳匹配函数的系数。假设多项式为$\small{f(x)=ax+b}$,最小二乘解就是让下面的残差平方和$\small{RSS}$达到最小的$\small{a}$和$\small{b}$。
|
||||
$$
|
||||
RSS = \sum_{i=0}^{k}(f(x_i) - y_i)^{2}
|
||||
$$
|
||||
例如,我们想利用收集到的月收入和网购支出的历史数据来建立一个预测模型,以达到通过某人的月收入预测他网购支出金额的目标,下面是我们收集到的收入和网购支出的数据,保存在两个数组中。
|
||||
|
||||
```python
|
||||
x = np.array([
|
||||
25000, 15850, 15500, 20500, 22000, 20010, 26050, 12500, 18500, 27300,
|
||||
15000, 8300, 23320, 5250, 5800, 9100, 4800, 16000, 28500, 32000,
|
||||
31300, 10800, 6750, 6020, 13300, 30020, 3200, 17300, 8835, 3500
|
||||
])
|
||||
y = np.array([
|
||||
2599, 1400, 1120, 2560, 1900, 1200, 2320, 800, 1650, 2200,
|
||||
980, 580, 1885, 600, 400, 800, 420, 1380, 1980, 3999,
|
||||
3800, 725, 520, 420, 1200, 4020, 350, 1500, 560, 500
|
||||
])
|
||||
```
|
||||
|
||||
我们可以先绘制散点图来了解两组数据是否具有正相关或负相关关系。正相关意味着数组`x`中较大的值对应到数组`y`中也是较大的值,而负相关则意味着数组`x`中较大的值对应到数组`y`中较小的值。
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.figure(dpi=120)
|
||||
plt.scatter(x, y, color='blue')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/in_out_scatter_plot.png" style="zoom:50%;">
|
||||
|
||||
如果需要定量的研究两组数据的相关性,我们可以计算协方差或相关系数,对应的 NumPy 函数分别是`cov`和`corrcoef`。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
np.corrcoef(x, y)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([[1. , 0.92275889],
|
||||
[0.92275889, 1. ]])
|
||||
```
|
||||
|
||||
> **说明**:相关系数是一个`-1`到`1`之间的值,越靠近`1` 说明正相关性越强,越靠近`-1`说明负相关性越强,靠近`0`则说明两组数据没有明显的相关性。上面月收入和网购支出之间的相关系数是`0.92275889`,说明二者是强正相关关系。
|
||||
|
||||
通过上面的操作,我们确定了收入和网购支出之前存在强正相关关系,于是我们用这些数据来创建一个回归模型,找出一条能够很好的拟合这些数据点的直线。这里,我们就可以用到上面提到的`fit`方法,具体的代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
from numpy.polynomial import Polynomial
|
||||
|
||||
Polynomial.fit(x, y, deg=1).convert().coef
|
||||
```
|
||||
|
||||
> **说明**:`deg=1`说明回归模型最高次项就是1次项,回归模型形如$\small{y=ax+b}$;如果要生一个类似于$\small{y=ax^2+bx+c}$的模型,就需要设置`deg=2`,以此类推。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([-2.94883437e+02, 1.10333716e-01])
|
||||
```
|
||||
|
||||
根据上面输出的结果,我们的回归方程应该是$\small{y=0.110333716x-294.883437}$。我们将这个回归方程绘制到刚才的散点图上,红色的点是我们的预测值,蓝色的点是历史数据,也就是真实值。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.scatter(x, y, color='blue')
|
||||
plt.scatter(x, 0.110333716 * x - 294.883437, color='red')
|
||||
plt.plot(x, 0.110333716 * x - 294.883437, color='darkcyan')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/in_out_regression_result.png" style="zoom:50%;">
|
||||
|
||||
如果不使用`Polynomial`类型的`fit`方法,我们也可以通过 NumPy 提供的`polyfit`函数来完成同样的操作,有兴趣的读者可以自行研究。
|
|
@ -1,509 +0,0 @@
|
|||
## Pandas的应用-2
|
||||
|
||||
### DataFrame的应用
|
||||
|
||||
#### 创建DataFrame对象
|
||||
|
||||
##### 通过二维数组创建`DataFrame`对象
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
scores = np.random.randint(60, 101, (5, 3))
|
||||
courses = ['语文', '数学', '英语']
|
||||
ids = [1001, 1002, 1003, 1004, 1005]
|
||||
df1 = pd.DataFrame(data=scores, columns=courses, index=ids)
|
||||
df1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
语文 数学 英语
|
||||
1001 69 80 79
|
||||
1002 71 60 100
|
||||
1003 94 81 93
|
||||
1004 88 88 67
|
||||
1005 82 66 60
|
||||
```
|
||||
|
||||
##### 通过字典创建`DataFrame`对象
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
scores = {
|
||||
'语文': [62, 72, 93, 88, 93],
|
||||
'数学': [95, 65, 86, 66, 87],
|
||||
'英语': [66, 75, 82, 69, 82],
|
||||
}
|
||||
ids = [1001, 1002, 1003, 1004, 1005]
|
||||
df2 = pd.DataFrame(data=scores, index=ids)
|
||||
df2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
语文 数学 英语
|
||||
1001 69 80 79
|
||||
1002 71 60 100
|
||||
1003 94 81 93
|
||||
1004 88 88 67
|
||||
1005 82 66 60
|
||||
```
|
||||
|
||||
##### 读取 CSV 文件创建`DataFrame`对象
|
||||
|
||||
可以通过`pandas` 模块的`read_csv`函数来读取 CSV 文件,`read_csv`函数的参数非常多,下面接受几个比较重要的参数。
|
||||
|
||||
- `sep` / `delimiter`:分隔符,默认是`,`。
|
||||
- `header`:表头(列索引)的位置,默认值是`infer`,用第一行的内容作为表头(列索引)。
|
||||
- `index_col`:用作行索引(标签)的列。
|
||||
- `usecols`:需要加载的列,可以使用序号或者列名。
|
||||
- `true_values` / `false_values`:哪些值被视为布尔值`True` / `False`。
|
||||
- `skiprows`:通过行号、索引或函数指定需要跳过的行。
|
||||
- `skipfooter`:要跳过的末尾行数。
|
||||
- `nrows`:需要读取的行数。
|
||||
- `na_values`:哪些值被视为空值。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
df3 = pd.read_csv('2018年北京积分落户数据.csv', index_col='id')
|
||||
df3
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
name birthday company score
|
||||
id
|
||||
1 杨x 1972-12 北京利德xxxx 122.59
|
||||
2 纪x 1974-12 北京航天xxxx 121.25
|
||||
3 王x 1974-05 品牌联盟xxxx 118.96
|
||||
4 杨x 1975-07 中科专利xxxx 118.21
|
||||
5 张x 1974-11 北京阿里xxxx 117.79
|
||||
... ... ... ... ...
|
||||
6015 孙x 1978-08 华为海洋xxxx 90.75
|
||||
6016 刘x 1976-11 福斯流体xxxx 90.75
|
||||
6017 周x 1977-10 赢创德固xxxx 90.75
|
||||
6018 赵x 1979-07 澳科利耳xxxx 90.75
|
||||
6019 贺x 1981-06 北京宝洁xxxx 90.75
|
||||
6019 rows × 4 columns
|
||||
```
|
||||
|
||||
> **说明**:如果需要上面例子中的 CSV 文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
|
||||
##### 读取Excel文件创建`DataFrame`对象
|
||||
|
||||
可以通过`pandas` 模块的`read_excel`函数来读取 Exce l文件,该函数与上面的`read_csv`非常相近,多了一个`sheet_name`参数来指定数据表的名称,但是不同于 CSV 文件,没有`sep`或`delimiter`这样的参数。下面的代码中,`read_excel`函数的`skiprows`参数是一个 Lambda 函数,通过该 Lambda 函数指定只读取 Excel 文件的表头和其中10%的数据,跳过其他的数据。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
import random
|
||||
|
||||
df4 = pd.read_excel(
|
||||
io='小宝剑大药房2018年销售数据.xlsx',
|
||||
usecols=['购药时间', '社保卡号', '商品名称', '销售数量', '应收金额', '实收金额'],
|
||||
skiprows=lambda x: x > 0 and random.random() > 0.1
|
||||
)
|
||||
df4
|
||||
```
|
||||
|
||||
> **说明**:如果需要上面例子中的 Excel 文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
购药时间 社保卡号 商品名称 销售数量 应收金额 实收金额
|
||||
0 2018-03-23 星期三 10012157328 强力xx片 1 13.8 13.80
|
||||
1 2018-07-12 星期二 108207828 强力xx片 1 13.8 13.80
|
||||
2 2018-01-17 星期日 13358228 清热xx液 1 28.0 28.00
|
||||
3 2018-07-11 星期一 10031402228 三九xx灵 5 149.0 130.00
|
||||
4 2018-01-20 星期三 10013340328 三九xx灵 3 84.0 73.92
|
||||
... ... ... ... ... ... ...
|
||||
618 2018-03-05 星期六 10066059228 开博xx通 2 56.0 49.28
|
||||
619 2018-03-22 星期二 10035514928 开博xx通 1 28.0 25.00
|
||||
620 2018-04-15 星期五 1006668328 开博xx通 2 56.0 50.00
|
||||
621 2018-04-24 星期日 10073294128 高特xx灵 1 5.6 5.60
|
||||
622 2018-04-24 星期日 10073294128 高特xx灵 10 56.0 56.0
|
||||
623 rows × 6 columns
|
||||
```
|
||||
|
||||
##### 通过SQL从数据库读取数据创建`DataFrame`对象
|
||||
|
||||
`pandas`模块的`read_sql`函数可以通过 SQL 语句从数据库中读取数据创建`DataFrame`对象,该函数的第二个参数代表了需要连接的数据库。对于 MySQL 数据库,我们可以通过`pymysql`或`mysqlclient`来创建数据库连接,得到一个`Connection` 对象,而这个对象就是`read_sql`函数需要的第二个参数,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
import pymysql
|
||||
|
||||
# 创建一个MySQL数据库的连接对象
|
||||
conn = pymysql.connect(
|
||||
host='47.104.31.138', port=3306,
|
||||
user='guest', password='Guest.618',
|
||||
database='hrs', charset='utf8mb4'
|
||||
)
|
||||
# 通过SQL从数据库读取数据创建DataFrame
|
||||
df5 = pd.read_sql('select * from tb_emp', conn, index_col='eno')
|
||||
df5
|
||||
```
|
||||
|
||||
> **提示**:执行上面的代码需要先安装`pymysql`库,如果尚未安装,可以先在 Notebook 的单元格中先执行`!pip install pymysql`,然后再运行上面的代码。上面的代码连接的是我部署在阿里云上的 MySQL 数据库,公网 IP 地址:`47.104.31.138`,用户名:`guest`,密码:`Guest.618`,数据库:`hrs`,表名:`tb_emp`,字符集:`utf8mb4`,大家可以使用这个数据库,但是不要进行恶意的访问。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344.0 1800 200.0 30
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
3577 杨过 会计 5566.0 2200 NaN 10
|
||||
3588 朱九真 会计 5566.0 2500 NaN 10
|
||||
4466 苗人凤 销售员 3344.0 2500 NaN 30
|
||||
5234 郭靖 出纳 5566.0 2000 NaN 10
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
#### 基本属性和方法
|
||||
|
||||
在开始讲解`DataFrame`的属性和方法前,我们先从之前提到的`hrs`数据库中读取三张表的数据,创建出三个`DataFrame`对象,代码如下所示。
|
||||
|
||||
```Python
|
||||
import pymysql
|
||||
|
||||
conn = pymysql.connect(
|
||||
host='47.104.31.138', port=3306,
|
||||
user='guest', password='Guest.618',
|
||||
database='hrs', charset='utf8mb4'
|
||||
)
|
||||
dept_df = pd.read_sql('select * from tb_dept', conn, index_col='dno')
|
||||
emp_df = pd.read_sql('select * from tb_emp', conn, index_col='eno')
|
||||
emp2_df = pd.read_sql('select * from tb_emp2', conn, index_col='eno')
|
||||
```
|
||||
|
||||
得到的三个`DataFrame`对象如下所示。
|
||||
|
||||
部门表(`dept_df`),其中`dno`是部门的编号,`dname`和`dloc`分别是部门的名称和所在地。
|
||||
|
||||
```
|
||||
dname dloc
|
||||
dno
|
||||
10 会计部 北京
|
||||
20 研发部 成都
|
||||
30 销售部 重庆
|
||||
40 运维部 天津
|
||||
```
|
||||
|
||||
员工表(`emp_df`),其中`eno`是员工编号,`ename`、`job`、`mgr`、`sal`、`comm`和`dno`分别代表员工的姓名、职位、主管编号、月薪、补贴和部门编号。
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344.0 1800 200.0 30
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
3577 杨过 会计 5566.0 2200 NaN 10
|
||||
3588 朱九真 会计 5566.0 2500 NaN 10
|
||||
4466 苗人凤 销售员 3344.0 2500 NaN 30
|
||||
5234 郭靖 出纳 5566.0 2000 NaN 10
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
> **说明**:在数据库中`mgr`和`comm`两个列的数据类型是`int`,但是因为有缺失值(空值),读取到`DataFrame`之后,列的数据类型变成了`float`,因为我们通常会用`float`类型的`NaN`来表示空值。
|
||||
|
||||
员工表(`emp2_df`),跟上面的员工表结构相同,但是保存了不同的员工数据。
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
9800 骆昊 架构师 7800 30000 5000 20
|
||||
9900 王小刀 程序员 9800 10000 1200 20
|
||||
9700 王大锤 程序员 9800 8000 600 20
|
||||
```
|
||||
|
||||
`DataFrame`对象的属性如下表所示。
|
||||
|
||||
| 属性名 | 说明 |
|
||||
| -------------- | ----------------------------------- |
|
||||
| `at` / `iat` | 通过标签获取`DataFrame`中的单个值。 |
|
||||
| `columns` | `DataFrame`对象列的索引 |
|
||||
| `dtypes` | `DataFrame`对象每一列的数据类型 |
|
||||
| `empty` | `DataFrame`对象是否为空 |
|
||||
| `loc` / `iloc` | 通过标签获取`DataFrame`中的一组值。 |
|
||||
| `ndim` | `DataFrame`对象的维度 |
|
||||
| `shape` | `DataFrame`对象的形状(行数和列数) |
|
||||
| `size` | `DataFrame`对象中元素的个数 |
|
||||
| `values` | `DataFrame`对象的数据对应的二维数组 |
|
||||
|
||||
关于`DataFrame`的方法,首先需要了解的是`info()`方法,它可以帮助我们了解`DataFrame`的相关信息,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
emp_df.info()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
<class 'pandas.core.frame.DataFrame'>
|
||||
Int64Index: 14 entries, 1359 to 7800
|
||||
Data columns (total 6 columns):
|
||||
# Column Non-Null Count Dtype
|
||||
--- ------ -------------- -----
|
||||
0 ename 14 non-null object
|
||||
1 job 14 non-null object
|
||||
2 mgr 13 non-null float64
|
||||
3 sal 14 non-null int64
|
||||
4 comm 6 non-null float64
|
||||
5 dno 14 non-null int64
|
||||
dtypes: float64(2), int64(2), object(2)
|
||||
memory usage: 1.3+ KB
|
||||
```
|
||||
|
||||
如果需要查看`DataFrame`的头部或尾部的数据,可以使用`head()`或`tail()`方法,这两个方法的默认参数是`5`,表示获取`DataFrame`最前面5行或最后面5行的数据,如下所示。
|
||||
|
||||
```Python
|
||||
emp_df.head()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344 1800 200 30
|
||||
2056 乔峰 分析师 7800 5000 1500 20
|
||||
3088 李莫愁 设计师 2056 3500 800 20
|
||||
3211 张无忌 程序员 2056 3200 NaN 20
|
||||
3233 丘处机 程序员 2056 3400 NaN 20
|
||||
```
|
||||
|
||||
#### 获取数据
|
||||
|
||||
##### 索引和切片
|
||||
|
||||
如果要获取`DataFrame`的某一列,例如取出上面`emp_df`的`ename`列,可以使用下面的两种方式。
|
||||
|
||||
```Python
|
||||
emp_df.ename
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```Python
|
||||
emp_df['ename']
|
||||
```
|
||||
|
||||
执行上面的代码可以发现,我们获得的是一个`Series`对象。事实上,`DataFrame`对象就是将多个`Series`对象组合到一起的结果。
|
||||
|
||||
如果要获取`DataFrame`的某一行,可以使用整数索引或我们设置的索引,例如取出员工编号为`2056`的员工数据,代码如下所示。
|
||||
|
||||
```Python
|
||||
emp_df.iloc[1]
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```Python
|
||||
emp_df.loc[2056]
|
||||
```
|
||||
|
||||
通过执行上面的代码我们发现,单独取`DataFrame` 的某一行或某一列得到的都是`Series`对象。我们当然也可以通过花式索引来获取多个行或多个列的数据,花式索引的结果仍然是一个`DataFrame`对象。
|
||||
|
||||
获取多个列:
|
||||
|
||||
```Python
|
||||
emp_df[['ename', 'job']]
|
||||
```
|
||||
|
||||
获取多个行:
|
||||
|
||||
```Python
|
||||
emp_df.loc[[2056, 7800, 3344]]
|
||||
```
|
||||
|
||||
如果要获取或修改`DataFrame` 对象某个单元格的数据,需要同时指定行和列的索引,例如要获取员工编号为`2056`的员工的职位信息,代码如下所示。
|
||||
|
||||
```Python
|
||||
emp_df['job'][2056]
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```Python
|
||||
emp_df.loc[2056]['job']
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```Python
|
||||
emp_df.loc[2056, 'job']
|
||||
```
|
||||
|
||||
我们推荐大家使用第三种做法,因为它只做了一次索引运算。如果要将该员工的职位修改为“架构师”,可以使用下面的代码。
|
||||
|
||||
```Python
|
||||
emp_df.loc[2056, 'job'] = '架构师'
|
||||
```
|
||||
|
||||
当然,我们也可以通过切片操作来获取多行多列,相信大家一定已经想到了这一点。
|
||||
|
||||
```Python
|
||||
emp_df.loc[2056:3344]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
```
|
||||
|
||||
##### 数据筛选
|
||||
|
||||
上面我们提到了花式索引,相信大家已经联想到了布尔索引。跟`ndarray`和`Series`一样,我们可以通过布尔索引对`DataFrame`对象进行数据筛选,例如我们要从`emp_df`中筛选出月薪超过`3500`的员工,代码如下所示。
|
||||
|
||||
```Python
|
||||
emp_df[emp_df.sal > 3500]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
当然,我们也可以组合多个条件来进行数据筛选,例如从`emp_df`中筛选出月薪超过`3500`且部门编号为`20`的员工,代码如下所示。
|
||||
|
||||
```Python
|
||||
emp_df[(emp_df.sal > 3500) & (emp_df.dno == 20)]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
除了使用布尔索引,`DataFrame`对象的`query`方法也可以实现数据筛选,`query`方法的参数是一个字符串,它代表了筛选数据使用的表达式,而且更符合 Python 程序员的使用习惯。下面我们使用`query`方法将上面的效果重新实现一遍,代码如下所示。
|
||||
|
||||
```Python
|
||||
emp_df.query('sal > 3500 and dno == 20')
|
||||
```
|
||||
|
||||
#### 重塑数据
|
||||
|
||||
有的时候,我们做数据分析需要的原始数据可能并不是来自一个地方,就像上面的例子中,我们从关系型数据库中读取了三张表,得到了三个`DataFrame`对象,但实际工作可能需要我们把他们的数据整合到一起。例如:`emp_df`和`emp2_df`其实都是员工的数据,而且数据结构完全一致,我们可以使用`pandas`提供的`concat`函数实现两个或多个`DataFrame`的数据拼接,代码如下所示。
|
||||
|
||||
```Python
|
||||
all_emp_df = pd.concat([emp_df, emp2_df])
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344.0 1800 200.0 30
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
3577 杨过 会计 5566.0 2200 NaN 10
|
||||
3588 朱九真 会计 5566.0 2500 NaN 10
|
||||
4466 苗人凤 销售员 3344.0 2500 NaN 30
|
||||
5234 郭靖 出纳 5566.0 2000 NaN 10
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
9800 骆昊 架构师 7800.0 30000 5000.0 20
|
||||
9900 王小刀 程序员 9800.0 10000 1200.0 20
|
||||
9700 王大锤 程序员 9800.0 8000 600.0 20
|
||||
```
|
||||
|
||||
上面的代码将两个代表员工数据的`DataFrame`拼接到了一起,接下来我们使用`merge`函数将员工表和部门表的数据合并到一张表中,代码如下所示。
|
||||
|
||||
先使用`reset_index`方法重新设置`all_emp_df`的索引,这样`eno` 不再是索引而是一个普通列,`reset_index`方法的`inplace`参数设置为`True`表示,重置索引的操作直接在`all_emp_df`上执行,而不是返回修改后的新对象。
|
||||
|
||||
```Python
|
||||
all_emp_df.reset_index(inplace=True)
|
||||
```
|
||||
|
||||
通过`merge`函数合并数据,当然,也可以调用`DataFrame`对象的`merge`方法来达到同样的效果。
|
||||
|
||||
```Python
|
||||
pd.merge(dept_df, all_emp_df, how='inner', on='dno')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
dno dname dloc eno ename job mgr sal comm
|
||||
0 10 会计部 北京 3577 杨过 会计 5566.0 2200 NaN
|
||||
1 10 会计部 北京 3588 朱九真 会计 5566.0 2500 NaN
|
||||
2 10 会计部 北京 5234 郭靖 出纳 5566.0 2000 NaN
|
||||
3 10 会计部 北京 5566 宋远桥 会计师 7800.0 4000 1000.0
|
||||
4 20 研发部 成都 2056 乔峰 架构师 7800.0 5000 1500.0
|
||||
5 20 研发部 成都 3088 李莫愁 设计师 2056.0 3500 800.0
|
||||
6 20 研发部 成都 3211 张无忌 程序员 2056.0 3200 NaN
|
||||
7 20 研发部 成都 3233 丘处机 程序员 2056.0 3400 NaN
|
||||
8 20 研发部 成都 3244 欧阳锋 程序员 3088.0 3200 NaN
|
||||
9 20 研发部 成都 3251 张翠山 程序员 2056.0 4000 NaN
|
||||
10 20 研发部 成都 7800 张三丰 总裁 NaN 9000 1200.0
|
||||
11 20 研发部 成都 9800 骆昊 架构师 7800.0 30000 5000.0
|
||||
12 20 研发部 成都 9900 王小刀 程序员 9800.0 10000 1200.0
|
||||
13 20 研发部 成都 9700 王大锤 程序员 9800.0 8000 600.0
|
||||
14 30 销售部 重庆 1359 胡一刀 销售员 3344.0 1800 200.0
|
||||
15 30 销售部 重庆 3344 黄蓉 销售主管 7800.0 3000 800.0
|
||||
16 30 销售部 重庆 4466 苗人凤 销售员 3344.0 2500 NaN
|
||||
```
|
||||
|
||||
`merge`函数的一个参数代表合并的左表、第二个参数代表合并的右表,有SQL编程经验的同学对这两个词是不是感觉到非常亲切。正如大家猜想的那样,`DataFrame`对象的合并跟数据库中的表连接非常类似,所以上面代码中的`how`代表了合并两张表的方式,有`left`、`right`、`inner`、`outer`四个选项;而`on`则代表了基于哪个列实现表的合并,相当于 SQL 表连接中的连表条件,如果左右两表对应的列列名不同,可以用`left_on`和`right_on`参数取代`on`参数分别进行指定。
|
||||
|
||||
如果对上面的代码稍作修改,将`how`参数修改为`left`,大家可以思考一下代码执行的结果。
|
||||
|
||||
```Python
|
||||
pd.merge(dept_df, all_emp_df, how='left', on='dno')
|
||||
```
|
||||
|
||||
运行结果比之前的输出多出了如下所示的一行,这是因为`left`代表左外连接,也就意味着左表`dept_df`中的数据会被完整的查出来,但是在`all_emp_df`中又没有编号为`40` 部门的员工,所以对应的位置都被填入了空值。
|
||||
|
||||
```
|
||||
17 40 运维部 天津 NaN NaN NaN NaN NaN NaN
|
||||
```
|
|
@ -0,0 +1,799 @@
|
|||
## 深入浅出pandas-1
|
||||
|
||||
Pandas 是 Wes McKinney 在2008年开发的一个强大的**分析结构化数据**的工具集。Pandas 以 NumPy 为基础(实现数据存储和运算),提供了专门用于数据分析的类型、方法和函数,对数据分析和数据挖掘提供了很好的支持;同时 pandas 还可以跟数据可视化工具 matplotlib 很好的整合在一起,非常轻松愉快的实现数据可视化呈现。
|
||||
|
||||
Pandas 核心的数据类型是`Series`(数据系列)、`DataFrame`(数据窗/数据框),分别用于处理一维和二维的数据,除此之外,还有一个名为`Index`的类型及其子类型,它们为`Series`和`DataFrame`提供了索引功能。日常工作中`DataFrame`使用得最为广泛,因为二维的数据结构刚好可以对应有行有列的表格。`Series`和`DataFrame`都提供了大量的处理数据的方法,数据分析师以此为基础,可以实现对数据的筛选、合并、拼接、清洗、预处理、聚合、透视和可视化等各种操作。
|
||||
|
||||
### 创建Series对象
|
||||
|
||||
Pandas 库中的`Series`对象可以用来表示一维数据结构,但是多了索引和一些额外的功能。`Series`类型的内部结构包含了两个数组,其中一个用来保存数据,另一个用来保存数据的索引。我们可以通过列表或数组创建`Series`对象,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
ser1 = pd.Series(data=[120, 380, 250, 360], index=['一季度', '二季度', '三季度', '四季度'])
|
||||
ser1
|
||||
```
|
||||
|
||||
> **说明**:`Series`构造器中的`data`参数表示数据,`index`参数表示数据的索引,相当于数据对应的标签。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 120
|
||||
二季度 380
|
||||
三季度 250
|
||||
四季度 360
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
通过字典创建Series对象。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2 = pd.Series({'一季度': 320, '二季度': 180, '三季度': 300, '四季度': 405})
|
||||
ser2
|
||||
```
|
||||
|
||||
> **说明**:通过字典创建`Series`对象时,字典的键就是数据的标签(索引),键对应的值就是数据。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 320
|
||||
二季度 180
|
||||
三季度 300
|
||||
四季度 405
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
### Series对象的运算
|
||||
|
||||
#### 标量运算
|
||||
|
||||
我们尝试给刚才的`ser1`每个季度加上`10`,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
ser1 += 10
|
||||
ser1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 130
|
||||
二季度 390
|
||||
三季度 260
|
||||
四季度 370
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
#### 矢量运算
|
||||
|
||||
我们尝试把`ser1`和`ser2`对应季度的数据加起来,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
ser1 + ser2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 450
|
||||
二季度 570
|
||||
三季度 560
|
||||
四季度 775
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
#### 索引运算
|
||||
|
||||
##### 普通索引
|
||||
|
||||
跟数组一样,`Series`对象也可以进行索引和切片操作,不同的是`Series`对象因为内部维护了一个保存索引的数组,所以除了可以使用整数索引检索数据外,还可以通过自己设置的索引(标签)获取对应的数据。
|
||||
|
||||
使用整数索引。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser1[2]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
260
|
||||
```
|
||||
|
||||
使用自定义索引。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser1['三季度']
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
260
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser1['一季度'] = 380
|
||||
ser1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 380
|
||||
二季度 390
|
||||
三季度 260
|
||||
四季度 370
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 切片索引
|
||||
|
||||
`Series`对象的切片操作跟列表、数组类似,通过给出起始和结束索引,从原来的`Series`对象中取出或修改部分数据,这里也可以使用整数索引和自定义的索引,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2[1:3]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
二季度 180
|
||||
三季度 300
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2['二季度':'四季度']
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
二季度 180
|
||||
三季度 300
|
||||
四季度 405
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
>**提示**:在使用自定义索引进行切片时,结束索引对应的元素也是可以取到的。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2[1:3] = 400, 500
|
||||
ser2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 320
|
||||
二季度 400
|
||||
三季度 500
|
||||
四季度 405
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 花式索引
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2[['二季度', '四季度']]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
二季度 400
|
||||
四季度 405
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2[['二季度', '四季度']] = 600, 520
|
||||
ser2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
一季度 320
|
||||
二季度 600
|
||||
三季度 500
|
||||
四季度 520
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
##### 布尔索引
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2[ser2 >= 500]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
二季度 600
|
||||
三季度 500
|
||||
四季度 520
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
### Series对象的属性和方法
|
||||
|
||||
`Series`对象的属性和方法非常多,我们就捡着重要的跟大家讲吧。先看看下面的表格,它展示了`Series`对象常用的属性。
|
||||
|
||||
| 属性 | 说明 |
|
||||
| ------------------------- | --------------------------------------- |
|
||||
| `dtype` / `dtypes` | 返回`Series`对象的数据类型 |
|
||||
| `hasnans` | 判断`Series`对象中有没有空值 |
|
||||
| `at` / `iat` | 通过索引访问`Series`对象中的单个值 |
|
||||
| `loc` / `iloc` | 通过索引访问`Series`对象中的单个值或一组值 |
|
||||
| `index` | 返回`Series`对象的索引(`Index`对象) |
|
||||
| `is_monotonic` | 判断`Series`对象中的数据是否单调 |
|
||||
| `is_monotonic_increasing` | 判断`Series`对象中的数据是否单调递增 |
|
||||
| `is_monotonic_decreasing` | 判断`Series`对象中的数据是否单调递减 |
|
||||
| `is_unique` | 判断`Series`对象中的数据是否独一无二 |
|
||||
| `size` | 返回`Series`对象中元素的个数 |
|
||||
| `values` | 以`ndarray`的方式返回`Series`对象中的值(`ndarray`对象) |
|
||||
|
||||
我们可以通过下面的代码来了解`Series`对象的属性。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
print(ser2.dtype) # 数据类型
|
||||
print(ser2.hasnans) # 有没有空值
|
||||
print(ser2.index) # 索引
|
||||
print(ser2.values) # 值
|
||||
print(ser2.is_monotonic_increasing) # 是否单调递增
|
||||
print(ser2.is_unique) # 是否每个值都独一无二
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
int64
|
||||
False
|
||||
Index(['一季度', '二季度', '三季度', '四季度'], dtype='object')
|
||||
[320 600 500 520]
|
||||
False
|
||||
True
|
||||
```
|
||||
|
||||
`Series`对象的方法很多,下面我们通过一些代码片段为大家介绍常用的方法。
|
||||
|
||||
#### 统计相关
|
||||
|
||||
`Series`对象支持各种获取描述性统计信息的方法。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
print(ser2.count()) # 计数
|
||||
print(ser2.sum()) # 求和
|
||||
print(ser2.mean()) # 求平均
|
||||
print(ser2.median()) # 找中位数
|
||||
print(ser2.max()) # 找最大
|
||||
print(ser2.min()) # 找最小
|
||||
print(ser2.std()) # 求标准差
|
||||
print(ser2.var()) # 求方差
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
4
|
||||
1940
|
||||
485.0
|
||||
510.0
|
||||
600
|
||||
320
|
||||
118.18065267490557
|
||||
13966.666666666666
|
||||
```
|
||||
|
||||
`Series`对象还有一个名为`describe()`的方法,可以获得上述所有的描述性统计信息,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser2.describe()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
count 4.000000
|
||||
mean 485.000000
|
||||
std 118.180653
|
||||
min 320.000000
|
||||
25% 455.000000
|
||||
50% 510.000000
|
||||
75% 540.000000
|
||||
max 600.000000
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
> **提示**:因为`describe()`返回的也是一个`Series`对象,所以也可以用`ser2.describe()['mean']`来获取平均值,用`ser2.describe()[['max', 'min']]`来获取最大值和最小值。
|
||||
|
||||
如果`Series`对象有重复的值,我们可以使用`unique()`方法获得由独一无二的值构成的数组;可以使用`nunique()`方法统计不重复值的数量;如果想要统计每个值重复的次数,可以使用`value_counts()`方法,这个方法会返回一个`Series`对象,它的索引就是原来的`Series`对象中的值,而每个值出现的次数就是返回的`Series`对象中的数据,在默认情况下会按照出现次数做降序排列,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3 = pd.Series(data=['apple', 'banana', 'apple', 'pitaya', 'apple', 'pitaya', 'durian'])
|
||||
ser3.value_counts()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
apple 3
|
||||
pitaya 2
|
||||
durian 1
|
||||
banana 1
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3.nunique()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
4
|
||||
```
|
||||
|
||||
对于`ser3`,我们还可以用`mode()`方法来找出数据的众数,由于众数可能不唯一,所以`mode()`方法的返回值仍然是一个`Series`对象。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
ser3.mode()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 apple
|
||||
dtype: object
|
||||
```
|
||||
|
||||
#### 处理数据
|
||||
|
||||
`Series`对象的`isna()`和`isnull()`方法可以用于空值的判断,`notna()`和`notnull()`方法可以用于非空值的判断,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4 = pd.Series(data=[10, 20, np.nan, 30, np.nan])
|
||||
ser4.isna()
|
||||
```
|
||||
|
||||
> **说明**:`np.nan`是一个IEEE 754标准的浮点小数,专门用来表示“不是一个数”,在上面的代码中我们用它来代表空值;当然,也可以用 Python 中的`None`来表示空值,在 pandas 中`None`也会被处理为`np.nan`。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 False
|
||||
1 False
|
||||
2 True
|
||||
3 False
|
||||
4 True
|
||||
dtype: bool
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4.notna()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 True
|
||||
1 True
|
||||
2 False
|
||||
3 True
|
||||
4 False
|
||||
dtype: bool
|
||||
```
|
||||
|
||||
`Series`对象的`dropna()`和`fillna()`方法分别用来删除空值和填充空值,具体的用法如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4.dropna()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10.0
|
||||
1 20.0
|
||||
3 30.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4.fillna(value=40) # 将空值填充为40
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10.0
|
||||
1 20.0
|
||||
2 40.0
|
||||
3 30.0
|
||||
4 40.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser4.fillna(method='ffill') # 用空值前面的非空值填充
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10.0
|
||||
1 20.0
|
||||
2 20.0
|
||||
3 30.0
|
||||
4 30.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
需要提醒大家注意的是,`dropna()`和`fillna()`方法都有一个名为`inplace`的参数,它的默认值是`False`,表示删除空值或填充空值不会修改原来的`Series`对象,而是返回一个新的`Series`对象。如果将`inplace`参数的值修改为`True`,那么删除或填充空值会就地操作,直接修改原来的`Series`对象,此时方法的返回值是`None`。后面我们会接触到的很多方法,包括`DataFrame`对象的很多方法都会有这个参数,它们的意义跟这里是一样的。
|
||||
|
||||
`Series`对象的`mask()`和`where()`方法可以将满足或不满足条件的值进行替换,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser5 = pd.Series(range(5))
|
||||
ser5.where(ser5 > 0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 NaN
|
||||
1 1.0
|
||||
2 2.0
|
||||
3 3.0
|
||||
4 4.0
|
||||
dtype: float64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser5.where(ser5 > 1, 10)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 10
|
||||
1 10
|
||||
2 2
|
||||
3 3
|
||||
4 4
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser5.mask(ser5 > 1, 10)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 0
|
||||
1 1
|
||||
2 10
|
||||
3 10
|
||||
4 10
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
`Series`对象的`duplicated()`方法可以帮助我们找出重复的数据,而`drop_duplicates()`方法可以帮我们删除重复数据。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3.duplicated()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 False
|
||||
1 False
|
||||
2 True
|
||||
3 False
|
||||
4 True
|
||||
5 True
|
||||
6 False
|
||||
dtype: bool
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser3.drop_duplicates()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 apple
|
||||
1 banana
|
||||
3 pitaya
|
||||
6 durian
|
||||
dtype: object
|
||||
```
|
||||
|
||||
`Series`对象的`apply()`和`map()`方法非常重要,它们可以通过字典或者指定的函数来处理数据,把数据映射或转换成我们想要的样子。这两个方法在数据准备阶段非常重要,我们先来试一试这个名为`map`的方法。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser6 = pd.Series(['cat', 'dog', np.nan, 'rabbit'])
|
||||
ser6
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 cat
|
||||
1 dog
|
||||
2 NaN
|
||||
3 rabbit
|
||||
dtype: object
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser6.map({'cat': 'kitten', 'dog': 'puppy'})
|
||||
```
|
||||
|
||||
> **说明**:通过字典给出的映射规则对数据进行处理。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 kitten
|
||||
1 puppy
|
||||
2 NaN
|
||||
3 NaN
|
||||
dtype: object
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser6.map('I am a {}'.format, na_action='ignore')
|
||||
```
|
||||
|
||||
> **说明**:将指定字符串的`format`方法作用到数据系列的数据上,忽略掉所有的空值。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
0 I am a cat
|
||||
1 I am a dog
|
||||
2 NaN
|
||||
3 I am a rabbit
|
||||
dtype: object
|
||||
```
|
||||
|
||||
我们创建一个新的`Series`对象,
|
||||
|
||||
```Python
|
||||
ser7 = pd.Series([20, 21, 12], index=['London', 'New York', 'Helsinki'])
|
||||
ser7
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
London 20
|
||||
New York 21
|
||||
Helsinki 12
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser7.apply(np.square)
|
||||
```
|
||||
|
||||
> **说明**:将求平方的函数作用到数据系列的数据上,也可以将参数`np.square`替换为`lambda x: x ** 2`。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
London 400
|
||||
New York 441
|
||||
Helsinki 144
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser7.apply(lambda x, value: x - value, args=(5, ))
|
||||
```
|
||||
|
||||
> 注意:上面`apply`方法中的`lambda`函数有两个参数,第一个参数是数据系列中的数据,而第二个参数需要我们传入,所以我们给`apply`方法增加了`args`参数,用于给`lambda`函数的第二个参数传值。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
London 15
|
||||
New York 16
|
||||
Helsinki 7
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
#### 取头部值和排序
|
||||
|
||||
`Series`对象的`sort_index()`和`sort_values()`方法可以用于对索引和数据的排序,排序方法有一个名为`ascending`的布尔类型参数,该参数用于控制排序的结果是升序还是降序;而名为`kind`的参数则用来控制排序使用的算法,默认使用了`quicksort`,也可以选择`mergesort`或`heapsort`;如果存在空值,那么可以用`na_position`参数空值放在最前还是最后,默认是`last`,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser8 = pd.Series(
|
||||
data=[35, 96, 12, 57, 25, 89],
|
||||
index=['grape', 'banana', 'pitaya', 'apple', 'peach', 'orange']
|
||||
)
|
||||
ser8.sort_values() # 按值从小到大排序
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
pitaya 12
|
||||
peach 25
|
||||
grape 35
|
||||
apple 57
|
||||
orange 89
|
||||
banana 96
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser8.sort_index(ascending=False) # 按索引从大到小排序
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
pitaya 12
|
||||
peach 25
|
||||
orange 89
|
||||
grape 35
|
||||
banana 96
|
||||
apple 57
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
如果要从`Series`对象中找出元素中最大或最小的“Top-N”,我们不需要对所有的值进行排序的,可以使用`nlargest()`和`nsmallest()`方法来完成,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser8.nlargest(3) # 值最大的3个
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
banana 96
|
||||
orange 89
|
||||
apple 57
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser8.nsmallest(2) # 值最小的2个
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
pitaya 12
|
||||
peach 25
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
#### 绘制图表
|
||||
|
||||
`Series`对象有一个名为`plot`的方法可以用来生成图表,如果选择生成折线图、饼图、柱状图等,默认会使用`Series`对象的索引作为横坐标,使用`Series`对象的数据作为纵坐标。下面我们创建一个`Series`对象并基于它绘制柱状图,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
ser9 = pd.Series({'Q1': 400, 'Q2': 520, 'Q3': 180, 'Q4': 380})
|
||||
# 通过plot方法的kind指定图表类型为柱状图
|
||||
ser9.plot(kind='bar')
|
||||
# 定制纵轴的取值范围
|
||||
plt.ylim(0, 600)
|
||||
# 定制横轴刻度(旋转到0度)
|
||||
plt.xticks(rotation=0)
|
||||
# 为柱子增加数据标签
|
||||
for i in range(ser9.size):
|
||||
plt.text(i, ser9[i] + 5, ser9[i], ha='center')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/ser_bar_graph.png" style="zoom:35%;">
|
||||
|
||||
我们也可以将其绘制为饼图,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
# plot方法的kind参数指定了图表类型为饼图
|
||||
# autopct会自动计算并显示百分比
|
||||
# pctdistance用来控制百分比到圆心的距离
|
||||
ser9.plot(kind='pie', autopct='%.1f%%', pctdistance=0.65)
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/ser_pie_graph.png" style="zoom:35%;">
|
|
@ -0,0 +1,473 @@
|
|||
## 深入浅出pandas-2
|
||||
|
||||
如果使用 pandas 做数据分析,那么`DataFrame`一定是被使用得最多的类型,它可以用来保存和处理异质的二维数据。这里所谓的“异质”是指`DataFrame`中每个列的数据类型不需要相同,这也是它区别于 NumPy 二维数组的地方。`DataFrame`提供了极为丰富的属性和方法,帮助我们实现对数据的重塑、清洗、预处理、透视、呈现等一系列操作。
|
||||
|
||||
### 创建DataFrame对象
|
||||
|
||||
#### 通过二维数组创建DataFrame对象
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
scores = np.random.randint(60, 101, (5, 3))
|
||||
courses = ['语文', '数学', '英语']
|
||||
stu_ids = np.arange(1001, 1006)
|
||||
df1 = pd.DataFrame(data=scores, columns=courses, index=stu_ids)
|
||||
df1
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
语文 数学 英语
|
||||
1001 69 80 79
|
||||
1002 71 60 100
|
||||
1003 94 81 93
|
||||
1004 88 88 67
|
||||
1005 82 66 60
|
||||
```
|
||||
|
||||
#### 通过字典创建DataFrame对象
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
scores = {
|
||||
'语文': [62, 72, 93, 88, 93],
|
||||
'数学': [95, 65, 86, 66, 87],
|
||||
'英语': [66, 75, 82, 69, 82],
|
||||
}
|
||||
stu_ids = np.arange(1001, 1006)
|
||||
df2 = pd.DataFrame(data=scores, index=stu_ids)
|
||||
df2
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
语文 数学 英语
|
||||
1001 62 95 66
|
||||
1002 72 65 75
|
||||
1003 93 86 82
|
||||
1004 88 66 69
|
||||
1005 93 87 82
|
||||
```
|
||||
|
||||
#### 读取CSV文件创建DataFrame对象
|
||||
|
||||
可以通过`pandas` 模块的`read_csv`函数来读取 CSV 文件,`read_csv`函数的参数非常多,下面介绍几个比较重要的参数。
|
||||
|
||||
- `sep` / `delimiter`:分隔符,默认是`,`。
|
||||
- `header`:表头(列索引)的位置,默认值是`infer`,用第一行的内容作为表头(列索引)。
|
||||
- `index_col`:用作行索引(标签)的列。
|
||||
- `usecols`:需要加载的列,可以使用序号或者列名。
|
||||
- `true_values` / `false_values`:哪些值被视为布尔值`True` / `False`。
|
||||
- `skiprows`:通过行号、索引或函数指定需要跳过的行。
|
||||
- `skipfooter`:要跳过的末尾行数。
|
||||
- `nrows`:需要读取的行数。
|
||||
- `na_values`:哪些值被视为空值。
|
||||
- `iterator`:设置为`True`,函数返回迭代器对象。
|
||||
- `chunksize`:配合上面的参数,设置每次迭代获取的数据体量。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
df3 = pd.read_csv('data/2018年北京积分落户数据.csv', index_col='id')
|
||||
df3
|
||||
```
|
||||
|
||||
> **提示**:上面代码中的CSV文件是用相对路径进行获取的,也就是说当前工作路径下有名为`data`的文件夹,而“2018年北京积分落户数据.csv”就在这个文件夹下。如果使用Windows系统,在写路径分隔符时也建议使用`/`而不是`\`,如果想使用`\`,建议在字符串前面添加一个`r`,使用原始字符串来避开转义字符,例如`r'c:\new\data\2018年北京积分落户数据.csv'`。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
name birthday company score
|
||||
id
|
||||
1 杨xx 1972-12 北京利德华福xxxx 122.59
|
||||
2 纪xx 1974-12 北京航天数据xxxx 121.25
|
||||
3 王x 1974-05 品牌联盟(北京)xx 118.96
|
||||
4 杨x 1975-07 中科专利商标xxxx 118.21
|
||||
5 张xx 1974-11 北京阿里巴巴xxxx 117.79
|
||||
... ... ... ... ...
|
||||
6015 孙xx 1978-08 华为海洋网络xxxx 90.75
|
||||
6016 刘xx 1976-11 福斯(上海)xxxx 90.75
|
||||
6017 周x 1977-10 赢创德固赛xxxxxx 90.75
|
||||
6018 赵x 1979-07 澳科利耳医疗xxxx 90.75
|
||||
6019 贺x 1981-06 北京宝洁技术xxxx 90.75
|
||||
|
||||
[6019 rows x 4 columns]
|
||||
```
|
||||
|
||||
> **说明**: 上面输出的内容隐去了姓名(name)和公司名称(company)字段中的部分信息。如果需要上面例子中的 CSV 文件,可以通过百度云盘获取,链接:<https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g>,提取码:e7b4。
|
||||
|
||||
#### 读取Excel工作表创建DataFrame对象
|
||||
|
||||
可以通过`pandas` 模块的`read_excel`函数来读取 Excel 文件,该函数与上面的`read_csv`非常类似,多了一个`sheet_name`参数来指定数据表的名称,但是不同于 CSV 文件,没有`sep`或`delimiter`这样的参数。假设有名为“2022年股票数据.xlsx”的 Excel 文件,里面有用股票代码命名的五个表单,分别是阿里巴巴(BABA)、百度(BIDU)、京东(JD)、亚马逊(AMZN)、甲骨文(ORCL)这五个公司2022年的股票数据,如果想加载亚马逊的股票数据,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
df4 = pd.read_excel('data/2022年股票数据.xlsx', sheet_name='AMZN', index_col='Date')
|
||||
df4
|
||||
```
|
||||
|
||||
> **说明**:上面例子中的 CSV 文件可以通过百度云盘获取,链接:<https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g>,提取码:e7b4。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
Open High Low Close Volume
|
||||
Date
|
||||
2022-12-30 83.120 84.050 82.4700 84.000 62401194
|
||||
2022-12-29 82.870 84.550 82.5500 84.180 54995895
|
||||
2022-12-28 82.800 83.480 81.6900 81.820 58228575
|
||||
2022-12-27 84.970 85.350 83.0000 83.040 57284035
|
||||
2022-12-23 83.250 85.780 82.9344 85.250 57433655
|
||||
... ... ... ... ... ...
|
||||
2022-01-07 163.839 165.243 162.0310 162.554 46605900
|
||||
2022-01-06 163.450 164.800 161.9370 163.254 51957780
|
||||
2022-01-05 166.883 167.126 164.3570 164.357 64302720
|
||||
2022-01-04 170.438 171.400 166.3490 167.522 70725160
|
||||
2022-01-03 167.550 170.704 166.1600 170.404 63869140
|
||||
|
||||
[251 rows x 5 columns]
|
||||
```
|
||||
|
||||
#### 读取关系数据库二维表创建DataFrame对象
|
||||
|
||||
`pandas`模块的`read_sql`函数可以通过 SQL 语句从数据库中读取数据创建`DataFrame`对象,该函数的第二个参数代表了需要连接的数据库。对于 MySQL 数据库,我们可以通过`pymysql`或`mysqlclient`来创建数据库连接(需要提前安装好三方库),得到一个`Connection` 对象,而这个对象就是`read_sql`函数需要的第二个参数,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
import pymysql
|
||||
|
||||
# 创建一个MySQL数据库的连接对象
|
||||
conn = pymysql.connect(
|
||||
host='101.42.16.8', port=3306,
|
||||
user='guest', password='Guest.618',
|
||||
database='hrs', charset='utf8mb4'
|
||||
)
|
||||
# 通过SQL从数据库二维表读取数据创建DataFrame
|
||||
df5 = pd.read_sql('select * from tb_emp', conn, index_col='eno')
|
||||
df5
|
||||
```
|
||||
|
||||
> **提示**:执行上面的代码需要先安装`pymysql`库,如果尚未安装,可以先在单元格中先执行魔法指令`%pip install pymysql`,然后再运行上面的代码。上面的代码连接的是我部署在腾讯云上的 MySQL 数据库,公网 IP 地址:`101.42.16.8`,用户名:`guest`,密码:`Guest.618`,数据库:`hrs`,字符集:`utf8mb4`,大家可以使用这个数据库,但是不要进行恶意的访问。`hrs`数据库一共有三张表,分别是:`tb_dept`(部门表)、`tb_emp`(员工表)、`tb_emp2`(员工表2)。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344.0 1800 200.0 30
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
3577 杨过 会计 5566.0 2200 NaN 10
|
||||
3588 朱九真 会计 5566.0 2500 NaN 10
|
||||
4466 苗人凤 销售员 3344.0 2500 NaN 30
|
||||
5234 郭靖 出纳 5566.0 2000 NaN 10
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
执行上面的代码会出现一个警告,因为 pandas 库希望我们使用`SQLAlchemy`三方库接入数据库,具体内容是:“UserWarning: pandas only supports SQLAlchemy connectable (engine/connection) or database string URI or sqlite3 DBAPI2 connection. Other DBAPI2 objects are not tested. Please consider using SQLAlchemy.”。如果不想看到这个警告,我们可以试一试下面的解决方案。
|
||||
|
||||
首先,安装三方库`SQLAlchemy`,在 Jupyter 中可以使用`%pip`魔法指令。
|
||||
|
||||
```python
|
||||
%pip install sqlalchemy
|
||||
```
|
||||
|
||||
通过`SQLAlchemy`的`create_engine`函数创建`Engine`对象作为`read_sql`函数的第二个参数,此时`read_sql`函数的第一个参数可以是 SQL 语句,也可以是二维表的表名。
|
||||
|
||||
```python
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
# 通过指定的URL(统一资源定位符)访问数据库
|
||||
engine = create_engine('mysql+pymysql://guest:Guest.618@101.42.16.8:3306/hrs')
|
||||
# 直接通过表名加载整张表的数据
|
||||
df5 = pd.read_sql('tb_emp', engine, index_col='eno')
|
||||
df5
|
||||
```
|
||||
|
||||
> **说明**:如果通过表名加载二维表数据,也可以将上面的函数换成`read_sql_table`。
|
||||
|
||||
我们再来加载部门表的数据创建`DataFrame`对象。
|
||||
|
||||
```python
|
||||
df6 = pd.read_sql('select dno, dname, dloc from tb_dept', engine, index_col='dno')
|
||||
df6
|
||||
```
|
||||
|
||||
> **说明**:如果通过 SQL 查询获取数据,也可以将上面的函数换成`read_sql_query`。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
dname dloc
|
||||
dno
|
||||
10 会计部 北京
|
||||
20 研发部 成都
|
||||
30 销售部 重庆
|
||||
40 运维部 深圳
|
||||
```
|
||||
|
||||
在完成数据加载后,如果希望释放数据库连接,可以使用下面的代码。
|
||||
|
||||
```python
|
||||
engine.connect().close()
|
||||
```
|
||||
|
||||
### 基本属性和方法
|
||||
|
||||
在开始讲解`DataFrame`的属性和方法前,我们先从之前提到的`hrs`数据库中读取三张表的数据,创建出三个`DataFrame`对象,完整的代码如下所示。
|
||||
|
||||
```python
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
engine = create_engine('mysql+pymysql://guest:Guest.618@101.42.16.8:3306/hrs')
|
||||
dept_df = pd.read_sql_table('tb_dept', engine, index_col='dno')
|
||||
emp_df = pd.read_sql_table('tb_emp', engine, index_col='eno')
|
||||
emp2_df = pd.read_sql_table('tb_emp2', engine, index_col='eno')
|
||||
```
|
||||
|
||||
得到的三个`DataFrame`对象如下所示。
|
||||
|
||||
部门表(`dept_df`),其中`dno`是部门的编号,`dname`和`dloc`分别是部门的名称和所在地。
|
||||
|
||||
```
|
||||
dname dloc
|
||||
dno
|
||||
10 会计部 北京
|
||||
20 研发部 成都
|
||||
30 销售部 重庆
|
||||
40 运维部 深圳
|
||||
```
|
||||
|
||||
员工表(`emp_df`),其中`eno`是员工编号,`ename`、`job`、`mgr`、`sal`、`comm`和`dno`分别代表员工的姓名、职位、主管编号、月薪、补贴和部门编号。
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344.0 1800 200.0 30
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
3577 杨过 会计 5566.0 2200 NaN 10
|
||||
3588 朱九真 会计 5566.0 2500 NaN 10
|
||||
4466 苗人凤 销售员 3344.0 2500 NaN 30
|
||||
5234 郭靖 出纳 5566.0 2000 NaN 10
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
> **说明**:在数据库中`mgr`和`comm`两个列的数据类型是`int`,但是因为有缺失值(空值),读取到`DataFrame`之后,列的数据类型变成了`float`,因为我们通常会用`float`类型的`NaN`来表示空值。
|
||||
|
||||
员工表(`emp2_df`),跟上面的员工表结构相同,但是保存了不同的员工数据。
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
9500 张三丰 总裁 NaN 50000 8000 20
|
||||
9600 王大锤 程序员 9800.0 8000 600 20
|
||||
9700 张三丰 总裁 NaN 60000 6000 20
|
||||
9800 骆昊 架构师 7800.0 30000 5000 20
|
||||
9900 陈小刀 分析师 9800.0 10000 1200 20
|
||||
```
|
||||
|
||||
`DataFrame`对象的属性如下表所示。
|
||||
|
||||
| 属性名 | 说明 |
|
||||
| -------------- | ----------------------------------- |
|
||||
| `at` / `iat` | 通过标签获取`DataFrame`中的单个值。 |
|
||||
| `columns` | `DataFrame`对象列的索引 |
|
||||
| `dtypes` | `DataFrame`对象每一列的数据类型 |
|
||||
| `empty` | `DataFrame`对象是否为空 |
|
||||
| `loc` / `iloc` | 通过标签获取`DataFrame`中的一组值。 |
|
||||
| `ndim` | `DataFrame`对象的维度 |
|
||||
| `shape` | `DataFrame`对象的形状(行数和列数) |
|
||||
| `size` | `DataFrame`对象中元素的个数 |
|
||||
| `values` | `DataFrame`对象的数据对应的二维数组 |
|
||||
|
||||
关于`DataFrame`的方法,首先需要了解的是`info()`方法,它可以帮助我们了解`DataFrame`的相关信息,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
emp_df.info()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
<class 'pandas.core.frame.DataFrame'>
|
||||
Int64Index: 14 entries, 1359 to 7800
|
||||
Data columns (total 6 columns):
|
||||
# Column Non-Null Count Dtype
|
||||
--- ------ -------------- -----
|
||||
0 ename 14 non-null object
|
||||
1 job 14 non-null object
|
||||
2 mgr 13 non-null float64
|
||||
3 sal 14 non-null int64
|
||||
4 comm 6 non-null float64
|
||||
5 dno 14 non-null int64
|
||||
dtypes: float64(2), int64(2), object(2)
|
||||
memory usage: 1.3+ KB
|
||||
```
|
||||
|
||||
如果需要查看`DataFrame`的头部或尾部的数据,可以使用`head()`或`tail()`方法,这两个方法的默认参数是`5`,表示获取`DataFrame`最前面5行或最后面5行的数据,如下所示。
|
||||
|
||||
```python
|
||||
emp_df.head()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344 1800 200 30
|
||||
2056 乔峰 分析师 7800 5000 1500 20
|
||||
3088 李莫愁 设计师 2056 3500 800 20
|
||||
3211 张无忌 程序员 2056 3200 NaN 20
|
||||
3233 丘处机 程序员 2056 3400 NaN 20
|
||||
```
|
||||
|
||||
### 操作数据
|
||||
|
||||
#### 索引和切片
|
||||
|
||||
如果要获取`DataFrame`的某一列,例如取出上面`emp_df`的`ename`列,可以使用下面的两种方式。
|
||||
|
||||
```python
|
||||
emp_df.ename
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```python
|
||||
emp_df['ename']
|
||||
```
|
||||
|
||||
执行上面的代码可以发现,我们获得的是一个`Series`对象。事实上,`DataFrame`对象就是将多个`Series`对象组合到一起的结果。
|
||||
|
||||
如果要获取`DataFrame`的某一行,可以使用整数索引或我们设置的索引,例如取出员工编号为`2056`的员工数据,代码如下所示。
|
||||
|
||||
```python
|
||||
emp_df.iloc[1]
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```python
|
||||
emp_df.loc[2056]
|
||||
```
|
||||
|
||||
通过执行上面的代码我们发现,单独取`DataFrame` 的某一行或某一列得到的都是`Series`对象。我们当然也可以通过花式索引来获取多个行或多个列的数据,花式索引的结果仍然是一个`DataFrame`对象。
|
||||
|
||||
获取多个列:
|
||||
|
||||
```python
|
||||
emp_df[['ename', 'job']]
|
||||
```
|
||||
|
||||
获取多个行:
|
||||
|
||||
```python
|
||||
emp_df.loc[[2056, 7800, 3344]]
|
||||
```
|
||||
|
||||
如果要获取或修改`DataFrame` 对象某个单元格的数据,需要同时指定行和列的索引,例如要获取员工编号为`2056`的员工的职位信息,代码如下所示。
|
||||
|
||||
```python
|
||||
emp_df['job'][2056]
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```python
|
||||
emp_df.loc[2056]['job']
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```python
|
||||
emp_df.loc[2056, 'job']
|
||||
```
|
||||
|
||||
我们推荐大家使用第三种做法,因为它只做了一次索引运算。如果要将该员工的职位修改为“架构师”,可以使用下面的代码。
|
||||
|
||||
```python
|
||||
emp_df.loc[2056, 'job'] = '架构师'
|
||||
```
|
||||
|
||||
当然,我们也可以通过切片操作来获取多行多列,相信大家一定已经想到了这一点。
|
||||
|
||||
```python
|
||||
emp_df.loc[2056:3344]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
```
|
||||
|
||||
#### 数据筛选
|
||||
|
||||
上面我们提到了花式索引,相信大家已经联想到了布尔索引。跟`ndarray`和`Series`一样,我们可以通过布尔索引对`DataFrame`对象进行数据筛选,例如我们要从`emp_df`中筛选出月薪超过`3500`的员工,代码如下所示。
|
||||
|
||||
```python
|
||||
emp_df[emp_df.sal > 3500]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
当然,我们也可以组合多个条件来进行数据筛选,例如从`emp_df`中筛选出月薪超过`3500`且部门编号为`20`的员工,代码如下所示。
|
||||
|
||||
```python
|
||||
emp_df[(emp_df.sal > 3500) & (emp_df.dno == 20)]
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
```
|
||||
|
||||
除了使用布尔索引,`DataFrame`对象的`query`方法也可以实现数据筛选,`query`方法的参数是一个字符串,它代表了筛选数据使用的表达式,而且更符合 Python 程序员的使用习惯。下面我们使用`query`方法将上面的效果重新实现一遍,代码如下所示。
|
||||
|
||||
```python
|
||||
emp_df.query('sal > 3500 and dno == 20')
|
||||
```
|
|
@ -1,448 +0,0 @@
|
|||
## 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://github.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://github.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://github.com/jackfrued/mypic/raw/master/20211208213325.png">
|
||||
|
||||
斯皮尔曼相关系数对数据条件的要求没有皮尔逊相关系数严格,只要两个变量的观测值是成对的等级评定资料,或者是由连续变量观测资料转化得到的等级资料,不论两个变量的总体分布形态、样本容量的大小如何,都可以用斯皮尔曼等级相关系数来进行研究。我们通过下面的方式来计算斯皮尔曼相关系数。
|
||||
|
||||
```Python
|
||||
boston_df.corr('spearman')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.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://github.com/jackfrued/mypic/raw/master/20211208215228.png">
|
||||
|
||||
上面代码中的`RdYlBu`代表的颜色如下所示,相关系数的数据值越接近`1`,颜色越接近红色;数据值越接近`1`,颜色越接近蓝色;数据值在`0`附件则是黄色。
|
||||
|
||||
```Python
|
||||
plt.get_cmap('RdYlBu')
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208215057.png">
|
||||
|
||||
### Index的应用
|
||||
|
||||
我们再来看看`Index`类型,它为`Series`和`DataFrame`对象提供了索引服务,常用的`Index`有以下几种。
|
||||
|
||||
#### 范围索引(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
|
||||
```
|
||||
|
||||
#### 分类索引(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
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
ser.groupby(level=0).sum()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
苹果 19
|
||||
香蕉 12
|
||||
桃子 8
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
#### 多级索引(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
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
#### 日期时间索引(DatetimeIndex)
|
||||
|
||||
1. 通过`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')
|
||||
```
|
||||
|
||||
2. 通过`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)
|
||||
```
|
||||
|
||||
4. 可以使用`DatatimeIndex`类型的相关方法来处理数据,具体包括:
|
||||
- `shift()`方法:通过时间前移或后移数据,我们仍然以上面百度股票数据为例,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.shift(3, fill_value=0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208220551.png" style="zoom:150%;">
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.shift(-1, fill_value=0)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208220713.png" style="zoom:150%;">
|
||||
|
||||
- `asfreq()`方法:指定一个时间频率抽取对应的数据,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.asfreq('5D')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221202.png">
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.asfreq('5D', method='ffill')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221249.png" style="zoom:150%;">
|
||||
|
||||
- `resample()`方法:基于时间对数据进行重采样,相当于根据时间周期对数据进行了分组操作,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.resample('1M').mean()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221429.png">
|
||||
|
||||
> **说明**:上面的代码中,`W`表示一周,`5D`表示`5`天,`1M`表示`1`个月。
|
||||
|
||||
5. 时区转换
|
||||
|
||||
- 获取时区信息。
|
||||
|
||||
```Python
|
||||
import pytz
|
||||
|
||||
pytz.common_timezones
|
||||
```
|
||||
|
||||
- `tz_localize()`方法:将日期时间本地化。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df = baidu_df.tz_localize('Asia/Chongqing')
|
||||
baidu_df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208221947.png">
|
||||
|
||||
- `tz_convert()`方法:转换时区。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.tz_convert('America/New_York')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211208222404.png">
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,12 +1,99 @@
|
|||
## Pandas的应用-3
|
||||
## 深入浅出pandas-3
|
||||
|
||||
### DataFrame的应用
|
||||
在完成数据加载之后,我们可能需要对事实表和维度表进行连接,这是对数据进行多维度拆解的基础;我们可能从不同的数据源加载了结构相同的数据,我们需要将这些数据拼接起来;我们把这些操作统称为数据重塑。当然,由于企业的信息化水平以及数据中台建设水平的差异,我们拿到的数据未必是质量很好的,可能还需要对数据中的缺失值、重复值、异常值进行适当的处理。即便我们获取的数据在质量上是没有问题的,但也可能需要对数据进行一系列的预处理,才能满足我们做数据分析的需求。接下来,我们就为大家讲解和梳理这方面的知识。
|
||||
|
||||
#### 数据清洗
|
||||
### 数据重塑
|
||||
|
||||
有的时候,我们做数据分析需要的原始数据可能并不是来自一个地方,就像上一章的例子中,我们从关系型数据库中读取了三张表,得到了三个`DataFrame`对象,但实际工作可能需要我们把他们的数据整合到一起。例如:`emp_df`和`emp2_df`其实都是员工的数据,而且数据结构完全一致,我们可以使用`pandas`提供的`concat`函数实现两个或多个`DataFrame`的数据拼接,代码如下所示。
|
||||
|
||||
```Python
|
||||
all_emp_df = pd.concat([emp_df, emp2_df])
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
ename job mgr sal comm dno
|
||||
eno
|
||||
1359 胡一刀 销售员 3344.0 1800 200.0 30
|
||||
2056 乔峰 分析师 7800.0 5000 1500.0 20
|
||||
3088 李莫愁 设计师 2056.0 3500 800.0 20
|
||||
3211 张无忌 程序员 2056.0 3200 NaN 20
|
||||
3233 丘处机 程序员 2056.0 3400 NaN 20
|
||||
3244 欧阳锋 程序员 3088.0 3200 NaN 20
|
||||
3251 张翠山 程序员 2056.0 4000 NaN 20
|
||||
3344 黄蓉 销售主管 7800.0 3000 800.0 30
|
||||
3577 杨过 会计 5566.0 2200 NaN 10
|
||||
3588 朱九真 会计 5566.0 2500 NaN 10
|
||||
4466 苗人凤 销售员 3344.0 2500 NaN 30
|
||||
5234 郭靖 出纳 5566.0 2000 NaN 10
|
||||
5566 宋远桥 会计师 7800.0 4000 1000.0 10
|
||||
7800 张三丰 总裁 NaN 9000 1200.0 20
|
||||
9500 张三丰 总裁 NaN 50000 8000.0 20
|
||||
9600 王大锤 程序员 9800.0 8000 600.0 20
|
||||
9700 张三丰 总裁 NaN 60000 6000.0 20
|
||||
9800 骆昊 架构师 7800.0 30000 5000.0 20
|
||||
9900 陈小刀 分析师 9800.0 10000 1200.0 20
|
||||
```
|
||||
|
||||
上面的代码将两个代表员工数据的`DataFrame`拼接到了一起,接下来我们使用`merge`函数将员工表和部门表的数据合并到一张表中,代码如下所示。
|
||||
|
||||
先使用`reset_index`方法重新设置`all_emp_df`的索引,这样`eno` 不再是索引而是一个普通列,`reset_index`方法的`inplace`参数设置为`True`表示,重置索引的操作直接在`all_emp_df`上执行,而不是返回修改后的新对象。
|
||||
|
||||
```Python
|
||||
all_emp_df.reset_index(inplace=True)
|
||||
```
|
||||
|
||||
通过`merge`函数合并数据,当然,也可以调用`DataFrame`对象的`merge`方法来达到同样的效果。
|
||||
|
||||
```Python
|
||||
pd.merge(all_emp_df, dept_df, how='inner', on='dno')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
eno ename job mgr sal comm dno dname dloc
|
||||
0 1359 胡一刀 销售员 3344.0 1800 200.0 30 销售部 重庆
|
||||
1 3344 黄蓉 销售主管 7800.0 3000 800.0 30 销售部 重庆
|
||||
2 4466 苗人凤 销售员 3344.0 2500 NaN 30 销售部 重庆
|
||||
3 2056 乔峰 分析师 7800.0 5000 1500.0 20 研发部 成都
|
||||
4 3088 李莫愁 设计师 2056.0 3500 800.0 20 研发部 成都
|
||||
5 3211 张无忌 程序员 2056.0 3200 NaN 20 研发部 成都
|
||||
6 3233 丘处机 程序员 2056.0 3400 NaN 20 研发部 成都
|
||||
7 3244 欧阳锋 程序员 3088.0 3200 NaN 20 研发部 成都
|
||||
8 3251 张翠山 程序员 2056.0 4000 NaN 20 研发部 成都
|
||||
9 7800 张三丰 总裁 NaN 9000 1200.0 20 研发部 成都
|
||||
10 9500 张三丰 总裁 NaN 50000 8000.0 20 研发部 成都
|
||||
11 9600 王大锤 程序员 9800.0 8000 600.0 20 研发部 成都
|
||||
12 9700 张三丰 总裁 NaN 60000 6000.0 20 研发部 成都
|
||||
13 9800 骆昊 架构师 7800.0 30000 5000.0 20 研发部 成都
|
||||
14 9900 陈小刀 分析师 9800.0 10000 1200.0 20 研发部 成都
|
||||
15 3577 杨过 会计 5566.0 2200 NaN 10 会计部 北京
|
||||
16 3588 朱九真 会计 5566.0 2500 NaN 10 会计部 北京
|
||||
17 5234 郭靖 出纳 5566.0 2000 NaN 10 会计部 北京
|
||||
18 5566 宋远桥 会计师 7800.0 4000 1000.0 10 会计部 北京
|
||||
```
|
||||
|
||||
`merge`函数的一个参数代表合并的左表、第二个参数代表合并的右表,有SQL编程经验的同学对这两个词是不是感觉到非常亲切。正如大家猜想的那样,`DataFrame`对象的合并跟数据库中的表连接非常类似,所以上面代码中的`how`代表了合并两张表的方式,有`left`、`right`、`inner`、`outer`四个选项;而`on`则代表了基于哪个列实现表的合并,相当于 SQL 表连接中的连表条件,如果左右两表对应的列列名不同,可以用`left_on`和`right_on`参数取代`on`参数分别进行指定。
|
||||
|
||||
如果对上面的代码稍作修改,将`how`参数修改为`'right'`,大家可以思考一下代码执行的结果。
|
||||
|
||||
```Python
|
||||
pd.merge(all_emp_df, dept_df, how='right', on='dno')
|
||||
```
|
||||
|
||||
运行结果比之前的输出多出了如下所示的一行,这是因为`how='right'`代表右外连接,也就意味着右表`dept_df`中的数据会被完整的查出来,但是在`all_emp_df`中又没有编号为`40` 部门的员工,所以对应的位置都被填入了空值。
|
||||
|
||||
```
|
||||
19 NaN NaN NaN NaN NaN NaN 40 运维部 深圳
|
||||
```
|
||||
|
||||
### 数据清洗
|
||||
|
||||
通常,我们从 Excel、CSV 或数据库中获取到的数据并不是非常完美的,里面可能因为系统或人为的原因混入了重复值或异常值,也可能在某些字段上存在缺失值;再者,`DataFrame`中的数据也可能存在格式不统一、量纲不统一等各种问题。因此,在开始数据分析之前,对数据进行清洗就显得特别重要。
|
||||
|
||||
##### 缺失值
|
||||
#### 缺失值
|
||||
|
||||
可以使用`DataFrame`对象的`isnull`或`isna`方法来找出数据表中的缺失值,如下所示。
|
||||
|
||||
|
@ -117,7 +204,7 @@ eno
|
|||
7800 张三丰 总裁 0.0 9000 1200.0 20
|
||||
```
|
||||
|
||||
##### 重复值
|
||||
#### 重复值
|
||||
|
||||
接下来,我们先给之前的部门表添加两行数据,让部门表中名为“研发部”和“销售部”的部门各有两个。
|
||||
|
||||
|
@ -193,24 +280,30 @@ dno
|
|||
60 销售部 长沙
|
||||
```
|
||||
|
||||
##### 异常值
|
||||
使用同样的方式,我们也可以清除`all_emp_df`中的重复数据,例如我们认定“ename”和“job”两个字段完全相同的就是重复数据,我们可以用下面的代码去除重复数据。
|
||||
|
||||
```python
|
||||
all_emp_df.drop_duplicates(['ename', 'job'], inplace=True)
|
||||
```
|
||||
|
||||
> **说明**:上面的`drop_duplicates`方法添加了参数`inplace=True`,该方法不会返回新的`DataFrame`对象,而是在原来的`DataFrame`对象上直接删除,大家可以查看`all_emp_df`看看是不是已经移除了重复的员工数据。
|
||||
|
||||
#### 异常值
|
||||
|
||||
异常值在统计学上的全称是疑似异常值,也称作离群点(outlier),异常值的分析也称作离群点分析。异常值是指样本中出现的“极端值”,数据值看起来异常大或异常小,其分布明显偏离其余的观测值。实际工作中,有些异常值可能是由系统或人为原因造成的,但有些异常值却不是,它们能够重复且稳定的出现,属于正常的极端值,例如很多游戏产品中头部玩家的数据往往都是离群的极端值。所以,我们既不能忽视异常值的存在,也不能简单地把异常值从数据分析中剔除。重视异常值的出现,分析其产生的原因,常常成为发现问题进而改进决策的契机。
|
||||
|
||||
异常值的检测有Z-score 方法、IQR 方法、DBScan 聚类、孤立森林等,这里我们对前两种方法做一个简单的介绍。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211004192858.png" style="zoom:50%;">
|
||||
<img src="http://localhost/mypic/20211004192858.png" style="zoom:50%;">
|
||||
|
||||
如果数据服从正态分布,依据3σ法则,异常值被定义与平均值的偏差超过三倍标准差的值。在正态分布下,距离平均值3σ之外的值出现的概率为$ P(|x-\mu|>3\sigma)<0.003 $,属于小概率事件。如果数据不服从正态分布,那么可以用远离平均值的多少倍的标准差来描述,这里的倍数就是Z-score。Z-score以标准差为单位去度量某一原始分数偏离平均值的距离,公式如下所示。
|
||||
如果数据服从正态分布,依据3σ法则,异常值被定义与平均值的偏差超过三倍标准差的值。在正态分布下,距离平均值3σ之外的值出现的概率为$ P(|x-\mu|>3\sigma)<0.003 $,属于小概率事件。如果数据不服从正态分布,那么可以用远离均值的多少倍的标准差来描述,这里的倍数就是Z-score。Z-score以标准差为单位去度量某一原始分数偏离平均值的距离,公式如下所示。
|
||||
$$
|
||||
z = \frac {X - \mu} {\sigma}
|
||||
z = \frac {X - \mu} {\sigma} \\
|
||||
|z| > 3
|
||||
$$
|
||||
Z-score需要根据经验和实际情况来决定,通常把远离标准差`3`倍距离以上的数据点视为离群点,下面的代给出了如何通过Z-score方法检测异常值。
|
||||
|
||||
```Python
|
||||
import numpy as np
|
||||
|
||||
|
||||
def detect_outliers_zscore(data, threshold=3):
|
||||
avg_value = np.mean(data)
|
||||
std_value = np.std(data)
|
||||
|
@ -218,12 +311,9 @@ def detect_outliers_zscore(data, threshold=3):
|
|||
return data[z_score > threshold]
|
||||
```
|
||||
|
||||
IQR 方法中的IQR(Inter-Quartile Range)代表四分位距离,即上四分位数(Q3)和下四分位数(Q1)的差值。通常情况下,可以认为小于 $ Q1 - 1.5 \times IQR $ 或大于 $ Q3 + 1.5 \times IQR $ 的就是异常值,而这种检测异常值的方法也是箱线图(后面会讲到)默认使用的方法。下面的代给出了如何通过 IQR 方法检测异常值。
|
||||
IQR 方法中的IQR(Inter-Quartile Range)代表四分位距离,即上四分位数(Q3)和下四分位数(Q1)的差值。通常情况下,可以认为小于 $ Q1 - 1.5 \times IQR $ 或大于 $ Q3 + 1.5 \times IQR $ 的就是异常值,而这种检测异常值的方法也是箱线图(后面会讲到)默认使用的方法。下面的代码给出了如何通过 IQR 方法检测异常值。
|
||||
|
||||
```Python
|
||||
import numpy as np
|
||||
|
||||
|
||||
def detect_outliers_iqr(data, whis=1.5):
|
||||
q1, q3 = np.quantile(data, [0.25, 0.75])
|
||||
iqr = q3 - q1
|
||||
|
@ -244,7 +334,7 @@ avg_sal = np.mean(emp_df.sal).astype(int)
|
|||
emp_df.replace({'sal': [1800, 9000], 'comm': 800}, {'sal': avg_sal, 'comm': 1000})
|
||||
```
|
||||
|
||||
##### 预处理
|
||||
#### 预处理
|
||||
|
||||
对数据进行预处理也是一个很大的话题,它包含了对数据的拆解、变换、归约、离散化等操作。我们先来看看数据的拆解。如果数据表中的数据是一个时间日期,我们通常都需要从年、季度、月、日、星期、小时、分钟等维度对其进行拆解,如果时间日期是用字符串表示的,可以先通过`pandas`的`to_datetime`函数将其处理成时间日期。
|
||||
|
||||
|
@ -252,13 +342,13 @@ emp_df.replace({'sal': [1800, 9000], 'comm': 800}, {'sal': avg_sal, 'comm': 1000
|
|||
|
||||
```Python
|
||||
sales_df = pd.read_excel(
|
||||
'2020年销售数据.xlsx',
|
||||
'data/2020年销售数据.xlsx',
|
||||
usecols=['销售日期', '销售区域', '销售渠道', '品牌', '销售额']
|
||||
)
|
||||
sales_df.info()
|
||||
```
|
||||
|
||||
> **说明**:如果需要上面例子中的 Excel 文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
> **说明**:上面代码中使用了相对路径来获取 Excel 文件,也就是说 Excel 文件在当前工作路径下名为`data`的文件夹中。如果需要上面例子中的 Excel 文件,可以通过下面的百度云盘地址进行获取。链接:<https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g>,提取码:e7b4。
|
||||
|
||||
输出:
|
||||
|
||||
|
@ -307,13 +397,13 @@ sales_df
|
|||
|
||||
```Python
|
||||
jobs_df = pd.read_csv(
|
||||
'某招聘网站招聘数据.csv',
|
||||
'data/某招聘网站招聘数据.csv',
|
||||
usecols=['city', 'companyFullName', 'positionName', 'salary']
|
||||
)
|
||||
jobs_df.info()
|
||||
```
|
||||
|
||||
> **说明**:如果需要上面例子中的 Excel 文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
> **说明**:上面代码中使用了相对路径来获取 CSV 文件,也就是说 CSV 文件在当前工作路径下名为`data`的文件夹中。如果需要上面例子中的 CSV 文件,可以通过下面的百度云盘地址进行获取。链接:<https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g>,提取码:e7b4。
|
||||
|
||||
输出:
|
||||
|
|
@ -1,17 +1,16 @@
|
|||
## Pandas的应用-4
|
||||
## 深入浅出pandas-4
|
||||
|
||||
### DataFrame的应用
|
||||
### 数据透视
|
||||
|
||||
#### 数据分析
|
||||
经过前面的学习,我们已经将数据准备就绪而且变成了我们想要的样子,接下来就是最为重要的数据透视阶段了。当我们拿到一大堆数据的时候,如何从数据中迅速的解读出有价值的信息,把繁杂的数据变成容易解读的统计图表并再此基础上产生业务洞察,这就是数据分析要解决的核心问题。
|
||||
|
||||
经过前面的学习,我们已经将数据准备就绪而且变成了我们想要的样子,接下来就是最为重要的数据分析阶段了。当我们拿到一大堆数据的时候,如何从数据中迅速的解读出有价值的信息,这就是数据分析要解决的问题。首先,我们可以获取数据的描述性统计信息,通过描述性统计信息,我们可以了解数据的集中趋势和离散趋势。
|
||||
#### 获取描述性统计信息
|
||||
|
||||
首先,我们可以获取数据的描述性统计信息,通过描述性统计信息,我们可以了解数据的集中趋势和离散趋势。
|
||||
|
||||
例如,我们有如下所示的学生成绩表。
|
||||
|
||||
```Python
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
scores = np.random.randint(50, 101, (5, 3))
|
||||
names = ('关羽', '张飞', '赵云', '马超', '黄忠')
|
||||
courses = ('语文', '数学', '英语')
|
||||
|
@ -79,7 +78,7 @@ df.var()
|
|||
dtype: float64
|
||||
```
|
||||
|
||||
> **说明**:通过方差可以看出,数学成绩波动最大,最不稳定。
|
||||
> **说明**:通过方差可以看出,数学成绩波动最大,两极分化可能更严重。
|
||||
|
||||
获取每门课程的描述性统计信息。
|
||||
|
||||
|
@ -101,7 +100,7 @@ min 72.000000 51.000000 54.000000
|
|||
max 100.000000 100.000000 97.000000
|
||||
```
|
||||
|
||||
##### 排序和Top-N
|
||||
#### 排序和取头部值
|
||||
|
||||
如果需要对数据进行排序,可以使用`DataFrame`对象的`sort_values`方法,该方法的`by`参数可以指定根据哪个列或哪些列进行排序,而`ascending`参数可以指定升序或是降序。例如,下面的代码展示了如何将学生表按语文成绩排降序。
|
||||
|
||||
|
@ -152,17 +151,15 @@ df.nsmallest(3, '数学')
|
|||
张飞 72 70 97
|
||||
```
|
||||
|
||||
##### 分组聚合操作
|
||||
#### 分组聚合
|
||||
|
||||
我们先从 Excel 文件中读取一组销售数据,然后再为大家演示如何进行分组聚合操作。
|
||||
我们先从之前使用过的 Excel 文件中读取2020年销售数据,然后再为大家演示如何进行分组聚合操作。
|
||||
|
||||
```Python
|
||||
df = pd.read_excel('2020年销售数据.xlsx')
|
||||
df = pd.read_excel('data/2020年销售数据.xlsx')
|
||||
df.head()
|
||||
```
|
||||
|
||||
> **说明**:如果需要上面例子中的 Excel 文件,可以通过百度云盘进行获取。链接:https://pan.baidu.com/s/1NhWtYcpFzF72cxcsoDoXjQ?pwd=swg1,提取码:swg1。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
|
@ -204,10 +201,9 @@ df.groupby('销售区域').销售额.sum()
|
|||
销售区域
|
||||
上海 11610489
|
||||
北京 12477717
|
||||
南京 1767301
|
||||
安徽 895463
|
||||
广东 1617949
|
||||
江苏 537079
|
||||
江苏 2304380
|
||||
浙江 687862
|
||||
福建 10178227
|
||||
Name: 销售额, dtype: int64
|
||||
|
@ -272,9 +268,6 @@ df.groupby(['销售区域', df['销售日期'].dt.month]).销售额.sum()
|
|||
10 671608
|
||||
11 678668
|
||||
12 596146
|
||||
南京 7 841032
|
||||
10 710962
|
||||
12 215307
|
||||
安徽 4 341308
|
||||
5 554155
|
||||
广东 3 388180
|
||||
|
@ -282,6 +275,9 @@ df.groupby(['销售区域', df['销售日期'].dt.month]).销售额.sum()
|
|||
9 365191
|
||||
11 395188
|
||||
江苏 4 537079
|
||||
7 841032
|
||||
10 710962
|
||||
12 215307
|
||||
浙江 3 248354
|
||||
8 439508
|
||||
福建 1 1852496
|
||||
|
@ -312,10 +308,9 @@ df.groupby('销售区域').销售额.agg(['sum', 'max', 'min'])
|
|||
销售区域
|
||||
上海 11610489 116303 948
|
||||
北京 12477717 133411 690
|
||||
南京 1767301 87527 1089
|
||||
安徽 895463 68502 1683
|
||||
广东 1617949 120807 990
|
||||
江苏 537079 114312 3383
|
||||
江苏 2304380 114312 1089
|
||||
浙江 687862 90909 3927
|
||||
福建 10178227 87527 897
|
||||
```
|
||||
|
@ -333,19 +328,18 @@ df.groupby('销售区域').销售额.agg(销售总额='sum', 单笔最高='max',
|
|||
销售区域
|
||||
上海 11610489 116303 948
|
||||
北京 12477717 133411 690
|
||||
南京 1767301 87527 1089
|
||||
安徽 895463 68502 1683
|
||||
广东 1617949 120807 990
|
||||
江苏 537079 114312 3383
|
||||
江苏 2304380 114312 1089
|
||||
浙江 687862 90909 3927
|
||||
福建 10178227 87527 897
|
||||
```
|
||||
|
||||
如果需要对多个列使用不同的聚合函数,例如“统计每个销售区域销售额的平均值以及销售数量的最低值和最高值”,我们可以按照下面的方式来操作。
|
||||
如果需要对多个列使用不同的聚合函数,例如“统计每个销售区域销售额的总和以及销售数量的最低值和最高值”,我们可以按照下面的方式来操作。
|
||||
|
||||
```Python
|
||||
df.groupby('销售区域')[['销售额', '销售数量']].agg({
|
||||
'销售额': 'mean', '销售数量': ['max', 'min']
|
||||
'销售额': 'sum', '销售数量': ['max', 'min']
|
||||
})
|
||||
```
|
||||
|
||||
|
@ -353,19 +347,18 @@ df.groupby('销售区域')[['销售额', '销售数量']].agg({
|
|||
|
||||
```
|
||||
销售额 销售数量
|
||||
mean max min
|
||||
sum max min
|
||||
销售区域
|
||||
上海 20622.538188 100 10
|
||||
北京 20125.350000 100 10
|
||||
南京 22370.898734 100 11
|
||||
安徽 26337.147059 98 16
|
||||
广东 32358.980000 98 10
|
||||
江苏 29837.722222 98 15
|
||||
浙江 27514.480000 95 20
|
||||
福建 18306.163669 100 10
|
||||
上海 11610489 100 10
|
||||
北京 12477717 100 10
|
||||
安徽 895463 98 16
|
||||
广东 1617949 98 10
|
||||
江苏 2304380 100 11
|
||||
浙江 687862 95 20
|
||||
福建 10178227 100 10
|
||||
```
|
||||
|
||||
##### 透视表和交叉表
|
||||
#### 透视表和交叉表
|
||||
|
||||
上面的例子中,“统计每个销售区域每个月的销售总额”会产生一个看起来很长的结果,在实际工作中我们通常把那些行很多列很少的表成为“窄表”,如果我们不想得到这样的一个“窄表”,可以使用`DataFrame`的`pivot_table`方法或者是`pivot_table`函数来生成透视表。透视表的本质就是对数据进行分组聚合操作,**根据 A 列对 B 列进行统计**,如果大家有使用 Excel 的经验,相信对透视表这个概念一定不会陌生。例如,我们要“统计每个销售区域的销售总额”,那么“销售区域”就是我们的 A 列,而“销售额”就是我们的 B 列,在`pivot_table`函数中分别对应`index`和`values`参数,这两个参数都可以是单个列或者多个列。
|
||||
|
||||
|
@ -375,45 +368,48 @@ pd.pivot_table(df, index='销售区域', values='销售额', aggfunc='sum')
|
|||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211106180912.png" style="zoom:50%">
|
||||
```
|
||||
销售额
|
||||
销售区域
|
||||
上海 11610489
|
||||
北京 12477717
|
||||
安徽 895463
|
||||
广东 1617949
|
||||
江苏 2304380
|
||||
浙江 687862
|
||||
福建 10178227
|
||||
```
|
||||
|
||||
> **注意**:上面的结果操作跟之前用`groupby`的方式得到的结果有一些区别,`groupby`操作后,如果对单个列进行聚合,得到的结果是一个`Series`对象,而上面的结果是一个`DataFrame` 对象。
|
||||
|
||||
如果要统计每个销售区域每个月的销售总额,也可以使用`pivot_table`函数,代码如下所示。
|
||||
|
||||
```Python
|
||||
pd.pivot_table(df, index=['销售区域', df['销售日期'].dt.month], values='销售额', aggfunc='sum')
|
||||
df['月份'] = df['销售日期'].dt.month
|
||||
pd.pivot_table(df, index=['销售区域', '月份'], values='销售额', aggfunc='sum')
|
||||
```
|
||||
|
||||
上面的操作结果是一个`DataFrame`,但也是一个长长的“窄表”,如果希望做成一个行比较少列比较多的“宽表”,可以将`index`参数中的列放到`columns`参数中,代码如下所示。
|
||||
|
||||
```Python
|
||||
pd.pivot_table(
|
||||
df, index='销售区域', columns=df['销售日期'].dt.month,
|
||||
values='销售额', aggfunc='sum', fill_value=0
|
||||
)
|
||||
pd.pivot_table(df, index='销售区域', columns='月份', values='销售额', aggfunc='sum', fill_value=0)
|
||||
```
|
||||
|
||||
> **说明**:`pivot_table`函数的`fill_value=0`会将空值处理为`0`。
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211106104551.png" style="zoom:50%">
|
||||
<img src="res/pivot_table_1.png" style="zoom:50%;">
|
||||
|
||||
使用`pivot_table`函数时,还可以通过添加`margins`和`margins_name`参数对分组聚合的结果做一个汇总,具体的操作和效果如下所示。
|
||||
|
||||
```Python
|
||||
df['月份'] = df['销售日期'].dt.month
|
||||
pd.pivot_table(
|
||||
df, index='销售区域', columns='月份',
|
||||
values='销售额', aggfunc='sum', fill_value=0,
|
||||
margins=True, margins_name='总计'
|
||||
)
|
||||
pd.pivot_table(df, index='销售区域', columns='月份', values='销售额', aggfunc='sum', fill_value=0, margins=True, margins_name='总计')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
![image-20211106181707655](https://github.com/jackfrued/mypic/raw/master/20211106181707.png)
|
||||
<img src="res/pivot_table_2.png" style="zoom:50%;">
|
||||
|
||||
交叉表就是一种特殊的透视表,它不需要先构造一个`DataFrame`对象,而是直接通过数组或`Series`对象指定两个或多个因素进行运算得到统计结果。例如,我们要统计每个销售区域的销售总额,也可以按照如下所示的方式来完成,我们先准备三组数据。
|
||||
|
||||
|
@ -424,14 +420,12 @@ sales_area, sales_month, sales_amount = df['销售区域'], df['月份'], df['
|
|||
使用`crosstab`函数生成交叉表。
|
||||
|
||||
```Python
|
||||
pd.crosstab(
|
||||
index=sales_area, columns=sales_month, values=sales_amount, aggfunc='sum'
|
||||
).fillna(0).applymap(int)
|
||||
pd.crosstab(index=sales_area, columns=sales_month, values=sales_amount, aggfunc='sum').fillna(0).astype('i8')
|
||||
```
|
||||
|
||||
> **说明**:上面的代码使用了`DataFrame`对象的`fillna`方法将空值处理为0,再使用`applymap`方法将数据类型处理成整数。
|
||||
> **说明**:上面的代码使用了`DataFrame`对象的`fillna`方法将空值处理为0,再使用`astype`方法将数据类型处理成整数。
|
||||
|
||||
#### 数据可视化
|
||||
### 数据呈现
|
||||
|
||||
一图胜千言,我们对数据进行透视的结果,最终要通过图表的方式呈现出来,因为图表具有极强的表现力,能够让我们迅速的解读数据中隐藏的价值。和`Series`一样,`DataFrame`对象提供了`plot`方法来支持绘图,底层仍然是通过`matplotlib`库实现图表的渲染。关于`matplotlib`的内容,我们在下一个章节进行详细的探讨,这里我们只简单的讲解`plot`方法的用法。
|
||||
|
||||
|
@ -464,21 +458,25 @@ plt.show()
|
|||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211106195040.png" style="zoom:50%">
|
||||
<img src="res/sales_bar_graph.png" style="zoom:45%;">
|
||||
|
||||
如果要绘制饼图,可以修改`plot`方法的`kind`参数为`pie`,然后使用定制饼图的参数对图表加以定制,代码如下所示。
|
||||
|
||||
```Python
|
||||
temp.sort_values(by='销售额', ascending=False).plot(
|
||||
figsize=(6, 6), kind='pie', y='销售额',
|
||||
autopct='%.2f%%', pctdistance=0.8,
|
||||
wedgeprops=dict(linewidth=1, width=0.35)
|
||||
figsize=(6, 6),
|
||||
kind='pie',
|
||||
y='销售额',
|
||||
ylabel='',
|
||||
autopct='%.2f%%',
|
||||
pctdistance=0.8,
|
||||
wedgeprops=dict(linewidth=1, width=0.35),
|
||||
legend=False
|
||||
)
|
||||
plt.legend(loc='center')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20211106201550.png" style="zoom:50%">
|
||||
<img src="res/sales_pie_graph.png" style="zoom:35%;">
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
## 深入浅出pandas-5
|
||||
|
||||
我们再来补充一些使用`DataFrame`做数据分析时会使用到的操作,这些操作不仅常见而且也非常重要。
|
||||
|
||||
### 计算同比环比
|
||||
|
||||
我们之前讲过一个统计月度销售额的例子,我们可以通过`groupby`方法做分组聚合,也可以通过`pivot_table`生成透视表,如下所示。
|
||||
|
||||
```python
|
||||
sales_df = pd.read_excel('data/2020年销售数据.xlsx')
|
||||
sales_df['月份'] = sales_df.销售日期.dt.month
|
||||
sales_df['销售额'] = sales_df.售价 * sales_df.销售数量
|
||||
result_df = sales_df.pivot_table(index='月份', values='销售额', aggfunc='sum')
|
||||
result_df.rename(columns={'销售额': '本月销售额'}, inplace=True)
|
||||
result_df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
本月销售额
|
||||
月份
|
||||
1 5409855
|
||||
2 4608455
|
||||
3 4164972
|
||||
4 3996770
|
||||
5 3239005
|
||||
6 2817936
|
||||
7 3501304
|
||||
8 2948189
|
||||
9 2632960
|
||||
10 2375385
|
||||
11 2385283
|
||||
12 1691973
|
||||
```
|
||||
|
||||
在得到月度销售额之后,如果我们需要计算月环比,这里有两种方案。第一种方案是我们可以使用`shift`方法对数据进行移动,将上一个月的数据与本月数据对齐,然后通过`(本月销售额 - 上月销售额) / 上月销售额`来计算月环比,代码如下所示。
|
||||
|
||||
```python
|
||||
result_df['上月销售额'] = result_df.本月销售额.shift(1)
|
||||
result_df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
本月销售额 上月销售额
|
||||
月份
|
||||
1 5409855 NaN
|
||||
2 4608455 5409855.0
|
||||
3 4164972 4608455.0
|
||||
4 3996770 4164972.0
|
||||
5 3239005 3996770.0
|
||||
6 2817936 3239005.0
|
||||
7 3501304 2817936.0
|
||||
8 2948189 3501304.0
|
||||
9 2632960 2948189.0
|
||||
10 2375385 2632960.0
|
||||
11 2385283 2375385.0
|
||||
12 1691973 2385283.0
|
||||
```
|
||||
|
||||
在上面的例子中,`shift`方法的参数为`1`表示将数据向下移动一个单元,当然我们可以使用参数`-1`将数据向上移动一个单元。相信大家能够想到,如果我们有更多年份的数据,我们可以将参数设置为`12`,这样就可以计算今年的每个月与去年的每个月之间的同比。
|
||||
|
||||
```python
|
||||
result_df['环比'] = (result_df.本月销售额 - result_df.上月销售额) / result_df.上月销售额
|
||||
result_df.style.format(
|
||||
formatter={'上月销售额': '{:.0f}', '环比': '{:.2%}'},
|
||||
na_rep='--------'
|
||||
)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
本月销售额 上月销售额 环比
|
||||
月份
|
||||
1 5409855 -------- --------
|
||||
2 4608455 5409855 -14.81%
|
||||
3 4164972 4608455 -9.62%
|
||||
4 3996770 4164972 -4.04%
|
||||
5 3239005 3996770 -18.96%
|
||||
6 2817936 3239005 -13.00%
|
||||
7 3501304 2817936 24.25%
|
||||
8 2948189 3501304 -15.80%
|
||||
9 2632960 2948189 -10.69%
|
||||
10 2375385 2632960 -9.78%
|
||||
11 2385283 2375385 0.42%
|
||||
12 1691973 2385283 -29.07%
|
||||
```
|
||||
|
||||
> **说明**:使用 JupyterLab 时,可以通过`DataFrame`对象的`style`属性在网页中对其进行渲染,上面的代码通过`Styler`对象的`format`方法将环比格式化为百分比进行显示,此外还指定了将空值替换为`--------`。
|
||||
|
||||
更为简单的第二种方案是直接使用`pct_change`方法计算变化的百分比,我们先将之前的上月销售额和环比列删除掉。
|
||||
|
||||
```python
|
||||
result_df.drop(columns=['上月销售额', '环比'], inplace=True)
|
||||
```
|
||||
|
||||
接下来,我们使用`DataFrame`对象的`pct_change`方法完成环比的计算。值得一提的是,`pct_change`方法有一个名为`periods`的参数,它的默认值是`1`,计算相邻两项数据变化的百分比,这不就是我们想要的环比吗?如果我们有很多年的数据,在计算时把这个参数的值修改为`12`,就可以得到相邻两年的月同比。
|
||||
|
||||
```python
|
||||
result_df['环比'] = result_df.pct_change()
|
||||
result_df
|
||||
```
|
||||
|
||||
### 窗口计算
|
||||
|
||||
`DataFrame`对象的`rolling`方法允许我们将数据置于窗口中,然后用函数对窗口中的数据进行运算和处理。例如,我们获取了某只股票近期的数据,想制作5日均线和10日均线,那么就需要先设置窗口再进行运算。我们先用如下所示的代码读取2022年百度的股票数据,数据文件可以通过下面的链接来获取。
|
||||
|
||||
```Python
|
||||
baidu_df = pd.read_excel('data/2022年股票数据.xlsx', sheet_name='BIDU')
|
||||
baidu_df.sort_index(inplace=True)
|
||||
baidu_df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock.png" style="zoom:50%;">
|
||||
|
||||
上面的`DataFrame`有`Open`、`High`、`Low`、`Close`、`Volume`五个列,分别代表股票的开盘价、最高价、最低价、收盘价和成交量,接下来我们对百度的股票数据进行窗口计算。
|
||||
|
||||
```Python
|
||||
baidu_df.rolling(5).mean()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock_ma5.png" style="zoom:50%;">
|
||||
|
||||
我们也可以在`Series`上使用`rolling`设置窗口并在窗口内完成运算,例如我们可以对上面的百度股票收盘价(`Close`列)计算5日均线和10日均线,并使用`merge`函数将其组装到一个`DataFrame`对象中并绘制出双均线图,代码如下所示。
|
||||
|
||||
```Python
|
||||
close_ma5 = baidu_df.Close.rolling(5).mean()
|
||||
close_ma10 = baidu_df.Close.rolling(10).mean()
|
||||
result_df = pd.merge(close_ma5, close_ma10, left_index=True, right_index=True)
|
||||
result_df.rename(columns={'Close_x': 'MA5', 'Close_y': 'MA10'}, inplace=True)
|
||||
result_df.plot(kind='line', figsize=(10, 6))
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_double_MA.png" style="zoom:50%;">
|
||||
|
||||
### 相关性判定
|
||||
|
||||
在统计学中,我们通常使用协方差(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`之间。
|
||||
|
||||
$$
|
||||
\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 \approx 0 $,认为变量之间是不相关的,但并不代表两个指标是统计独立的。
|
||||
2. 判断指标间的相关程度。
|
||||
- 当 $ \rho $ 的绝对值在 $ [0.6,1] $ 之间,认为变量之间是强相关的。
|
||||
- 当 $ \rho $ 的绝对值在 $ [0.1,0.6) $ 之间,认为变量之间是弱相关的。
|
||||
- 当 $ \rho $ 的绝对值在 $ [0,0.1) $ 之间,认为变量之间没有相关性。
|
||||
|
||||
皮尔逊相关系数适用于:
|
||||
|
||||
1. 两个变量之间是线性关系,都是连续数据。
|
||||
2. 两个变量的总体是正态分布,或接近正态的单峰分布。
|
||||
3. 两个变量的观测值是成对的,每对观测值之间相互独立。
|
||||
|
||||
这里,我们顺便说一下,如果两组变量并不是来自于正态总体的连续值,我们该如何判断相关性呢?对于定序尺度(等级),我们可以使用斯皮尔曼秩相关系数,其计算公式如下所示:
|
||||
$$
|
||||
r_{s}=1-{\frac {6\sum d_{i}^{2}}{n(n^{2}-1)}}
|
||||
$$
|
||||
其中,$d_{i}=\operatorname {R} (X_{i})-\operatorname {R} (Y_{i})$,即每组观测中两个变量的等级差值,$n$为观测样本数。
|
||||
|
||||
对于定类尺度(类别),我们可以使用卡方检验的方式来判定其是否相关。其实很多时候,连续值也可以通过分箱的方式处理成离散的等级或类别,然后使用斯皮尔曼秩相关系数或卡方检验的方式来判定相关性。
|
||||
|
||||
`DataFrame`对象的`cov`方法和`corr`方法分别用于计算协方差和相关系数,`corr`方法有一个名为`method`的参数,其默认值是`pearson`,表示计算皮尔逊相关系数;除此之外,还可以指定`kendall`或`spearman`来计算肯德尔系数或斯皮尔曼秩相关系数。
|
||||
|
||||
我们从名为`boston_house_price.csv`的文件中获取著名的波士顿房价数据集来创建一个`DataFrame`。
|
||||
|
||||
```python
|
||||
boston_df = pd.read_csv('data/boston_house_price.csv')
|
||||
boston_df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="/Users/Hao/Desktop/Python-Data-Analysis/res/boston_house_price.png" style="zoom:50%;">
|
||||
|
||||
> **说明**:上面代码中使用了相对路径来访问 CSV 文件,也就是说 CSV 文件在当前工作路径下名为`data`的文件夹中。如果需要上面例子中的 CSV 文件,可以通过下面的百度云盘地址进行获取。链接:<https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g?pwd=e7b4>,提取码:e7b4。
|
||||
|
||||
可以看出,该数据集中包含了诸多影响房价的特征,包括犯罪率、一氧化氮浓度、平均房间数、低收入人群占比等,其中`PRICE`代表房价,具体情况如下所示。
|
||||
|
||||
<img src="res/boston_house_price_features.png" style="zoom:50%;">
|
||||
|
||||
接下来,我们将其中可以视为来自于正态总体的连续值,通过`corr`方法计算皮尔逊相关系数,看看哪些跟房价是正相关或负相关的关系,代码如下所示。
|
||||
|
||||
```Python
|
||||
boston_df[['NOX', 'RM', 'PTRATIO', 'LSTAT', 'PRICE']].corr()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/boston_person_correlation.png" style="zoom:50%;">
|
||||
|
||||
可以看出,平均房间数(`RM`)跟房价有较强的正相关性,而低收入人群占比(`LSTAT`)跟房价之间存在明显的负相关性。
|
||||
|
||||
斯皮尔曼秩相关系数对数据条件的要求没有皮尔逊相关系数严格,只要两个变量的观测值是成对的等级数据,或者是由连续变量转化成等级的数据,不论两个变量的总体分布形态、样本容量的大小如何,都可以用斯皮尔曼等级相关系数来进行研究。我们可以通过下面的方式对部分特征进行预处理,然后计算斯皮尔曼秩相关系数。
|
||||
|
||||
```Python
|
||||
boston_df['CRIM'] = boston_df.CRIM.apply(lambda x: x // 5 if x < 25 else 5).map(int)
|
||||
boston_df['ZN'] = pd.qcut(boston_df.ZN, q=[0, 0.75, 0.8, 0.85, 0.9, 0.95, 1], labels=np.arange(6))
|
||||
boston_df['AGE'] = (boston_df.AGE // 20).map(int)
|
||||
boston_df['DIS'] = (boston_df.DIS // 2.05).map(int)
|
||||
boston_df['B'] = (boston_df.B // 66).map(int)
|
||||
boston_df['PRICE'] = pd.qcut(boston_df.PRICE, q=[0, 0.15, 0.3, 0.5, 0.7, 0.85, 1], labels=np.arange(6))
|
||||
boston_df[['CRIM', 'ZN', 'AGE', 'DIS', 'B', 'PRICE']].corr(method='spearman')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/boston_spearman_correlation.png" style="zoom:50%;">
|
||||
|
||||
可以看出,房价跟犯罪率(`CRIM`)和房龄(`AGE`)之间存在较为明显的负相关关系,跟住房用地尺寸(`ZN`)存在微弱的正相关关系。相关性可以帮助我们在实际工作中找到业务抓手,即找到那些能够影响或改变工作结果的相关因素。
|
||||
|
|
@ -1,329 +0,0 @@
|
|||
## 概率统计基础
|
||||
|
||||
概率论源于赌博游戏。意大利文艺复兴时代,百科全书式的学者卡尔达诺(死后)发表的《论赌博游戏》被认为是第一部论述概率论的著作。到了17世纪的法国,宫廷贵族里盛行着掷骰子游戏,游戏规则是玩家连续掷4次骰子,如果其中没有6点出现,玩家赢,如果出现一次6点,则庄家(相当于现在的赌场)赢;后来为了使游戏更刺激,游戏规则发生了些许变化,玩家用2个骰子连续掷24次,不同时出现2个6点,玩家赢,否则庄家赢。在这样的时代背景下,法国数学家帕斯卡和费马创立了概率论,后来雅各布·伯努利发现,概率论远远不止用于赌博,他将他的思考和研究记录下来,写成了《猜度数》一书,提出了大数定理(**在一个随机事件中,随着试验次数的增加,事件发生的频率越趋近于一个稳定值**),这个定理在当时的保险公司得到了充分利用。
|
||||
|
||||
> **思考**:回到刚才的骰子游戏,按照旧玩法,庄家获胜的概率是多少?换成新玩法,庄家获胜的概率与之前的玩法相同吗?
|
||||
|
||||
以概率论为基础的统计学显然比概率论出现的时间更晚,而且一直以来都是一种尴尬的存在,处于各种鄙视链的底端。从数学的角度看,统计学中的数学原理过于肤浅;从应用科学的角度看,统计学太数学,跟应用沾不上边。卢瑟福(原子核物理学之父)曾经说过,“如果你的实验需要统计学,那么你应该再做一个更好的实验”;波普尔(20世纪最伟大的哲学家之一)也曾经对归纳逻辑进行过强烈的抨击。曾经,由于欧几里得、笛卡尔带给人们的完美体系实在太过迷人,导致很多人都忽视了统计思维这一重要的科学思维方式。但是最近十年时间,随着大数据和人工智能时代的来临,统计学又以惊人的速度流行起来,因为大数据时代已经充分证明了经验主义、归纳推理的强大之处;而人工智能实际上也是大数据加上深度学习的归纳方法所取得的成功。有了统计学,我们能够处有效的处理海量的数据,也能够正确理解数据分析的结果。
|
||||
|
||||
按照统计方法的不同,我们可以将统计学分类为描述统计学、推断统计学、贝叶斯统计学等。描述统计学是用来描绘或总结观察量的基本情况的统计方法,通常会将整理后的数据做成表格或图表,具体包括数据的集中趋势分析、离散趋势分析和相关分析。推断统计学是研究如何根据样本数据推断总体数据特征的方法,在无法获得全量数据的情况下,推断统计就是最为行之有效的方法。贝叶斯统计学的基础是贝叶斯定理,贝叶斯定理将经验和直觉与概率相关联,和人类大脑的判断原理十分类似,简单的说就是在获取到新的数据之后,先前凭借经验和直觉获得的概率是可以改变的。
|
||||
|
||||
### 数据和数据的分类
|
||||
|
||||
在统计学中,通过试验、观察、调查等获得的材料被称为数据,数据大致可以分为定性数据和定量数据,其中定性数据又可以分为定类尺度和定序尺度,定量数据又可以分为定距尺度和定比尺度,如下表所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220320232245.png" style="zoom:50%">
|
||||
|
||||
####定性数据的处理
|
||||
|
||||
1. 定类尺度(名义尺度):定类尺度通常会处理成虚拟变量(哑变量),多个不同的类型最终会变成一个虚拟变量矩阵。
|
||||
2. 定序尺度:定序尺度可以处理成一个序号,并通过该序号表示等级的高低。
|
||||
|
||||
#### 定量数据的处理
|
||||
|
||||
1. 线性归一化
|
||||
$$ x_i' = \frac {x_i - min(X)} {max(X) - min(X)} $$
|
||||
|
||||
2. 零均值归一化
|
||||
$$ x_i' = \frac {x_i - \mu} {\sigma} $$
|
||||
|
||||
### 数据的集中趋势
|
||||
|
||||
我们经常会使用以下几个指标来描述一组数据的集中趋势:
|
||||
|
||||
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. 中位数 - 将数据按照升序或降序排列后位于中间的数,它描述了数据的中等水平。中位数的计算分两种情况:
|
||||
- 当数据体量$n$为奇数时,中位数是位于$\frac{n + 1}{2}$位置的元素。
|
||||
- 当数据体量$n$为偶数时,中位数是位于$\frac{n}{2}$和${\frac{n}{2}+1}$两个位置元素的均值。
|
||||
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`、`GEOMEAN`、`MEDIAN`、`MODE.SNGL`、`MODE.MULT`函数分别计算均值、中位数和众数。求中位数也可以使用`QUARTILE.EXC`或`QUARTILE.INC`函数,将第二个参数设置为2即可。
|
||||
|
||||
对A组的数据进行一些调整。
|
||||
|
||||
```
|
||||
A组:5, 6, 6, 6, 6, 8, 10, 500
|
||||
B组:3, 5, 5, 6, 6, 9, 12
|
||||
```
|
||||
|
||||
A组的均值会大幅度提升,但中位数和众数却没有变化。
|
||||
|
||||
| | 优点 | 缺点 |
|
||||
| ------ | -------------------------------- | ------------------------------------ |
|
||||
| 均值 | 充分利用了所有数据,适应性强 | 容易收到极端值(异常值)的影响 |
|
||||
| 中位数 | 能够避免被极端值(异常值)的影响 | 不敏感 |
|
||||
| 众数 | 能够很好的反映数据的集中趋势 | 有可能不存在(数据没有明显集中趋势) |
|
||||
|
||||
### 数据的离散趋势
|
||||
|
||||
如果说数据的集中趋势,说明了数据最主要的特征是什么;那么数据的离散趋势,则体现了这个特征的稳定性。简单的说就是数据越集中,均值的代表性就越强;数据波动越大,均值的代表性就越弱。
|
||||
|
||||
1. 极值:就是最大值(maximum)、最小值(minimum),代表着数据集的上限和下限。
|
||||
|
||||
> **说明**:Excel 中,计算极值的函数分别是`MAX`和`MIN`。
|
||||
|
||||
2. 极差:又称“全距”,是一组数据中的最大观测值和最小观测值之差,记作$R$。一般情况下,极差越大,离散程度越大,数据受极值的影响越严重。
|
||||
|
||||
3. 四分位距离:$\small{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`。
|
||||
|
||||
### 数据的频数分析
|
||||
|
||||
用一定的方式将数据分组,然后统计每个分组中样本的数量,再辅以图表(如直方图)就可以更直观的展示数据分布趋势的一种方法。
|
||||
|
||||
频数分析的业务意义:
|
||||
|
||||
1. 大问题变小问题,迅速聚焦到需要关注的群体。
|
||||
2. 找到合理的分类机制,有利于长期的数据分析(维度拆解)。
|
||||
|
||||
例如:一个班有50个学生,考试成绩如下所示:
|
||||
|
||||
```
|
||||
87, 80, 79, 78, 55, 80, 81, 60, 78, 82, 67, 74, 67, 74, 66, 91, 100, 70, 82, 71, 77, 94, 75, 83, 85, 84, 47, 75, 84, 96, 53, 86, 86, 89, 71, 76, 75, 80, 70, 83, 77, 91, 90, 82, 74, 74, 78, 53, 88, 72
|
||||
```
|
||||
|
||||
在获得数据后,我们先解读数据的集中趋势和离散趋势。
|
||||
|
||||
均值:`77.4`,中位数:`78.0`,众数:`74`。
|
||||
|
||||
最高分:`100`,最低分:`47`,极差:`53`,方差:`120.16`。
|
||||
|
||||
但是,仅仅依靠上面的指标是很难对一个数据集做出全面的解读,我们可以把学生按照考试成绩进行分组,如下所示。
|
||||
|
||||
| 分数段 | 学生人数 |
|
||||
| -------- | -------- |
|
||||
| <60 | 4 |
|
||||
| [60, 65) | 1 |
|
||||
| [65, 70) | 3 |
|
||||
| [70, 75) | 9 |
|
||||
| [75, 80) | 10 |
|
||||
| [80, 85) | 11 |
|
||||
| [85, 90) | 6 |
|
||||
| [90, 95) | 4 |
|
||||
| >=95 | 2 |
|
||||
|
||||
我们可以利用直方图来查看数据分布的形态,对数据分布形态的测度主要以正态分布为标准进行衡量,正态分布在坐标轴上的形状是一个铃铛型(钟型),正态曲线以均值为中心左右对称,如下图所示,而上面的学生考试成绩数据就呈现出正态分布的轮廓。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210716155507.png" width="80%">
|
||||
|
||||
我们可以数据分布的直方图拟合出一条曲线与正态曲线进行比较,主要比较曲线的尖峭程度和对称性,通常称之为峰度和偏态。数据分布的不对称性称为偏态,偏态又分为正偏(右偏)或负偏(左偏)两种。在正态分布的情况下,中位数和均值应该都在对称轴的位置,如果中位数在左边,均值在右边,那么数据的极端值也在右边,数据分布曲线向右延伸,就是我们说的右偏;如果均值在左边,中位数在右边,那么数据的极端值在左边,数据分布曲线向左延伸,就是我们说的左偏。测定偏态的指标是偏态系数,Excel 中计算偏度系数使用的公式如下所示:
|
||||
$$
|
||||
SK = \frac{n}{(n - 1)(n - 2)} \sum(\frac{x_i - \bar{x}}{s})^3
|
||||
$$
|
||||
$\small{SK > 0}$时,分布呈现正偏,SK值越大,正偏程度越高。
|
||||
|
||||
$\small{SK < 0}$时,分布呈现负偏,SK值越小,负偏程度越高。
|
||||
|
||||
峰度是指数据分布的尖峭程度,一般可以表现为尖顶峰度、平顶峰度和标准峰度(正态分布的峰度)。测定峰度的指标是峰度系数,Excel 中计算峰度系数使用的公式如下所示:
|
||||
$$
|
||||
K = \frac{n(n + 1)}{(n - 1)(n - 2)(n - 3)}\sum(\frac{x_i - \bar{x}}{s})^4-\frac{3(n - 1)^2}{(n - 2)(n - 3)}
|
||||
$$
|
||||
峰度系数$\small{K < 0}$时,分布与正态分布相比更为扁平、宽肩、瘦尾;峰度系数$\small{K > 0}$时,分布与正态分布相比更为尖峰、瘦肩、肥尾。
|
||||
|
||||
### 数据的概率分布
|
||||
|
||||
#### 基本概念
|
||||
|
||||
1. 随机现象:在一定条件下可能发生也可能不发生,结果具有偶然性的现象。
|
||||
|
||||
2. 样本空间(*sample space*):随机现象一切可能的结果组成的集合。
|
||||
|
||||
- 抛一枚硬币的样本空间:$\Omega = \{ \omega_1, \omega_2 \}$。
|
||||
- 抛两枚硬币的样本空间:$\Omega = \{ \omega_1, \omega_2, \omega_3, \omega_4 \}$,其中$\omega_1 = (H, H)$,$\omega_2 = (H, T)$,$\omega_3 = (T, H)$,$\omega_4 = (T, T)$。
|
||||
- 离散型的样本空间的元素是可列的,连续型的样本空间的元素是(无限)不可列的。
|
||||
|
||||
3. 随机试验(*trials*):在相同条件下对某种随机现象进行观测的试验。随机试验满足三个特点:
|
||||
|
||||
- 可以在相同条件下重复的进行。
|
||||
|
||||
- 每次试验的结果不止一个,事先可以明确指出全部可能的结果。
|
||||
|
||||
- 重复试验的结果以随机的方式出现(事先不确定会出现哪个结果)。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220322075000.png" style="zoom:75%">
|
||||
|
||||
4. 随机变量(*random variable*):如果$X$指定给概率空间$S$中每一个事件$e$有一个实数$X(e)$,同时针对每一个实数$r$都有一个事件集合$A_r$与其相对应,其中$A_r=\{e: X(e) \le r\}$,那么$X$被称作随机变量。从这个定义看出,$X$的本质是一个实值函数,以给定事件为自变量的实值函数,因为函数在给定自变量时会产生因变量,所以将$X$称为随机变量。简单的说,随机变量的值需要通过试验来确认。
|
||||
|
||||
- 离散型随机变量:数据可以一一列出。
|
||||
- 连续型随机变量:数据不可以一一列出。
|
||||
|
||||
> **说明**:如果离散型随机变量的取值非常庞大时,可以近似看做连续型随机变量。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220322075148.png" style="zoom:50%;">
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220322075331.png" style="zoom:50%;">
|
||||
|
||||
5. 概率(*probability*):用一个0~1之间的数字表示随机现象发生的可能性,也就是说概率是随机事件出现可能性的度量。
|
||||
|
||||
6. 概率质量函数/概率密度函数:概率质量函数是描述离散型随机变量为特定取值的概率的函数,通常缩写为**PMF**。概率密度函数是描述连续型随机变量在某个确定的取值点可能性的函数,通常缩写为**PDF**。二者的区别在于,概率密度函数本身不是概率,只有对概率密度函数在某区间内进行积分后才是概率。
|
||||
|
||||
7. 随机变量的数字特征:
|
||||
|
||||
- (数学)期望:随机变量按照概率的加权平均,它表示了概率分布的中心位置,反映随机变量平均取值的大小。
|
||||
|
||||
对于离散型随机变量$ X $,若$ \sum_{i=1}^{\infty} x_ip_i $收敛,那么它就是随机变量$ X $的期望,记为$ E(X) $,即$ E(X) = \sum_{i=1}^{\infty} x_ip_i $,否则随机变量$ X $的期望不存在。
|
||||
|
||||
对于连续型随机变量$ X $,其概率密度函数为$ f(x) $,若$ \int_{-\infty}^{\infty}xf(x)dx $收敛,则称$ E(x) = \int_{-\infty}^{\infty}xf(x)dx $为随机变量$ X $的数学期望,否则随机变量$ X $的期望不存在。
|
||||
|
||||
- 方差:方差用来表示随机变量概率分布的离散程度,对于随机变量$ X $,若$ E((X - E(X))^2) $存在,则称$ E((X - E(X))^2) $为$ X $的方差,记为$ Var(X) $。很显然,离散型随机变量$ X $的方差为$ Var(X) = \sum_{i=1}^{\infty} [x_i - E(X)]^2p_i$,连续型随机变量$ X $的方差为$ Var(X) = \int_{-\infty}^{\infty} [x - E(X)]^2f(x)dx $。
|
||||
|
||||
8. 期望与方差的性质:
|
||||
|
||||
- 对于任意两个随机变量$ X_1 $和$ X_2 $,则有$ E(X_1 + X_2) = E(X_1) + E(X_2) $。
|
||||
- 若$ X $是随机变量,$ a $和$ b $是任意常量,则有$ E(aX + b) = aE(X) + b $和$ Var(aX + b) = a^2Var(X)$。
|
||||
- 若随机变量$ X_1 $和$ X_2 $独立,则有$ Var(X_1 + X_2) = Var(X_1) + Var(X_2) $。
|
||||
|
||||
9. 其他零碎小概念:
|
||||
|
||||
- 互斥(*mutually exclusive*):事件不能同时发生。
|
||||
- 独立(*independant*):一个试验的结果不会对另一个试验的结果产生影响。
|
||||
- 排列(permutation):$ P_k^n = \frac{n!}{(n - k)!} $,国内教科书一般记为$ P_n^k $或$ A_n^k $。
|
||||
- 组合(*combination*):$ C_k^n = \frac{n!}{k!(n-k)!} $,国内教科书一般记为$ C_n^k $。
|
||||
|
||||
#### 离散型分布
|
||||
|
||||
1. 伯努利分布(*Bernoulli distribution*):又名**两点分布**或者**0-1分布**,是一个离散型概率分布。若伯努利试验成功,则随机变量取值为1。若伯努利试验失败,则随机变量取值为0。记其成功概率为$ p (0 \le p \le 1) $,失败概率为$ q=1-p $,则概率质量函数为:
|
||||
|
||||
$$ f(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 $次成功的概率由概率质量函数给出,
|
||||
$$ P(X=k) = C_k^np^k(1-p)^{n-k} $$
|
||||
|
||||
> **提示**:Excel 中,可以通过`BINOM.DIST.RANGE`函数计算二项分布的概率。
|
||||
|
||||
3. 泊松分布(*Poisson distribution*):适合于描述单位时间内随机事件发生的次数的概率分布。如某一服务设施在一定时间内受到的服务请求的次数、汽车站台的候客人数、机器出现的故障数、自然灾害发生的次数、DNA序列的变异数、放射性原子核的衰变数等等。泊松分布的概率质量函数为:$P(X=k)=\frac{e^{-\lambda}\lambda^k}{k!}$,泊松分布的参数$\lambda$是单位时间(或单位面积)内随机事件的平均发生率。
|
||||
|
||||
> **说明**:泊松分布是在没有计算机的年代,由于二项分布的运算量太大运算比较困难,为了减少运算量,数学家为二项分布提供的一种近似。当二项分布的$n$很大,$p$很小的时候,我们可以让$\lambda = np$,然后用泊松分布的概率质量函数计算概率来近似二项分布的概率。
|
||||
|
||||
#### 分布函数
|
||||
|
||||
对于连续型随机变量,我们不可能去罗列每一个值出现的概率,因此要引入分布函数的概念。
|
||||
$$
|
||||
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}{{\sqrt {2 \pi } \sigma}}}e^{-{\frac {\left(x-\mu \right)^{2}}{2\sigma ^{2}}}}$。
|
||||
|
||||
根据“棣莫弗-拉普拉斯积分定理”,假设$ \mu_{n} (n=1, 2, \cdots) $表示$ n $重伯努利试验中成功的次数,已知每次试验成功的概率为$p$,那么:
|
||||
$$ \lim_{n \to \infty} P\lbrace \frac{\mu_n - np} {\sqrt{np(1-p)}} \le x \rbrace = \frac {1} {\sqrt{2\pi}} \int_{-\infty}^{x} e^{-\frac {\mu^2} {2}} dx $$,该定理表明正态分布是二项分布的极限分布。
|
||||
|
||||
|
||||
提到正态分布,就必须说一下“3$\sigma$法则”,该法则也称为“68-95-99.7”法则,如下图所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210716155542.png" style="zoom:65%">
|
||||
|
||||
正态分布有一个非常重要的性质,**大量统计独立的随机变量的平均值的分布趋于正态分布**,即$ \bar{X} \sim N(\mu, \frac{\sigma^2}{n}) $这就是**中心极限定理**。中心极限定理的重要意义在于,我们可以用正态分布作为其他概率分布的近似。
|
||||
|
||||
一个例子:假设某校入学新生的智力测验平均分数与标准差分别为 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 $。
|
||||
|
||||
|
||||
> **说明**:上面标准正态分布的概率值可以查表得到,在 Excel 中可以使用`NORM.DIST`函数获得。例如在上面的例子中,我们可以通过`NORM.DIST(2.94, 0, 1, TRUE)`获得$P(z\le2.94)$的概率为`0.998359`。
|
||||
|
||||
#### 基于正态分布的三大分布
|
||||
|
||||
1. 卡方分布(*Chi-square distribution*):若$k$个随机变量$Z_1,Z_2,...,Z_k$是相互独立且服从标准正态分布$N(0, 1)$的随机变量,则随机变量$X = \sum_{i=1}^{k}Z_i^2$被称为服从自由度为$k$的卡方分布,记为$X \sim \chi^2(k)$。卡方分布的概率密度曲线如下所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220323201608.png" style="zoom:50%;">
|
||||
|
||||
2. $t$分布:设$X \sim N(0, 1)$, $Y \sim {\chi}^2(n)$,且$X$与$Y$相互独立,则随机变量$T = \frac {X} {\sqrt{Y/n}}$称为自由度为$n$的$t$分布,记作$T \sim t(n)$。$t$分布的概率密度曲线如下所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220323203530.png" style="zoom:50%">
|
||||
|
||||
3. $F$分布:设$X \sim \chi^2(n_1)$,$Y \sim \chi^2(n_2)$,且$X$与$Y$相互独立,则随机变量$F = \frac{X / n_1}{Y / n_2}$称为自由度为$(n_1, n_2)$的$F$分布,记作$F \sim F(n_1, n_2)$,它的概率密度曲线如下所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220619164716.png" style="zoom: 50%;">
|
||||
|
||||
这三个分布有什么用呢?
|
||||
|
||||
1. $ \chi^2 $分布:常用于独立性检验、拟合优度检验。
|
||||
2. $ F $分布:常用于比例的估计和检验,方差分析和回归分析中也会用到$ F $分布。
|
||||
3. $ t $分布:在信息不足的情况下,要对总体均值进行估计和检验,就会使用到$ t $分布。
|
||||
|
||||
### 其他内容
|
||||
|
||||
#### 贝叶斯定理
|
||||
|
||||
**联合概率**是指事件A和事件B共同发生的概率,通常记为$\small{P(A \cap B)}$。
|
||||
|
||||
**条件概率**是指事件A在事件B发生的条件下发生的概率,通常记为$\small{P(A|B)}$。设A与B为样本空间$\Omega$中的两个事件,其中$\small{P(B) \gt 0}$。那么在事件B发生的条件下,事件A发生的条件概率为:${P(A|B)=\frac{P(A \cap B)}{P(B)}}$,当$ P(B)=0 $时,规定$ P(A|B) = 0 $。
|
||||
|
||||
> **思考**:
|
||||
>
|
||||
> 1. 某家庭有两个孩子,问两个孩子都是女孩的概率是多少?
|
||||
> 2. 某家庭有两个孩子,已知其中一个是女孩,问两个孩子都是女孩的概率是多少?
|
||||
> 3. 某家庭有两个孩子,已知老大是女孩,问两个孩子都是女孩的概率是多少?
|
||||
|
||||
事件A在事件B已发生的条件下发生的概率,与事件B在事件A已发生的条件下发生的概率是不一样的。然而,这两者是有确定的关系的,**贝叶斯定理**就是对这种关系的陈述,如下所示:
|
||||
$$
|
||||
P(A|B)=\frac{P(B|A)}{P(B)}P(A)
|
||||
$$
|
||||
|
||||
- $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$。
|
||||
|
||||
#### 假设检验
|
||||
|
||||
假设检验就是通过抽取样本数据,并且通过**小概率反证法**去验证整体情况的方法。假设检验的核心思想是小概率反证法(首先假设想推翻的命题是成立的,然后试图找出矛盾,找出不合理的地方来证明命题为假命题),即在**零假设**(通常记为$H_0$)的前提下,估算某事件发生的可能性,如果该事件是小概率事件,在一次试验中本不应该发生,但现在却发生了,此时我们就有足够的理由怀疑零假设,转而接受**备择假设**(通常记为$H_A$)。
|
||||
|
||||
假设检验会存在两种错误情况,一种称为“拒真”,一种称为“取伪”。如果原假设是对的,但你拒绝了原假设,这种错误就叫作“拒真”,这个错误的概率也叫作显著性水平$\alpha$,或称为容忍度;如果原假设是错的,但你承认了原假设,这种错误就叫作“取伪”,这个错误的概率我们记为$\beta$。
|
||||
|
||||
### 总结
|
||||
|
||||
描述性统计通常用于研究表象,将现象用数据的方式描述出来(用整体的数据来描述整体的特征);推理性统计通常用于推测本质(通过样本数据特征去推理总体数据特征),也就是你看到的表象的东西有多大概率符合你对隐藏在表象后的本质的猜测。
|
|
@ -0,0 +1,471 @@
|
|||
## 深入浅出pandas-6
|
||||
|
||||
我们再来看看`Index`类型,它为`Series`和`DataFrame`对象提供了索引服务,有了索引我们就可以排序数据(`sort_index`方法)、对齐数据(在运算和合并数据时非常重要)并实现对数据的快速检索(索引运算)。由于`DataFrame`类型表示的是二维数据,所以它的行和列都有索引,分别是`index`和`columns`。`Index`类型的创建的比较简单,通常给出`data`、`dtype`和`name`三个参数即可,分别表示作为索引的数据、索引的数据类型和索引的名称。由于`Index`本身也是一维的数据,索引它的方法和属性跟`Series`非常类似,你可以尝试创建一个`Index`对象,然后尝试一下之前学过的属性和方法在`Index`类型上是否生效。接下来,我们主要看看`Index`的几种子类型。
|
||||
|
||||
### 范围索引
|
||||
|
||||
范围索引是由具有单调性的整数构成的索引,我们可以通过`RangeIndex`构造器来创建范围索引,也可以通过`RangeIndex`类的类方法`from_range`来创建范围索引,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
sales_data = np.random.randint(400, 1000, 12)
|
||||
index = pd.RangeIndex(1, 13, name='月份')
|
||||
ser = pd.Series(data=sales_data, index=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
|
||||
```
|
||||
|
||||
### 分类索引
|
||||
|
||||
分类索引是由定类尺度构成的索引。如果我们需要通过索引将数据分组,然后再进行聚合操作,分类索引就可以派上用场。分类索引还有一个名为`reorder_categories`的方法,可以给索引指定一个顺序,分组聚合的结果会按照这个指定的顺序进行呈现,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
sales_data = [6, 6, 7, 6, 8, 6]
|
||||
index = pd.CategoricalIndex(
|
||||
data=['苹果', '香蕉', '苹果', '苹果', '桃子', '香蕉'],
|
||||
categories=['苹果', '香蕉', '桃子'],
|
||||
ordered=True
|
||||
)
|
||||
ser = pd.Series(data=sales_data, index=index)
|
||||
ser
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
苹果 6
|
||||
香蕉 6
|
||||
苹果 7
|
||||
苹果 6
|
||||
桃子 8
|
||||
香蕉 6
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
基于索引分组数据,然后使用`sum`进行求和。
|
||||
|
||||
```Python
|
||||
ser.groupby(level=0).sum()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
苹果 19
|
||||
香蕉 12
|
||||
桃子 8
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
指定索引的顺序。
|
||||
|
||||
```python
|
||||
ser.index = index.reorder_categories(['香蕉', '桃子', '苹果'])
|
||||
ser.groupby(level=0).sum()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
香蕉 12
|
||||
桃子 8
|
||||
苹果 19
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
### 多级索引
|
||||
|
||||
Pandas 中的`MultiIndex`类型用来表示层次或多级索引。可以使用`MultiIndex`类的类方法`from_arrays`、`from_product`、`from_tuples`等来创建多级索引,我们给大家举几个例子。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
tuples = [(1, 'red'), (1, 'blue'), (2, 'red'), (2, 'blue')]
|
||||
index = pd.MultiIndex.from_tuples(tuples, names=['no', 'color'])
|
||||
index
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
MultiIndex([(1, 'red'),
|
||||
(1, 'blue'),
|
||||
(2, 'red'),
|
||||
(2, 'blue')],
|
||||
names=['no', 'color'])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
arrays = [[1, 1, 2, 2], ['red', 'blue', 'red', 'blue']]
|
||||
index = pd.MultiIndex.from_arrays(arrays, names=['no', 'color'])
|
||||
index
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
MultiIndex([(1, 'red'),
|
||||
(1, 'blue'),
|
||||
(2, 'red'),
|
||||
(2, 'blue')],
|
||||
names=['no', 'color'])
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
sales_data = np.random.randint(1, 100, 4)
|
||||
ser = pd.Series(data=sales_data, index=index)
|
||||
ser
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
no color
|
||||
1 red 43
|
||||
blue 31
|
||||
2 red 55
|
||||
blue 75
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
ser.groupby('no').sum()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
no
|
||||
1 74
|
||||
2 130
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
ser.groupby(level=1).sum()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
color
|
||||
blue 106
|
||||
red 98
|
||||
dtype: int64
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
stu_ids = np.arange(1001, 1006)
|
||||
semisters = ['期中', '期末']
|
||||
index = pd.MultiIndex.from_product((stu_ids, semisters), names=['学号', '学期'])
|
||||
courses = ['语文', '数学', '英语']
|
||||
scores = np.random.randint(60, 101, (10, 3))
|
||||
df = pd.DataFrame(data=scores, columns=courses, index=index)
|
||||
df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
语文 数学 英语
|
||||
学号 学期
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
### 间隔索引
|
||||
|
||||
间隔索引顾名思义是使用固定的间隔范围充当索引,我们通常会使用`interval_range`函数来创建间隔索引,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
index = pd.interval_range(start=0, end=5)
|
||||
index
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]], dtype='interval[int64, right]')
|
||||
```
|
||||
|
||||
`IntervalIndex`有一个名为`contains`的方法,可以检查范围内是否包含了某个元素,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
index.contains(1.5)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([False, True, False, False, False])
|
||||
```
|
||||
|
||||
`IntervalIndex`还有一个名为`overlaps`的方法,可以检查一个范围跟其他的范围是否有重叠,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
index.overlaps(pd.Interval(1.5, 3.5))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
array([False, True, True, True, False])
|
||||
```
|
||||
|
||||
如果希望间隔范围是左闭右开的状态,可以在创建间隔索引时通过`closed='left'`来做到;如果希望两边都是关闭状态,可以将`close`参数的值赋值为`both`,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
index = pd.interval_range(start=0, end=5, closed='left')
|
||||
index
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
IntervalIndex([[0, 1), [1, 2), [2, 3), [3, 4), [4, 5)], dtype='interval[int64, left]')
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
index = pd.interval_range(start=pd.Timestamp('2022-01-01'), end=pd.Timestamp('2022-01-04'), closed='both')
|
||||
index
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
IntervalIndex([[2022-01-01, 2022-01-02], [2022-01-02, 2022-01-03], [2022-01-03, 2022-01-04]], dtype='interval[datetime64[ns], both]')
|
||||
```
|
||||
|
||||
|
||||
### 日期时间索引
|
||||
|
||||
`DatetimeIndex`应该是众多索引中最复杂最重要的一种索引,我们通常会使用`date_range()`函数来创建日期时间索引,该函数有几个非常重要的参数`start`、`end`、`periods`、`freq`、`tz`,分别代表起始日期时间、结束日期时间、生成周期、采样频率和时区。我们先来看看如何创建`DatetimeIndex`对象,再来讨论它的相关运算和操作,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
pd.date_range('2021-1-1', '2021-6-30', periods=10)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
DatetimeIndex(['2021-01-01', '2021-01-21', '2021-02-10', '2021-03-02',
|
||||
'2021-03-22', '2021-04-11', '2021-05-01', '2021-05-21',
|
||||
'2021-06-10', '2021-06-30'],
|
||||
dtype='datetime64[ns]', freq=None)
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
pd.date_range('2021-1-1', '2021-6-30', freq='W')
|
||||
```
|
||||
|
||||
> **说明**:`freq=W`表示采样周期为一周,它会默认星期日是一周的开始;如果你希望星期一表示一周的开始,你可以将其修改为`freq=W-MON`;你也可以试着将该参数的值修改为`12H`,`M`,`Q`等,看看会发生什么,相信你不难猜到它们的含义。
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
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', '2021-06-06', '2021-06-13',
|
||||
'2021-06-20', '2021-06-27'],
|
||||
dtype='datetime64[ns]', freq='W-SUN')
|
||||
```
|
||||
|
||||
`DatatimeIndex`可以跟`DateOffset`类型进行运算,这一点很好理解,以为我们可以设置一个时间差让时间向前或向后偏移,具体的操作如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
index = pd.date_range('2021-1-1', '2021-6-30', 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', '2021-06-04', '2021-06-11',
|
||||
'2021-06-18', '2021-06-25'],
|
||||
dtype='datetime64[ns]', freq=None)
|
||||
```
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
index + pd.DateOffset(hours=2, minutes=10)
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
DatetimeIndex(['2021-01-03 02:10:00', '2021-01-10 02:10:00',
|
||||
'2021-01-17 02:10:00', '2021-01-24 02:10:00',
|
||||
'2021-01-31 02:10:00', '2021-02-07 02:10:00',
|
||||
'2021-02-14 02:10:00', '2021-02-21 02:10:00',
|
||||
'2021-02-28 02:10:00', '2021-03-07 02:10:00',
|
||||
'2021-03-14 02:10:00', '2021-03-21 02:10:00',
|
||||
'2021-03-28 02:10:00', '2021-04-04 02:10:00',
|
||||
'2021-04-11 02:10:00', '2021-04-18 02:10:00',
|
||||
'2021-04-25 02:10:00', '2021-05-02 02:10:00',
|
||||
'2021-05-09 02:10:00', '2021-05-16 02:10:00',
|
||||
'2021-05-23 02:10:00', '2021-05-30 02:10:00',
|
||||
'2021-06-06 02:10:00', '2021-06-13 02:10:00',
|
||||
'2021-06-20 02:10:00', '2021-06-27 02:10:00'],
|
||||
dtype='datetime64[ns]', freq=None)
|
||||
```
|
||||
|
||||
如果`Series`对象或`DataFrame`对象使用了`DatetimeIndex`类型的索引,此时我们可以通过`asfreq()`方法指定一个时间频率来实现对数据的抽样,我们仍然以之前讲过的百度股票数据为例,给大家做一个演示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df = pd.read_excel('data/2022年股票数据.xlsx', sheet_name='BIDU', index_col='Date')
|
||||
baidu_df.sort_index(inplace=True)
|
||||
baidu_df.asfreq('5D')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock_asfreq.png" style="zoom:50%;">
|
||||
|
||||
大家可能注意到了,每5天抽取1天有可能会抽中非交易日,那么对应的列都变成了空值,为了解决这个问题,在使用`asfreq`方法时可以通过`method`参数来指定一种填充空值的方法,可以将相邻的交易日的数据填入进来。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.asfreq('5D', method='ffill')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock_asfreq_ffill.png" style="zoom:50%;">
|
||||
|
||||
当使用`DatetimeIndex`索引时,我们也可以通过`resample()`方法基于时间对数据进行重采样,相当于根据时间周期对数据进行了分组操作,分组之后还可以进行聚合统计,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.resample('1M').mean()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock_resample.png" style="zoom:50%;">
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
baidu_df.resample('1M').agg(['mean', 'std'])
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock_resample_agg.png" style="zoom:100%;">
|
||||
|
||||
> **提示**:不知大家是否注意到,上面输出的`DataFrame` 的列索引是一个`MultiIndex`对象。你可以访问上面的`DataFrame`对象的`columns`属性看看。
|
||||
|
||||
如果要实现日期时间的时区转换,我们可以先用`tz_localize()`方法将日期时间本地化,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df = baidu_df.tz_localize('Asia/Chongqing')
|
||||
baidu_df
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock_tz_localize.png" style="zoom:50%;">
|
||||
|
||||
在对时间本地化以后,我们再使用`tz_convert()`方法就可以实现转换时区,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
baidu_df.tz_convert('America/New_York')
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/baidu_stock_tz_convert.png" style="zoom:50%;">
|
||||
|
||||
如果你的数据使用了`DatetimeIndex`类型的索引,那么你就很有可能要对数据进行时间序列分析,关于时间序列分析的方法和模型并不是本章节要探讨的内容,我们在其他的专栏中为大家分享。
|
|
@ -1,41 +1,37 @@
|
|||
## 数据可视化-1
|
||||
|
||||
在完成了对数据的透视之后,我们可以将数据透视的结果通过可视化的方式呈现出来,简单的说,就是将数据变成漂亮的统计图表,然后进一步发现和解读数据背后隐藏的商业价值。在之前的课程中,我们已经为大家展示过用使用`Series`或`DataFrame`对象的`plot`方法生成可视化图表的操作,本章我们为大家讲解`plot`方法的基石,它就是大名鼎鼎的`matplotlib`库。
|
||||
在完成了对数据的透视之后,我们可以将数据透视的结果通过可视化的方式呈现出来,简单的说,就是将数据变成漂亮的统计图表,因为人类对颜色和形状会更加敏感,然后再进一步解读数据背后隐藏的商业价值。在之前的课程中,我们已经为大家展示过用使用`Series`或`DataFrame`对象的`plot`方法生成可视化图表的操作,本章我们为大家讲解这个绘图方法的基石,它就是大名鼎鼎的 matplotlib 库。
|
||||
|
||||
### 常用的图表类型
|
||||
在讲解 matplotlib 之前,请大家先看看下面这张图,它给出了常用的图表类型及其应用场景。我们在选择统计图表时,如果不知道做出怎样的选择最合适,相信这张图就能帮到你。简单的说,看趋势折线图,比数据柱状图,定关系散点图,查占比饼状图,看分布直方图,找离群箱线图。
|
||||
|
||||
常用的图表类型及其应用场景如下图所示。
|
||||
<img src="res/choose_your_chart.png" style="zoom:65%;">
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220315193326.png" style="zoom:65%">
|
||||
### 导入和配置
|
||||
|
||||
### Matplotlib 的安装和导入
|
||||
之前的课程中,我们为大家讲解过如何安装和导入 matplotlib 库,如果不确定是否已经安装了 matplotlib,可以使用下面的魔法指令尝试安装或升级你的 matplotlib。
|
||||
|
||||
如果还没有安装`matplotlib`库,可以使用 Python 的包管理工具 pip 来安装,命令如下所示。
|
||||
|
||||
```Shell
|
||||
pip install matplotlib
|
||||
```
|
||||
%pip install -U matplotlib
|
||||
```
|
||||
|
||||
在 Notebook 中,我们可以用下面的方式导入`matplotlib`。为了解决图表中文显示的问题,我们可以通过`pyplot`模块的`rcParams`属性修改配置参数,具体的操作如下所示。
|
||||
为了解决 matplotlib 图表中文显示的问题,我们需要修改`pyplot`模块的`rcParams`配置参数,具体的操作如下所示。
|
||||
|
||||
```Python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei', 'Songti SC']
|
||||
plt.rcParams['font.sans-serif'].insert(0, 'SimHei')
|
||||
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`,这样才能让坐标轴上的负号正常显示。
|
||||
> **说明**:上面代码中的`SimHei`是字体名称,大家可以通过百度云盘下载并安装该字体,链接地址:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g?pwd=e7b4。你可以尝试安装其他的中文字体,安装之后如果不知道字体叫什么名字,可以到用户主目录下名为`.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)。
|
||||
通过下面的魔法指令,我们可以在绘图时生成[矢量图](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`对象,它代表了绘图使用的画布,我们可以基于画布来创建绘图使用的坐标系。
|
||||
|
||||
|
@ -43,20 +39,22 @@ plt.rcParams['axes.unicode_minus'] = False
|
|||
plt.figure(figsize=(8, 4), dpi=120, facecolor='darkgray')
|
||||
```
|
||||
|
||||
#### 创建坐标系
|
||||
### 创建坐标系
|
||||
|
||||
可以直接使用`pyplot`模块的`subplot`函数来创建坐标系,该函数会返回`Axes`对象。`subplot`的前三个参数分别用来指定整个画布分成几行几列以及当前坐标系的索引,这三个参数的默认值都是`1`。如果需要在画布上创建多个坐标系,就需要使用该函数,否则就直接使用默认的也是唯一的坐标系。当然,也可以通过上面创建的`Figure`对象的`add_subplot`方法或`add_axes`方法来创建坐标系,前者跟`subplot`函数的作用一致,后者会产生嵌套的坐标系。
|
||||
可以直接使用`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
|
||||
|
||||
|
@ -71,10 +69,14 @@ 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%;">
|
||||
输出:
|
||||
|
||||
<img src="res/20220501173344.png" style="zoom:50%;">
|
||||
|
||||
如果要在一个坐标系上同时绘制正弦和余弦曲线,可以对上面的代码稍作修改。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
x = np.linspace(-2 * np.pi, 2 * np.pi, 120)
|
||||
y1, y2 = np.sin(x), np.cos(x)
|
||||
|
@ -82,7 +84,7 @@ 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是定制箭头样式的参数
|
||||
# 定制图表上的标注(annotate函数的参数如果不理解可以先不管它)
|
||||
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'
|
||||
})
|
||||
|
@ -92,10 +94,14 @@ plt.annotate('cos(x)', xytext=(-3, 0.75), xy=(-1.25, 0.5), fontsize=12, arrowpro
|
|||
plt.show()
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502095949.png" style="zoom:50%;">
|
||||
输出:
|
||||
|
||||
<img src="res/20220502095949.png" style="zoom:50%;">
|
||||
|
||||
如果要使用两个坐标系分别绘制正弦和余弦,可以用上面提到的`subplot`函数来创建坐标系,然后再绘图。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
plt.figure(figsize=(8, 4), dpi=120)
|
||||
# 创建坐标系(第1个图)
|
||||
|
@ -107,7 +113,9 @@ 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%;">
|
||||
输出:
|
||||
|
||||
<img src="res/20220501173446.png" style="zoom:50%;">
|
||||
|
||||
当然也可以像下面这么做,大家可以运行代码看看跟上面的图有什么区别。
|
||||
|
||||
|
@ -125,9 +133,8 @@ plt.show()
|
|||
```Python
|
||||
fig = plt.figure(figsize=(10, 4), dpi=120)
|
||||
plt.plot(x, y1, linewidth=2, marker='*', color='red')
|
||||
# 用Figure对象的add_axes方法在现有坐标系中嵌套一个新的坐标系
|
||||
# 该方法的参数是一个四元组,代表了新坐标系在原坐标系中的位置
|
||||
# 前两个值是左下角的位置,后两个值是坐标系的宽度和高度
|
||||
# 用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))
|
||||
|
@ -135,24 +142,28 @@ 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])
|
||||
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%;">
|
||||
输出:
|
||||
|
||||
##### 柱状图
|
||||
<img src="res/20220501173034.png" style="zoom:50%;">
|
||||
|
||||
在对比数据的差异时,柱状图是非常棒的选择,我们可以使用`pyplot`模块的`bar`函数来生成柱状图,也可以使用`barh`函数来生成水平柱状图。我们先为柱状图准备一些数据,代码如下所示。
|
||||
#### 柱状图
|
||||
|
||||
在对比数据的差异时,柱状图是非常棒的选择,我们可以使用`pyplot`模块的`bar`函数来生成柱状图,也可以使用`barh`函数来生成水平柱状图(也称为“条状图”)。我们先为柱状图准备一些数据,代码如下所示。
|
||||
|
||||
```Python
|
||||
x = np.arange(4)
|
||||
|
@ -162,10 +173,11 @@ y2 = np.random.randint(10, 60, 4)
|
|||
|
||||
绘制柱状图的代码。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
plt.figure(figsize=(6, 4), dpi=120)
|
||||
# 通过横坐标的偏移,让两组数据对应的柱子分开
|
||||
# width参数控制柱子的粗细,label参数为柱子添加标签
|
||||
# 通过横坐标的偏移,让两组数据对应的柱子分开,width参数控制柱子的粗细,label参数为柱子添加标签
|
||||
plt.bar(x - 0.1, y1, width=0.2, label='销售A组')
|
||||
plt.bar(x + 0.1, y2, width=0.2, label='销售B组')
|
||||
# 定制横轴的刻度
|
||||
|
@ -175,27 +187,34 @@ plt.legend()
|
|||
plt.show()
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220501173557.png" style="zoom:50%;">
|
||||
输出:
|
||||
|
||||
<img src="res/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参数指定底部数据,新柱子绘制在底部数据之上
|
||||
# 注意:堆叠柱状图的关键是将之前的柱子作为新柱子的底部,可以通过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%;">
|
||||
输出:
|
||||
|
||||
##### 饼状图
|
||||
<img src="res/20220501173645.png" style="zoom:50%;">
|
||||
|
||||
#### 饼状图
|
||||
|
||||
饼状图通常简称为饼图,是一个将数据划分为几个扇形区域的统计图表,它主要用于描述数量、频率等之间的相对关系。在饼图中,每个扇形区域的大小就是其所表示的数量的比例,这些扇形区域合在一起刚好是一个完整的饼。在需要展示数据构成的场景下,饼状图、树状图和瀑布图是不错的选择,我们可以使用`pyplot`模块的`pie`函数来绘制饼图,代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```Python
|
||||
data = np.random.randint(100, 500, 7)
|
||||
labels = ['苹果', '香蕉', '桃子', '荔枝', '石榴', '山竹', '榴莲']
|
||||
|
@ -227,11 +246,13 @@ plt.title('水果销售额占比')
|
|||
plt.show()
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502094128.png" style="zoom:50%;">
|
||||
输出:
|
||||
|
||||
<img src="res/20220502094128.png" style="zoom:50%;">
|
||||
|
||||
>**说明**:大家可以试一试将上面代码中被注释的部分恢复,看看有什么样的效果。
|
||||
|
||||
##### 直方图
|
||||
#### 直方图
|
||||
|
||||
在统计学中,直方图是一种展示数据分布情况的图形,是一种二维统计图表,它的两个坐标分别是统计样本和该样本对应的某个属性的度量。下面的数据是某学校100名男学生的身高,如果我们想知道数据的分布,就可以使用直方图。
|
||||
|
||||
|
@ -250,16 +271,14 @@ heights = np.array([
|
|||
])
|
||||
```
|
||||
|
||||
可以使用`pyplot`模块的`hist`函数来绘制直方图,代码如下所示。
|
||||
可以使用`pyplot`模块的`hist`函数来绘制直方图,其中`bins`参数代表了我们使用的分箱方式(身高从150厘米到190厘米,每5厘米为一个分箱),代码如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```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.hist(heights, bins=np.arange(145, 196, 5), color='darkcyan')
|
||||
# 定制横轴标签
|
||||
plt.xlabel('身高')
|
||||
# 定制纵轴标签
|
||||
|
@ -267,14 +286,37 @@ plt.ylabel('概率密度')
|
|||
plt.show()
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502093924.png" style="zoom:50%;">
|
||||
输出:
|
||||
|
||||
##### 箱线图
|
||||
<img src="res/hist_count.png" style="zoom:50%;">
|
||||
|
||||
绘制直方图时,如果将`hist`函数的`density`参数修改为`True`,同时将`cumulative`参数也修改为`True`,那么一方面纵轴会显示为概率密度,而图表会绘制概率的累计分布,如下所示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
plt.figure(figsize=(6, 4), dpi=120)
|
||||
# 绘制直方图
|
||||
plt.hist(heights, bins=np.arange(145, 196, 5), color='darkcyan', density=True, cumulative=True)
|
||||
# 定制横轴标签
|
||||
plt.xlabel('身高')
|
||||
# 定制纵轴标签
|
||||
plt.ylabel('概率')
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/hist_cumulative.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)
|
||||
|
@ -284,8 +326,7 @@ data = np.append(data, 200)
|
|||
data = np.append(data, -50)
|
||||
|
||||
plt.figure(figsize=(6, 4), dpi=120)
|
||||
# whis参数的默认值是1.5,将其设置为3可以检测极端离群值
|
||||
# showmeans=True表示在图中标记均值的位置
|
||||
# whis参数的默认值是1.5,将其设置为3可以检测极端离群值,showmeans=True表示在图中标记均值的位置
|
||||
plt.boxplot(data, whis=1.5, showmeans=True, notch=True)
|
||||
# 定制纵轴的取值范围
|
||||
plt.ylim([-100, 250])
|
||||
|
@ -294,11 +335,13 @@ plt.xticks([1], labels=['data'])
|
|||
plt.show()
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220501172802.png" style="zoom:50%;" />
|
||||
输出:
|
||||
|
||||
> **说明**:由于数据是随机生成的,所以大家运行上面的代码生成的图可能跟我这里并不相同。
|
||||
<img src="res/20220501172802.png" style="zoom:50%;" />
|
||||
|
||||
#### 显示或保存图像
|
||||
> **说明**:由于数据是随机生成的,大家运行上面的代码生成的图表可能跟我这里并不相同,以实际运行结果为准。
|
||||
|
||||
### 显示或保存图像
|
||||
|
||||
可以使用`pyplot`模块的`show`函数来显示绘制的图表,我们在上面的代码中使用过这个函数。如果希望保存图表,可以使用`savefig`函数。需要注意的是,如果要同时显示和保存图表,应该先执行`savefig`函数,再执行`show`函数,因为在调用`show`函数时,图表已经被释放,位于`show`函数之后的`savefig`保存的只是一个空白的区域。
|
||||
|
||||
|
@ -309,4 +352,4 @@ plt.show()
|
|||
|
||||
### 其他图表
|
||||
|
||||
使用 matplotlib,我们还可以绘制出其他的统计图表(如:雷达图、玫瑰图、热力图等),但实际工作中,使用频率最高的几类图表我们在上面已经为大家完整的展示出来了。此外,matplotlib 还有很多对统计图表进行定制的细节,例如定制坐标轴、定制图表上的文字和标签等。如果想了解如何用 matplotlib 绘制和定制更多的统计图表,可以直接查看 matplotlib 官方网站上的[文档](https://matplotlib.org/stable/tutorials/index.html)和[示例](https://matplotlib.org/stable/gallery/index.html)。
|
||||
使用 matplotlib,我们还可以绘制出其他的统计图表(如:雷达图、玫瑰图、热力图等),但实际工作中,使用频率最高的几类图表我们在上面已经为大家完整的展示出来了。此外,matplotlib 还有很多对统计图表进行定制的细节,例如定制坐标轴、定制图表上的文字和标签等。如果想了解如何用 matplotlib 绘制和定制更多的统计图表,可以直接查看 matplotlib 官方网站上的[文档](https://matplotlib.org/stable/tutorials/index.html)和[示例](https://matplotlib.org/stable/gallery/index.html),在下一个章节我们会为大家做一个简要的介绍。
|
|
@ -1,112 +0,0 @@
|
|||
## 方差分析和参数估计
|
||||
|
||||
### 方差分析
|
||||
|
||||
#### 基本概念
|
||||
|
||||
在产品运营中,我们会遇到各种需要评估运营效果的场景,包括促活的活动是否起到作用、A/B 测试的策略有无成效等等。具体例如,产品升级前的平均 DAU 是 155 万,产品升级后的平均 DAU 是 157 万,那么如何判断 DAU 提升的 2 万是正常的波动,还是升级带来的效果呢?对比同一组数据在实施某些策略前后的数据变化,判断数据波动是不是某一因素导致的,这种方法我们称之为方差分析。方差分析通常缩写为 ANOVA(Analysis of Variance),也叫“F 检验”,用于两个及两个以上分组样本的差异性检验。简单的说,**分析差异的显著性是否明显的方法就是方差分析**。
|
||||
|
||||
举一个例子,如果我们需要分析优惠券的金额对用户的购买转化率是否能起到有效作用,我们可以将数据分成以下三个组:
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085210.png" width="60%">
|
||||
|
||||
用户购买行为是随机的,购买率很高的不会很多,购买率极低的也不会很多,绝大部分用户的购买率都集中在某个值附近,这个值我们叫作整体购买率的平均值。如果每个客群分组自身的购买率均值与这个整体购买率平均值不一致,就会出现以下两种情况。
|
||||
|
||||
1. 第一种情况
|
||||
|
||||
蓝色分组的购买率平均值(蓝色线)比整体平均值(黑色线)要高,有可能是最右边那个很高的购买率把分组的均值抬升的,同时蓝色分组的数据分布很散(方差大),此时不能有十足把握说明该组用户的购买转化率很高。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085506.png" width="50%">
|
||||
|
||||
2. 第二种情况
|
||||
|
||||
绿色分组的购买率平均值(绿色线)比整体平均值(黑色线)要高,但是绿色分组的数据非常集中,都集中在分组的平均值(绿色线)附近,此时我们可以认为该组的转化率平均值与整体有明显区别。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085608.png" width="50%">
|
||||
|
||||
为了更好表述上面的问题,我们可以引入“组内方差”的概念,即描述每个分组内部数据分布的离散情况。如下图所示,对于上面蓝色和绿色分组的“组内方差”,显然蓝色的组内方差更大,绿色的组内方差更小。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210713085808.png" width="75%">
|
||||
|
||||
综上所述,如果上面三个分组的用户购买率平均值不在中线(整体购买率)左右,而是有明显的偏高或偏低,并且该组内的每个转化率都紧紧围绕在该组购买率平均值的附近(即组内方差很小)。那么我们就可以断定:该组的购买率与整体不一致,是该组对应优惠金额的影响造成的。
|
||||
|
||||
#### 定量分析
|
||||
|
||||
如果要进行定量分析,可以使用 F 检验值和 F crit 临界值这两个指标。F 检验值用来精确表达这几组差异大小的,F crit临界值是一个判断基线:
|
||||
|
||||
- 当 F > F crit,这几组之间的差异超过判断基准了,认为不同优惠金额的分组间的购买率是不一样的,优惠金额这个因素会对购买率产生影响,也就是说通过运营优惠金额这个抓手,是可以提升用户购买转化率的;
|
||||
- 当 F < F crit,则认为不同优惠金额的分组间的购买率是一样的,优惠金额这个因素不会对购买率产生影响,也就是说需要继续寻找其他与购买转化率有关的抓手。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210713090505.png">
|
||||
|
||||
> **说明**:图中 SS 代表方差、df 代表指标自由度、MS 是均方差、P-value 是差异的显著性水平。
|
||||
|
||||
上图是用 Excel 得出的 A、B、C 三组的方差分析结果,如图所示 F < F crit,所以从定量分析角度,可以判定优惠金额不会对购买率产生影响。
|
||||
|
||||
#### 实施方法
|
||||
|
||||
实施方差分析可以分为以下三步走:
|
||||
|
||||
1. 判断样本是否满足“方差分析”的前提条件
|
||||
|
||||
- 每个分组中的每个值都必须来自同一个总体样本;
|
||||
- 方差分析只能分析满足正态分布的指标,事实上,在产品运营中大部分指标都是正态分布,例如:
|
||||
- 几乎所有的转化率都满足正态分布:购买率、点击率、转化率、活跃率、留存率、复购率等。
|
||||
- 几乎所有的业务量都满足正态分布:客单价、每日新增用户数、渠道引流的流量等。
|
||||
- 几乎所有的用户画像指标都满足正态分布:年龄、城市、登录次数、使用时长等。
|
||||
- 分析的样本必须是随机抽样
|
||||
|
||||
2. 计算 F 检验值和 F crit 临界值
|
||||
|
||||
3. 如果有差异,需要评估差异大小
|
||||
|
||||
我们用一个新的指标来表示:$ R^2=SSA / SST $,其中 $ R^2 $ 表示差异大小,$ SSA $ 是组间误差平方和,$ SST $ 是总误差平方和。
|
||||
|
||||
- 当 $ R^2 \gt 0.5 $,认为各个分组间的差异非常显著;
|
||||
- 当 $ R^2 $ 在 $ [0.1, 0.5] $ 之间时,认为各个分组间的差异一般显著;
|
||||
- 当 $ R^2 \lt 0.1 $ 时,认为各个分组间的差异微弱显著。
|
||||
|
||||
> **练习**:打开“方差分析练习.xlsx”文件,完成练习1。
|
||||
|
||||
#### 多因素方差分析
|
||||
|
||||
上面的案例是针对一种策略来分析效果。我们把这种形式的方差分析叫作单因素方差分析,实际工作中,我们可能需要研究多种策略(例如运营中的渠道、活动、客群等)对结果的影响,我们称之为多因素方差分析。例如我们会在多个运营渠道上安排多种运营活动,评价各个渠道的转化率。此时,影响转化率的因素有渠道和活动两个因素,我们可以使用“无重复双因素方差分析”来检查数据。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210714125251.png" width="75%">
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210714130853.png" width="75%">
|
||||
|
||||
#### 应用场景
|
||||
|
||||
工作中遇到以下两类场景就可以使用方差分析:
|
||||
|
||||
1. 同一个客群在实施某个策略前后的指标对比。
|
||||
2. 两个或多个客群对比同一指标,评估同一指标在不同客群上的差异。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210714131318.png" width="85%">
|
||||
|
||||
### 参数估计
|
||||
|
||||
在产品运营的工作中,数据分析常会遭遇诸多非常让人困扰的情况,例如:产品运营面对的数据量动辄百万级、千万级,带来的就是分析速度急剧下降,跑个数等一两天时间已经是很理想情况;另外,在很多场景下,我们都只能拿到部分数据(样本),而无法获取全量数据(总体)。在这种情况下我们就必须通过分析非常小量样本的特征,再用这些特征去评估海量总体数据的特征,可以称之为**样本检验**。
|
||||
|
||||
**推断型统计的核心就是用样本推测总体**。在实际生产环境中,可能无法获得所有的数据,或者即便获取了所有的数据,但是没有足够的资源来分析所有的数据,在这种情况下,我们都需要用非常小量的样本特征去评估总体数据的特征,这其中的一项工作就是参数估计。
|
||||
|
||||
参数估计应用的场景非常的多,例如:
|
||||
|
||||
1. 在产品侧,我们可以用参数估计的方式评估A/B测试的效果。
|
||||
2. 在运营侧,我们可以用参数估计的方式优化活动配置和推荐策略。
|
||||
3. 在市场侧,我们可以用参数估计的方式制定广告投放策略。
|
||||
|
||||
#### 实施步骤
|
||||
|
||||
1. 确定分析的置信水平
|
||||
|
||||
2. 确定估计的参数类型
|
||||
|
||||
3. 计算参数估计的区间
|
||||
|
||||
- 数值型指标:$ A = z \times 样本标准差 / \sqrt{样本数量} $,其中 $ z $ 的值可以通过查表得到,如果置信水平选择95%,那么 $ z $ 的值就是1.96。大部分运营指标都是数值型指标,例如DAU、ARPU、转化率等。
|
||||
- 占比型指标:$ A = z \times \sqrt{占比 \times (1 - 占比) / 样本数量} $,$ z $ 值同上。占比型指标如性别占比、渠道占比、品类占比等。
|
||||
|
||||
最终得到的估计区间就是:$ [样本均值 - A, 样本均值 + A] $。
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
## 数据可视化-2
|
||||
|
||||
本章我们尝试用 matplotlib 来绘制一些高阶统计图表。正如前面所说的,大家可以通过 matplotlib 官方网站上提供的[文档](https://matplotlib.org/stable/tutorials/index.html)和[示例](https://matplotlib.org/stable/gallery/index.html)来学习如何使用 matplotlib 并绘制出更加高级的统计图表;尤其是在定制一些比较复杂的图表时,我们建议大家直接找到官网提供的示例,然后只需要做出相应的修改,就可以绘制出自己想要的图表。这种“拷贝+修改”的做法应该会大大提高你的工作效率,因为大多数时候,你的代码跟官网上的代码就仅仅是数据有差别而已,没有必要去做重复乏味的事情。
|
||||
|
||||
### 气泡图
|
||||
|
||||
气泡图可以用来了解三个变量之间的关系,通过比较气泡位置和大小来分析数据维度之间的相关性。例如在我们之前绘制的月收入和网购支出的散点图中,我们已经发现了二者的正相关关系,如果我们引入第三个变量网购次数,那么我们就需要使用气泡图来进行展示。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
income = np.array([5550, 7500, 10500, 15000, 20000, 25000, 30000, 40000])
|
||||
outcome = np.array([800, 1800, 1250, 2000, 1800, 2100, 2500, 3500])
|
||||
nums = np.array([5, 3, 10, 5, 12, 20, 8, 10])
|
||||
|
||||
# 通过scatter函数的s参数和c参数分别控制面积和颜色
|
||||
plt.scatter(income, outcome, s=nums * 30, c=nums, cmap='Reds')
|
||||
# 显示颜色条
|
||||
plt.colorbar()
|
||||
# 显示图表
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/bubble_chart.png" style="zoom:50%;">
|
||||
|
||||
### 面积图
|
||||
|
||||
面积图又叫堆叠折线图,是在折线图的基础上,对折线以下的区域进行颜色填充(展示面积),用于在连续间隔或时间跨度上展示数值,一般用来显示趋势和对比数值,不同颜色的填充可以让多个面积块之间的对比和趋势更好的突显。下面的例子中,我们用面积图来展示从周一到周日花在睡觉、吃饭、工作和玩耍上的时间。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
plt.figure(figsize=(8, 4))
|
||||
days = np.arange(7)
|
||||
sleeping = [7, 8, 6, 6, 7, 8, 10]
|
||||
eating = [2, 3, 2, 1, 2, 3, 2]
|
||||
working = [7, 8, 7, 8, 6, 2, 3]
|
||||
playing = [8, 5, 9, 9, 9, 11, 9]
|
||||
# 绘制堆叠折线图
|
||||
plt.stackplot(days, sleeping, eating, working, playing)
|
||||
# 定制横轴刻度
|
||||
plt.xticks(days, labels=[f'星期{x}' for x in '一二三四五六日'])
|
||||
# 定制图例
|
||||
plt.legend(['睡觉', '吃饭', '工作', '玩耍'], fontsize=10)
|
||||
# 显示图表
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/stacked_line_chart.png" style="zoom:50%;">
|
||||
|
||||
### 雷达图
|
||||
|
||||
雷达图通常用来比较多个定量数据,用于查看哪些变量具有相似的值。 雷达图也可用于查看数据集中哪些变量的值比较低,哪些变量的值比较高,是显示性能或表现的理想选择。经常观看篮球、足球比赛的读者应该对雷达图非常熟悉,例如在 NBA 的转播中就经常使用雷达图来展示球员的各项数据。雷达图的本质折线图,只不过将折线图映射到了极坐标系。在绘制雷达图时,需要让折线闭合,简单的说就是首尾相连,下面是绘制雷达图的代码。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
labels = np.array(['速度', '力量', '经验', '防守', '发球', '技术'])
|
||||
# 马龙和水谷隼的数据
|
||||
malong_values = np.array([93, 95, 98, 92, 96, 97])
|
||||
shuigu_values = np.array([30, 40, 65, 80, 45, 60])
|
||||
angles = np.linspace(0, 2 * np.pi, labels.size, endpoint=False)
|
||||
# 多加一条数据让图形闭合
|
||||
malong_values = np.append(malong_values, malong_values[0])
|
||||
shuigu_values = np.append(shuigu_values, shuigu_values[0])
|
||||
angles = np.append(angles, angles[0])
|
||||
# 创建画布
|
||||
plt.figure(figsize=(4, 4), dpi=120)
|
||||
# 创建坐标系
|
||||
ax = plt.subplot(projection='polar')
|
||||
# 绘图和填充
|
||||
plt.plot(angles, malong_values, color='r', linewidth=2, label='马龙')
|
||||
plt.fill(angles, malong_values, color='r', alpha=0.3)
|
||||
plt.plot(angles, shuigu_values, color='g', linewidth=2, label='水谷隼')
|
||||
plt.fill(angles, shuigu_values, color='g', alpha=0.2)
|
||||
# 显示图例
|
||||
ax.legend()
|
||||
# 显示图表
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/radar_chart.png" style="zoom:50%;">
|
||||
|
||||
### 玫瑰图
|
||||
|
||||
玫瑰图是映射在极坐标下的柱状图,由弗罗伦斯·南丁格尔(Florence Nightingale)所发明,当年是南丁格尔用来呈现战地医院季节性死亡率的一种图表。由于半径和面积的关系是平方的关系,南丁格尔玫瑰图会将数据的比例大小夸大,尤其适合对比大小相近的数值,同时由于圆形有周期的特性,所以南丁格尔玫瑰图也适用于表示一个周期内的时间概念,比如星期、月份。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
group1 = np.random.randint(20, 50, 4)
|
||||
group2 = np.random.randint(10, 60, 4)
|
||||
x = np.array([f'A组-Q{i}' for i in range(1, 5)] + [f'B组-Q{i}' for i in range(1, 5)])
|
||||
y = np.array(group1.tolist() + group2.tolist())
|
||||
# 玫瑰花瓣的角度和宽度
|
||||
theta = np.linspace(0, 2 * np.pi, x.size, endpoint=False)
|
||||
width = 2 * np.pi / x.size
|
||||
# 生成8种随机颜色
|
||||
colors = np.random.rand(8, 3)
|
||||
# 将柱状图投影到极坐标
|
||||
ax = plt.subplot(projection='polar')
|
||||
# 绘制柱状图
|
||||
plt.bar(theta, y, width=width, color=colors, bottom=0)
|
||||
# 设置网格
|
||||
ax.set_thetagrids(theta * 180 / np.pi, x, fontsize=10)
|
||||
# 显示图表
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/rose_chart.png" style="zoom:50%;">
|
||||
|
||||
### 3D图
|
||||
|
||||
matplotlib 还可以用于绘制3D图,具体的内容大家可以参考官方文档,下面我们用一段简单的代码为大家展示如何绘制3D图表。
|
||||
|
||||
代码:
|
||||
|
||||
```python
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
|
||||
fig = plt.figure(figsize=(8, 4), dpi=120)
|
||||
# 创建3D坐标系并添加到画布上
|
||||
ax = Axes3D(fig, auto_add_to_figure=False)
|
||||
fig.add_axes(ax)
|
||||
x = np.arange(-2, 2, 0.1)
|
||||
y = np.arange(-2, 2, 0.1)
|
||||
x, y = np.meshgrid(x, y)
|
||||
z = (1 - y ** 5 + x ** 5) * np.exp(-x ** 2 - y ** 2)
|
||||
# 绘制3D曲面
|
||||
ax.plot_surface(x, y, z)
|
||||
# 显示图表
|
||||
plt.show()
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
<img src="res/3d_surface_chart.png" style="zoom:60%;">
|
||||
|
||||
需要指出的是, JupyterLab 中渲染的3D图并不是真正的3D图,因为你没有办法调整观察者的视角,也没有办法旋转或者缩放。如果想要看到真正的3D效果,需要在将图表渲染到 Qt 窗口中,为此我们可以先安装名为 PyQt6 的三方库,如下所示。
|
||||
|
||||
```
|
||||
%pip install PyQt6
|
||||
```
|
||||
|
||||
然后,我们使用魔法指令让 JupyterLab 将图表渲染到 Qt 窗口中。
|
||||
|
||||
```
|
||||
%matplotlib qt
|
||||
```
|
||||
|
||||
在完成上面的操作后,我们可以重新运行刚才绘制3D图的代码,看到如下所示的窗口。在这个窗口中,我们可以通过鼠标对3D进行旋转、缩放,我们有可以选中图表的一部分数据进行观测,是不是非常的酷。
|
||||
|
||||
<img src="res/3d_surface_chart_qt.png" style="zoom:50%;">
|
|
@ -1,119 +0,0 @@
|
|||
## 相关和回归
|
||||
|
||||
我们知道,可以通过对指标的维度拆来解寻找指标变化的原因。当我们找到问题的原因时,自然会进一步思考一个问题:指标变化的原因这么多,其中的关键因素又是哪个呢?例如,我们在工作场景中时不时会讨论这些问题:
|
||||
|
||||
1. 电商类产品想知道哪个品类销售对整体销售贡献更大;
|
||||
2. 渠道运营想知道哪个渠道的用户对整体活跃作用更大;
|
||||
3. 负责留存的想知道哪个客群对整体的留存关系更大;
|
||||
4. 产品想知道到底哪些维度(城市、年龄、接入设备等)会影响整体活跃。
|
||||
|
||||
还有很多类似的场景,在这种情况下我们不仅要要找到数据变化的原因,还需要明确出不同原因的重要性。因为实际工作中可用资源有限,只能集中优势资源解决核心问题。
|
||||
|
||||
### 相关分析基本概念
|
||||
|
||||
相关性分析,指对两个或多个指标进行分析,评估它们两两之间联系或相互影响的程度。相关性分析不仅可以分析出多个指标间是否存在相关关系,还能给出相关程度的量化值。在进行相关性分析时,我们会使用“相关系数”定量给出几个指标间联系和影响的程度,通常用 $ \rho $ 来表示,计算公式为:
|
||||
|
||||
$$
|
||||
\rho = \frac {cov(X, Y)} {\sqrt{var(X) \cdot var(Y)}}
|
||||
$$
|
||||
需要注意的是,$ \rho $ 只能用来度量线性关系,它的取值在 $ [-1, 1] $ 之间。数据中的离群值会对 $ \rho $ 产生影响,在计算时必须先剔除,实际使用相关关系时,还需要**关注相关关系的稳定性**。
|
||||
|
||||
我们用 $ \rho $ 值判断指标的相关性时遵循以下两个步骤。
|
||||
|
||||
1. 判断指标间是正相关、负相关,还是不相关。
|
||||
- 当 $ \rho \gt 0 $,认为指标间是正相关,也就是两者的趋势一致。如果指标 A 与指标 B 的 $ \rho \gt 0 $,那么指标 A 上涨,指标 B 也会上涨;反之亦然。
|
||||
- 当 $ \rho \lt 0 $,认为指标间是负相关,也就是两者的趋势相反。如果指标 A 与指标 B 的 $ \rho \lt 0 $,那么指标 A 上涨,指标 B 会下降;反之亦然。
|
||||
- 当 $ \rho = 0 $,认为指标间是不相关的,但并不代表两个指标是统计独立的。
|
||||
2. 判断指标间的相关程度。
|
||||
- 当 $ \rho $ 的值在 $ [0.5,1] $ 之间,认为指标间是强相关,指标间的业务联系非常紧密。
|
||||
- 当 $ \rho $ 的值在 $ [0.1,0.5) $ 之间,认为指标间是弱相关,指标间的业务联系不太紧密。
|
||||
- 当 $ \rho $ 的值在 $ [0,0.1) $ 之间,认为指标间是无相关,指标间的业务联系无任何联系,也就是说当我们去运营指标 A 时,指标 B 不会产生相应的变化。
|
||||
|
||||
### 相关分析应用场景
|
||||
|
||||
事实上,相关性分析的应用场景非常多,基本上当问到“这两个东西有什么关系”、“哪个指标的作用(贡献或价值)更大”、“我们应该重点解决哪个问题”这类问题时,都可以用相关性分析给出比较准确的回答,非常便于产品运营找到解决问题的核心抓手。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210713095938.png" width="80%">
|
||||
|
||||
在使用相关分析时,应注意以下几个方面:
|
||||
|
||||
1. 业务意义:当我们想知道A指标的情况时,可以监控B指标。
|
||||
2. 注意事项:千万不要将相关关系判断为因果关系,相关关系是伴随关系而不是因果关系。
|
||||
3. 强相关关系才是有业务价值的,建议寻找相关系数在 0.6 以上甚至 0.8 以上的指标。
|
||||
4. 相关关系的本质是 Y 的变化有多少能被 X 解释,跟 X 和 Y 之间的斜率大小无关。
|
||||
|
||||
### Excel计算相关系数
|
||||
|
||||
1. 方法一:使用 CORREL 函数。
|
||||
2. 方法二:使用“数据分析”模块的“相关系数”功能。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210713164021.png" width="75%">
|
||||
|
||||
### 相关分析案例
|
||||
|
||||
#### 分析哪个客群的留存对整体留存贡献更大
|
||||
|
||||
留存的运营中我们最常看的就是新客的留存和活跃客群的留存,用来评估哪个客群的留存与整体的留存联系更紧密,以便制定后续运营的策略。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210928214403.png" style="zoom:65%;">
|
||||
|
||||
利用Excel进行相关分析的结果如下所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210928214522.png" style="zoom:65%;">
|
||||
|
||||
可以看出,活跃访客的留存率与整体留存率的相关是强相关;而新增访客的留存率与整体留存率的相关是弱相关,所以如果要提升整体留存率,我们的产品运营资源应当更多地投放给活跃用户,以提升整体的留存率;而新增访客,虽然不会拿到很多运营资源,但是我们也要去深入分析为什么新增访客的留存的贡献比较小,适时做一些提升这部分客群与整体留存的策略。
|
||||
|
||||
#### 案例2:找出对购买转化率贡献最高的渠道
|
||||
|
||||
基本上电商运营会同时部署多个渠道,包括线上电商平台以及线下的门店。由于现有某产品从各个渠道获客的用户在产品上的购买转化率,需要评估哪些渠道的用户对整体购买转化率贡献最大,后续将重点营销此渠道。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210928214725.png" style="zoom:65%;">
|
||||
|
||||
#### 案例3:分析哪些因素对 DAU 的影响更大
|
||||
|
||||
我们分析 DAU 时常会将它拆解为各种维度来分析,这里我们分析与 DAU 联系最紧密的维度到底是哪些,以帮助我们制定针对性的运营策略,如下图所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210928215043.png" style="zoom:65%;">
|
||||
|
||||
对于这样的报表,我们需要找出到底是哪几个城市、哪个操作系统,以及哪个年龄段的用户对于 DAU 的影响最大。如果能找出来这个关系,那么后续要提升 DAU,就有非常清晰的方向。
|
||||
|
||||
|
||||
### 线性回归
|
||||
|
||||
如果只有一个自变量 X,而且因变量 Y 和自变量 X 之间的数量变化关系呈现近似的线性关系,就可以建立一元线性回归方程,通过自变量 X 的值来预测因变量 Y 的值,这就是所谓的**一元线性回归预测**,回归方程如下所示:
|
||||
$$
|
||||
Y = aX + b
|
||||
$$
|
||||
我们可以通过历史数据(已知的 $ X $ 和 $ Y $ ),确定参数 $ a $ 和 $ b $ 的值,还原出回归方程,从而实现预测。很显然,$ a $和 $ b $ 的取值可以有很多种选择,那么什么才是最好的 $ a $ 和 $ b$ 呢?如果把真实值记为 $ y $,把预测值记为 $ \hat{y} $,那么让 $ SSR $ 值最小的 $ a $ 和 $ b $ 就是最好的 $ a $ 和 $ b $ ,称之为**最小二乘解**,其中$ SSR $ 值计算公式如下所示:
|
||||
$$
|
||||
SSR = \sum_{i=1}^{n}(y_i - \hat{y_i})^2
|
||||
$$
|
||||
损失函数是凹函数,找到使函数最小的`a`和`b`的值,可以通过向凹函数的拐点进行逼近的方式来找到更好的`a`和`b`的值,具体的公式如下所示:
|
||||
|
||||
$$
|
||||
a^\prime = a + (-1) \times \frac {\partial loss(a, b)} {\partial a} \times \Delta \\
|
||||
b^\prime = b + (-1) \times \frac {\partial loss(a, b)} {\partial b} \times \Delta
|
||||
$$
|
||||
对于上面的求 $ SSR $ 的函数来说,可以用下面的公式计算偏导数:
|
||||
$$
|
||||
f(a, b) = \frac {1} {N} \sum_{i=1}^{N}(y_i - (ax_i + b))^2 \\
|
||||
\frac {\partial {f(a, b)}} {\partial {a}} = \frac {2} {N} \sum_{i=1}^{N}(-x_iy_i + x_i^2a + x_ib) \\
|
||||
\frac {\partial {f(a, b)}} {\partial {b}} = \frac {2} {N} \sum_{i=1}^{N}(-y_i + x_ia + b)
|
||||
$$
|
||||
上面的方法称为**梯度下降法**。
|
||||
|
||||
在Excel中,可以使用“数据分析”模块的“”来实现线性回归。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210714073655.png" width="75%">
|
||||
|
||||
对于回归分析,最为重要的是评价回归的结果是否理想,这关系到能否通过回归方程去预测将来,我们先看看决定系数(Multiple R-Squared,通常称之为$ R^2 $)。在统计学习中,决定系数用于度量因变量的变化中可由自变量解释部分所占的比例,也就是你的回归模型的解释力是否良好,$ R^2 $ 的值越接近`1`越好。
|
||||
$$
|
||||
SS_{tot} = \sum_{i}(y_{i} - \bar {y})^2 \\
|
||||
SS_{res} = \sum_{i}(y_{i} - \hat {y_i})^2 \\
|
||||
R^2 = 1 - \frac {SS_{res}} {SS_{tot}}
|
||||
$$
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20210714074159.png" width="60%">
|
||||
|
||||
接下来我们还要对回归方程的显著性进行检验,主要包括 t 检验(回归系数的检验)和F检验(回归方程的检验)。对于F检验(F-statistic)的结果,主要关注其 p-value ,如果 p-value 小于0.05,那么说明拟合效果是不错的。
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
## 数据分析方法论
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
## 数据可视化-2
|
||||
## 数据可视化-3
|
||||
|
||||
通过前面的学习,我们已经对数据可视化工具 matplotlib 有一个初步的认知。大家可能也会发现了,matplotlib 提供的函数虽然强大,但是参数太多,要想对图表进行深度的定制就需要修改一系列的参数,这一点对新手并不友好。另一方面,使用 matplotlib 定制的统计图是静态图表,可能在某些需要交互效果的场景下并不合适。为了解决这两个问题,我们为大家介绍两个新的可视化工具,一个是 seaborn,一个是 pyecharts。
|
||||
|
||||
|
@ -26,7 +26,7 @@ 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%;">
|
||||
<img src="res/20220502115005.png" style="zoom:75%;">
|
||||
|
||||
使用 seaborn,首先需要导入该库并设置主题,代码如下所示。
|
||||
|
||||
|
@ -41,7 +41,7 @@ sns.set_theme()
|
|||
```Python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei', ]
|
||||
plt.rcParams['font.sans-serif'].insert(0, 'SimHei')
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
```
|
||||
|
||||
|
@ -87,7 +87,7 @@ ssl._create_default_https_context = ssl._create_unverified_context
|
|||
sns.histplot(data=tips_df, x='total_bill', kde=True)
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502115531.png" style="zoom:50%;">
|
||||
<img src="res/20220502115531.png" style="zoom:50%;">
|
||||
|
||||
如果想了解变量之间的两两关系,我们可以绘制点对图,代码和效果如下所示。
|
||||
|
||||
|
@ -95,11 +95,11 @@ sns.histplot(data=tips_df, x='total_bill', kde=True)
|
|||
sns.pairplot(data=tips_df, hue='sex')
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502120236.png" style="zoom:50%;">
|
||||
<img src="res/20220502120236.png" style="zoom:42%;">
|
||||
|
||||
如果对上面图表的颜色不满意,还可以通过 palette 参数选择 seaborn 自带的“调色板”来修改颜色,这种方式相比于自行指定颜色或使用随机颜色方便和靠谱了很多,下图为大家展示了部分 seaborn 自带的“调色板”。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502120749.png" style="zoom:50%;">
|
||||
<img src="http://localhost//mypic/20220502120749.png" style="zoom:45%;">
|
||||
|
||||
我们可以将上面的代码稍作修改,看看运行结果有什么差别。
|
||||
|
||||
|
@ -113,7 +113,7 @@ sns.pairplot(data=tips_df, hue='sex', palette='Dark2')
|
|||
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%;">
|
||||
<img src="res/20220502121226.png" style="zoom:50%;">
|
||||
|
||||
上面清晰的展示了,total_bill 和 tip 之间存在正相关关系,这一点我们也可以通过 DataFrame 对象的 corr 方法进行验证。接下来,我们可以建立回归模型来拟合这些数据点,而 seaborn 的线性回归模型图已经帮我们实现了这项功能,代码如下所示。
|
||||
|
||||
|
@ -121,7 +121,7 @@ sns.jointplot(data=tips_df, x='total_bill', y='tip', hue='sex')
|
|||
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%;">
|
||||
<img src="res/20220502121656.png" style="zoom:50%;">
|
||||
|
||||
如果我们希望了解账单金额的集中和离散趋势,可以绘制箱线图或小提琴图,代码如下所示,我们将数据按星期四、星期五、星期六和星期天分别进行展示。
|
||||
|
||||
|
@ -129,13 +129,13 @@ sns.lmplot(data=tips_df, x='total_bill', y='tip', hue='sex')
|
|||
sns.boxplot(data=tips_df, x='day', y='total_bill')
|
||||
```
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502122106.png" style="zoom:50%;">
|
||||
<img src="res/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%;">
|
||||
<img src="res/20220502122144.png" style="zoom:50%;">
|
||||
|
||||
> **说明**:相较于箱线图,小提琴图没有标注异常点而是显示了数据的整个范围,另一方面,小提琴图很好的展示了数据的分布(密度轨迹)。
|
||||
|
||||
|
@ -149,84 +149,79 @@ Echarts 原来是百度开发的一个前端图表库,2018年1月16日,EChar
|
|||
pip install pyecharts
|
||||
```
|
||||
|
||||
在 Jupyter 中,可以直接使用魔法指令进行安装,如下所示。
|
||||
在 JupyterLab 中,可以直接使用魔法指令进行安装,如下所示。
|
||||
|
||||
```Bash
|
||||
%pip install pyecharts
|
||||
```
|
||||
|
||||
如果想在 JupyterLab 中使用 pyecharts 绘图,我们还需要做一些准备工作,主要是修改 pyecharts 的配置,代码如下所示。
|
||||
|
||||
```python
|
||||
from pyecharts.globals import CurrentConfig, NotebookType
|
||||
|
||||
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
|
||||
```
|
||||
|
||||
接下来,我们通过来自于 pyecharts 官方网站新手教程中的一个例子,来认识 pyecharts。当然,我们对官网的例子进行一些调整,代码如下所示。
|
||||
|
||||
```Python
|
||||
from pyecharts.charts import Bar
|
||||
from pyecharts import options
|
||||
from pyecharts.globals import ThemeType
|
||||
from pyecharts import options as opts
|
||||
|
||||
# 创建柱状图对象并设置初始参数(宽度、高度、主题)
|
||||
bar = Bar(init_opts=options.InitOpts(
|
||||
width='600px',
|
||||
height='450px',
|
||||
theme=ThemeType.CHALK
|
||||
))
|
||||
# 创建柱状图对象并设置初始参数(宽度、高度)
|
||||
bar_chart = Bar(init_opts=opts.InitOpts(width='600px', height='450px'))
|
||||
# 设置横轴数据
|
||||
bar.add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])
|
||||
bar_chart.add_xaxis(["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"])
|
||||
# 设置纵轴数据(第一组)
|
||||
bar.add_yaxis(
|
||||
"商家A",
|
||||
[25, 20, 36, 10, 75, 90],
|
||||
)
|
||||
bar_chart.add_yaxis("商家A", [25, 20, 36, 10, 75, 90])
|
||||
# 设置纵轴数据(第二组)
|
||||
bar.add_yaxis(
|
||||
"商家B",
|
||||
[15, 12, 30, 20, 45, 60]
|
||||
)
|
||||
bar_chart.add_yaxis("商家B", [15, 12, 30, 20, 45, 60])
|
||||
# 设置纵轴数据(第三组)
|
||||
bar.add_yaxis(
|
||||
"商家C",
|
||||
[12, 32, 40, 52, 35, 26]
|
||||
)
|
||||
bar_chart.add_yaxis("商家C", [12, 32, 40, 52, 35, 26])
|
||||
# 添加全局配置参数
|
||||
bar.set_global_opts(
|
||||
bar_chart.set_global_opts(
|
||||
# 横轴相关的参数
|
||||
xaxis_opts=options.AxisOpts(
|
||||
axislabel_opts=options.LabelOpts(
|
||||
color='white'
|
||||
)
|
||||
xaxis_opts=opts.AxisOpts(
|
||||
axislabel_opts=opts.LabelOpts(color='navy')
|
||||
),
|
||||
# 纵轴相关的参数(标签、最小值、最大值、间隔)
|
||||
yaxis_opts=options.AxisOpts(
|
||||
axislabel_opts=options.LabelOpts(
|
||||
color='white'
|
||||
),
|
||||
yaxis_opts=opts.AxisOpts(
|
||||
axislabel_opts=opts.LabelOpts(color='navy'),
|
||||
min_=0,
|
||||
max_=100,
|
||||
interval=10
|
||||
),
|
||||
# 标题相关的参数(内容、链接、位置、文本样式)
|
||||
title_opts=options.TitleOpts(
|
||||
title='2021年销售数据展示',
|
||||
title_link='http://www.qfedu.com',
|
||||
title_opts=opts.TitleOpts(
|
||||
title='2022年销售数据展示',
|
||||
pos_left='2%',
|
||||
title_textstyle_opts=options.TextStyleOpts(
|
||||
color='white',
|
||||
title_textstyle_opts=opts.TextStyleOpts(
|
||||
color='navy',
|
||||
font_size=16,
|
||||
font_family='SimHei',
|
||||
font_family='苹方-简',
|
||||
font_weight='bold'
|
||||
)
|
||||
),
|
||||
# 工具箱相关的参数
|
||||
toolbox_opts=options.ToolboxOpts(
|
||||
toolbox_opts=opts.ToolboxOpts(
|
||||
orient='vertical',
|
||||
pos_left='right'
|
||||
)
|
||||
)
|
||||
# 在Jupyter Notebook中渲染图表
|
||||
bar.render_notebook()
|
||||
# 加载绘图需要的JavaScript文件
|
||||
bar_chart.load_javascript()
|
||||
```
|
||||
|
||||
上面代码的运行效果如下图所示。值得一提的是,下图中的标题、图例、右侧的工具箱都是可以点击的,大家可以点击它们看看会有什么样的效果,ECharts 的魅力就在于它的交互效果,大家一定要试一试。如果要将下面的统计图表保存成一个网页,可以将上面最后一行代码修改为`bar.render('index.html')`即可。
|
||||
在执行完上面的代码后,我们就可以通过调用`bar`对象的方法来完成对图表的渲染。如果直接使用`render`方法,那么绘制好的统计图表将保存到一个 HTML 文件中,打开该文件也能够看到绘制好的统计图表,而`render_notebook`方法则是将图表渲染到浏览器窗口中。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502185650.png" style="zoom:50%;">
|
||||
```python
|
||||
bar_chart.render_notebook()
|
||||
```
|
||||
|
||||
上面代码的运行效果如下图所示。值得一提的是,下图中的标题、图例、右侧的工具箱都是可以点击的,大家可以点击它们看看会有什么样的效果,ECharts 的魅力就在于它的交互效果,大家一定要试一试。
|
||||
|
||||
<img src="res/pyecharts_bar_chart.png" style="zoom:55%;">
|
||||
|
||||
接下来,我们也是通过一个官方示例,看看如何绘制饼图。
|
||||
|
||||
|
@ -240,16 +235,16 @@ 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_chart = Pie(init_opts=opts.InitOpts(width="800px", height="400px"))
|
||||
# 向饼图添加数据
|
||||
pie.add(
|
||||
pie_chart.add(
|
||||
'',
|
||||
data_pair=data,
|
||||
radius=["50%", "75%"],
|
||||
label_opts=opts.LabelOpts(is_show=False),
|
||||
)
|
||||
# 设置全局配置项
|
||||
pie.set_global_opts(
|
||||
pie_chart.set_global_opts(
|
||||
# 配置图例相关的参数
|
||||
legend_opts=opts.LegendOpts(
|
||||
pos_left="legft",
|
||||
|
@ -257,18 +252,22 @@ pie.set_global_opts(
|
|||
)
|
||||
)
|
||||
# 设置数据系列配置参数
|
||||
pie.set_series_opts(
|
||||
pie_chart.set_series_opts(
|
||||
# 设置不显示工具提示
|
||||
tooltip_opts=opts.TooltipOpts(is_show=False),
|
||||
# 设置饼图标签的样式
|
||||
label_opts=opts.LabelOpts(formatter="{b}({c}): {d}%")
|
||||
)
|
||||
pie.render_notebook()
|
||||
pie_chart.load_javascript()
|
||||
```
|
||||
|
||||
```python
|
||||
pie_chart.render_notebook()
|
||||
```
|
||||
|
||||
运行上面的代码,效果如下图所示。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502190558.png" style="zoom:50%;">
|
||||
<img src="res/pyecharts_pie_chart.png" style="zoom:50%;">
|
||||
|
||||
需要提醒大家注意的是,pyecharts 并不能直接使用 NumPy 的 ndarray 和 Pandas 的 Series、DataFrame 为其提供数据,它需要的是 Python 原生的数据类型。可能大家也注意到了,上面的代码中,我们使用的都是列表、元组这样的数据类型。
|
||||
|
||||
|
@ -289,7 +288,7 @@ pip install echarts-countries-pypkg echarts-china-provinces-pypkg echarts-china-
|
|||
|
||||
> **说明**:上面的四个库分别包含了世界各国、中国省级行政区域、中国市级行政区域、中国区/县级行政区域的数据。
|
||||
|
||||
然后,我们将全国各省抖音大V的数据放在一个列表中,代码如下所示。
|
||||
然后,我们将全国各省的数据放在一个列表中,代码如下所示。
|
||||
|
||||
```Python
|
||||
data = [
|
||||
|
@ -306,15 +305,20 @@ data = [
|
|||
接下来,我们使用 pyecharts 在地图上标记各省抖音大V人数。
|
||||
|
||||
```Python
|
||||
import pyecharts.options as opts
|
||||
from pyecharts.charts import Map
|
||||
|
||||
map_chart = Map()
|
||||
map_chart = Map(init_opts=opts.InitOpts(width='1000px', height='1000px'))
|
||||
map_chart.add('', data, 'china', is_roam=False)
|
||||
map_chart.load_javascript()
|
||||
```
|
||||
|
||||
```python
|
||||
map_chart.render_notebook()
|
||||
```
|
||||
|
||||
代码的运行效果如下图所示,将鼠标置于地图上时,会高亮对应的省并看到相关的信息。
|
||||
|
||||
<img src="https://github.com/jackfrued/mypic/raw/master/20220502192142.png" style="zoom:50%;">
|
||||
<img src="res/pyecharts_map_chart.png" style="zoom:55%;">
|
||||
|
||||
和 seaborn 一样,我们建议大家参考官方提供的示例来使用 pyecharts,我们可以在 pyecharts [官方网站](https://pyecharts.org/#/zh-cn/)的左侧导航栏中找到“图表类型”选项,下面每种类型的图表都有对应的官方示例,很多代码是可以直接使用的,我们需要做的就是将数据换成自己的数据。
|
|
@ -1,862 +0,0 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"import matplotlib.pyplot as plt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array1 = np.array([1, 2, 3, 4, 5])\n",
|
||||
"array1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array2 = np.arange(0, 20, 2)\n",
|
||||
"array2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array3 = np.linspace(-5, 5, 101)\n",
|
||||
"array3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array4 = np.random.rand(10)\n",
|
||||
"array4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array5 = np.random.randint(1, 101, 10)\n",
|
||||
"array5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array6 = np.random.normal(50, 10, 20)\n",
|
||||
"array6"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array7 = np.array([[1, 2, 3], [4, 5, 6]])\n",
|
||||
"array7"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array8 = np.zeros((3, 4))\n",
|
||||
"array8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array9 = np.ones((3, 4))\n",
|
||||
"array9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array10 = np.full((3, 4), 10)\n",
|
||||
"array10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array11 = np.eye(4)\n",
|
||||
"array11"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array12 = np.array([1, 2, 3, 4, 5, 6]).reshape(2, 3)\n",
|
||||
"array12"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array13 = np.random.rand(3, 4)\n",
|
||||
"array13"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array14 = np.random.randint(1, 100, (3, 4))\n",
|
||||
"array14"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array15 = np.random.randint(1, 100, (3, 4, 5))\n",
|
||||
"array15"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array16 = np.arange(1, 25).reshape((2, 3, 4))\n",
|
||||
"array16"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array17 = np.random.randint(1, 100, (4, 6)).reshape((4, 3, 2))\n",
|
||||
"array17"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array18 = plt.imread('guido.jpg')\n",
|
||||
"array18"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array19 = np.arange(1, 100, 2)\n",
|
||||
"array20 = np.random.rand(3, 4)\n",
|
||||
"print(array19.size, array20.size)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array19.shape, array20.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array19.dtype, array20.dtype)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array19.ndim, array20.ndim)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array21 = np.arange(1, 100, 2, dtype=np.int8)\n",
|
||||
"print(array19.itemsize, array20.itemsize, array21.itemsize)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array19.nbytes, array20.nbytes, array21.nbytes)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import Iterable\n",
|
||||
"\n",
|
||||
"print(isinstance(array20.flat, np.ndarray), isinstance(array20.flat, Iterable))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array22 = array19[:]\n",
|
||||
"print(array22.base is array19, array22.base is array21)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array23 = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
|
||||
"print(array23[0], array23[array23.size - 1])\n",
|
||||
"print(array23[-array23.size], array23[-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array24 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
|
||||
"print(array24[2])\n",
|
||||
"print(array24[0][0], array24[-1][-1])\n",
|
||||
"print(array24[1][1], array24[1, 1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array24[1][1] = 10\n",
|
||||
"print(array24)\n",
|
||||
"array24[1] = [10, 11, 12]\n",
|
||||
"print(array24)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[:2, 1:])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[2])\n",
|
||||
"print(array24[2, :])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[2:, :])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[:, :2])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[1, :2])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[1:2, :2])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array24[1:2, :2].base"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[::2, ::2])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(array24[::-2, ::-2])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cell_style": "center",
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"guido_image = plt.imread('guido.jpg')\n",
|
||||
"guido_shape"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cell_style": "split"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.imshow(guido_image)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cell_style": "split"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.imshow(guido_image[::-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cell_style": "split"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.imshow(guido_image)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cell_style": "split"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.imshow(guido_image[:,::-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cell_style": "split"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.imshow(guido_image)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"cell_style": "split"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"plt.imshow(guido_image[30:350, 90:300])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array25 = np.array([50, 30, 15, 20, 40])\n",
|
||||
"array25[[0, 1, -1]]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array26 = np.array([[30, 20, 10], [40, 60, 50], [10, 90, 80]])\n",
|
||||
"array26[[0, 2]]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array26[[0, 2], [1, 2]]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array26[[0, 2], [1]]\n",
|
||||
"array26[[0, 2], 1]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array27 = np.arange(1, 10)\n",
|
||||
"array27[[True, False, True, True, False, False, False, False, True]]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array27 >= 5"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"~(array27 >= 5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array27[array27 >= 5]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array28 = np.array([1, 2, 3, 4, 5, 5, 4, 3, 2, 1])\n",
|
||||
"print(array28.sum())\n",
|
||||
"print(array28.mean())\n",
|
||||
"print(array28.max())\n",
|
||||
"print(array28.min())\n",
|
||||
"print(array28.std())\n",
|
||||
"print(array28.var())\n",
|
||||
"print(array28.cumsum())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array29 = np.array([3, 4])\n",
|
||||
"array30 = np.array([5, 6])\n",
|
||||
"array29.dot(array30)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array31 = np.array([[1, 2, 3], [4, 5, 6]])\n",
|
||||
"array32 = np.array([[1, 2], [3, 4], [5, 6]])\n",
|
||||
"array31.dot(array32)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array31.dump('array31-data')\n",
|
||||
"array32 = np.load('array31-data', allow_pickle=True)\n",
|
||||
"array32"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array32.flatten()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array33 = np.array([35, 96, 12, 78, 66, 54, 40, 82])\n",
|
||||
"array33.sort()\n",
|
||||
"array33"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array32.swapaxes(0, 1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array32.transpose()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array34 = array33.take([0, 2, -3, -1])\n",
|
||||
"array34"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array35 = np.arange(1, 10)\n",
|
||||
"print(array35 + 10)\n",
|
||||
"print(array35 * 10)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array36 = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])\n",
|
||||
"print(array35 + array36)\n",
|
||||
"print(array35 * array36)\n",
|
||||
"print(array35 ** array36)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(np.sqrt(array35))\n",
|
||||
"print(np.log2(array35))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array37 = np.array([[4, 5, 6], [7, 8, 9]])\n",
|
||||
"array38 = np.array([[1, 2, 3], [3, 2, 1]])\n",
|
||||
"print(array37 * array38)\n",
|
||||
"print(np.power(array37, array38))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array39 = np.array([[0, 0, 0], [1, 1, 1], [2, 2, 2], [3, 3, 3]])\n",
|
||||
"array40 = np.array([1, 2, 3])\n",
|
||||
"array39 + array40"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array41 = np.array([[1], [2], [3], [4]])\n",
|
||||
"array39 + array41"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"array42 = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]])\n",
|
||||
"array43 = np.array([[4, 4, 4], [5, 5, 5], [6, 6, 6]])\n",
|
||||
"np.hstack((array42, array43))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"np.vstack((array42, array43))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"np.concatenate((array42, array43))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"np.concatenate((array42, array43), axis=1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"x = np.array([1, 2, 3])\n",
|
||||
"y = np.array([4, 5, 6])\n",
|
||||
"np.cross(x, y)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m1 = np.matrix('1 2 3; 4 5 6')\n",
|
||||
"m1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m2 = np.asmatrix(np.array([[1, 1], [2, 2], [3, 3]]))\n",
|
||||
"m2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m1.dot(m2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m1 * m2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m3 = np.array([[1., 2.], [3., 4.]])\n",
|
||||
"np.linalg.inv(m3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m4 = np.array([[1, 3, 5], [2, 4, 6], [4, 7, 9]])\n",
|
||||
"np.linalg.det(m4)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 解线性方程组ax=b\n",
|
||||
"# 3x + y = 9,x + 2y = 8\n",
|
||||
"a = np.array([[3,1], [1,2]])\n",
|
||||
"b = np.array([9, 8])\n",
|
||||
"np.linalg.solve(a, b)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.7.7"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
"nav_menu": {},
|
||||
"number_sections": true,
|
||||
"sideBar": true,
|
||||
"skip_h1_title": false,
|
||||
"title_cell": "Table of Contents",
|
||||
"title_sidebar": "Contents",
|
||||
"toc_cell": false,
|
||||
"toc_position": {},
|
||||
"toc_section_display": true,
|
||||
"toc_window_display": false
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"import matplotlib.pyplot as plt"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"%matplotlib inline\n",
|
||||
"%config InlineBackend.figure_format='svg'"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"plt.rcParams['font.sans-serif'] = 'FZJKai-Z03S'\n",
|
||||
"plt.rcParams['axes.unicode_minus'] = False"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"ser1 = pd.Series(data=[320, 180, 300, 405], index=['一季度', '二季度', '三季度', '四季度'])\n",
|
||||
"ser1"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"ser2 = pd.Series({'一季度': 320, '二季度': 180, '三季度': 300, '四季度': 405})\n",
|
||||
"ser2"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"print(ser2[0], ser2[2], ser2[-1])\n",
|
||||
"ser2[0], ser2[-1] = 350, 360 \n",
|
||||
"print(ser2)"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"print(ser2['一季度'], ser2['三季度'])\n",
|
||||
"ser2['一季度'] = 380\n",
|
||||
"print(ser2)"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"print(ser2[1:3])\n",
|
||||
"print(ser2['二季度': '四季度'])\n",
|
||||
"ser2[1:3] = 400, 500\n",
|
||||
"print(ser2)"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"print(ser2[['二季度', '四季度']])\n",
|
||||
"ser2[['二季度', '四季度']] = 500, 520\n",
|
||||
"print(ser2)"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"print(ser2[ser2 >= 500])"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"# 求和\n",
|
||||
"print(ser2.sum())\n",
|
||||
"# 求均值\n",
|
||||
"print(ser2.mean())\n",
|
||||
"# 求最大\n",
|
||||
"print(ser2.max())\n",
|
||||
"# 求最小\n",
|
||||
"print(ser2.min())\n",
|
||||
"# 计数\n",
|
||||
"print(ser2.count())\n",
|
||||
"# 求标准差\n",
|
||||
"print(ser2.std())\n",
|
||||
"# 求方差\n",
|
||||
"print(ser2.var())\n",
|
||||
"# 求中位数\n",
|
||||
"print(ser2.median())"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"ser2.describe()"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"ser3 = pd.Series(data=['apple', 'banana', 'apple', 'pitaya', 'apple', 'pitaya', 'durian'])\n",
|
||||
"ser3.value_counts()"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"ser4 = pd.Series(data=[10, 20, np.NaN, 30, np.NaN])\n",
|
||||
"ser4.dropna()"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"ser4.fillna(value=40)"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"source": [
|
||||
"ser4.fillna(method='ffill')"
|
||||
],
|
||||
"outputs": [],
|
||||
"metadata": {}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.7.7"
|
||||
},
|
||||
"toc": {
|
||||
"base_numbering": 1,
|
||||
"nav_menu": {},
|
||||
"number_sections": true,
|
||||
"sideBar": true,
|
||||
"skip_h1_title": false,
|
||||
"title_cell": "Table of Contents",
|
||||
"title_sidebar": "Contents",
|
||||
"toc_cell": false,
|
||||
"toc_position": {},
|
||||
"toc_section_display": true,
|
||||
"toc_window_display": false
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
|
@ -0,0 +1,507 @@
|
|||
CRIM,ZN,INDUS,CHAS,NOX,RM,AGE,DIS,RAD,TAX,PTRATIO,B,LSTAT,PRICE
|
||||
0.00632,18,2.31,0,0.538,6.575,65.2,4.09,1,296,15.3,396.9,4.98,24
|
||||
0.02731,0,7.07,0,0.469,6.421,78.9,4.9671,2,242,17.8,396.9,9.14,21.6
|
||||
0.02729,0,7.07,0,0.469,7.185,61.1,4.9671,2,242,17.8,392.83,4.03,34.7
|
||||
0.03237,0,2.18,0,0.458,6.998,45.8,6.0622,3,222,18.7,394.63,2.94,33.4
|
||||
0.06905,0,2.18,0,0.458,7.147,54.2,6.0622,3,222,18.7,396.9,5.33,36.2
|
||||
0.02985,0,2.18,0,0.458,6.43,58.7,6.0622,3,222,18.7,394.12,5.21,28.7
|
||||
0.08829,12.5,7.87,0,0.524,6.012,66.6,5.5605,5,311,15.2,395.6,12.43,22.9
|
||||
0.14455,12.5,7.87,0,0.524,6.172,96.1,5.9505,5,311,15.2,396.9,19.15,27.1
|
||||
0.21124,12.5,7.87,0,0.524,5.631,100,6.0821,5,311,15.2,386.63,29.93,16.5
|
||||
0.17004,12.5,7.87,0,0.524,6.004,85.9,6.5921,5,311,15.2,386.71,17.1,18.9
|
||||
0.22489,12.5,7.87,0,0.524,6.377,94.3,6.3467,5,311,15.2,392.52,20.45,15
|
||||
0.11747,12.5,7.87,0,0.524,6.009,82.9,6.2267,5,311,15.2,396.9,13.27,18.9
|
||||
0.09378,12.5,7.87,0,0.524,5.889,39,5.4509,5,311,15.2,390.5,15.71,21.7
|
||||
0.62976,0,8.14,0,0.538,5.949,61.8,4.7075,4,307,21,396.9,8.26,20.4
|
||||
0.63796,0,8.14,0,0.538,6.096,84.5,4.4619,4,307,21,380.02,10.26,18.2
|
||||
0.62739,0,8.14,0,0.538,5.834,56.5,4.4986,4,307,21,395.62,8.47,19.9
|
||||
1.05393,0,8.14,0,0.538,5.935,29.3,4.4986,4,307,21,386.85,6.58,23.1
|
||||
0.7842,0,8.14,0,0.538,5.99,81.7,4.2579,4,307,21,386.75,14.67,17.5
|
||||
0.80271,0,8.14,0,0.538,5.456,36.6,3.7965,4,307,21,288.99,11.69,20.2
|
||||
0.7258,0,8.14,0,0.538,5.727,69.5,3.7965,4,307,21,390.95,11.28,18.2
|
||||
1.25179,0,8.14,0,0.538,5.57,98.1,3.7979,4,307,21,376.57,21.02,13.6
|
||||
0.85204,0,8.14,0,0.538,5.965,89.2,4.0123,4,307,21,392.53,13.83,19.6
|
||||
1.23247,0,8.14,0,0.538,6.142,91.7,3.9769,4,307,21,396.9,18.72,15.2
|
||||
0.98843,0,8.14,0,0.538,5.813,100,4.0952,4,307,21,394.54,19.88,14.5
|
||||
0.75026,0,8.14,0,0.538,5.924,94.1,4.3996,4,307,21,394.33,16.3,15.6
|
||||
0.84054,0,8.14,0,0.538,5.599,85.7,4.4546,4,307,21,303.42,16.51,13.9
|
||||
0.67191,0,8.14,0,0.538,5.813,90.3,4.682,4,307,21,376.88,14.81,16.6
|
||||
0.95577,0,8.14,0,0.538,6.047,88.8,4.4534,4,307,21,306.38,17.28,14.8
|
||||
0.77299,0,8.14,0,0.538,6.495,94.4,4.4547,4,307,21,387.94,12.8,18.4
|
||||
1.00245,0,8.14,0,0.538,6.674,87.3,4.239,4,307,21,380.23,11.98,21
|
||||
1.13081,0,8.14,0,0.538,5.713,94.1,4.233,4,307,21,360.17,22.6,12.7
|
||||
1.35472,0,8.14,0,0.538,6.072,100,4.175,4,307,21,376.73,13.04,14.5
|
||||
1.38799,0,8.14,0,0.538,5.95,82,3.99,4,307,21,232.6,27.71,13.2
|
||||
1.15172,0,8.14,0,0.538,5.701,95,3.7872,4,307,21,358.77,18.35,13.1
|
||||
1.61282,0,8.14,0,0.538,6.096,96.9,3.7598,4,307,21,248.31,20.34,13.5
|
||||
0.06417,0,5.96,0,0.499,5.933,68.2,3.3603,5,279,19.2,396.9,9.68,18.9
|
||||
0.09744,0,5.96,0,0.499,5.841,61.4,3.3779,5,279,19.2,377.56,11.41,20
|
||||
0.08014,0,5.96,0,0.499,5.85,41.5,3.9342,5,279,19.2,396.9,8.77,21
|
||||
0.17505,0,5.96,0,0.499,5.966,30.2,3.8473,5,279,19.2,393.43,10.13,24.7
|
||||
0.02763,75,2.95,0,0.428,6.595,21.8,5.4011,3,252,18.3,395.63,4.32,30.8
|
||||
0.03359,75,2.95,0,0.428,7.024,15.8,5.4011,3,252,18.3,395.62,1.98,34.9
|
||||
0.12744,0,6.91,0,0.448,6.77,2.9,5.7209,3,233,17.9,385.41,4.84,26.6
|
||||
0.1415,0,6.91,0,0.448,6.169,6.6,5.7209,3,233,17.9,383.37,5.81,25.3
|
||||
0.15936,0,6.91,0,0.448,6.211,6.5,5.7209,3,233,17.9,394.46,7.44,24.7
|
||||
0.12269,0,6.91,0,0.448,6.069,40,5.7209,3,233,17.9,389.39,9.55,21.2
|
||||
0.17142,0,6.91,0,0.448,5.682,33.8,5.1004,3,233,17.9,396.9,10.21,19.3
|
||||
0.18836,0,6.91,0,0.448,5.786,33.3,5.1004,3,233,17.9,396.9,14.15,20
|
||||
0.22927,0,6.91,0,0.448,6.03,85.5,5.6894,3,233,17.9,392.74,18.8,16.6
|
||||
0.25387,0,6.91,0,0.448,5.399,95.3,5.87,3,233,17.9,396.9,30.81,14.4
|
||||
0.21977,0,6.91,0,0.448,5.602,62,6.0877,3,233,17.9,396.9,16.2,19.4
|
||||
0.08873,21,5.64,0,0.439,5.963,45.7,6.8147,4,243,16.8,395.56,13.45,19.7
|
||||
0.04337,21,5.64,0,0.439,6.115,63,6.8147,4,243,16.8,393.97,9.43,20.5
|
||||
0.0536,21,5.64,0,0.439,6.511,21.1,6.8147,4,243,16.8,396.9,5.28,25
|
||||
0.04981,21,5.64,0,0.439,5.998,21.4,6.8147,4,243,16.8,396.9,8.43,23.4
|
||||
0.0136,75,4,0,0.41,5.888,47.6,7.3197,3,469,21.1,396.9,14.8,18.9
|
||||
0.01311,90,1.22,0,0.403,7.249,21.9,8.6966,5,226,17.9,395.93,4.81,35.4
|
||||
0.02055,85,0.74,0,0.41,6.383,35.7,9.1876,2,313,17.3,396.9,5.77,24.7
|
||||
0.01432,100,1.32,0,0.411,6.816,40.5,8.3248,5,256,15.1,392.9,3.95,31.6
|
||||
0.15445,25,5.13,0,0.453,6.145,29.2,7.8148,8,284,19.7,390.68,6.86,23.3
|
||||
0.10328,25,5.13,0,0.453,5.927,47.2,6.932,8,284,19.7,396.9,9.22,19.6
|
||||
0.14932,25,5.13,0,0.453,5.741,66.2,7.2254,8,284,19.7,395.11,13.15,18.7
|
||||
0.17171,25,5.13,0,0.453,5.966,93.4,6.8185,8,284,19.7,378.08,14.44,16
|
||||
0.11027,25,5.13,0,0.453,6.456,67.8,7.2255,8,284,19.7,396.9,6.73,22.2
|
||||
0.1265,25,5.13,0,0.453,6.762,43.4,7.9809,8,284,19.7,395.58,9.5,25
|
||||
0.01951,17.5,1.38,0,0.4161,7.104,59.5,9.2229,3,216,18.6,393.24,8.05,33
|
||||
0.03584,80,3.37,0,0.398,6.29,17.8,6.6115,4,337,16.1,396.9,4.67,23.5
|
||||
0.04379,80,3.37,0,0.398,5.787,31.1,6.6115,4,337,16.1,396.9,10.24,19.4
|
||||
0.05789,12.5,6.07,0,0.409,5.878,21.4,6.498,4,345,18.9,396.21,8.1,22
|
||||
0.13554,12.5,6.07,0,0.409,5.594,36.8,6.498,4,345,18.9,396.9,13.09,17.4
|
||||
0.12816,12.5,6.07,0,0.409,5.885,33,6.498,4,345,18.9,396.9,8.79,20.9
|
||||
0.08826,0,10.81,0,0.413,6.417,6.6,5.2873,4,305,19.2,383.73,6.72,24.2
|
||||
0.15876,0,10.81,0,0.413,5.961,17.5,5.2873,4,305,19.2,376.94,9.88,21.7
|
||||
0.09164,0,10.81,0,0.413,6.065,7.8,5.2873,4,305,19.2,390.91,5.52,22.8
|
||||
0.19539,0,10.81,0,0.413,6.245,6.2,5.2873,4,305,19.2,377.17,7.54,23.4
|
||||
0.07896,0,12.83,0,0.437,6.273,6,4.2515,5,398,18.7,394.92,6.78,24.1
|
||||
0.09512,0,12.83,0,0.437,6.286,45,4.5026,5,398,18.7,383.23,8.94,21.4
|
||||
0.10153,0,12.83,0,0.437,6.279,74.5,4.0522,5,398,18.7,373.66,11.97,20
|
||||
0.08707,0,12.83,0,0.437,6.14,45.8,4.0905,5,398,18.7,386.96,10.27,20.8
|
||||
0.05646,0,12.83,0,0.437,6.232,53.7,5.0141,5,398,18.7,386.4,12.34,21.2
|
||||
0.08387,0,12.83,0,0.437,5.874,36.6,4.5026,5,398,18.7,396.06,9.1,20.3
|
||||
0.04113,25,4.86,0,0.426,6.727,33.5,5.4007,4,281,19,396.9,5.29,28
|
||||
0.04462,25,4.86,0,0.426,6.619,70.4,5.4007,4,281,19,395.63,7.22,23.9
|
||||
0.03659,25,4.86,0,0.426,6.302,32.2,5.4007,4,281,19,396.9,6.72,24.8
|
||||
0.03551,25,4.86,0,0.426,6.167,46.7,5.4007,4,281,19,390.64,7.51,22.9
|
||||
0.05059,0,4.49,0,0.449,6.389,48,4.7794,3,247,18.5,396.9,9.62,23.9
|
||||
0.05735,0,4.49,0,0.449,6.63,56.1,4.4377,3,247,18.5,392.3,6.53,26.6
|
||||
0.05188,0,4.49,0,0.449,6.015,45.1,4.4272,3,247,18.5,395.99,12.86,22.5
|
||||
0.07151,0,4.49,0,0.449,6.121,56.8,3.7476,3,247,18.5,395.15,8.44,22.2
|
||||
0.0566,0,3.41,0,0.489,7.007,86.3,3.4217,2,270,17.8,396.9,5.5,23.6
|
||||
0.05302,0,3.41,0,0.489,7.079,63.1,3.4145,2,270,17.8,396.06,5.7,28.7
|
||||
0.04684,0,3.41,0,0.489,6.417,66.1,3.0923,2,270,17.8,392.18,8.81,22.6
|
||||
0.03932,0,3.41,0,0.489,6.405,73.9,3.0921,2,270,17.8,393.55,8.2,22
|
||||
0.04203,28,15.04,0,0.464,6.442,53.6,3.6659,4,270,18.2,395.01,8.16,22.9
|
||||
0.02875,28,15.04,0,0.464,6.211,28.9,3.6659,4,270,18.2,396.33,6.21,25
|
||||
0.04294,28,15.04,0,0.464,6.249,77.3,3.615,4,270,18.2,396.9,10.59,20.6
|
||||
0.12204,0,2.89,0,0.445,6.625,57.8,3.4952,2,276,18,357.98,6.65,28.4
|
||||
0.11504,0,2.89,0,0.445,6.163,69.6,3.4952,2,276,18,391.83,11.34,21.4
|
||||
0.12083,0,2.89,0,0.445,8.069,76,3.4952,2,276,18,396.9,4.21,38.7
|
||||
0.08187,0,2.89,0,0.445,7.82,36.9,3.4952,2,276,18,393.53,3.57,43.8
|
||||
0.0686,0,2.89,0,0.445,7.416,62.5,3.4952,2,276,18,396.9,6.19,33.2
|
||||
0.14866,0,8.56,0,0.52,6.727,79.9,2.7778,5,384,20.9,394.76,9.42,27.5
|
||||
0.11432,0,8.56,0,0.52,6.781,71.3,2.8561,5,384,20.9,395.58,7.67,26.5
|
||||
0.22876,0,8.56,0,0.52,6.405,85.4,2.7147,5,384,20.9,70.8,10.63,18.6
|
||||
0.21161,0,8.56,0,0.52,6.137,87.4,2.7147,5,384,20.9,394.47,13.44,19.3
|
||||
0.1396,0,8.56,0,0.52,6.167,90,2.421,5,384,20.9,392.69,12.33,20.1
|
||||
0.13262,0,8.56,0,0.52,5.851,96.7,2.1069,5,384,20.9,394.05,16.47,19.5
|
||||
0.1712,0,8.56,0,0.52,5.836,91.9,2.211,5,384,20.9,395.67,18.66,19.5
|
||||
0.13117,0,8.56,0,0.52,6.127,85.2,2.1224,5,384,20.9,387.69,14.09,20.4
|
||||
0.12802,0,8.56,0,0.52,6.474,97.1,2.4329,5,384,20.9,395.24,12.27,19.8
|
||||
0.26363,0,8.56,0,0.52,6.229,91.2,2.5451,5,384,20.9,391.23,15.55,19.4
|
||||
0.10793,0,8.56,0,0.52,6.195,54.4,2.7778,5,384,20.9,393.49,13,21.7
|
||||
0.10084,0,10.01,0,0.547,6.715,81.6,2.6775,6,432,17.8,395.59,10.16,22.8
|
||||
0.12329,0,10.01,0,0.547,5.913,92.9,2.3534,6,432,17.8,394.95,16.21,18.8
|
||||
0.22212,0,10.01,0,0.547,6.092,95.4,2.548,6,432,17.8,396.9,17.09,18.7
|
||||
0.14231,0,10.01,0,0.547,6.254,84.2,2.2565,6,432,17.8,388.74,10.45,18.5
|
||||
0.17134,0,10.01,0,0.547,5.928,88.2,2.4631,6,432,17.8,344.91,15.76,18.3
|
||||
0.13158,0,10.01,0,0.547,6.176,72.5,2.7301,6,432,17.8,393.3,12.04,21.2
|
||||
0.15098,0,10.01,0,0.547,6.021,82.6,2.7474,6,432,17.8,394.51,10.3,19.2
|
||||
0.13058,0,10.01,0,0.547,5.872,73.1,2.4775,6,432,17.8,338.63,15.37,20.4
|
||||
0.14476,0,10.01,0,0.547,5.731,65.2,2.7592,6,432,17.8,391.5,13.61,19.3
|
||||
0.06899,0,25.65,0,0.581,5.87,69.7,2.2577,2,188,19.1,389.15,14.37,22
|
||||
0.07165,0,25.65,0,0.581,6.004,84.1,2.1974,2,188,19.1,377.67,14.27,20.3
|
||||
0.09299,0,25.65,0,0.581,5.961,92.9,2.0869,2,188,19.1,378.09,17.93,20.5
|
||||
0.15038,0,25.65,0,0.581,5.856,97,1.9444,2,188,19.1,370.31,25.41,17.3
|
||||
0.09849,0,25.65,0,0.581,5.879,95.8,2.0063,2,188,19.1,379.38,17.58,18.8
|
||||
0.16902,0,25.65,0,0.581,5.986,88.4,1.9929,2,188,19.1,385.02,14.81,21.4
|
||||
0.38735,0,25.65,0,0.581,5.613,95.6,1.7572,2,188,19.1,359.29,27.26,15.7
|
||||
0.25915,0,21.89,0,0.624,5.693,96,1.7883,4,437,21.2,392.11,17.19,16.2
|
||||
0.32543,0,21.89,0,0.624,6.431,98.8,1.8125,4,437,21.2,396.9,15.39,18
|
||||
0.88125,0,21.89,0,0.624,5.637,94.7,1.9799,4,437,21.2,396.9,18.34,14.3
|
||||
0.34006,0,21.89,0,0.624,6.458,98.9,2.1185,4,437,21.2,395.04,12.6,19.2
|
||||
1.19294,0,21.89,0,0.624,6.326,97.7,2.271,4,437,21.2,396.9,12.26,19.6
|
||||
0.59005,0,21.89,0,0.624,6.372,97.9,2.3274,4,437,21.2,385.76,11.12,23
|
||||
0.32982,0,21.89,0,0.624,5.822,95.4,2.4699,4,437,21.2,388.69,15.03,18.4
|
||||
0.97617,0,21.89,0,0.624,5.757,98.4,2.346,4,437,21.2,262.76,17.31,15.6
|
||||
0.55778,0,21.89,0,0.624,6.335,98.2,2.1107,4,437,21.2,394.67,16.96,18.1
|
||||
0.32264,0,21.89,0,0.624,5.942,93.5,1.9669,4,437,21.2,378.25,16.9,17.4
|
||||
0.35233,0,21.89,0,0.624,6.454,98.4,1.8498,4,437,21.2,394.08,14.59,17.1
|
||||
0.2498,0,21.89,0,0.624,5.857,98.2,1.6686,4,437,21.2,392.04,21.32,13.3
|
||||
0.54452,0,21.89,0,0.624,6.151,97.9,1.6687,4,437,21.2,396.9,18.46,17.8
|
||||
0.2909,0,21.89,0,0.624,6.174,93.6,1.6119,4,437,21.2,388.08,24.16,14
|
||||
1.62864,0,21.89,0,0.624,5.019,100,1.4394,4,437,21.2,396.9,34.41,14.4
|
||||
3.32105,0,19.58,1,0.871,5.403,100,1.3216,5,403,14.7,396.9,26.82,13.4
|
||||
4.0974,0,19.58,0,0.871,5.468,100,1.4118,5,403,14.7,396.9,26.42,15.6
|
||||
2.77974,0,19.58,0,0.871,4.903,97.8,1.3459,5,403,14.7,396.9,29.29,11.8
|
||||
2.37934,0,19.58,0,0.871,6.13,100,1.4191,5,403,14.7,172.91,27.8,13.8
|
||||
2.15505,0,19.58,0,0.871,5.628,100,1.5166,5,403,14.7,169.27,16.65,15.6
|
||||
2.36862,0,19.58,0,0.871,4.926,95.7,1.4608,5,403,14.7,391.71,29.53,14.6
|
||||
2.33099,0,19.58,0,0.871,5.186,93.8,1.5296,5,403,14.7,356.99,28.32,17.8
|
||||
2.73397,0,19.58,0,0.871,5.597,94.9,1.5257,5,403,14.7,351.85,21.45,15.4
|
||||
1.6566,0,19.58,0,0.871,6.122,97.3,1.618,5,403,14.7,372.8,14.1,21.5
|
||||
1.49632,0,19.58,0,0.871,5.404,100,1.5916,5,403,14.7,341.6,13.28,19.6
|
||||
1.12658,0,19.58,1,0.871,5.012,88,1.6102,5,403,14.7,343.28,12.12,15.3
|
||||
2.14918,0,19.58,0,0.871,5.709,98.5,1.6232,5,403,14.7,261.95,15.79,19.4
|
||||
1.41385,0,19.58,1,0.871,6.129,96,1.7494,5,403,14.7,321.02,15.12,17
|
||||
3.53501,0,19.58,1,0.871,6.152,82.6,1.7455,5,403,14.7,88.01,15.02,15.6
|
||||
2.44668,0,19.58,0,0.871,5.272,94,1.7364,5,403,14.7,88.63,16.14,13.1
|
||||
1.22358,0,19.58,0,0.605,6.943,97.4,1.8773,5,403,14.7,363.43,4.59,41.3
|
||||
1.34284,0,19.58,0,0.605,6.066,100,1.7573,5,403,14.7,353.89,6.43,24.3
|
||||
1.42502,0,19.58,0,0.871,6.51,100,1.7659,5,403,14.7,364.31,7.39,23.3
|
||||
1.27346,0,19.58,1,0.605,6.25,92.6,1.7984,5,403,14.7,338.92,5.5,27
|
||||
1.46336,0,19.58,0,0.605,7.489,90.8,1.9709,5,403,14.7,374.43,1.73,50
|
||||
1.83377,0,19.58,1,0.605,7.802,98.2,2.0407,5,403,14.7,389.61,1.92,50
|
||||
1.51902,0,19.58,1,0.605,8.375,93.9,2.162,5,403,14.7,388.45,3.32,50
|
||||
2.24236,0,19.58,0,0.605,5.854,91.8,2.422,5,403,14.7,395.11,11.64,22.7
|
||||
2.924,0,19.58,0,0.605,6.101,93,2.2834,5,403,14.7,240.16,9.81,25
|
||||
2.01019,0,19.58,0,0.605,7.929,96.2,2.0459,5,403,14.7,369.3,3.7,50
|
||||
1.80028,0,19.58,0,0.605,5.877,79.2,2.4259,5,403,14.7,227.61,12.14,23.8
|
||||
2.3004,0,19.58,0,0.605,6.319,96.1,2.1,5,403,14.7,297.09,11.1,23.8
|
||||
2.44953,0,19.58,0,0.605,6.402,95.2,2.2625,5,403,14.7,330.04,11.32,22.3
|
||||
1.20742,0,19.58,0,0.605,5.875,94.6,2.4259,5,403,14.7,292.29,14.43,17.4
|
||||
2.3139,0,19.58,0,0.605,5.88,97.3,2.3887,5,403,14.7,348.13,12.03,19.1
|
||||
0.13914,0,4.05,0,0.51,5.572,88.5,2.5961,5,296,16.6,396.9,14.69,23.1
|
||||
0.09178,0,4.05,0,0.51,6.416,84.1,2.6463,5,296,16.6,395.5,9.04,23.6
|
||||
0.08447,0,4.05,0,0.51,5.859,68.7,2.7019,5,296,16.6,393.23,9.64,22.6
|
||||
0.06664,0,4.05,0,0.51,6.546,33.1,3.1323,5,296,16.6,390.96,5.33,29.4
|
||||
0.07022,0,4.05,0,0.51,6.02,47.2,3.5549,5,296,16.6,393.23,10.11,23.2
|
||||
0.05425,0,4.05,0,0.51,6.315,73.4,3.3175,5,296,16.6,395.6,6.29,24.6
|
||||
0.06642,0,4.05,0,0.51,6.86,74.4,2.9153,5,296,16.6,391.27,6.92,29.9
|
||||
0.0578,0,2.46,0,0.488,6.98,58.4,2.829,3,193,17.8,396.9,5.04,37.2
|
||||
0.06588,0,2.46,0,0.488,7.765,83.3,2.741,3,193,17.8,395.56,7.56,39.8
|
||||
0.06888,0,2.46,0,0.488,6.144,62.2,2.5979,3,193,17.8,396.9,9.45,36.2
|
||||
0.09103,0,2.46,0,0.488,7.155,92.2,2.7006,3,193,17.8,394.12,4.82,37.9
|
||||
0.10008,0,2.46,0,0.488,6.563,95.6,2.847,3,193,17.8,396.9,5.68,32.5
|
||||
0.08308,0,2.46,0,0.488,5.604,89.8,2.9879,3,193,17.8,391,13.98,26.4
|
||||
0.06047,0,2.46,0,0.488,6.153,68.8,3.2797,3,193,17.8,387.11,13.15,29.6
|
||||
0.05602,0,2.46,0,0.488,7.831,53.6,3.1992,3,193,17.8,392.63,4.45,50
|
||||
0.07875,45,3.44,0,0.437,6.782,41.1,3.7886,5,398,15.2,393.87,6.68,32
|
||||
0.12579,45,3.44,0,0.437,6.556,29.1,4.5667,5,398,15.2,382.84,4.56,29.8
|
||||
0.0837,45,3.44,0,0.437,7.185,38.9,4.5667,5,398,15.2,396.9,5.39,34.9
|
||||
0.09068,45,3.44,0,0.437,6.951,21.5,6.4798,5,398,15.2,377.68,5.1,37
|
||||
0.06911,45,3.44,0,0.437,6.739,30.8,6.4798,5,398,15.2,389.71,4.69,30.5
|
||||
0.08664,45,3.44,0,0.437,7.178,26.3,6.4798,5,398,15.2,390.49,2.87,36.4
|
||||
0.02187,60,2.93,0,0.401,6.8,9.9,6.2196,1,265,15.6,393.37,5.03,31.1
|
||||
0.01439,60,2.93,0,0.401,6.604,18.8,6.2196,1,265,15.6,376.7,4.38,29.1
|
||||
0.01381,80,0.46,0,0.422,7.875,32,5.6484,4,255,14.4,394.23,2.97,50
|
||||
0.04011,80,1.52,0,0.404,7.287,34.1,7.309,2,329,12.6,396.9,4.08,33.3
|
||||
0.04666,80,1.52,0,0.404,7.107,36.6,7.309,2,329,12.6,354.31,8.61,30.3
|
||||
0.03768,80,1.52,0,0.404,7.274,38.3,7.309,2,329,12.6,392.2,6.62,34.6
|
||||
0.0315,95,1.47,0,0.403,6.975,15.3,7.6534,3,402,17,396.9,4.56,34.9
|
||||
0.01778,95,1.47,0,0.403,7.135,13.9,7.6534,3,402,17,384.3,4.45,32.9
|
||||
0.03445,82.5,2.03,0,0.415,6.162,38.4,6.27,2,348,14.7,393.77,7.43,24.1
|
||||
0.02177,82.5,2.03,0,0.415,7.61,15.7,6.27,2,348,14.7,395.38,3.11,42.3
|
||||
0.0351,95,2.68,0,0.4161,7.853,33.2,5.118,4,224,14.7,392.78,3.81,48.5
|
||||
0.02009,95,2.68,0,0.4161,8.034,31.9,5.118,4,224,14.7,390.55,2.88,50
|
||||
0.13642,0,10.59,0,0.489,5.891,22.3,3.9454,4,277,18.6,396.9,10.87,22.6
|
||||
0.22969,0,10.59,0,0.489,6.326,52.5,4.3549,4,277,18.6,394.87,10.97,24.4
|
||||
0.25199,0,10.59,0,0.489,5.783,72.7,4.3549,4,277,18.6,389.43,18.06,22.5
|
||||
0.13587,0,10.59,1,0.489,6.064,59.1,4.2392,4,277,18.6,381.32,14.66,24.4
|
||||
0.43571,0,10.59,1,0.489,5.344,100,3.875,4,277,18.6,396.9,23.09,20
|
||||
0.17446,0,10.59,1,0.489,5.96,92.1,3.8771,4,277,18.6,393.25,17.27,21.7
|
||||
0.37578,0,10.59,1,0.489,5.404,88.6,3.665,4,277,18.6,395.24,23.98,19.3
|
||||
0.21719,0,10.59,1,0.489,5.807,53.8,3.6526,4,277,18.6,390.94,16.03,22.4
|
||||
0.14052,0,10.59,0,0.489,6.375,32.3,3.9454,4,277,18.6,385.81,9.38,28.1
|
||||
0.28955,0,10.59,0,0.489,5.412,9.8,3.5875,4,277,18.6,348.93,29.55,23.7
|
||||
0.19802,0,10.59,0,0.489,6.182,42.4,3.9454,4,277,18.6,393.63,9.47,25
|
||||
0.0456,0,13.89,1,0.55,5.888,56,3.1121,5,276,16.4,392.8,13.51,23.3
|
||||
0.07013,0,13.89,0,0.55,6.642,85.1,3.4211,5,276,16.4,392.78,9.69,28.7
|
||||
0.11069,0,13.89,1,0.55,5.951,93.8,2.8893,5,276,16.4,396.9,17.92,21.5
|
||||
0.11425,0,13.89,1,0.55,6.373,92.4,3.3633,5,276,16.4,393.74,10.5,23
|
||||
0.35809,0,6.2,1,0.507,6.951,88.5,2.8617,8,307,17.4,391.7,9.71,26.7
|
||||
0.40771,0,6.2,1,0.507,6.164,91.3,3.048,8,307,17.4,395.24,21.46,21.7
|
||||
0.62356,0,6.2,1,0.507,6.879,77.7,3.2721,8,307,17.4,390.39,9.93,27.5
|
||||
0.6147,0,6.2,0,0.507,6.618,80.8,3.2721,8,307,17.4,396.9,7.6,30.1
|
||||
0.31533,0,6.2,0,0.504,8.266,78.3,2.8944,8,307,17.4,385.05,4.14,44.8
|
||||
0.52693,0,6.2,0,0.504,8.725,83,2.8944,8,307,17.4,382,4.63,50
|
||||
0.38214,0,6.2,0,0.504,8.04,86.5,3.2157,8,307,17.4,387.38,3.13,37.6
|
||||
0.41238,0,6.2,0,0.504,7.163,79.9,3.2157,8,307,17.4,372.08,6.36,31.6
|
||||
0.29819,0,6.2,0,0.504,7.686,17,3.3751,8,307,17.4,377.51,3.92,46.7
|
||||
0.44178,0,6.2,0,0.504,6.552,21.4,3.3751,8,307,17.4,380.34,3.76,31.5
|
||||
0.537,0,6.2,0,0.504,5.981,68.1,3.6715,8,307,17.4,378.35,11.65,24.3
|
||||
0.46296,0,6.2,0,0.504,7.412,76.9,3.6715,8,307,17.4,376.14,5.25,31.7
|
||||
0.57529,0,6.2,0,0.507,8.337,73.3,3.8384,8,307,17.4,385.91,2.47,41.7
|
||||
0.33147,0,6.2,0,0.507,8.247,70.4,3.6519,8,307,17.4,378.95,3.95,48.3
|
||||
0.44791,0,6.2,1,0.507,6.726,66.5,3.6519,8,307,17.4,360.2,8.05,29
|
||||
0.33045,0,6.2,0,0.507,6.086,61.5,3.6519,8,307,17.4,376.75,10.88,24
|
||||
0.52058,0,6.2,1,0.507,6.631,76.5,4.148,8,307,17.4,388.45,9.54,25.1
|
||||
0.51183,0,6.2,0,0.507,7.358,71.6,4.148,8,307,17.4,390.07,4.73,31.5
|
||||
0.08244,30,4.93,0,0.428,6.481,18.5,6.1899,6,300,16.6,379.41,6.36,23.7
|
||||
0.09252,30,4.93,0,0.428,6.606,42.2,6.1899,6,300,16.6,383.78,7.37,23.3
|
||||
0.11329,30,4.93,0,0.428,6.897,54.3,6.3361,6,300,16.6,391.25,11.38,22
|
||||
0.10612,30,4.93,0,0.428,6.095,65.1,6.3361,6,300,16.6,394.62,12.4,20.1
|
||||
0.1029,30,4.93,0,0.428,6.358,52.9,7.0355,6,300,16.6,372.75,11.22,22.2
|
||||
0.12757,30,4.93,0,0.428,6.393,7.8,7.0355,6,300,16.6,374.71,5.19,23.7
|
||||
0.20608,22,5.86,0,0.431,5.593,76.5,7.9549,7,330,19.1,372.49,12.5,17.6
|
||||
0.19133,22,5.86,0,0.431,5.605,70.2,7.9549,7,330,19.1,389.13,18.46,18.5
|
||||
0.33983,22,5.86,0,0.431,6.108,34.9,8.0555,7,330,19.1,390.18,9.16,24.3
|
||||
0.19657,22,5.86,0,0.431,6.226,79.2,8.0555,7,330,19.1,376.14,10.15,20.5
|
||||
0.16439,22,5.86,0,0.431,6.433,49.1,7.8265,7,330,19.1,374.71,9.52,24.5
|
||||
0.19073,22,5.86,0,0.431,6.718,17.5,7.8265,7,330,19.1,393.74,6.56,26.2
|
||||
0.1403,22,5.86,0,0.431,6.487,13,7.3967,7,330,19.1,396.28,5.9,24.4
|
||||
0.21409,22,5.86,0,0.431,6.438,8.9,7.3967,7,330,19.1,377.07,3.59,24.8
|
||||
0.08221,22,5.86,0,0.431,6.957,6.8,8.9067,7,330,19.1,386.09,3.53,29.6
|
||||
0.36894,22,5.86,0,0.431,8.259,8.4,8.9067,7,330,19.1,396.9,3.54,42.8
|
||||
0.04819,80,3.64,0,0.392,6.108,32,9.2203,1,315,16.4,392.89,6.57,21.9
|
||||
0.03548,80,3.64,0,0.392,5.876,19.1,9.2203,1,315,16.4,395.18,9.25,20.9
|
||||
0.01538,90,3.75,0,0.394,7.454,34.2,6.3361,3,244,15.9,386.34,3.11,44
|
||||
0.61154,20,3.97,0,0.647,8.704,86.9,1.801,5,264,13,389.7,5.12,50
|
||||
0.66351,20,3.97,0,0.647,7.333,100,1.8946,5,264,13,383.29,7.79,36
|
||||
0.65665,20,3.97,0,0.647,6.842,100,2.0107,5,264,13,391.93,6.9,30.1
|
||||
0.54011,20,3.97,0,0.647,7.203,81.8,2.1121,5,264,13,392.8,9.59,33.8
|
||||
0.53412,20,3.97,0,0.647,7.52,89.4,2.1398,5,264,13,388.37,7.26,43.1
|
||||
0.52014,20,3.97,0,0.647,8.398,91.5,2.2885,5,264,13,386.86,5.91,48.8
|
||||
0.82526,20,3.97,0,0.647,7.327,94.5,2.0788,5,264,13,393.42,11.25,31
|
||||
0.55007,20,3.97,0,0.647,7.206,91.6,1.9301,5,264,13,387.89,8.1,36.5
|
||||
0.76162,20,3.97,0,0.647,5.56,62.8,1.9865,5,264,13,392.4,10.45,22.8
|
||||
0.7857,20,3.97,0,0.647,7.014,84.6,2.1329,5,264,13,384.07,14.79,30.7
|
||||
0.57834,20,3.97,0,0.575,8.297,67,2.4216,5,264,13,384.54,7.44,50
|
||||
0.5405,20,3.97,0,0.575,7.47,52.6,2.872,5,264,13,390.3,3.16,43.5
|
||||
0.09065,20,6.96,1,0.464,5.92,61.5,3.9175,3,223,18.6,391.34,13.65,20.7
|
||||
0.29916,20,6.96,0,0.464,5.856,42.1,4.429,3,223,18.6,388.65,13,21.1
|
||||
0.16211,20,6.96,0,0.464,6.24,16.3,4.429,3,223,18.6,396.9,6.59,25.2
|
||||
0.1146,20,6.96,0,0.464,6.538,58.7,3.9175,3,223,18.6,394.96,7.73,24.4
|
||||
0.22188,20,6.96,1,0.464,7.691,51.8,4.3665,3,223,18.6,390.77,6.58,35.2
|
||||
0.05644,40,6.41,1,0.447,6.758,32.9,4.0776,4,254,17.6,396.9,3.53,32.4
|
||||
0.09604,40,6.41,0,0.447,6.854,42.8,4.2673,4,254,17.6,396.9,2.98,32
|
||||
0.10469,40,6.41,1,0.447,7.267,49,4.7872,4,254,17.6,389.25,6.05,33.2
|
||||
0.06127,40,6.41,1,0.447,6.826,27.6,4.8628,4,254,17.6,393.45,4.16,33.1
|
||||
0.07978,40,6.41,0,0.447,6.482,32.1,4.1403,4,254,17.6,396.9,7.19,29.1
|
||||
0.21038,20,3.33,0,0.4429,6.812,32.2,4.1007,5,216,14.9,396.9,4.85,35.1
|
||||
0.03578,20,3.33,0,0.4429,7.82,64.5,4.6947,5,216,14.9,387.31,3.76,45.4
|
||||
0.03705,20,3.33,0,0.4429,6.968,37.2,5.2447,5,216,14.9,392.23,4.59,35.4
|
||||
0.06129,20,3.33,1,0.4429,7.645,49.7,5.2119,5,216,14.9,377.07,3.01,46
|
||||
0.01501,90,1.21,1,0.401,7.923,24.8,5.885,1,198,13.6,395.52,3.16,50
|
||||
0.00906,90,2.97,0,0.4,7.088,20.8,7.3073,1,285,15.3,394.72,7.85,32.2
|
||||
0.01096,55,2.25,0,0.389,6.453,31.9,7.3073,1,300,15.3,394.72,8.23,22
|
||||
0.01965,80,1.76,0,0.385,6.23,31.5,9.0892,1,241,18.2,341.6,12.93,20.1
|
||||
0.03871,52.5,5.32,0,0.405,6.209,31.3,7.3172,6,293,16.6,396.9,7.14,23.2
|
||||
0.0459,52.5,5.32,0,0.405,6.315,45.6,7.3172,6,293,16.6,396.9,7.6,22.3
|
||||
0.04297,52.5,5.32,0,0.405,6.565,22.9,7.3172,6,293,16.6,371.72,9.51,24.8
|
||||
0.03502,80,4.95,0,0.411,6.861,27.9,5.1167,4,245,19.2,396.9,3.33,28.5
|
||||
0.07886,80,4.95,0,0.411,7.148,27.7,5.1167,4,245,19.2,396.9,3.56,37.3
|
||||
0.03615,80,4.95,0,0.411,6.63,23.4,5.1167,4,245,19.2,396.9,4.7,27.9
|
||||
0.08265,0,13.92,0,0.437,6.127,18.4,5.5027,4,289,16,396.9,8.58,23.9
|
||||
0.08199,0,13.92,0,0.437,6.009,42.3,5.5027,4,289,16,396.9,10.4,21.7
|
||||
0.12932,0,13.92,0,0.437,6.678,31.1,5.9604,4,289,16,396.9,6.27,28.6
|
||||
0.05372,0,13.92,0,0.437,6.549,51,5.9604,4,289,16,392.85,7.39,27.1
|
||||
0.14103,0,13.92,0,0.437,5.79,58,6.32,4,289,16,396.9,15.84,20.3
|
||||
0.06466,70,2.24,0,0.4,6.345,20.1,7.8278,5,358,14.8,368.24,4.97,22.5
|
||||
0.05561,70,2.24,0,0.4,7.041,10,7.8278,5,358,14.8,371.58,4.74,29
|
||||
0.04417,70,2.24,0,0.4,6.871,47.4,7.8278,5,358,14.8,390.86,6.07,24.8
|
||||
0.03537,34,6.09,0,0.433,6.59,40.4,5.4917,7,329,16.1,395.75,9.5,22
|
||||
0.09266,34,6.09,0,0.433,6.495,18.4,5.4917,7,329,16.1,383.61,8.67,26.4
|
||||
0.1,34,6.09,0,0.433,6.982,17.7,5.4917,7,329,16.1,390.43,4.86,33.1
|
||||
0.05515,33,2.18,0,0.472,7.236,41.1,4.022,7,222,18.4,393.68,6.93,36.1
|
||||
0.05479,33,2.18,0,0.472,6.616,58.1,3.37,7,222,18.4,393.36,8.93,28.4
|
||||
0.07503,33,2.18,0,0.472,7.42,71.9,3.0992,7,222,18.4,396.9,6.47,33.4
|
||||
0.04932,33,2.18,0,0.472,6.849,70.3,3.1827,7,222,18.4,396.9,7.53,28.2
|
||||
0.49298,0,9.9,0,0.544,6.635,82.5,3.3175,4,304,18.4,396.9,4.54,22.8
|
||||
0.3494,0,9.9,0,0.544,5.972,76.7,3.1025,4,304,18.4,396.24,9.97,20.3
|
||||
2.63548,0,9.9,0,0.544,4.973,37.8,2.5194,4,304,18.4,350.45,12.64,16.1
|
||||
0.79041,0,9.9,0,0.544,6.122,52.8,2.6403,4,304,18.4,396.9,5.98,22.1
|
||||
0.26169,0,9.9,0,0.544,6.023,90.4,2.834,4,304,18.4,396.3,11.72,19.4
|
||||
0.26938,0,9.9,0,0.544,6.266,82.8,3.2628,4,304,18.4,393.39,7.9,21.6
|
||||
0.3692,0,9.9,0,0.544,6.567,87.3,3.6023,4,304,18.4,395.69,9.28,23.8
|
||||
0.25356,0,9.9,0,0.544,5.705,77.7,3.945,4,304,18.4,396.42,11.5,16.2
|
||||
0.31827,0,9.9,0,0.544,5.914,83.2,3.9986,4,304,18.4,390.7,18.33,17.8
|
||||
0.24522,0,9.9,0,0.544,5.782,71.7,4.0317,4,304,18.4,396.9,15.94,19.8
|
||||
0.40202,0,9.9,0,0.544,6.382,67.2,3.5325,4,304,18.4,395.21,10.36,23.1
|
||||
0.47547,0,9.9,0,0.544,6.113,58.8,4.0019,4,304,18.4,396.23,12.73,21
|
||||
0.1676,0,7.38,0,0.493,6.426,52.3,4.5404,5,287,19.6,396.9,7.2,23.8
|
||||
0.18159,0,7.38,0,0.493,6.376,54.3,4.5404,5,287,19.6,396.9,6.87,23.1
|
||||
0.35114,0,7.38,0,0.493,6.041,49.9,4.7211,5,287,19.6,396.9,7.7,20.4
|
||||
0.28392,0,7.38,0,0.493,5.708,74.3,4.7211,5,287,19.6,391.13,11.74,18.5
|
||||
0.34109,0,7.38,0,0.493,6.415,40.1,4.7211,5,287,19.6,396.9,6.12,25
|
||||
0.19186,0,7.38,0,0.493,6.431,14.7,5.4159,5,287,19.6,393.68,5.08,24.6
|
||||
0.30347,0,7.38,0,0.493,6.312,28.9,5.4159,5,287,19.6,396.9,6.15,23
|
||||
0.24103,0,7.38,0,0.493,6.083,43.7,5.4159,5,287,19.6,396.9,12.79,22.2
|
||||
0.06617,0,3.24,0,0.46,5.868,25.8,5.2146,4,430,16.9,382.44,9.97,19.3
|
||||
0.06724,0,3.24,0,0.46,6.333,17.2,5.2146,4,430,16.9,375.21,7.34,22.6
|
||||
0.04544,0,3.24,0,0.46,6.144,32.2,5.8736,4,430,16.9,368.57,9.09,19.8
|
||||
0.05023,35,6.06,0,0.4379,5.706,28.4,6.6407,1,304,16.9,394.02,12.43,17.1
|
||||
0.03466,35,6.06,0,0.4379,6.031,23.3,6.6407,1,304,16.9,362.25,7.83,19.4
|
||||
0.05083,0,5.19,0,0.515,6.316,38.1,6.4584,5,224,20.2,389.71,5.68,22.2
|
||||
0.03738,0,5.19,0,0.515,6.31,38.5,6.4584,5,224,20.2,389.4,6.75,20.7
|
||||
0.03961,0,5.19,0,0.515,6.037,34.5,5.9853,5,224,20.2,396.9,8.01,21.1
|
||||
0.03427,0,5.19,0,0.515,5.869,46.3,5.2311,5,224,20.2,396.9,9.8,19.5
|
||||
0.03041,0,5.19,0,0.515,5.895,59.6,5.615,5,224,20.2,394.81,10.56,18.5
|
||||
0.03306,0,5.19,0,0.515,6.059,37.3,4.8122,5,224,20.2,396.14,8.51,20.6
|
||||
0.05497,0,5.19,0,0.515,5.985,45.4,4.8122,5,224,20.2,396.9,9.74,19
|
||||
0.06151,0,5.19,0,0.515,5.968,58.5,4.8122,5,224,20.2,396.9,9.29,18.7
|
||||
0.01301,35,1.52,0,0.442,7.241,49.3,7.0379,1,284,15.5,394.74,5.49,32.7
|
||||
0.02498,0,1.89,0,0.518,6.54,59.7,6.2669,1,422,15.9,389.96,8.65,16.5
|
||||
0.02543,55,3.78,0,0.484,6.696,56.4,5.7321,5,370,17.6,396.9,7.18,23.9
|
||||
0.03049,55,3.78,0,0.484,6.874,28.1,6.4654,5,370,17.6,387.97,4.61,31.2
|
||||
0.03113,0,4.39,0,0.442,6.014,48.5,8.0136,3,352,18.8,385.64,10.53,17.5
|
||||
0.06162,0,4.39,0,0.442,5.898,52.3,8.0136,3,352,18.8,364.61,12.67,17.2
|
||||
0.0187,85,4.15,0,0.429,6.516,27.7,8.5353,4,351,17.9,392.43,6.36,23.1
|
||||
0.01501,80,2.01,0,0.435,6.635,29.7,8.344,4,280,17,390.94,5.99,24.5
|
||||
0.02899,40,1.25,0,0.429,6.939,34.5,8.7921,1,335,19.7,389.85,5.89,26.6
|
||||
0.06211,40,1.25,0,0.429,6.49,44.4,8.7921,1,335,19.7,396.9,5.98,22.9
|
||||
0.0795,60,1.69,0,0.411,6.579,35.9,10.7103,4,411,18.3,370.78,5.49,24.1
|
||||
0.07244,60,1.69,0,0.411,5.884,18.5,10.7103,4,411,18.3,392.33,7.79,18.6
|
||||
0.01709,90,2.02,0,0.41,6.728,36.1,12.1265,5,187,17,384.46,4.5,30.1
|
||||
0.04301,80,1.91,0,0.413,5.663,21.9,10.5857,4,334,22,382.8,8.05,18.2
|
||||
0.10659,80,1.91,0,0.413,5.936,19.5,10.5857,4,334,22,376.04,5.57,20.6
|
||||
8.98296,0,18.1,1,0.77,6.212,97.4,2.1222,24,666,20.2,377.73,17.6,17.8
|
||||
3.8497,0,18.1,1,0.77,6.395,91,2.5052,24,666,20.2,391.34,13.27,21.7
|
||||
5.20177,0,18.1,1,0.77,6.127,83.4,2.7227,24,666,20.2,395.43,11.48,22.7
|
||||
4.26131,0,18.1,0,0.77,6.112,81.3,2.5091,24,666,20.2,390.74,12.67,22.6
|
||||
4.54192,0,18.1,0,0.77,6.398,88,2.5182,24,666,20.2,374.56,7.79,25
|
||||
3.83684,0,18.1,0,0.77,6.251,91.1,2.2955,24,666,20.2,350.65,14.19,19.9
|
||||
3.67822,0,18.1,0,0.77,5.362,96.2,2.1036,24,666,20.2,380.79,10.19,20.8
|
||||
4.22239,0,18.1,1,0.77,5.803,89,1.9047,24,666,20.2,353.04,14.64,16.8
|
||||
3.47428,0,18.1,1,0.718,8.78,82.9,1.9047,24,666,20.2,354.55,5.29,21.9
|
||||
4.55587,0,18.1,0,0.718,3.561,87.9,1.6132,24,666,20.2,354.7,7.12,27.5
|
||||
3.69695,0,18.1,0,0.718,4.963,91.4,1.7523,24,666,20.2,316.03,14,21.9
|
||||
13.5222,0,18.1,0,0.631,3.863,100,1.5106,24,666,20.2,131.42,13.33,23.1
|
||||
4.89822,0,18.1,0,0.631,4.97,100,1.3325,24,666,20.2,375.52,3.26,50
|
||||
5.66998,0,18.1,1,0.631,6.683,96.8,1.3567,24,666,20.2,375.33,3.73,50
|
||||
6.53876,0,18.1,1,0.631,7.016,97.5,1.2024,24,666,20.2,392.05,2.96,50
|
||||
9.2323,0,18.1,0,0.631,6.216,100,1.1691,24,666,20.2,366.15,9.53,50
|
||||
8.26725,0,18.1,1,0.668,5.875,89.6,1.1296,24,666,20.2,347.88,8.88,50
|
||||
11.1081,0,18.1,0,0.668,4.906,100,1.1742,24,666,20.2,396.9,34.77,13.8
|
||||
18.4982,0,18.1,0,0.668,4.138,100,1.137,24,666,20.2,396.9,37.97,13.8
|
||||
19.6091,0,18.1,0,0.671,7.313,97.9,1.3163,24,666,20.2,396.9,13.44,15
|
||||
15.288,0,18.1,0,0.671,6.649,93.3,1.3449,24,666,20.2,363.02,23.24,13.9
|
||||
9.82349,0,18.1,0,0.671,6.794,98.8,1.358,24,666,20.2,396.9,21.24,13.3
|
||||
23.6482,0,18.1,0,0.671,6.38,96.2,1.3861,24,666,20.2,396.9,23.69,13.1
|
||||
17.8667,0,18.1,0,0.671,6.223,100,1.3861,24,666,20.2,393.74,21.78,10.2
|
||||
88.9762,0,18.1,0,0.671,6.968,91.9,1.4165,24,666,20.2,396.9,17.21,10.4
|
||||
15.8744,0,18.1,0,0.671,6.545,99.1,1.5192,24,666,20.2,396.9,21.08,10.9
|
||||
9.18702,0,18.1,0,0.7,5.536,100,1.5804,24,666,20.2,396.9,23.6,11.3
|
||||
7.99248,0,18.1,0,0.7,5.52,100,1.5331,24,666,20.2,396.9,24.56,12.3
|
||||
20.0849,0,18.1,0,0.7,4.368,91.2,1.4395,24,666,20.2,285.83,30.63,8.8
|
||||
16.8118,0,18.1,0,0.7,5.277,98.1,1.4261,24,666,20.2,396.9,30.81,7.2
|
||||
24.3938,0,18.1,0,0.7,4.652,100,1.4672,24,666,20.2,396.9,28.28,10.5
|
||||
22.5971,0,18.1,0,0.7,5,89.5,1.5184,24,666,20.2,396.9,31.99,7.4
|
||||
14.3337,0,18.1,0,0.7,4.88,100,1.5895,24,666,20.2,372.92,30.62,10.2
|
||||
8.15174,0,18.1,0,0.7,5.39,98.9,1.7281,24,666,20.2,396.9,20.85,11.5
|
||||
6.96215,0,18.1,0,0.7,5.713,97,1.9265,24,666,20.2,394.43,17.11,15.1
|
||||
5.29305,0,18.1,0,0.7,6.051,82.5,2.1678,24,666,20.2,378.38,18.76,23.2
|
||||
11.5779,0,18.1,0,0.7,5.036,97,1.77,24,666,20.2,396.9,25.68,9.7
|
||||
8.64476,0,18.1,0,0.693,6.193,92.6,1.7912,24,666,20.2,396.9,15.17,13.8
|
||||
13.3598,0,18.1,0,0.693,5.887,94.7,1.7821,24,666,20.2,396.9,16.35,12.7
|
||||
8.71675,0,18.1,0,0.693,6.471,98.8,1.7257,24,666,20.2,391.98,17.12,13.1
|
||||
5.87205,0,18.1,0,0.693,6.405,96,1.6768,24,666,20.2,396.9,19.37,12.5
|
||||
7.67202,0,18.1,0,0.693,5.747,98.9,1.6334,24,666,20.2,393.1,19.92,8.5
|
||||
38.3518,0,18.1,0,0.693,5.453,100,1.4896,24,666,20.2,396.9,30.59,5
|
||||
9.91655,0,18.1,0,0.693,5.852,77.8,1.5004,24,666,20.2,338.16,29.97,6.3
|
||||
25.0461,0,18.1,0,0.693,5.987,100,1.5888,24,666,20.2,396.9,26.77,5.6
|
||||
14.2362,0,18.1,0,0.693,6.343,100,1.5741,24,666,20.2,396.9,20.32,7.2
|
||||
9.59571,0,18.1,0,0.693,6.404,100,1.639,24,666,20.2,376.11,20.31,12.1
|
||||
24.8017,0,18.1,0,0.693,5.349,96,1.7028,24,666,20.2,396.9,19.77,8.3
|
||||
41.5292,0,18.1,0,0.693,5.531,85.4,1.6074,24,666,20.2,329.46,27.38,8.5
|
||||
67.9208,0,18.1,0,0.693,5.683,100,1.4254,24,666,20.2,384.97,22.98,5
|
||||
20.7162,0,18.1,0,0.659,4.138,100,1.1781,24,666,20.2,370.22,23.34,11.9
|
||||
11.9511,0,18.1,0,0.659,5.608,100,1.2852,24,666,20.2,332.09,12.13,27.9
|
||||
7.40389,0,18.1,0,0.597,5.617,97.9,1.4547,24,666,20.2,314.64,26.4,17.2
|
||||
14.4383,0,18.1,0,0.597,6.852,100,1.4655,24,666,20.2,179.36,19.78,27.5
|
||||
51.1358,0,18.1,0,0.597,5.757,100,1.413,24,666,20.2,2.6,10.11,15
|
||||
14.0507,0,18.1,0,0.597,6.657,100,1.5275,24,666,20.2,35.05,21.22,17.2
|
||||
18.811,0,18.1,0,0.597,4.628,100,1.5539,24,666,20.2,28.79,34.37,17.9
|
||||
28.6558,0,18.1,0,0.597,5.155,100,1.5894,24,666,20.2,210.97,20.08,16.3
|
||||
45.7461,0,18.1,0,0.693,4.519,100,1.6582,24,666,20.2,88.27,36.98,7
|
||||
18.0846,0,18.1,0,0.679,6.434,100,1.8347,24,666,20.2,27.25,29.05,7.2
|
||||
10.8342,0,18.1,0,0.679,6.782,90.8,1.8195,24,666,20.2,21.57,25.79,7.5
|
||||
25.9406,0,18.1,0,0.679,5.304,89.1,1.6475,24,666,20.2,127.36,26.64,10.4
|
||||
73.5341,0,18.1,0,0.679,5.957,100,1.8026,24,666,20.2,16.45,20.62,8.8
|
||||
11.8123,0,18.1,0,0.718,6.824,76.5,1.794,24,666,20.2,48.45,22.74,8.4
|
||||
11.0874,0,18.1,0,0.718,6.411,100,1.8589,24,666,20.2,318.75,15.02,16.7
|
||||
7.02259,0,18.1,0,0.718,6.006,95.3,1.8746,24,666,20.2,319.98,15.7,14.2
|
||||
12.0482,0,18.1,0,0.614,5.648,87.6,1.9512,24,666,20.2,291.55,14.1,20.8
|
||||
7.05042,0,18.1,0,0.614,6.103,85.1,2.0218,24,666,20.2,2.52,23.29,13.4
|
||||
8.79212,0,18.1,0,0.584,5.565,70.6,2.0635,24,666,20.2,3.65,17.16,11.7
|
||||
15.8603,0,18.1,0,0.679,5.896,95.4,1.9096,24,666,20.2,7.68,24.39,8.3
|
||||
12.2472,0,18.1,0,0.584,5.837,59.7,1.9976,24,666,20.2,24.65,15.69,10.2
|
||||
37.6619,0,18.1,0,0.679,6.202,78.7,1.8629,24,666,20.2,18.82,14.52,10.9
|
||||
7.36711,0,18.1,0,0.679,6.193,78.1,1.9356,24,666,20.2,96.73,21.52,11
|
||||
9.33889,0,18.1,0,0.679,6.38,95.6,1.9682,24,666,20.2,60.72,24.08,9.5
|
||||
8.49213,0,18.1,0,0.584,6.348,86.1,2.0527,24,666,20.2,83.45,17.64,14.5
|
||||
10.0623,0,18.1,0,0.584,6.833,94.3,2.0882,24,666,20.2,81.33,19.69,14.1
|
||||
6.44405,0,18.1,0,0.584,6.425,74.8,2.2004,24,666,20.2,97.95,12.03,16.1
|
||||
5.58107,0,18.1,0,0.713,6.436,87.9,2.3158,24,666,20.2,100.19,16.22,14.3
|
||||
13.9134,0,18.1,0,0.713,6.208,95,2.2222,24,666,20.2,100.63,15.17,11.7
|
||||
11.1604,0,18.1,0,0.74,6.629,94.6,2.1247,24,666,20.2,109.85,23.27,13.4
|
||||
14.4208,0,18.1,0,0.74,6.461,93.3,2.0026,24,666,20.2,27.49,18.05,9.6
|
||||
15.1772,0,18.1,0,0.74,6.152,100,1.9142,24,666,20.2,9.32,26.45,8.7
|
||||
13.6781,0,18.1,0,0.74,5.935,87.9,1.8206,24,666,20.2,68.95,34.02,8.4
|
||||
9.39063,0,18.1,0,0.74,5.627,93.9,1.8172,24,666,20.2,396.9,22.88,12.8
|
||||
22.0511,0,18.1,0,0.74,5.818,92.4,1.8662,24,666,20.2,391.45,22.11,10.5
|
||||
9.72418,0,18.1,0,0.74,6.406,97.2,2.0651,24,666,20.2,385.96,19.52,17.1
|
||||
5.66637,0,18.1,0,0.74,6.219,100,2.0048,24,666,20.2,395.69,16.59,18.4
|
||||
9.96654,0,18.1,0,0.74,6.485,100,1.9784,24,666,20.2,386.73,18.85,15.4
|
||||
12.8023,0,18.1,0,0.74,5.854,96.6,1.8956,24,666,20.2,240.52,23.79,10.8
|
||||
10.6718,0,18.1,0,0.74,6.459,94.8,1.9879,24,666,20.2,43.06,23.98,11.8
|
||||
6.28807,0,18.1,0,0.74,6.341,96.4,2.072,24,666,20.2,318.01,17.79,14.9
|
||||
9.92485,0,18.1,0,0.74,6.251,96.6,2.198,24,666,20.2,388.52,16.44,12.6
|
||||
9.32909,0,18.1,0,0.713,6.185,98.7,2.2616,24,666,20.2,396.9,18.13,14.1
|
||||
7.52601,0,18.1,0,0.713,6.417,98.3,2.185,24,666,20.2,304.21,19.31,13
|
||||
6.71772,0,18.1,0,0.713,6.749,92.6,2.3236,24,666,20.2,0.32,17.44,13.4
|
||||
5.44114,0,18.1,0,0.713,6.655,98.2,2.3552,24,666,20.2,355.29,17.73,15.2
|
||||
5.09017,0,18.1,0,0.713,6.297,91.8,2.3682,24,666,20.2,385.09,17.27,16.1
|
||||
8.24809,0,18.1,0,0.713,7.393,99.3,2.4527,24,666,20.2,375.87,16.74,17.8
|
||||
9.51363,0,18.1,0,0.713,6.728,94.1,2.4961,24,666,20.2,6.68,18.71,14.9
|
||||
4.75237,0,18.1,0,0.713,6.525,86.5,2.4358,24,666,20.2,50.92,18.13,14.1
|
||||
4.66883,0,18.1,0,0.713,5.976,87.9,2.5806,24,666,20.2,10.48,19.01,12.7
|
||||
8.20058,0,18.1,0,0.713,5.936,80.3,2.7792,24,666,20.2,3.5,16.94,13.5
|
||||
7.75223,0,18.1,0,0.713,6.301,83.7,2.7831,24,666,20.2,272.21,16.23,14.9
|
||||
6.80117,0,18.1,0,0.713,6.081,84.4,2.7175,24,666,20.2,396.9,14.7,20
|
||||
4.81213,0,18.1,0,0.713,6.701,90,2.5975,24,666,20.2,255.23,16.42,16.4
|
||||
3.69311,0,18.1,0,0.713,6.376,88.4,2.5671,24,666,20.2,391.43,14.65,17.7
|
||||
6.65492,0,18.1,0,0.713,6.317,83,2.7344,24,666,20.2,396.9,13.99,19.5
|
||||
5.82115,0,18.1,0,0.713,6.513,89.9,2.8016,24,666,20.2,393.82,10.29,20.2
|
||||
7.83932,0,18.1,0,0.655,6.209,65.4,2.9634,24,666,20.2,396.9,13.22,21.4
|
||||
3.1636,0,18.1,0,0.655,5.759,48.2,3.0665,24,666,20.2,334.4,14.13,19.9
|
||||
3.77498,0,18.1,0,0.655,5.952,84.7,2.8715,24,666,20.2,22.01,17.15,19
|
||||
4.42228,0,18.1,0,0.584,6.003,94.5,2.5403,24,666,20.2,331.29,21.32,19.1
|
||||
15.5757,0,18.1,0,0.58,5.926,71,2.9084,24,666,20.2,368.74,18.13,19.1
|
||||
13.0751,0,18.1,0,0.58,5.713,56.7,2.8237,24,666,20.2,396.9,14.76,20.1
|
||||
4.34879,0,18.1,0,0.58,6.167,84,3.0334,24,666,20.2,396.9,16.29,19.9
|
||||
4.03841,0,18.1,0,0.532,6.229,90.7,3.0993,24,666,20.2,395.33,12.87,19.6
|
||||
3.56868,0,18.1,0,0.58,6.437,75,2.8965,24,666,20.2,393.37,14.36,23.2
|
||||
4.64689,0,18.1,0,0.614,6.98,67.6,2.5329,24,666,20.2,374.68,11.66,29.8
|
||||
8.05579,0,18.1,0,0.584,5.427,95.4,2.4298,24,666,20.2,352.58,18.14,13.8
|
||||
6.39312,0,18.1,0,0.584,6.162,97.4,2.206,24,666,20.2,302.76,24.1,13.3
|
||||
4.87141,0,18.1,0,0.614,6.484,93.6,2.3053,24,666,20.2,396.21,18.68,16.7
|
||||
15.0234,0,18.1,0,0.614,5.304,97.3,2.1007,24,666,20.2,349.48,24.91,12
|
||||
10.233,0,18.1,0,0.614,6.185,96.7,2.1705,24,666,20.2,379.7,18.03,14.6
|
||||
14.3337,0,18.1,0,0.614,6.229,88,1.9512,24,666,20.2,383.32,13.11,21.4
|
||||
5.82401,0,18.1,0,0.532,6.242,64.7,3.4242,24,666,20.2,396.9,10.74,23
|
||||
5.70818,0,18.1,0,0.532,6.75,74.9,3.3317,24,666,20.2,393.07,7.74,23.7
|
||||
5.73116,0,18.1,0,0.532,7.061,77,3.4106,24,666,20.2,395.28,7.01,25
|
||||
2.81838,0,18.1,0,0.532,5.762,40.3,4.0983,24,666,20.2,392.92,10.42,21.8
|
||||
2.37857,0,18.1,0,0.583,5.871,41.9,3.724,24,666,20.2,370.73,13.34,20.6
|
||||
3.67367,0,18.1,0,0.583,6.312,51.9,3.9917,24,666,20.2,388.62,10.58,21.2
|
||||
5.69175,0,18.1,0,0.583,6.114,79.8,3.5459,24,666,20.2,392.68,14.98,19.1
|
||||
4.83567,0,18.1,0,0.583,5.905,53.2,3.1523,24,666,20.2,388.22,11.45,20.6
|
||||
0.15086,0,27.74,0,0.609,5.454,92.7,1.8209,4,711,20.1,395.09,18.06,15.2
|
||||
0.18337,0,27.74,0,0.609,5.414,98.3,1.7554,4,711,20.1,344.05,23.97,7
|
||||
0.20746,0,27.74,0,0.609,5.093,98,1.8226,4,711,20.1,318.43,29.68,8.1
|
||||
0.10574,0,27.74,0,0.609,5.983,98.8,1.8681,4,711,20.1,390.11,18.07,13.6
|
||||
0.11132,0,27.74,0,0.609,5.983,83.5,2.1099,4,711,20.1,396.9,13.35,20.1
|
||||
0.17331,0,9.69,0,0.585,5.707,54,2.3817,6,391,19.2,396.9,12.01,21.8
|
||||
0.27957,0,9.69,0,0.585,5.926,42.6,2.3817,6,391,19.2,396.9,13.59,24.5
|
||||
0.17899,0,9.69,0,0.585,5.67,28.8,2.7986,6,391,19.2,393.29,17.6,23.1
|
||||
0.2896,0,9.69,0,0.585,5.39,72.9,2.7986,6,391,19.2,396.9,21.14,19.7
|
||||
0.26838,0,9.69,0,0.585,5.794,70.6,2.8927,6,391,19.2,396.9,14.1,18.3
|
||||
0.23912,0,9.69,0,0.585,6.019,65.3,2.4091,6,391,19.2,396.9,12.92,21.2
|
||||
0.17783,0,9.69,0,0.585,5.569,73.5,2.3999,6,391,19.2,395.77,15.1,17.5
|
||||
0.22438,0,9.69,0,0.585,6.027,79.7,2.4982,6,391,19.2,396.9,14.33,16.8
|
||||
0.06263,0,11.93,0,0.573,6.593,69.1,2.4786,1,273,21,391.99,9.67,22.4
|
||||
0.04527,0,11.93,0,0.573,6.12,76.7,2.2875,1,273,21,396.9,9.08,20.6
|
||||
0.06076,0,11.93,0,0.573,6.976,91,2.1675,1,273,21,396.9,5.64,23.9
|
||||
0.10959,0,11.93,0,0.573,6.794,89.3,2.3889,1,273,21,393.45,6.48,22
|
||||
0.04741,0,11.93,0,0.573,6.03,80.8,2.505,1,273,21,396.9,7.88,11.9
|
|
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 224 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 145 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 234 KiB |
After Width: | Height: | Size: 584 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 199 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 270 KiB |
After Width: | Height: | Size: 372 KiB |
After Width: | Height: | Size: 450 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 450 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 292 KiB |
After Width: | Height: | Size: 143 KiB |
After Width: | Height: | Size: 147 KiB |
After Width: | Height: | Size: 161 KiB |
After Width: | Height: | Size: 169 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 299 KiB |
After Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
After Width: | Height: | Size: 111 KiB |
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 217 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 265 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 228 KiB |
After Width: | Height: | Size: 227 KiB |
After Width: | Height: | Size: 226 KiB |
After Width: | Height: | Size: 247 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 511 KiB |
Before Width: | Height: | Size: 474 KiB |
Before Width: | Height: | Size: 509 KiB |
After Width: | Height: | Size: 115 KiB |
After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 113 KiB |