提交 e6934a59 编写于 作者: J jackfrued

更新了数据分析部分的文档

上级 8c4e7d2b
......@@ -234,7 +234,7 @@ Notebook是基于网页的用于交互计算的应用程序,可以用于代码
- **分位数**:将一个随机变量的概率分布范围分为几个具有相同概率的连续区间,比如最常见的中位数(二分位数,median),就是将数据集划分为数量相等的上下两个部分。除此之外,常见的分位数还有四分位数(quartile)、百分位数(percentile)等。
- 中位数:${Q}_{\frac{1}{2}}(x)=\left\{\begin{matrix} x_{\frac{n+1}{2}} &{n \mbox{ is odd}} \\ (x_{\frac{n}{2}}+x_{{\frac{n}{2}}+1})/2 &{n \mbox{ is even}}\end{matrix}\right.$
- 中位数:${Q}_{\frac{1}{2}}(x)=\begin{cases} x_{\frac{n+1}{2}}, \quad &n \mbox{ is odd} \\ (x_{\frac{n}{2}}+x_{{\frac{n}{2}}+1})/2, \quad &n \mbox{ is even}\end{cases}$
- 四分位数:
......@@ -282,8 +282,8 @@ Notebook是基于网页的用于交互计算的应用程序,可以用于代码
- 泊松分布(poisson distribution):适合于描述单位时间内随机事件发生的次数的概率分布。如某一服务设施在一定时间内受到的服务请求的次数、汽车站台的候客人数、机器出现的故障数、自然灾害发生的次数、DNA序列的变异数、放射性原子核的衰变数等等。泊松分布的概率质量函数为:$P(X=k)=\frac{e^{-\lambda}\lambda^k}{k!}$,泊松分布的参数$\lambda$是单位时间(或单位面积)内随机事件的平均发生率。
- 连续型分布:
- 均匀分布(uniform distribution):如果连续型随机变量$X$具有概率密度函数$f(x)=\left\{\begin{matrix}{\frac{1}{b-a}} &{a \leq x \leq b} \\ 0 &{\mbox{other}}\end{matrix}\right.$,则称$X$服从$[a,b]$上的均匀分布,记作$X\sim U[a,b]$。
- 指数分布(exponential distribution):如果连续型随机变量$X$具有概率密度函数$f(x)=\left\{\begin{matrix} \lambda e^{- \lambda x} &{x \ge 0} \\ 0 &{x \lt 0} \end{matrix}\right.$,则称$X$服从参数为$\lambda$的指数分布,记为$X \sim Exp(\lambda)$。指数分布可以用来表示独立随机事件发生的时间间隔,比如旅客进入机场的时间间隔、客服中心接入电话的时间间隔、知乎上出现新问题的时间间隔等等。指数分布的一个重要特征是无记忆性(无后效性),这表示如果一个随机变量呈指数分布,它的条件概率遵循:$P(T \gt s+t\ |\ T \gt t)=P(T \gt s), \forall s,t \ge 0$。
- 均匀分布(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]$。
- 指数分布(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$。
- 正态分布(normal distribution):又名**高斯分布**(Gaussian distribution),是一个非常常见的连续概率分布,经常用自然科学和社会科学中来代表一个不明的随机变量。若随机变量$X$服从一个位置参数为$\mu$、尺度参数为$\sigma$的正态分布,记为$X \sim N(\mu,\sigma^2)$,其概率密度函数为:$\displaystyle f(x)={\frac {1}{\sigma {\sqrt {2\pi }}}}e^{-{\frac {\left(x-\mu \right)^{2}}{2\sigma ^{2}}}}$。
- 伽马分布(gamma distribution):假设$X_1, X_2, ... X_n$为连续发生事件的等候时间,且这$n$次等候时间为独立的,那么这$n$次等候时间之和$Y$($Y=X_1+X_2+...+X_n$)服从伽玛分布,即$Y \sim \Gamma(\alpha,\beta)$,其中$\alpha=n, \beta=\lambda$,这里的$\lambda$是连续发生事件的平均发生频率。
- 卡方分布(chi-square distribution):若$k$个随机变量$Z_1,Z_2,...,Z_k$是相互独立且符合标准正态分布(数学期望为0,方差为1)的随机变量,则随机变量$Z$的平方和$X=\sum_{i=1}^{k}Z_i^2$被称为服从自由度为$k$的卡方分布,记为$X \sim \chi^2(k)$。
......
......@@ -400,7 +400,7 @@ array18
...,
[ 84, 70, 61],
[ 81, 69, 57],
[ 79, 67, 53]]], dtype=uint8)
[ 79, 67, 53]]], dtype=uint8)
```
> **说明**:上面的代码读取了当前路径下名为`guido.jpg` 的图片文件,计算机系统中的图片通常由若干行若干列的像素点构成,而每个像素点又是由红绿蓝三原色构成的,所以能够用三维数组来表示。读取图片用到了matplotlib库的`imread`函数。
......@@ -516,7 +516,7 @@ array18
False True
```
8. base属性:数组的基对象(如果数组共享了其他数组的内存空间)
8. `base`属性:数组的基对象(如果数组共享了其他数组的内存空间)
代码:
......@@ -537,7 +537,7 @@ array18
和Python中的列表类似,NumPy的`ndarray`对象可以进行索引和切片操作,通过索引可以获取或修改数组中的元素,通过切片可以取出数组的一部分。
1. 索引运算
1. 索引运算(普通索引)
一维数组,代码:
......@@ -594,7 +594,9 @@ array18
[ 7 8 9]]
```
2. 切片运算
2. 切片运算(切片索引)
切片是形如`[开始索引:结束索引:步长]`的语法,通过指定**开始索引**(默认值无穷小)、**结束索引**(默认值无穷大)和**步长**(默认值1),从数组中取出指定部分的元素并构成新的数组。因为开始索引、结束索引和步长都有默认值,所以它们都可以省略,如果不指定步长,第二个冒号也可以省略。一维数组的切片运算跟Python中的`list`类型的切片非常类似,此处不再赘述,二维数组的切片可以参考下面的代码,相信非常容易理解。
代码:
......@@ -653,31 +655,196 @@ array18
```Python
print(array24[1, :2])
print(array24[1:2, :2])
```
输出:
```
[4 5]
[[4 5]]
```
代码:
```Python
print(array24[1:2, :2])
print(array24[::2, ::2])
```
输出:
```
[[4 5]]
[[1 3]
[7 9]]
```
代码:
```Python
print(array24[::-2, ::-2])
```
输出:
```
[[9 7]
[3 1]]
```
关于数组的索引和切片运算,大家可以通过下面的两张图来增强印象,这两张图来自[《利用Python进行数据分析》](https://item.jd.com/12398725.html)一书,它是pandas的作者Wes McKinney撰写的Python数据分析领域的经典教科书,有兴趣的读者可以购买和阅读原书。
![](res/ndarray-index.png)
![](res/ndarray-slice.png)
3. 花式索引(fancy index)
花式索引(Fancy indexing)是指利用整数数组进行索引,这里所说的整数数组可以是NumPy的`ndarray`,也可以是Python中`list`、`tuple`等可迭代类型,可以使用正向或负向索引。
一维数组的花式索引,代码:
```Python
array25 = np.array([50, 30, 15, 20, 40])
array25[[0, 1, -1]]
```
输出:
```
array([50, 30, 40])
```
二维数组的花式索引,代码:
```Python
array26 = np.array([[30, 20, 10], [40, 60, 50], [10, 90, 80]])
# 取二维数组的第1行和第3行
array26[[0, 2]]
```
输出:
![](res/ndarray-slice.png)
```
array([[30, 20, 10],
[10, 90, 80]])
```
代码:
```Python
# 取二维数组第1行第2列,第3行第3列的两个元素
array26[[0, 2], [1, 2]]
```
输出:
```
array([20, 80])
```
代码:
```Python
# 取二维数组第1行第2列,第3行第2列的两个元素
array26[[0, 2], 1]
```
输出:
```
array([20, 90])
```
4. 布尔索引
布尔索引就是通过布尔类型的数组对数组元素进行索引,布尔类型的数组可以手动构造,也可以通过关系运算来产生布尔类型的数组。
代码:
```Python
array27 = np.arange(1, 10)
array27[[True, False, True, True, False, False, False, False, True]]
```
输出:
```
array([1, 3, 4, 9])
```
代码:
```Python
array27 >= 5
```
输出:
```
array([False, False, False, False, True, True, True, True, True])
```
代码:
```Python
# ~运算符可以实现逻辑变反,看看运行结果跟上面有什么不同
~(array27 >= 5)
```
输出:
```
array([ True, True, True, True, False, False, False, False, False])
```
代码:
```Python
array27[array27 >= 5]
```
输出:
```
array([5, 6, 7, 8, 9])
```
> **提示**:切片操作虽然创建了新的数组对象,但是新数组和原数组共享了数组中的数据,简单的说,如果通过新数组对象或原数组对象修改数组中的数据,其实修改的是同一块数据。花式索引和布尔索引也会创建新的数组对象,而且新数组复制了原数组的元素,新数组和原数组并不是共享数据的关系,这一点通过前面讲的数组的`base`属性也可以了解到,大家一定要注意。
#### 案例:通过数组切片处理图像
学习基础知识总是比较枯燥且没有成就感的,所以我们还是来个案例为大家演示下上面学习的数组索引和切片操作到底有什么用。前面我们说到过,可以用三维数组来表示图像,那么通过图像对应的三维数组进行操作,就可以实现对图像的处理,如下所示。
读入图片创建三维数组对象。
```Python
guido_image = plt.imread('guido.jpg')
plt.imshow(guido_image)
```
对数组的0轴进行反向切片,实现图像的垂直翻转。
```Python
plt.imshow(guido_image[::-1])
```
![](res/image-flip-1.png)
对数组的1轴进行反向切片,实现图像的水平翻转。
```Python
plt.imshow(guido_image[:,::-1])
```
![](res/image-flip-2.png)
将Guido的头切出来。
```Python
plt.imshow(guido_image[30:350, 90:300])
```
![](res/image-flip-3.png)
### 数组对象的方法
......@@ -686,14 +853,14 @@ array18
`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())
array28 = np.array([1, 2, 3, 4, 5, 5, 4, 3, 2, 1])
print(array28.sum())
print(array28.mean())
print(array28.max())
print(array28.min())
print(array28.std())
print(array28.var())
print(array28.cumsum())
```
输出:
......@@ -710,56 +877,79 @@ print(array25.cumsum())
####其他方法
1. `all()` / `any()`方法:判断数组是否所有元素都是True / 判断数组是否有为True的元素。
1. `all()` / `any()`方法:判断数组是否所有元素都是`True` / 判断数组是否有为`True`的元素。
2. `astype()`方法:拷贝数组,并将数组中的元素转换为指定的类型。
3. `dot()`方法:实现一个数组和另一个数组的点乘运算。
3. `dot()`方法:实现一个数组和另一个数组的点积运算。
在数学上,**点积**(dot product)又称**数量积**或**标量积**,是一种接受两个等长的数字序列,返回单个数字的代数运算。从代数角度看,先对两个数字序列中的每组对应元素求积,再对所有积求和,结果即为点积,即:$\boldsymbol{A} \cdot \boldsymbol{B} = \sum_{i=1}^{n}a_ib_i$。从几何角度看,点积则是两个向量的长度与它们夹角余弦的积,即:$\boldsymbol{A} \cdot \boldsymbol{B}=|\boldsymbol{A}||\boldsymbol{B}|\cos{\theta}$。
在欧几里得几何中,两个笛卡尔坐标向量的点积也称为**内积**(inner product),NumPy中也提供了实现内积的函数,但是内积的含义要高于点积,点积相当于是内积在欧几里得空间$\mathbb{R}^n$的特例,而内积可以推广到**赋范向量空间**(不理解没有关系,当我没说就行了)。
一维数组的点积运算,代码:
```Python
array29 = np.array([3, 4])
array30 = np.array([5, 6])
array29.dot(array30)
```
输出:
```
39
```
4. `dump()`/`load()`方法:保存数组到文件中/从文件中加载数组。
二维数组的点积运算,代码:
```Python
array31 = np.array([[1, 2, 3], [4, 5, 6]])
array32 = np.array([[1, 2], [3, 4], [5, 6]])
array31.dot(array32)
```
输出:
```
array([[22, 28],
[49, 64]])
```
5. `fill()`方法。
> **说明**:可以看出,二维数组的点积就是矩阵乘法运算。
4. `dump()`方法:保存数组到文件中,可以通过NumPy中的`load`函数从保存的文件中加载数据创建数组。
代码:
```Python
array31.dump('array31-data')
array32 = np.load('array31-data', allow_pickle=True)
array32
```
输出:
```
array([[1, 2],
[3, 4],
[5, 6]])
```
5. `fill()`方法:向数组中填充指定的元素。
6. `flatten()`方法:将多维数组扁平化为一维数组。
代码:
```Python
array32.flatten()
```
输出:
```
array([1, 2, 3, 4, 5, 6])
```
7. `nonzero()`方法:返回非0元素的索引。
......@@ -768,29 +958,312 @@ print(array25.cumsum())
9. `sort()`方法:对数组进行就地排序。
代码:
```Python
array33 = np.array([35, 96, 12, 78, 66, 54, 40, 82])
array33.sort()
array33
```
输出:
```
array([12, 35, 40, 54, 66, 78, 82, 96])
```
10. `swapaxes()`和`transpose()`方法:交换数组指定的轴
10. `swapaxes()`和`transpose()`方法:交换数组指定的轴。
代码:
```Python
# 指定需要交换的两个轴,顺序无所谓
array32.swapaxes(0, 1)
```
输出:
```
array([[1, 3, 5],
[2, 4, 6]])
```
代码:
```Python
# 对于二维数组,transpose相当于实现了矩阵的转置
array32.transpose()
```
输出:
11. `take()`方法:从数组中取指定索引的元素。
```
array([[1, 3, 5],
[2, 4, 6]])
```
11. `take()`方法:从数组中取指定索引的元素,类似于花式索引。
代码:
```Python
array34 = array33.take([0, 2, -3, -1])
array34
```
输出:
```
array([12, 40, 78, 96])
```
12. `tolist()`方法:将数组转成Python中的`list`。
### 数组的运算
#### 标量运算
使用NumPy最为方便的是当需要对数组元素进行运算时,不用编写循环代码遍历每个元素,所有的运算都会自动的矢量化(使用高效的提前编译的底层语言代码来对数据序列进行数学操作)。简单的说就是,NumPy中的数学运算和数学函数会自动作用于数组中的每个成员。
#### 数组跟标量的运算
代码:
```Python
array35 = np.arange(1, 10)
print(array35 + 10)
print(array35 * 10)
```
输出:
```
[11 12 13 14 15 16 17 18 19]
[10 20 30 40 50 60 70 80 90]
```
#### 数组跟数组的运算
#### 矢量运算
代码:
```Python
array36 = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])
print(array35 + array36)
print(array35 * array36)
print(array35 ** array36)
```
输出:
```
[ 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]
```
#### 通用一元函数
通用函数是对`ndarray`中的数据执行元素级运算的函数。你可以将其看做普通函数(接收一个标量值作为参数,返回一个标量值)的矢量化包装器,如下所示。
代码:
```Python
print(np.sqrt(array35))
print(np.log2(array35))
```
输出:
```
[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` / `around` | 四舍五入函数 |
#### 通用二元函数
代码:
```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))
```
输出:
```
[[ 4 10 18]
[21 16 9]]
[[ 4 25 216]
[343 64 9]]
```
**表2:通用二元函数**
| 函数 | 说明 |
| --------------------------------- | ---- |
| `add` / `substract` / `multiply` | 加法函数 / 减法函数 / 乘法函数 |
| `divide` / `floor_divide` / `mod` | 除法函数 / 整除函数 / 求模函数 |
| `power` | 数组$A$的元素$A_i$和数组$B$的元素$B_i$,计算$A_i^{B_i}$ |
| `maximum` / `fmax` | 获取最大值 / 获取最大值,忽略NaN |
| `minimum` / `fmin` | 获取最小值 / 获取最小值,忽略NaN |
#### 广播机制
上面的例子中,两个二元运算的数组形状是完全相同的,我们再来研究一下,两个形状不同的数组是否可以直接做二元运算或使用二元函数进行运算,请看下面的例子。
代码:
```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]])
```
代码:
```Python
array41 = np.array([[1], [2], [3], [4]])
array39 + array41
```
输出:
```
array([[1, 1, 1],
[3, 3, 3],
[5, 5, 5],
[7, 7, 7]])
```
通过上面的例子,我们发现形状不同的数组仍然有机会进行二元运算,但也绝对不是任意的数组都可以进行二元运算。简单的说,只有两个数组后缘维度相同或者其中一个数组后缘维度为1时,两个数组才能进行二元运算。所谓后缘维度,指的是数组`shape`属性对应的元组中最后一个元素的值(从后往前数最后一个维度的值),例如,我们之前打开的图像对应的数组后缘维度为3,3行4列的二维数组后缘维度为4,而有5个元素的一维数组后缘维度为5。后缘维度相同或者其中一个数组为1就可以应用广播机制对元素进行扩散,从而满足两个数组对应元素做运算的需求,如下图所示。
![](res/broadcast-1.png)
![](res/broadcast-2.png)
![](res/broadcast-3.png)
### 其他常用函数
除了上面讲到的函数外,NumPy中还提供了很多用于处理数组的函数,`ndarray`对象的很多方法也可以通过直接调用函数来实现,下表给出了一些常用的函数。
**表3:NumPy其他常用函数**
| 函数 | 说明 |
| ------------------- | ----------------------------------------------------- |
| `unique(x)` | 去除数组`x`重复元素,返回唯一元素构成的有序数组 |
| `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`的对称差,返回这些元素构成的数组 |
| `copy(x)` | 返回拷贝数组`x`得到的数组 |
| `sort(x)` | 返回数组`x`元素排序后的拷贝 |
| `split(x)` | 将数组`x`拆成若干个子数组 |
| `hstack` / `vstack` | 将多个数组水平/垂直堆叠构成新数组 |
| `concatenate` | 沿着指定的轴连接多个数组构成新数组 |
代码:
```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]])
```
代码:
```Python
np.vstack((array42, array43))
```
输出:
```
array([[1, 1, 1],
[2, 2, 2],
[3, 3, 3],
[4, 4, 4],
[5, 5, 5],
[6, 6, 6]])
```
代码:
```Python
np.concatenate((array42, array43))
```
输出:
```
array([[1, 1, 1],
[2, 2, 2],
[3, 3, 3],
[4, 4, 4],
[5, 5, 5],
[6, 6, 6]])
```
代码:
```Python
np.concatenate((array42, array43), axis=1)
```
输出:
```
array([[1, 1, 1, 4, 4, 4],
[2, 2, 2, 5, 5, 5],
[3, 3, 3, 6, 6, 6]])
```
### 矩阵运算
我们可以用二维数组表示数学上的矩阵,NumPy中也提供了专门用于矩阵运算的模块和函数。
#### 线性代数回顾
#### 线性代数模块
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册