diff --git "a/Day66-70/66.\346\225\260\346\215\256\345\210\206\346\236\220\346\246\202\350\277\260.md" "b/Day66-70/66.\346\225\260\346\215\256\345\210\206\346\236\220\346\246\202\350\277\260.md" index 34c648a14c10d92b1ce6a2aca8dd4226a85c7767..7e619d67134b61ddc2c6261fd9b95f569e7d043b 100644 --- "a/Day66-70/66.\346\225\260\346\215\256\345\210\206\346\236\220\346\246\202\350\277\260.md" +++ "b/Day66-70/66.\346\225\260\346\215\256\345\210\206\346\236\220\346\246\202\350\277\260.md" @@ -111,13 +111,19 @@ 对于安装了Python环境但是没有安装Anaconda的用户,可以用Python的包管理工具pip来安装`jupyter`,然后在终端(Windows系统称之为命令行提示符)中运行`jupyter notebook`命令来启动Notebook,如下所示。 -安装: +安装Notebook: ```Bash pip install jupyter ``` -运行: +安装三大神器: + +```Bash +pip install numpy pandas matplotlib +``` + +运行Notebook: ```Bash jupyter notebook @@ -234,7 +240,7 @@ Notebook是基于网页的用于交互计算的应用程序,可以用于代码 **第一四分位数**($Q_1$),又称**较小四分位数**或**下四分位数**,等于该样本中所有数值由小到大排列后第25%的数字。 - **第二四分位数**($Q_2$),又称**中位数**,等于该样本中所有数值由小到大排列后第50%的数字。 + **第二四分位数**($Q_2$),即**中位数**,等于该样本中所有数值由小到大排列后第50%的数字。 **第三四分位数**($Q_3$),又称**较大四分位数**或**上四分位数**,等于该样本中所有数值由小到大排列后第75%的数字。 diff --git "a/Day66-70/67.NumPy\347\232\204\345\272\224\347\224\250.md" "b/Day66-70/67.NumPy\347\232\204\345\272\224\347\224\250.md" index 01fdad3d79b9f6c6866fd1261a8b467db98681b6..5ed14d17fc2c062d47d62f9ab39609b13b22e88f 100644 --- "a/Day66-70/67.NumPy\347\232\204\345\272\224\347\224\250.md" +++ "b/Day66-70/67.NumPy\347\232\204\345\272\224\347\224\250.md" @@ -1,59 +1,789 @@ ## NumPy的应用 -Numpy是一个开源的Python科学计算库,**用于快速处理任意维度的数组**。Numpy**支持常见的数组和矩阵操作**,对于同样的数值计算任务,使用NumPy不仅代码要简洁的多,而且NumPy的性能远远优于原生Python,基本是1个到2个数量级的差距,而且数据量越大,NumPy的优势就越明显。 +Numpy是一个开源的Python科学计算库,**用于快速处理任意维度的数组**。Numpy**支持常见的数组和矩阵操作**,对于同样的数值计算任务,使用NumPy不仅代码要简洁的多,而且NumPy的性能远远优于原生Python,基本是一个到两个数量级的差距,而且数据量越大,NumPy的优势就越明显。 Numpy最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一维、二维和多维数组,该对象相当于是一个快速而灵活的大数据容器。NumPy底层代码使用C语言编写,解决了GIL的限制,`ndarray`在存储数据的时候,数据与数据的地址都是连续的,这样就给使得批量操作速度很快,远远优于Python中的`list`;另一方面`ndarray`对象提供了更多的方法来处理数据,尤其是和统计相关的方法,这些方法也是Python原生的`list`没有的。 - ### 安装和导入NumPy + ### 准备工作 -1. 安装 +1. 启动Notebook ```Bash - pip install numpy + jupyter notebook ``` + > **提示**:在启动Notebook之前,建议先安装好数据分析相关依赖项,包括之前提到的三大神器以及相关依赖项,包括:`numpy`、`pandas`、`matplotlib`、`openpyxl`、`xlrd`、`xlwt`等。如果使用Anaconda,则无需单独安装。 + 2. 导入 ```Python import numpy as np + import pandas as pd + import matplotlib.pyplot as plt ``` - > **说明**:如果已经启动了Notebook但尚未安装NumPy,可以在单元格中输入`!pip install numpy `并运行该单元格来安装NumPy,安装成功后记得重启Notebook内核来使新安装的库生效。 + > **说明**:如果已经启动了Notebook但尚未安装相关依赖库,例如NumPy,可以在Notebook的单元格中输入`!pip install numpy`并运行该单元格来安装NumPy,其他库如法炮制。安装成功后选择“Kernel”(服务)菜单的“Restart”(重启)选项来重启Notebook内核(前面有讲到重启的快捷键)来使新安装的库生效。上面我们不仅导入了NumPy,还将pandas和matplotlib库一并导入了。 ### 创建数组对象 -1. 一维数组 - - 方法一:使用array函数,通过list创建数组对象 - - 方法二:使用arange函数,指定取值范围创建数组对象 - - 方法三:使用linspace函数,用指定范围均匀间隔的数字创建数组对象 - - 方法四:使用numpy.random模块的函数生成随机数创建数组对象 -2. 二维数组 - - 方法一:使用array函数,通过嵌套的list创建数组对象 - - 方法二:使用zeros、ones、full函数指定数组的形状创建数组对象 - - 方法三:使用eye函数创建单位矩阵 - - 方法四:通过reshape将一维数组变成二维数组 - - 方法五:通过numpy.random模块的函数生成随机数创建数组对象 -3. 多维数组:跟上面的情况相似,可以通过下面的例子进行了解。 +创建`ndarray`对象有很多种方法,下面就如何创建一维数组、二维数组和多维数组进行说明。 + +#### 一维数组 + +- 方法一:使用`array`函数,通过`list`创建数组对象 + + 代码: + + ```Python + array1 = np.array([1, 2, 3, 4, 5]) + array1 + ``` + + 输出: + + ``` + array([1, 2, 3, 4, 5]) + ``` + +- 方法二:使用`arange`函数,指定取值范围创建数组对象 + + 代码: + + ```Python + array2 = np.arange(0, 20, 2) + array2 + ``` + + 输出: + + ``` + array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]) + ``` + +- 方法三:使用`linspace`函数,用指定范围均匀间隔的数字创建数组对象 + + 代码: + + ```Python + array3 = np.linspace(-5, 5, 101) + array3 + ``` + + 输出: + + ``` + array([-5. , -4.9, -4.8, -4.7, -4.6, -4.5, -4.4, -4.3, -4.2, -4.1, -4. , + -3.9, -3.8, -3.7, -3.6, -3.5, -3.4, -3.3, -3.2, -3.1, -3. , -2.9, + -2.8, -2.7, -2.6, -2.5, -2.4, -2.3, -2.2, -2.1, -2. , -1.9, -1.8, + -1.7, -1.6, -1.5, -1.4, -1.3, -1.2, -1.1, -1. , -0.9, -0.8, -0.7, + -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0. , 0.1, 0.2, 0.3, 0.4, + 0.5, 0.6, 0.7, 0.8, 0.9, 1. , 1.1, 1.2, 1.3, 1.4, 1.5, + 1.6, 1.7, 1.8, 1.9, 2. , 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, + 2.7, 2.8, 2.9, 3. , 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, + 3.8, 3.9, 4. , 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, + 4.9, 5. ]) + ``` + +- 方法四:使用`numpy.random`模块的函数生成随机数创建数组对象 + + 产生10个$[0, 1)$范围的随机小数,代码: + + ```Python + array4 = np.random.rand(10) + array4 + ``` + + 输出: + + ``` + array([0.45556132, 0.67871326, 0.4552213 , 0.96671509, 0.44086463, + 0.72650875, 0.79877188, 0.12153022, 0.24762739, 0.6669852 ]) + ``` + + 产生10个$[1, 100)$范围的随机整数,代码: + + ```Python + array5 = np.random.randint(1, 101, 10) + array5 + ``` + + 输出: + + ``` + array([29, 97, 87, 47, 39, 19, 71, 32, 79, 34]) + ``` + + 产生20个$\mu=50$,$\sigma=10$的正态分布随机数,代码: + + ```Python + array6 = np.random.normal(50, 10, 20) + array6 + ``` + + 输出: + + ``` + array([55.04155586, 46.43510797, 20.28371158, 62.67884053, 61.23185964, + 38.22682148, 53.17126151, 43.54741592, 36.11268017, 40.94086676, + 63.27911699, 46.92688903, 37.1593374 , 67.06525656, 67.47269463, + 23.37925889, 31.45312239, 48.34532466, 55.09180924, 47.95702787]) + ``` + +#### 二维数组 + +- 方法一:使用`array`函数,通过嵌套的`list`创建数组对象 + + 代码: + + ```Python + array7 = np.array([[1, 2, 3], [4, 5, 6]]) + array7 + ``` + + 输出: + + ``` + array([[1, 2, 3], + [4, 5, 6]]) + ``` + +- 方法二:使用`zeros`、`ones`、`full`函数指定数组的形状创建数组对象 + + 使用`zeros`函数,代码: + + ```Python + array8 = np.zeros((3, 4)) + array8 + ``` + + 输出: + + ``` + array([[0., 0., 0., 0.], + [0., 0., 0., 0.], + [0., 0., 0., 0.]]) + ``` + + 使用`ones`函数,代码: + + ```Python + array9 = np.ones((3, 4)) + array9 + ``` + + 输出: + + ``` + array([[1., 1., 1., 1.], + [1., 1., 1., 1.], + [1., 1., 1., 1.]]) + ``` + + 使用`full`函数,代码: + + ```Python + array10 = np.full((3, 4), 10) + array10 + ``` + + 输出: + + ``` + array([[10, 10, 10, 10], + [10, 10, 10, 10], + [10, 10, 10, 10]]) + ``` + +- 方法三:使用eye函数创建单位矩阵 + + 代码: + + ```Python + array11 = np.eye(4) + array11 + ``` + + 输出: + + ``` + array([[1., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 0., 1., 0.], + [0., 0., 0., 1.]]) + ``` + +- 方法四:通过`reshape`将一维数组变成二维数组 + + 代码: + + ```Python + array12 = np.array([1, 2, 3, 4, 5, 6]).reshape(2, 3) + array12 + ``` + + 输出: + + ``` + array([[1, 2, 3], + [4, 5, 6]]) + ``` + + > **提示**:`reshape`是`ndarray`对象的一个方法,使用`reshape`方法时需要确保调形后的数组元素个数与调形前数组元素个数保持一致,否则将会产生异常。 + +- 方法五:通过`numpy.random`模块的函数生成随机数创建数组对象 + + 产生$[0, 1)$范围的随机小数构成的3行4列的二维数组,代码: + + ```Python + array13 = np.random.rand(3, 4) + array13 + ``` + + 输出: + + ``` + array([[0.54017809, 0.46797771, 0.78291445, 0.79501326], + [0.93973783, 0.21434806, 0.03592874, 0.88838892], + [0.84130479, 0.3566601 , 0.99935473, 0.26353598]]) + ``` + + 产生$[1, 100)$范围的随机整数构成的3行4列的二维数组,代码: + + ```Python + array14 = np.random.randint(1, 100, (3, 4)) + array14 + ``` + + 输出: + + ``` + array([[83, 30, 64, 53], + [39, 92, 53, 43], + [43, 48, 91, 72]]) + ``` + +#### 多维数组 + +- 使用随机的方式创建多维数组 + + 代码: + + ```Python + array15 = np.random.randint(1, 100, (3, 4, 5)) + array15 + ``` + + 输出: + + ``` + array([[[94, 26, 49, 24, 43], + [27, 27, 33, 98, 33], + [13, 73, 6, 1, 77], + [54, 32, 51, 86, 59]], + + [[62, 75, 62, 29, 87], + [90, 26, 6, 79, 41], + [31, 15, 32, 56, 64], + [37, 84, 61, 71, 71]], + + [[45, 24, 78, 77, 41], + [75, 37, 4, 74, 93], + [ 1, 36, 36, 60, 43], + [23, 84, 44, 89, 79]]]) + ``` + +- 将一维二维的数组调形为多维数组 + + 一维数组调形为多维数组,代码: + + ```Python + array16 = np.arange(1, 25).reshape((2, 3, 4)) + array16 + ``` + + 输出: + + ```Python + array([[[ 1, 2, 3, 4], + [ 5, 6, 7, 8], + [ 9, 10, 11, 12]], + + [[13, 14, 15, 16], + [17, 18, 19, 20], + [21, 22, 23, 24]]]) + ``` + + 二维数组调形为多维数组,代码: + + ```Python + array17 = np.random.randint(1, 100, (4, 6)).reshape((4, 3, 2)) + array17 + ``` + + 输出: + + ``` + array([[[60, 59], + [31, 80], + [54, 91]], + + [[67, 4], + [ 4, 59], + [47, 49]], + + [[16, 4], + [ 5, 71], + [80, 53]], + + [[38, 49], + [70, 5], + [76, 80]]]) + ``` + +- 读取图片获得对应的三维数组 + + 代码: + + ```Python + array18 = plt.imread('guido.jpg') +array18 + ``` + + 输出: + + ``` + array([[[ 36, 33, 28], + [ 36, 33, 28], + [ 36, 33, 28], + ..., + [ 32, 31, 29], + [ 32, 31, 27], + [ 31, 32, 26]], + + [[ 37, 34, 29], + [ 38, 35, 30], + [ 38, 35, 30], + ..., + [ 31, 30, 28], + [ 31, 30, 26], + [ 30, 31, 25]], + + [[ 38, 35, 30], + [ 38, 35, 30], + [ 38, 35, 30], + ..., + [ 30, 29, 27], + [ 30, 29, 25], + [ 29, 30, 25]], + + ..., + + [[239, 178, 123], + [237, 176, 121], + [235, 174, 119], + ..., + [ 78, 68, 56], + [ 75, 67, 54], + [ 73, 65, 52]], + + [[238, 177, 120], + [236, 175, 118], + [234, 173, 116], + ..., + [ 82, 70, 58], + [ 78, 68, 56], + [ 75, 66, 51]], + + [[238, 176, 119], + [236, 175, 118], + [234, 173, 116], + ..., + [ 84, 70, 61], + [ 81, 69, 57], + [ 79, 67, 53]]], dtype=uint8) + ``` + + > **说明**:上面的代码读取了当前路径下名为`guido.jpg` 的图片文件,计算机系统中的图片通常由若干行若干列的像素点构成,而每个像素点又是由红绿蓝三原色构成的,所以能够用三维数组来表示。读取图片用到了matplotlib库的`imread`函数。 ### 数组对象的属性 -1. size属性:数组元素个数 -2. itemsize属性:数组单个元素占用内存空间的字节数 -3. shape属性:数组的形状 -4. dtype属性:数组元素的数据类型 -5. ndim属性:数组的维度 -6. flat属性:数组(一维化之后)元素的迭代器 -7. nbytes属性:数组所有元素占用内存空间的字节数 +1. `size`属性:数组元素个数 + + 代码: + + ```Python + array19 = np.arange(1, 100, 2) + array20 = np.random.rand(3, 4) + print(array19.size, array20.size) + ``` + + 输出: + + ``` + 50 12 + ``` + +2. `shape`属性:数组的形状 + + 代码: + + ```Python + print(array19.shape, array20.shape) + ``` + + 输出: + + ``` + (50,) (3, 4) + ``` + +3. `dtype`属性:数组元素的数据类型 + + 代码: + + ```Python + print(array19.dtype, array20.dtype) + ``` + + 输出: + + ``` + int64 float64 + ``` + + `ndarray`对象元素的数据类型可以参考如下所示的表格。 + + ![](res/ndarray-dtype.png) + +4. `ndim`属性:数组的维度 + + 代码: + + ```Python + print(array19.ndim, array20.ndim) + ``` + + 输出: + + ``` + 1 2 + ``` + +5. `itemsize`属性:数组单个元素占用内存空间的字节数 + + 代码: + + ```Python + array21 = np.arange(1, 100, 2, dtype=np.int8) + print(array19.itemsize, array20.itemsize, array21.itemsize) + ``` + + 输出: + + ``` + 8 8 1 + ``` + + > **说明**:在使用`arange`创建数组对象时,通过`dtype`参数指定元素的数据类型。可以看出,`np.int8`代表的是8位有符号整数,只占用1个字节的内存空间,取值范围是$[-128,127]$。 + +6. `nbytes`属性:数组所有元素占用内存空间的字节数 + + 代码: + + ```Python + print(array19.nbytes, array20.nbytes, array21.nbytes) + ``` + + 输出: + + ``` + 400 96 50 + ``` + +7. `flat`属性:数组(一维化之后)元素的迭代器 + + 代码: + + ```Python + from typing import Iterable + + print(isinstance(array20.flat, np.ndarray), isinstance(array20.flat, Iterable)) + ``` + + 输出: + + ``` + False True + ``` + 8. base属性:数组的基对象(如果数组共享了其他数组的内存空间) -### 数组对象的方法 + 代码: + + ```Python + array22 = array19[:] + print(array22.base is array19, array22.base is array21) + ``` + + 输出: + + ``` + True False + ``` + + > **说明**:上面的代码用到了数组的切片操作,它类似于Python中`list`类型的切片,但在细节上又不完全相同,下面会专门讲解这个知识点。通过上面的代码可以发现,`ndarray`切片后得到的新的数组对象跟原来的数组对象共享了内存中的数据,因此`array22`的`base`属性就是`array19`对应的数组对象。 + +### 数组的索引和切片 + +和Python中的列表类似,NumPy的`ndarray`对象可以进行索引和切片操作,通过索引可以获取或修改数组中的元素,通过切片可以取出数组的一部分。 + +1. 索引运算 + + 一维数组,代码: + + ```Python + array23 = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + print(array23[0], array23[array23.size - 1]) + print(array23[-array23.size], array23[-1]) + ``` + + 输出: + + ``` + 1 9 + 1 9 + ``` + + 二维数组,代码: + + ```Python + array24 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + print(array24[2]) + print(array24[0][0], array24[-1][-1]) + print(array24[1][1], array24[1, 1]) + ``` + + 输出: + + ``` + [7 8 9] + 1 9 + 5 5 + [[ 1 2 3] + [ 4 10 6] + [ 7 8 9]] + ``` + + 代码: + + ```Python + array24[1][1] = 10 + print(array24) + array24[1] = [10, 11, 12] + print(array24) + ``` + + 输出: + + ``` + [[ 1 2 3] + [ 4 10 6] + [ 7 8 9]] + [[ 1 2 3] + [10 11 12] + [ 7 8 9]] + ``` + +2. 切片运算 + + 代码: + + ```Python + print(array24[:2, 1:]) + ``` + + 输出: + + ``` + [[2 3] + [5 6]] + ``` + + 代码: + + ```Python + print(array24[2]) + print(array24[2, :]) + ``` + + 输出: + + ``` + [7 8 9] + [7 8 9] + ``` + + 代码: + + ```Python + print(array24[2:, :]) + ``` + + 输出: + + ``` + [[7 8 9]] + ``` + + 代码: + + ```Python + print(array24[:, :2]) + ``` + + 输出: + + ``` + [[1 2] + [4 5] + [7 8]] + ``` + + 代码: + + ```Python + print(array24[1, :2]) + ``` + + 输出: + + ``` + [4 5] + ``` -#### 常用方法 + 代码: + + ```Python + print(array24[1:2, :2]) + ``` + + 输出: + + ``` + [[4 5]] + ``` + + 关于数组的索引和切片运算,大家可以通过下面的两张图来增强印象,这两张图来自[《利用Python进行数据分析》](https://item.jd.com/12398725.html)一书,它是pandas的作者Wes McKinney撰写的Python数据分析领域的经典教科书,有兴趣的读者可以购买和阅读原书。 + + ![](res/ndarray-index.png) + + ![](res/ndarray-slice.png) + +### 数组对象的方法 #### 统计方法 +`ndarray`对象的统计方法主要包括:`sum`、`mean`、`std`、`var`、`min`、`max`、`argmin`、`argmax`、`cumsum`等,分别用于对数组中的元素求和、求平均、求标准差、求方差、找最大、找最小、求累积和等,请参考下面的代码。 + +```Python +array25 = np.array([1, 2, 3, 4, 5, 5, 4, 3, 2, 1]) +print(array25.sum()) +print(array25.mean()) +print(array25.max()) +print(array25.min()) +print(array25.std()) +print(array25.var()) +print(array25.cumsum()) +``` + +输出: + +``` +30 +3.0 +5 +1 +1.4142135623730951 +2.0 +[ 1 3 6 10 15 20 24 27 29 30] +``` + ####其他方法 +1. `all()` / `any()`方法:判断数组是否所有元素都是True / 判断数组是否有为True的元素。 + +2. `astype()`方法:拷贝数组,并将数组中的元素转换为指定的类型。 + +3. `dot()`方法:实现一个数组和另一个数组的点乘运算。 + + ```Python + + ``` + + 输出: + + ``` + + ``` + +4. `dump()`/`load()`方法:保存数组到文件中/从文件中加载数组。 + + ```Python + + ``` + + 输出: + + ``` + + ``` + +5. `fill()`方法。 + + ```Python + + ``` + + 输出: + + ``` + + ``` + +6. `flatten()`方法:将多维数组扁平化为一维数组。 + + ```Python + + ``` + + 输出: + + ``` + + ``` + +7. `nonzero()`方法:返回非0元素的索引。 + +8. `round()`方法:对数组中的元素做四舍五入操作。 + +9. `sort()`方法:对数组进行就地排序。 + + ```Python + + ``` + + 输出: + + ``` + + ``` + +10. `swapaxes()`和`transpose()`方法:交换数组指定的轴 + +11. `take()`方法:从数组中取指定索引的元素。 + +12. `tolist()`方法:将数组转成Python中的`list`。 + ### 数组的运算 #### 标量运算 diff --git a/Day66-70/res/C558C1F83388892F5A2305AE4AAEB865.jpg b/Day66-70/res/3sigma_rule.jpg similarity index 100% rename from Day66-70/res/C558C1F83388892F5A2305AE4AAEB865.jpg rename to Day66-70/res/3sigma_rule.jpg diff --git a/Day66-70/res/IMG_3306(20201030-083427).PNG b/Day66-70/res/ndarray-dtype.png similarity index 100% rename from Day66-70/res/IMG_3306(20201030-083427).PNG rename to Day66-70/res/ndarray-dtype.png diff --git a/Day66-70/res/IMG_3307(20201030-083545).PNG b/Day66-70/res/ndarray-index.PNG similarity index 100% rename from Day66-70/res/IMG_3307(20201030-083545).PNG rename to Day66-70/res/ndarray-index.PNG diff --git a/Day66-70/res/IMG_3308(20201030-083633).PNG b/Day66-70/res/ndarray-slice.PNG similarity index 100% rename from Day66-70/res/IMG_3308(20201030-083633).PNG rename to Day66-70/res/ndarray-slice.PNG