更新了部分文档
parent
1ef5bf058a
commit
6f4066d6c3
|
@ -10,24 +10,23 @@ Date: 2018-03-20
|
|||
# 每个进程都有自己独立的内存空间 所以进程之间共享数据只能通过IPC的方式
|
||||
|
||||
|
||||
from multiprocessing import Process, Queue
|
||||
from multiprocessing import Process, Queue, current_process
|
||||
from time import sleep
|
||||
|
||||
|
||||
def sub_task(string, q):
|
||||
number = q.get()
|
||||
while number:
|
||||
print('%d: %s' % (number, string))
|
||||
sleep(0.001)
|
||||
number = q.get()
|
||||
def sub_task(content, counts):
|
||||
print(f'PID: {current_process().pid}')
|
||||
counter = 0
|
||||
while counter < counts:
|
||||
counter += 1
|
||||
print(f'{counter}: {content}')
|
||||
sleep(0.01)
|
||||
|
||||
|
||||
def main():
|
||||
q = Queue(10)
|
||||
for number in range(1, 11):
|
||||
q.put(number)
|
||||
Process(target=sub_task, args=('Ping', q)).start()
|
||||
Process(target=sub_task, args=('Pong', q)).start()
|
||||
number = random.randrange(5, 10)
|
||||
Process(target=sub_task, args=('Ping', number)).start()
|
||||
Process(target=sub_task, args=('Pong', number)).start()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1110,19 +1110,6 @@ Python中实现并发编程的三种方案:多线程、多进程和异步I/O
|
|||
self.balance = new_balance
|
||||
|
||||
|
||||
class AddMoneyThread(threading.Thread):
|
||||
"""自定义线程类"""
|
||||
|
||||
def __init__(self, account, money):
|
||||
self.account = account
|
||||
self.money = money
|
||||
# 自定义线程的初始化方法中必须调用父类的初始化方法
|
||||
super().__init__()
|
||||
|
||||
def run(self):
|
||||
# 线程启动之后要执行的操作
|
||||
self.account.deposit(self.money)
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
account = Account()
|
||||
|
@ -1130,14 +1117,6 @@ Python中实现并发编程的三种方案:多线程、多进程和异步I/O
|
|||
pool = ThreadPoolExecutor(max_workers=10)
|
||||
futures = []
|
||||
for _ in range(100):
|
||||
# 创建线程的第1种方式
|
||||
# threading.Thread(
|
||||
# target=account.deposit, args=(1, )
|
||||
# ).start()
|
||||
# 创建线程的第2种方式
|
||||
# AddMoneyThread(account, 1).start()
|
||||
# 创建线程的第3种方式
|
||||
# 调用线程池中的线程来执行特定的任务
|
||||
future = pool.submit(account.deposit, 1)
|
||||
futures.append(future)
|
||||
# 关闭线程池
|
||||
|
|
|
@ -69,3 +69,5 @@ insert into tb_emp values
|
|||
-- 查询主管的姓名和职位
|
||||
|
||||
-- 查询月薪排名4~6名的员工排名、姓名和月薪
|
||||
|
||||
-- 查询每个部门月薪排前2名的员工姓名、月薪和部门编号
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
## 单元测试
|
||||
|
||||
Python标准库中提供了名为`unittest` 的模块来支持我们对代码进行单元测试。所谓单元测试是指针对程序中最小的功能单元(在Python中指函数或类中的方法)进行的测试。
|
||||
请各位读者移步到[《使用Django开发商业项目》](../Day91-100/95.使用Django开发商业项目.md)一文。
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
|
||||
下面我们以“360图片”网站为例,说明什么是JavaScript逆向工程。其实所谓的JavaScript逆向工程就是找到通过Ajax技术动态获取数据的接口。在浏览器中输入<http://image.so.com/z?ch=beauty>就可以打开“360图片”的“美女”版块,如下图所示。
|
||||
|
||||
![](./res/image360-website.png)
|
||||
![](https://gitee.com/jackfrued/mypic/raw/master/20210824004714.png)
|
||||
|
||||
但是当我们在浏览器中通过右键菜单“显示网页源代码”的时候,居然惊奇的发现页面的HTML代码中连一个`<img>`标签都没有,那么我们看到的图片是怎么显示出来的呢?原来所有的图片都是通过JavaScript动态加载的,而在浏览器的“开发人员工具”的“网络”中可以找到获取这些图片数据的网络API接口,如下图所示。
|
||||
|
||||
![](./res/api-image360.png)
|
||||
![](https://gitee.com/jackfrued/mypic/raw/master/20210824004727.png)
|
||||
|
||||
那么结论就很简单了,只要我们找到了这些网络API接口,那么就能通过这些接口获取到数据,当然实际开发的时候可能还要对这些接口的参数以及接口返回的数据进行分析,了解每个参数的意义以及返回的JSON数据的格式,这样才能在我们的爬虫中使用这些数据。
|
||||
|
||||
|
|
|
@ -111,9 +111,6 @@ class TaobaoDownloaderMiddleWare(object):
|
|||
|
||||
def __init__(self, timeout=None):
|
||||
self.timeout = timeout
|
||||
# options = webdriver.ChromeOptions()
|
||||
# options.add_argument('--headless')
|
||||
# self.browser = webdriver.Chrome(options=options)
|
||||
options = webdriver.ChromeOptions()
|
||||
options.add_argument('--headless')
|
||||
self.browser = webdriver.Chrome(options)
|
||||
|
@ -128,7 +125,6 @@ class TaobaoDownloaderMiddleWare(object):
|
|||
def process_request(self, request, spider):
|
||||
try:
|
||||
self.browser.get(request.url)
|
||||
# Chrome对象的page_source代表了页面的HTML代码(带动态内容)
|
||||
return HtmlResponse(url=request.url, body=self.browser.page_source,
|
||||
request=request, encoding='utf-8', status=200)
|
||||
except TimeoutException:
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from io import StringIO
|
||||
from urllib.parse import urlencode
|
||||
import re
|
||||
|
|
|
@ -348,7 +348,7 @@ Numpy最为核心的数据类型是`ndarray`,使用`ndarray`可以处理一维
|
|||
|
||||
```Python
|
||||
array18 = plt.imread('guido.jpg')
|
||||
array18
|
||||
array18
|
||||
```
|
||||
|
||||
输出:
|
||||
|
@ -1107,15 +1107,15 @@ print(np.log2(array35))
|
|||
```Python
|
||||
array37 = np.array([[4, 5, 6], [7, 8, 9]])
|
||||
array38 = np.array([[1, 2, 3], [3, 2, 1]])
|
||||
print(array37 * array38)
|
||||
print(array37 ** array38)
|
||||
print(np.power(array37, array38))
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
[[ 4 10 18]
|
||||
[21 16 9]]
|
||||
[[ 4 25 216]
|
||||
[343 64 9]]
|
||||
[[ 4 25 216]
|
||||
[343 64 9]]
|
||||
```
|
||||
|
@ -1376,7 +1376,7 @@ NumPy的`linalg`模块中有一组标准的矩阵分解运算以及诸如求逆
|
|||
| `inner` | 数组的内积 |
|
||||
| `outer` | 数组的叉积 |
|
||||
| `trace` | 计算对角线元素的和 |
|
||||
| `norm` | 求模运算 |
|
||||
| `norm` | 求模(范数)运算 |
|
||||
| `det` | 计算行列式的值(在方阵上计算得到的标量) |
|
||||
| `matrix_rank` | 计算矩阵的秩 |
|
||||
| `eig` | 计算矩阵的特征值(eigenvalue)和特征向量(eigenvector) |
|
||||
|
|
|
@ -582,7 +582,7 @@ Series对象的常用属性如下表所示。
|
|||
代码:
|
||||
|
||||
```Python
|
||||
ser8 = pd.Series(
|
||||
ser8 = pd.Series(
|
||||
data=[35, 96, 12, 57, 25, 89],
|
||||
index=['grape', 'banana', 'pitaya', 'apple', 'peach', 'orange']
|
||||
)
|
||||
|
@ -791,7 +791,7 @@ plt.show()
|
|||
6019 rows × 4 columns
|
||||
```
|
||||
|
||||
> **说明**:如果需要上面例子中的CSV文件,可以通过下面的百度云盘地址进行获取。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
> **说明**:如果需要上面例子中的CSV文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
|
||||
4. 读取Excel文件创建`DataFrame`对象。
|
||||
|
||||
|
@ -810,9 +810,7 @@ plt.show()
|
|||
df4
|
||||
```
|
||||
|
||||
> **说明**:如果需要上面例子中的Excel文件,可以通过下面的百度云盘地址进行获取。
|
||||
>
|
||||
> 链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
> **说明**:如果需要上面例子中的Excel文件,可以通过下面的百度云盘地址进行获取,数据在《从零开始学数据分析》目录中。链接:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取码:e7b4。
|
||||
|
||||
输出:
|
||||
|
||||
|
@ -953,10 +951,192 @@ memory usage: 1.3+ KB
|
|||
#### 其他方法
|
||||
|
||||
1. 独热编码
|
||||
|
||||
数据表中的字符串字段通常需要做预处理,因为字符串字段没有办法计算相关性,也没有办法进行$\chi^2$检验、$ \gamma $检验等操作。处理字符串通常有以下几种方式:
|
||||
1. 有序变量(Ordinal Variable):字符串表示的数据有大小关系,那么可以对字符串进行序号化处理。
|
||||
2. 分类变量(Categorical Variable)/ 名义变量(Nominal Variable):字符串表示的数据没有大小关系和等级之分,那么就可以使用独热编码处理成哑变量(虚拟变量)矩阵。
|
||||
3. 定距变量(Scale Variable):数据有大小高低之分,可以进行加减运算。
|
||||
|
||||
可以使用`get_dummies()`函数来生成哑变量(虚拟变量)矩阵,将哑变量引入回归模型,虽然使模型变得较为复杂,但可以更直观地反映出该自变量的不同属性对于因变量的影响。
|
||||
|
||||
2. 窗口计算
|
||||
|
||||
|
||||
|
||||
3. 相关性
|
||||
|
||||
协方差(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}}
|
||||
$$
|
||||
|
||||
估算样本的协方差和标准差,可以得到样本皮尔逊系数,通常用英文小写字母$r$表示。
|
||||
|
||||
$$
|
||||
r = \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}}
|
||||
$$
|
||||
|
||||
等价的表达式为:
|
||||
|
||||
$$
|
||||
r = \frac {1} {n - 1} \sum_{i=1}^n \left( \frac {X_i - \bar{X}} {\sigma_X} \right) \left( \frac {Y_i - \bar{Y}} {\sigma_{Y}} \right)
|
||||
$$
|
||||
|
||||
皮尔逊相关系数适用于:
|
||||
|
||||
1. 两个变量之间是线性关系,都是连续数据。
|
||||
2. 两个变量的总体是正态分布,或接近正态的单峰分布。
|
||||
3. 两个变量的观测值是成对的,每对观测值之间相互独立。
|
||||
|
||||
斯皮尔曼相关系数对数据条件的要求没有皮尔逊相关系数严格,只要两个变量的观测值是成对的等级评定资料,或者是由连续变量观测资料转化得到的等级资料,不论两个变量的总体分布形态、样本容量的大小如何,都可以用斯皮尔曼等级相关系数来进行研究。
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
输出:
|
||||
|
||||
```
|
||||
语文 数学 英语
|
||||
学号 学期
|
||||
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()`函数
|
||||
2. `to_datetime()`函数
|
||||
3. `DateOffset`类型
|
||||
4. 相关方法
|
||||
- `shift()`方法
|
||||
- `asfreq()`方法
|
||||
- `resample()`方法
|
||||
5. 时区转换
|
||||
- `tz_localize()`方法
|
||||
- `tz_convert()`方法
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
## 参数估计
|
||||
|
||||
推断型统计的核心就是用样本推测总体。在实际生产环境中,可能无法获得所有的数据,或者即便获取了所有的数据,但是没有足够的资源来分析所有的数据,在这种情况下,我们都需要用非常小量的样本特征去评估总体数据的特征,这其中的一项工作就是参数估计。
|
||||
|
||||
参数估计应用的场景非常的多,例如:
|
||||
|
||||
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,85 @@
|
|||
## 方差分析
|
||||
|
||||
### 基本概念
|
||||
|
||||
在产品运营中,我们会遇到各种需要评估运营效果的场景,包括促活的活动是否起到作用、A/B 测试的策略有无成效等等。具体例如,产品升级前的平均 DAU 是 155 万,产品升级后的平均 DAU 是 157 万,那么如何判断 DAU 提升的 2 万是正常的波动,还是升级带来的效果呢?对比同一组数据在实施某些策略前后的数据变化,判断数据波动是不是某一因素导致的,这种方法我们称之为方差分析。方差分析通常缩写为 ANOVA(Analysis of Variance),也叫“F 检验”,用于两个及两个以上分组样本的差异性检验。简单的说,**分析差异的显著性是否明显的方法就是方差分析**。
|
||||
|
||||
举一个例子,如果我们需要分析优惠券的金额对用户的购买转化率是否能起到有效作用,我们可以将数据分成以下三个组:
|
||||
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085210.png" width="60%">
|
||||
|
||||
用户购买行为是随机的,购买率很高的不会很多,购买率极低的也不会很多,绝大部分用户的购买率都集中在某个值附近,这个值我们叫作整体购买率的平均值。如果每个客群分组自身的购买率均值与这个整体购买率平均值不一致,就会出现以下两种情况。
|
||||
|
||||
1. 第一种情况
|
||||
|
||||
蓝色分组的购买率平均值(蓝色线)比整体平均值(黑色线)要高,有可能是最右边那个很高的购买率把分组的均值抬升的,同时蓝色分组的数据分布很散(方差大),此时不能有十足把握说明该组用户的购买转化率很高。
|
||||
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085506.png" width="50%">
|
||||
|
||||
2. 第二种情况
|
||||
|
||||
绿色分组的购买率平均值(绿色线)比整体平均值(黑色线)要高,但是绿色分组的数据非常集中,都集中在分组的平均值(绿色线)附近,此时我们可以认为该组的转化率平均值与整体有明显区别。
|
||||
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085608.png" width="50%">
|
||||
|
||||
为了更好表述上面的问题,我们可以引入“组内方差”的概念,即描述每个分组内部数据分布的离散情况。如下图所示,对于上面蓝色和绿色分组的“组内方差”,显然蓝色的组内方差更大,绿色的组内方差更小。
|
||||
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210713085808.png" width="75%">
|
||||
|
||||
综上所述,如果上面三个分组的用户购买率平均值不在中线(整体购买率)左右,而是有明显的偏高或偏低,并且该组内的每个转化率都紧紧围绕在该组购买率平均值的附近(即组内方差很小)。那么我们就可以断定:该组的购买率与整体不一致,是该组对应优惠金额的影响造成的。
|
||||
|
||||
### 定量分析
|
||||
|
||||
如果要进行定量分析,可以使用 F 检验值和 F crit 临界值这两个指标。F 检验值用来精确表达这几组差异大小的,F crit临界值是一个判断基线:
|
||||
|
||||
- 当 F > F crit,这几组之间的差异超过判断基准了,认为不同优惠金额的分组间的购买率是不一样的,优惠金额这个因素会对购买率产生影响,也就是说通过运营优惠金额这个抓手,是可以提升用户购买转化率的;
|
||||
- 当 F < F crit,则认为不同优惠金额的分组间的购买率是一样的,优惠金额这个因素不会对购买率产生影响,也就是说需要继续寻找其他与购买转化率有关的抓手。
|
||||
|
||||
<img src="https://gitee.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://gitee.com/jackfrued/mypic/raw/master/20210714125251.png" width="75%">
|
||||
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210714130853.png" width="75%">
|
||||
|
||||
### 应用场景
|
||||
|
||||
工作中遇到以下两类场景就可以使用方差分析:
|
||||
|
||||
1. 同一个客群在实施某个策略前后的指标对比。
|
||||
2. 两个或多个客群对比同一指标,评估同一指标在不同客群上的差异。
|
||||
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210714131318.png" width="85%">
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
我们经常会使用以下几个指标来描述一组数据的集中趋势:
|
||||
|
||||
1. 均值 - 均值代表某个数据集的整体水平,我们经常提到的客单价、平均访问时长、平均配送时长等指标都是均值。均值的缺点是容易受极值的影响,虽然可以使用加权平均值来消除极值的影响,但是可能事先并不清楚数据的权重;对于正数可以用几何平均值来替代算术平均值。
|
||||
- 算术平均值:$$\bar{x}=\frac{\sum_{i=1}^{n}x_{i}}{n}=\frac{x_{1}+x_{2}+\cdots +x_{n}}{n}$$。
|
||||
- 几何平均值:$$\left(\prod_{i=1}^{n}x_{i}\right)^{\frac{1}{n}}={\sqrt[{n}]{x_{1}x_{2} \cdots x_{n}}}$$。
|
||||
- 算术平均值:$$\bar{x}=\frac{\sum_{i=1}^{n}x_{i}}{n}=\frac{x_{1}+x_{2}+\cdots +x_{n}}{n}$$,例如计算最近30天日均DAU、日均新增访客等,都可以使用算术平均值。
|
||||
- 几何平均值:$$\left(\prod_{i=1}^{n}x_{i}\right)^{\frac{1}{n}}={\sqrt[{n}]{x_{1}x_{2} \cdots x_{n}}}$$,例如计算不同渠道的平均转化率、不同客群的平均留存率、不同品类的平均付费率等,就可以使用几何平均值。
|
||||
2. 中位数 - 将数据按照升序或降序排列后位于中间的数,它描述了数据的中等水平。
|
||||
3. 众数 - 数据集合中出现频次最多的数据,它代表了数据的一般水平。数据的趋势越集中,众数的代表性就越好。众数不受极值的影响,但是无法保证唯一性和存在性。
|
||||
|
||||
|
@ -17,24 +17,22 @@ A组:5, 6, 6, 6, 6, 8, 10
|
|||
B组:3, 5, 5, 6, 6, 9, 12
|
||||
```
|
||||
|
||||
A组:
|
||||
A组的均值:6.74,中位数:6,众数:6。
|
||||
|
||||
均值:6.74,中位数:6,众数:6。
|
||||
B组的均值:6.57,中位数:6,众数:5, 6。
|
||||
|
||||
B组:
|
||||
|
||||
均值:6.57,中位数:6,众数:5, 6。
|
||||
> **说明**:在Excel中,可以使用AVERAGE、MEDIAN、MODE函数分别计算均值、中位数和众数。
|
||||
|
||||
对A组的数据进行一些调整。
|
||||
|
||||
```
|
||||
A组:5, 6, 6, 6, 6, 8, 10, 20
|
||||
A组:5, 6, 6, 6, 6, 8, 10, 200
|
||||
B组:3, 5, 5, 6, 6, 9, 12
|
||||
```
|
||||
|
||||
A组的均值会大幅度提升,但中位数和众数却没有变化。
|
||||
|
||||
> **思考**:怎样判断上面的20到底是不是一个异常值?
|
||||
> **思考**:怎样判断上面的200到底是不是一个异常值?
|
||||
|
||||
| | 优点 | 缺点 |
|
||||
| ------ | -------------------------------- | ------------------------------------ |
|
||||
|
@ -48,17 +46,26 @@ A组的均值会大幅度提升,但中位数和众数却没有变化。
|
|||
|
||||
### 数据的离散趋势
|
||||
|
||||
如果说数据的集中趋势,说明了数据最主要的特征是什么;那么数据的离散趋势,则体现了这个特征的稳定性。例如A地区冬季平均气温`0`摄氏度,最低气温`-10`摄氏度;B地区冬季平均气温`-2`摄氏度,最低气温`-4`摄氏度;如果你是一个特别怕冷的人,在选择A和B两个区域作为工作和生活的城市时,你会做出怎样的选择?
|
||||
如果说数据的集中趋势,说明了数据最主要的特征是什么;那么数据的离散趋势,则体现了这个特征的稳定性。例如 A 地区冬季平均气温`0`摄氏度,最低气温`-10`摄氏度;B 地区冬季平均气温`-2`摄氏度,最低气温`-4`摄氏度;如果你是一个特别怕冷的人,在选择 A 和 B 两个区域作为工作和生活的城市时,你会做出怎样的选择?
|
||||
|
||||
1. 极值:就是最大值(maximum)、最小值(minimum),代表着数据集的上限和下限。
|
||||
|
||||
> **说明**:在Excel中,计算极值的函数是MAX和MIN。
|
||||
|
||||
2. 极差:又称“全距”,是一组数据中的最大观测值和最小观测值之差,记作$R$。一般情况下,极差越大,离散程度越大,数据受极值的影响越严重。
|
||||
|
||||
3. 方差:将每个值与均值的偏差进行平方,然后除以总数据量得到的值。简单来说就是表示数据与期望值的偏离程度。方差越大,就意味着数据越不稳定、波动越剧烈,因此代表着数据整体比较分散,呈现出离散的趋势;而方差越小,意味着数据越稳定、波动越平滑,因此代表着数据整体比较集中。
|
||||
- 总体方差:$$ \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。
|
||||
|
||||
4. 标准差:将方差进行平方根运算后的结果,与方差一样都是表示数据与期望值的偏离程度。
|
||||
- 总体标准差:$$ \sigma = \sqrt{\frac{\sum_{i=1}^{N}(X_i - \mu)^2}{N}} $$。
|
||||
- 样本标准差:$$ S = \sqrt{\frac{\sum_{i=1}^{N}(X_i - \bar{X})^2}{N-1}} $$。
|
||||
|
||||
> **说明**:在Excel中,计算标准差的函数分别是STDEV.P和STDEV.S。
|
||||
|
||||
> **练习3**:复制“概率基础练习.xlsx”文件的表单“练习1”,将复制的表单命名为“练习3”,计算订单支付金额的最大值、最小值、极差、方差和标准差。
|
||||
|
||||
### 数据的频数分析
|
||||
|
@ -130,6 +137,23 @@ A组的均值会大幅度提升,但中位数和众数却没有变化。
|
|||
|
||||
> **说明**:泊松分布是在没有计算机的年代,由于二项分布的运算量太大运算比较困难,为了减少运算量,数学家为二项分布提供的一种近似。
|
||||
|
||||
#### 分布函数和密度函数
|
||||
|
||||
对于连续型随机变量,我们不可能去罗列每一个值出现的概率,因此要引入分布函数的概念。
|
||||
$$
|
||||
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]$。
|
||||
|
@ -138,12 +162,25 @@ A组的均值会大幅度提升,但中位数和众数却没有变化。
|
|||
|
||||
3. 正态分布(*Normal distribution*):又名**高斯分布**(*Gaussian distribution*),是一个非常常见的连续概率分布,经常用自然科学和社会科学中来代表一个不明的随机变量。若随机变量$X$服从一个位置参数为$\mu$、尺度参数为$\sigma$的正态分布,记为$X \sim N(\mu,\sigma^2)$,其概率密度函数为:$\displaystyle f(x)={\frac {1}{\sigma {\sqrt {2\pi }}}}e^{-{\frac {\left(x-\mu \right)^{2}}{2\sigma ^{2}}}}$。
|
||||
|
||||
<img src="normal-distribution.png" width="600">
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210716155507.png" width="80%">
|
||||
|
||||
“3$\sigma$法则”:
|
||||
|
||||
<img src="3sigma.png" height="600">
|
||||
<img src="https://gitee.com/jackfrued/mypic/raw/master/20210716155542.png" width="75%">
|
||||
|
||||
正态分布有一个非常重要的性质,**大量统计独立的随机变量的平均值的分布趋于正态分布**,这就是**中心极限定理**。中心极限定理的重要意义在于,我们可以用正态分布作为其他概率分布的近似。
|
||||
|
||||
一个例子:假设某校入学新生的智力测验平均分数与标准差分别为 100 与 12。那么随机抽取 50 个学生,他们智力测验平均分数大于 105 的概率是多少?小于 90 的概率是多少?
|
||||
|
||||
本例没有正态分布的假设,还好中心极限定理提供一个可行解,那就是当随机样本数量超过30,样本平均数 近似于一个正态变量,标准正态变量$ Z = \frac {\bar{X} - \mu} {\sigma / \sqrt{n}} $。
|
||||
|
||||
平均分数大于 105 的概率为:$ P(Z \gt \frac{105 - 100}{12 / \sqrt{50}}) = P(Z \gt 5/1.7) = P(Z \gt 2.94) = 0.0016$。
|
||||
|
||||
平均分数小于 90 的概率为:$ P(Z \lt \frac{90-100}{12/\sqrt{50}}) = P(Z < -5.88) = 0.0000 $。
|
||||
|
||||
> **说明**:上面标准正态分布的概率值可以查表得到。
|
||||
|
||||
4. 伽马分布(*Gamma distribution*):假设$X_1, X_2, ... X_n$为连续发生事件的等候时间,且这$n$次等候时间为独立的,那么这$n$次等候时间之和$Y$($Y=X_1+X_2+...+X_n$)服从伽玛分布,即$Y \sim \Gamma(\alpha,\beta)$,其中$\alpha=n, \beta=\lambda$,这里的$\lambda$是连续发生事件的平均发生频率。
|
||||
|
||||
5. 卡方分布(*Chi-square distribution*):若$k$个随机变量$Z_1,Z_2,...,Z_k$是相互独立且符合标准正态分布(数学期望为0,方差为1)的随机变量,则随机变量$Z$的平方和$X=\sum_{i=1}^{k}Z_i^2$被称为服从自由度为$k$的卡方分布,记为$X \sim \chi^2(k)$。
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
## 相关和回归
|
||||
|
||||
我们知道,可以通过对指标的维度拆来解寻找指标变化的原因。当我们找到问题的原因时,自然会进一步思考一个问题:指标变化的原因这么多,其中的关键因素又是哪个呢?例如,我们在工作场景中时不时会讨论这些问题:
|
||||
|
||||
1. 电商类产品想知道哪个品类销售对整体销售贡献更大;
|
||||
2. 渠道运营想知道哪个渠道的用户对整体活跃作用更大;
|
||||
3. 产品想知道到底哪些维度(城市、年龄、接入设备等)会影响整体活跃。
|
||||
|
||||
还有很多类似的场景,在这种情况下我们不仅要要找到数据变化的原因,还需要明确出不同原因的重要性。因为实际工作中可用资源有限,只能集中优势资源解决核心问题。
|
||||
|
||||
### 相关分析基本概念
|
||||
|
||||
相关性分析,指对两个或多个指标进行分析,评估它们两两之间联系或相互影响的程度。相关性分析不仅可以分析出多个指标间是否存在相关关系,还能给出相关程度的量化值。在进行相关性分析时,我们会使用“相关系数”定量给出几个指标间联系和影响的程度,通常用 $ \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://gitee.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://gitee.com/jackfrued/mypic/raw/master/20210713164021.png" width="75%">
|
||||
|
||||
### 线性回归
|
||||
|
||||
如果只有一个自变量 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://gitee.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://gitee.com/jackfrued/mypic/raw/master/20210714074159.png" width="60%">
|
||||
|
||||
接下来我们还要对回归方程的显著性进行检验,主要包括 t 检验(回归系数的检验)和F检验(回归方程的检验)。对于F检验(F-statistic)的结果,主要关注其 p-value ,如果 p-value 小于0.05,那么说明拟合效果是不错的。
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
#### Git简介
|
||||
|
||||
![](./res/git-logo.png)
|
||||
![](res/git-logo.png)
|
||||
|
||||
Git是诞生于2005年的一个开源分布式版本控制系统,最初是Linus Torvalds(Linux之父) 为了帮助管理Linux内核开发而开发的一个版本控制软件。Git与常用的版本控制工具Subversion等不同,它采用了分布式版本控制的方式,在没有中央服务器支持的环境下也能够实施版本控制。
|
||||
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
## Zen of Python(Python之禅)
|
||||
|
||||
Beautiful is better than ugly. (优美比丑陋好)
|
||||
Explicit is better than implicit.(清晰比晦涩好)
|
||||
Simple is better than complex.(简单比复杂好)
|
||||
Complex is better than complicated.(复杂比错综复杂好)
|
||||
Flat is better than nested.(扁平比嵌套好)
|
||||
Sparse is better than dense.(稀疏比密集好)
|
||||
Readability counts.(可读性很重要)
|
||||
Special cases aren't special enough to break the rules.(特殊情况也不应该违反这些规则)
|
||||
Although practicality beats purity.(但现实往往并不那么完美)
|
||||
Errors should never pass silently.(异常不应该被静默处理)
|
||||
Unless explicitly silenced.(除非你希望如此)
|
||||
In the face of ambiguity, refuse the temptation to guess.(遇到模棱两可的地方,不要胡乱猜测)
|
||||
There should be one-- and preferably only one --obvious way to do it.(肯定有一种通常也是唯一一种最佳的解决方案)
|
||||
Although that way may not be obvious at first unless you're Dutch.(虽然这种方案并不是显而易见的,因为你不是那个荷兰人^这里指的是Python之父Guido^)
|
||||
Now is better than never.(现在开始做比不做好)
|
||||
Although never is often better than \*right\* now.(不做比盲目去做好^极限编程中的YAGNI原则^)
|
||||
If the implementation is hard to explain, it's a bad idea.(如果一个实现方案难于理解,它就不是一个好的方案)
|
||||
If the implementation is easy to explain, it may be a good idea.(如果一个实现方案易于理解,它很有可能是一个好的方案)
|
||||
Namespaces are one honking great idea -- let's do more of those!(命名空间非常有用,我们应当多加利用)
|
||||
1. Beautiful is better than ugly. (优美比丑陋好)
|
||||
2. Explicit is better than implicit.(清晰比晦涩好)
|
||||
3. Simple is better than complex.(简单比复杂好)
|
||||
4. Complex is better than complicated.(复杂比错综复杂好)
|
||||
5. Flat is better than nested.(扁平比嵌套好)
|
||||
6. Sparse is better than dense.(稀疏比密集好)
|
||||
7. Readability counts.(可读性很重要)
|
||||
8. Special cases aren't special enough to break the rules.(特殊情况也不应该违反这些规则)
|
||||
9. Although practicality beats purity.(但现实往往并不那么完美)
|
||||
10. Errors should never pass silently.(异常不应该被静默处理)
|
||||
11. Unless explicitly silenced.(除非你希望如此)
|
||||
12. In the face of ambiguity, refuse the temptation to guess.(遇到模棱两可的地方,不要胡乱猜测)
|
||||
13. There should be one-- and preferably only one --obvious way to do it.(肯定有一种通常也是唯一一种最佳的解决方案)
|
||||
14. Although that way may not be obvious at first unless you're Dutch.(虽然这种方案并不是显而易见的,因为你不是那个荷兰人[^1])
|
||||
15. Now is better than never.(现在开始做比不做好)
|
||||
16. Although never is often better than \*right\* now.(不做比盲目去做好[^2])
|
||||
17. If the implementation is hard to explain, it's a bad idea.(如果一个实现方案难于理解,它通常不是一个好的方案)
|
||||
18. If the implementation is easy to explain, it may be a good idea.(如果一个实现方案易于理解,它很有可能是一个好的方案)
|
||||
19. Namespaces are one honking great idea -- let's do more of those!(命名空间非常有用,我们应当多加利用)
|
||||
|
||||
[^1]:这里指的是 Python 之父 Guido van Rossumm。
|
||||
[^2]:极限编程中的YAGNI原则
|
||||
|
||||
|
|
Loading…
Reference in New Issue