0%

使用 Pandas 进行数据探索

本次实验通过分析电信运营商的客户离网率数据集来熟悉 Pandas 数据探索的常用方法,并构建一个预测客户离网率的简单模型。

Pandas 的主要方法

Pandas 是基于 NumPy 的一种工具,提供了大量数据探索的方法。Pandas 可以使用类似 SQL 的方式对 .csv、.tsv、.xlsx 等格式的数据进行处理分析。

Pandas 主要使用的数据结构是 Series 和 DataFrame 类。下面简要介绍下这两类:

  • Series 是一种类似于一维数组的对象,它由一组数据(各种 NumPy 数据类型)及一组与之相关的数据标签(即索引)组成。
  • DataFrame 是一个二维数据结构,即一张表格,其中每列数据的类型相同。你可以把它看成由 Series 实例构成的字典。

下面开始此次实验,我们将通过分析电信运营商的客户离网率数据集来展示 Pandas 的主要方法。

首先载入必要的库,即 NumPy 和 Pandas。

1
2
import numpy as np
import pandas as pd

通过 read_csv() 方法读取数据,然后使用 head() 方法查看前 5 行数据。

1
dataFrame = pd.read_csv('./data/telecom_churn.csv')

上图中的每行对应一位客户,每列对应客户的一个特征。

让我们查看一下该数据库的维度、特征名称和特征类型。

1
dataFrame.shape
(3333, 20)

上述结果表明,我们的列表包含 3333 行和 20 列。下面我们尝试打印列名。

1
dataFrame.columns
Index(['State', 'Account length', 'Area code', 'International plan',
       'Voice mail plan', 'Number vmail messages', 'Total day minutes',
       'Total day calls', 'Total day charge', 'Total eve minutes',
       'Total eve calls', 'Total eve charge', 'Total night minutes',
       'Total night calls', 'Total night charge', 'Total intl minutes',
       'Total intl calls', 'Total intl charge', 'Customer service calls',
       'Churn'],
      dtype='object')

我们还可以使用 info() 方法输出 DataFrame 的一些总体信息。

1
dataFrame.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3333 entries, 0 to 3332
Data columns (total 20 columns):
State                     3333 non-null object
Account length            3333 non-null int64
Area code                 3333 non-null int64
International plan        3333 non-null object
Voice mail plan           3333 non-null object
Number vmail messages     3333 non-null int64
Total day minutes         3333 non-null float64
Total day calls           3333 non-null int64
Total day charge          3333 non-null float64
Total eve minutes         3333 non-null float64
Total eve calls           3333 non-null int64
Total eve charge          3333 non-null float64
Total night minutes       3333 non-null float64
Total night calls         3333 non-null int64
Total night charge        3333 non-null float64
Total intl minutes        3333 non-null float64
Total intl calls          3333 non-null int64
Total intl charge         3333 non-null float64
Customer service calls    3333 non-null int64
Churn                     3333 non-null bool
dtypes: bool(1), float64(8), int64(8), object(3)
memory usage: 498.1+ KB

boolint64float64object 是该数据库特征的数据类型。这一方法同时也会显示是否有缺失值,上述结果表明在该数据集中不存在缺失值,因为每列都包含 3333 个观测,和我们之前使用 shape 方法得到的数字是一致的。

astype() 方法可以更改列的类型,下列公式将 Churn 离网率 特征修改为 int64 类型。

1
2
3
4
df = dataFrame
df.head(5)
df['Churn'] = df['Churn'].astype('int64')
df.head(5)

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
0 KS 128 415 No Yes 25 265.1 110 45.07 197.4 99 16.78 244.7 91 11.01 10.0 3 2.70 1 0
1 OH 107 415 No Yes 26 161.6 123 27.47 195.5 103 16.62 254.4 103 11.45 13.7 3 3.70 1 0
2 NJ 137 415 No No 0 243.4 114 41.38 121.2 110 10.30 162.6 104 7.32 12.2 5 3.29 0 0
3 OH 84 408 Yes No 0 299.4 71 50.90 61.9 88 5.26 196.9 89 8.86 6.6 7 1.78 2 0
4 OK 75 415 Yes No 0 166.7 113 28.34 148.3 122 12.61 186.9 121 8.41 10.1 3 2.73 3 0

describe() 方法可以显示数值特征(int64float64)的基本统计学特性,如未缺失值的数值、均值、标准差、范围、四分位数等。

1
df.describe()

Account length Area code Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
count 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000 3333.000000
mean 101.064806 437.182418 8.099010 179.775098 100.435644 30.562307 200.980348 100.114311 17.083540 200.872037 100.107711 9.039325 10.237294 4.479448 2.764581 1.562856 0.144914
std 39.822106 42.371290 13.688365 54.467389 20.069084 9.259435 50.713844 19.922625 4.310668 50.573847 19.568609 2.275873 2.791840 2.461214 0.753773 1.315491 0.352067
min 1.000000 408.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 23.200000 33.000000 1.040000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 74.000000 408.000000 0.000000 143.700000 87.000000 24.430000 166.600000 87.000000 14.160000 167.000000 87.000000 7.520000 8.500000 3.000000 2.300000 1.000000 0.000000
50% 101.000000 415.000000 0.000000 179.400000 101.000000 30.500000 201.400000 100.000000 17.120000 201.200000 100.000000 9.050000 10.300000 4.000000 2.780000 1.000000 0.000000
75% 127.000000 510.000000 20.000000 216.400000 114.000000 36.790000 235.300000 114.000000 20.000000 235.300000 113.000000 10.590000 12.100000 6.000000 3.270000 2.000000 0.000000
max 243.000000 510.000000 51.000000 350.800000 165.000000 59.640000 363.700000 170.000000 30.910000 395.000000 175.000000 17.770000 20.000000 20.000000 5.400000 9.000000 1.000000

通过 include 参数显式指定包含的数据类型,可以查看非数值特征的统计数据。

1
df.describe(include=['object', 'bool'])

State International plan Voice mail plan
count 3333 3333 3333
unique 51 2 2
top WV No No
freq 106 3010 2411

value_counts() 方法可以查看类别(类型为 object )和布尔值(类型为 bool )特征。让我们看下 Churn 离网率 的分布。

1
df['Churn'].value_counts()
0    2850
1     483
Name: Churn, dtype: int64

上述结果表明,在 3333 位客户中, 2850 位是忠实客户,他们的 Churn 值为 0。调用 value_counts() 函数时,加上 normalize=True 参数可以显示比例。

1
df['Churn'].value_counts(normalize=True)
0    0.855086
1    0.144914
Name: Churn, dtype: float64

排序

DataFrame 可以根据某个变量的值(也就是列)排序。比如,根据每日消费额排序(设置 ascending=False 倒序排列)。

1
df.sort_values(by='Total day charge', ascending=False).head(5)

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
365 CO 154 415 No No 0 350.8 75 59.64 216.5 94 18.40 253.9 100 11.43 10.1 9 2.73 1 1
985 NY 64 415 Yes No 0 346.8 55 58.96 249.5 79 21.21 275.4 102 12.39 13.3 9 3.59 1 1
2594 OH 115 510 Yes No 0 345.3 81 58.70 203.4 106 17.29 217.5 107 9.79 11.8 8 3.19 1 1
156 OH 83 415 No No 0 337.4 120 57.36 227.4 116 19.33 153.9 114 6.93 15.8 7 4.27 0 1
605 MO 112 415 No No 0 335.5 77 57.04 212.5 109 18.06 265.0 132 11.93 12.7 8 3.43 2 1

此外,还可以根据多个列的数值排序。下面函数实现的功能为:先按 Churn 离网率 升序排列,再按 Total day charge 每日总话费 降序排列,优先级 Churn > Tatal day charge。

1
df.sort_values(by=['Churn', 'Total day charge'], ascending=[True, False]).head()

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
688 MN 13 510 No Yes 21 315.6 105 53.65 208.9 71 17.76 260.1 123 11.70 12.1 3 3.27 3 0
2259 NC 210 415 No Yes 31 313.8 87 53.35 147.7 103 12.55 192.7 97 8.67 10.1 7 2.73 3 0
534 LA 67 510 No No 0 310.4 97 52.77 66.5 123 5.65 246.5 99 11.09 9.2 10 2.48 4 0
575 SD 114 415 No Yes 36 309.9 90 52.68 200.3 89 17.03 183.5 105 8.26 14.2 2 3.83 1 0
2858 AL 141 510 No Yes 28 308.0 123 52.36 247.8 128 21.06 152.9 103 6.88 7.4 3 2.00 1 0

索引和获取数据

DataFrame 可以以不同的方式进行索引。

使用 DataFrame['Name'] 可以得到一个单独的列。比如,离网率有多高?

1
df['Churn'].mean()
0.14491449144914492

对一家公司而言,14.5% 的离网率是一个很糟糕的数据,这么高的离网率可能导致公司破产。

布尔值索引同样很方便,语法是 df[P(df['Name'])],P 是在检查 Name 列每个元素时所使用的逻辑条件。这一索引的输出是 DataFrame 的 Name 列中满足 P 条件的行。

让我们使用布尔值索引来回答这样以下问题:离网用户的数值变量的均值是多少?

1
df[df['Churn'] == 1].mean()
Account length            102.664596
Area code                 437.817805
Number vmail messages       5.115942
Total day minutes         206.914079
Total day calls           101.335404
Total day charge           35.175921
Total eve minutes         212.410145
Total eve calls           100.561077
Total eve charge           18.054969
Total night minutes       205.231677
Total night calls         100.399586
Total night charge          9.235528
Total intl minutes         10.700000
Total intl calls            4.163561
Total intl charge           2.889545
Customer service calls      2.229814
Churn                       1.000000
dtype: float64

离网用户在白天打电话的总时长的均值是多少?

1
df[df['Churn'] == 1]['Total day minutes'].mean()
206.91407867494823

未使用国际套餐(International plan == NO)的忠实用户(Churn == 0)所打的最长的国际长途是多久?

1
2
df[(df['Churn'] == 1) & (df['International plan'] == 'No')]['Total intl minutes'].max()
df[(df['Churn'] == 0) & (df['International plan'] == 'No')]['Total intl minutes'].max()
18.9

DataFrame 可以通过列名、行名、行号进行索引。loc 方法为通过名称索引,iloc 方法为通过数字索引。

通过 loc 方法输出 0 至 5 行、State 州 至 Area code 区号 的数据。

1
df.loc[0:5, ['State','Area code']]

State Area code
0 KS 415
1 OH 415
2 NJ 415
3 OH 408
4 OK 415
5 AL 510

通过 ilo 方法输出前 5 行的前 3 列数据(和典型的 Python 切片一样,不含最大值)。

1
df.iloc[0:5, 0:3]

State Account length Area code
0 KS 128 415
1 OH 107 415
2 NJ 137 415
3 OH 84 408
4 OK 75 415

df[:1]df[-1:] 可以得到 DataFrame 的首行和末行。

1
df[-2:]

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
3331 CT 184 510 Yes No 0 213.8 105 36.35 159.6 84 13.57 139.2 137 6.26 5.0 10 1.35 2 0
3332 TN 74 415 No Yes 25 234.4 113 39.85 265.9 82 22.60 241.4 77 10.86 13.7 4 3.70 0 0

应用函数到单元格、列、行

下面通过 apply() 方法应用函数 max 至每一列,即输出每列的最大值。

1
df.apply(np.max)
State                        WY
Account length              243
Area code                   510
International plan          Yes
Voice mail plan             Yes
Number vmail messages        51
Total day minutes         350.8
Total day calls             165
Total day charge          59.64
Total eve minutes         363.7
Total eve calls             170
Total eve charge          30.91
Total night minutes         395
Total night calls           175
Total night charge        17.77
Total intl minutes           20
Total intl calls             20
Total intl charge           5.4
Customer service calls        9
Churn                         1
dtype: object

apply() 方法也可以应用函数至每一行,指定 axis=1 即可。在这种情况下,使用 lambda 函数十分方便。比如,下面函数选中了所有以 W 开头的州。

1
df[df['State'].apply(lambda state: state[0] == 'W')].head()

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
9 WV 141 415 Yes Yes 37 258.6 84 43.96 222.0 111 18.87 326.4 97 14.69 11.2 5 3.02 0 0
26 WY 57 408 No Yes 39 213.0 115 36.21 191.1 112 16.24 182.7 115 8.22 9.5 3 2.57 0 0
44 WI 64 510 No No 0 154.0 67 26.18 225.8 118 19.19 265.3 86 11.94 3.5 3 0.95 1 0
49 WY 97 415 No Yes 24 133.2 135 22.64 217.2 58 18.46 70.6 79 3.18 11.0 3 2.97 1 0
54 WY 87 415 No No 0 151.0 83 25.67 219.7 116 18.67 203.9 127 9.18 9.7 3 2.62 5 1

map() 方法可以通过一个 {old_value:new_value} 形式的字典替换某一列中的值。

1
2
3
d = {'No': False, 'Yes': True}
df['International plan'] = df['International plan'].map(d)
df.head()

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
0 KS 128 415 False Yes 25 265.1 110 45.07 197.4 99 16.78 244.7 91 11.01 10.0 3 2.70 1 0
1 OH 107 415 False Yes 26 161.6 123 27.47 195.5 103 16.62 254.4 103 11.45 13.7 3 3.70 1 0
2 NJ 137 415 False No 0 243.4 114 41.38 121.2 110 10.30 162.6 104 7.32 12.2 5 3.29 0 0
3 OH 84 408 True No 0 299.4 71 50.90 61.9 88 5.26 196.9 89 8.86 6.6 7 1.78 2 0
4 OK 75 415 True No 0 166.7 113 28.34 148.3 122 12.61 186.9 121 8.41 10.1 3 2.73 3 0

当然,使用 repalce() 方法一样可以达到替换的目的。

1
2
df = df.replace({'Voice mail plan': d})
df.head()

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes Total eve calls Total eve charge Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn
0 KS 128 415 False True 25 265.1 110 45.07 197.4 99 16.78 244.7 91 11.01 10.0 3 2.70 1 0
1 OH 107 415 False True 26 161.6 123 27.47 195.5 103 16.62 254.4 103 11.45 13.7 3 3.70 1 0
2 NJ 137 415 False False 0 243.4 114 41.38 121.2 110 10.30 162.6 104 7.32 12.2 5 3.29 0 0
3 OH 84 408 True False 0 299.4 71 50.90 61.9 88 5.26 196.9 89 8.86 6.6 7 1.78 2 0
4 OK 75 415 True False 0 166.7 113 28.34 148.3 122 12.61 186.9 121 8.41 10.1 3 2.73 3 0

增减 DataFrame 的行列

在 DataFrame 中新增列有很多方法,比如,使用 insert()方法添加列,为所有用户计算总的 Total calls 电话量。

1
2
3
4
5
6
total_calls = df['Total day calls'] + df['Total eve calls'] + \
df['Total night calls'] + df['Total intl calls']

df.insert(loc=len(df.columns), column='Total calls 2', value=total_calls)

df.head()

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes ... Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn Total calls Total calls 2
0 KS 128 415 False True 25 265.1 110 45.07 197.4 ... 244.7 91 11.01 10.0 3 2.70 1 0 303 303
1 OH 107 415 False True 26 161.6 123 27.47 195.5 ... 254.4 103 11.45 13.7 3 3.70 1 0 332 332
2 NJ 137 415 False False 0 243.4 114 41.38 121.2 ... 162.6 104 7.32 12.2 5 3.29 0 0 333 333
3 OH 84 408 True False 0 299.4 71 50.90 61.9 ... 196.9 89 8.86 6.6 7 1.78 2 0 255 255
4 OK 75 415 True False 0 166.7 113 28.34 148.3 ... 186.9 121 8.41 10.1 3 2.73 3 0 359 359

5 rows × 22 columns

上面的代码创建了一个中间 Series 实例,即 tatal_calls,其实可以在不创造这个实例的情况下直接添加列。

1
2
3
df['Total charge'] = df['Total day charge'] + df['Total eve charge'] + \
df['Total night charge'] + df['Total intl charge']
df.head()

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes ... Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn Total calls Total calls 2 Total charge
0 KS 128 415 False True 25 265.1 110 45.07 197.4 ... 91 11.01 10.0 3 2.70 1 0 303 303 75.56
1 OH 107 415 False True 26 161.6 123 27.47 195.5 ... 103 11.45 13.7 3 3.70 1 0 332 332 59.24
2 NJ 137 415 False False 0 243.4 114 41.38 121.2 ... 104 7.32 12.2 5 3.29 0 0 333 333 62.29
3 OH 84 408 True False 0 299.4 71 50.90 61.9 ... 89 8.86 6.6 7 1.78 2 0 255 255 66.80
4 OK 75 415 True False 0 166.7 113 28.34 148.3 ... 121 8.41 10.1 3 2.73 3 0 359 359 52.09

5 rows × 23 columns

使用 drop() 方法删除列和行。

1
2
df1 = df.drop([1, 2])
df1.head()

State Account length Area code International plan Voice mail plan Number vmail messages Total day minutes Total day calls Total day charge Total eve minutes ... Total night minutes Total night calls Total night charge Total intl minutes Total intl calls Total intl charge Customer service calls Churn Total calls Total charge
0 KS 128 415 False True 25 265.1 110 45.07 197.4 ... 244.7 91 11.01 10.0 3 2.70 1 0 303 75.56
3 OH 84 408 True False 0 299.4 71 50.90 61.9 ... 196.9 89 8.86 6.6 7 1.78 2 0 255 66.80
4 OK 75 415 True False 0 166.7 113 28.34 148.3 ... 186.9 121 8.41 10.1 3 2.73 3 0 359 52.09
5 AL 118 510 True False 0 223.4 98 37.98 220.6 ... 203.9 118 9.18 6.3 6 1.70 0 0 323 67.61
6 MA 121 510 False True 24 218.2 88 37.09 348.5 ... 212.6 118 9.57 7.5 7 2.03 3 0 321 78.31

5 rows × 22 columns

对上述代码的部分解释:

  • 将相应的索引 ['Total charge', 'Total calls']axis 参数(1 表示删除列,0 表示删除行,默认值为 0)传给 drop
  • inplace 参数表示是否修改原始 DataFrame (False 表示不修改现有 DataFrame,返回一个新 DataFrame,True 表示修改当前 DataFrame)。

预测离网率

首先,通过 crosstab() 方法构建一个交叉表来查看 International plan 国际套餐 变量和 Churn 离网率 的相关性,同时使用 countplot() 方法构建计数直方图来可视化结果。

1
2
3
# 加载模块,配置绘图
import matplotlib.pyplot as plt
import seaborn as sns
1
sns.countplot(x='International plan', hue='Churn', data=df)
<matplotlib.axes._subplots.AxesSubplot at 0x11f0c8450>

png

上图表明,开通了国际套餐的用户的离网率要高很多,这是一个很有趣的观测结果。也许,国际电话高昂的话费让客户很不满意。

同理,查看 Customer service calls 客服呼叫 变量与 Chunrn 离网率 的相关性,并可视化结果。

1
pd.crosstab(df['Churn'], df['Customer service calls'], margins=True)

Customer service calls 0 1 2 3 4 5 6 7 8 9 All
Churn
0 605 1059 672 385 90 26 8 4 1 0 2850
1 92 122 87 44 76 40 14 5 1 2 483
All 697 1181 759 429 166 66 22 9 2 2 3333
1
sns.countplot(x='Customer service calls', hue='Churn', data=df)
<matplotlib.axes._subplots.AxesSubplot at 0x11cb70d50>

png

上图表明,在客服呼叫 4 次之后,客户的离网率显著提升。

为了更好的突出 Customer service call 客服呼叫 和 Churn 离网率 的关系,可以给 DataFrame 添加一个二元属性 Many_service_calls,即客户呼叫超过 3 次(Customer service calls > 3)。看下它与离网率的相关性,并可视化结果。

1
2
3
df['Many_service_calls'] = (df['Customer service calls'] > 3).astype('int')

pd.crosstab(df['Many_service_calls'], df['Churn'], margins=True)

Churn 0 1 All
Many_service_calls
0 2721 345 3066
1 129 138 267
All 2850 483 3333
1
sns.countplot(x='Many_service_calls', hue='Churn', data=df)
<matplotlib.axes._subplots.AxesSubplot at 0x11ce1c850>

png

现在我们可以创建另一张交叉表,将 Churn 离网率 与 International plan 国际套餐 及新创建的 Many_service_calls 多次客服呼叫 关联起来。

1
pd.crosstab(df['Many_service_calls'] & df['International plan'], df['Churn'])

Churn 0 1
row_0
False 2841 464
True 9 19

上表表明,在客服呼叫次数超过 3 次并且已办理 International Plan 国际套餐 的情况下,预测一名客户不忠诚的准确率(Accuracy)可以达到 85.8%,计算公式如下:

$$准确率(Accuracy)=\frac{TP+TN}{TP+TN+FP+FN}=\frac{2841+19}{2841+9+19+464}\times100%$$

其中,TP 表示将 True 预测为 True 的数量,TN 表示将 Flase 预测为 Flase 的数量,FP 表示将 Flase 预测为 True 的数量,FN 表示将 True 预测为 Flase 的数量。

复习一下本次实验的内容:

  • 样本中忠实客户的份额为 85.5%。这意味着最简单的预测「忠实客户」的模型有 85.5% 的概率猜对。也就是说,后续模型的准确率(Accuracy)不应该比这个数字少,并且很有希望显著高于这个数字。
  • 基于一个简单的「(客服呼叫次数 > 3) & (国际套餐 = True) => Churn = 1, else Churn = 0」规则的预测模型,可以得到 85.8% 的准确率。以后我们将讨论决策树,看看如何仅仅基于输入数据自动找出类似的规则,而不需要我们手工设定。我们没有应用机器学习方法就得到了两个准确率(85.5% 和 85.8%),它们可作为后续其他模型的基线。如果经过大量的努力,我们仅将准确率提高了 0.5%,那么我们努力的方向可能出现了偏差,因为仅仅使用一个包含两个限制规则的简单模型就已提升了 0.3% 的准确率。
  • 在训练复杂模型之前,建议预处理一下数据,绘制一些图表,做一些简单的假设。此外,在实际任务上应用机器学习时,通常从简单的方案开始,接着尝试更复杂的方案。

实验总结

本次实验使用 Pandas 对数据进行了一定程度的分析和探索,交叉表、透视表等方法的运用将使你在数据探索过程中事半功倍。

Linux

1
wget https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh

MacOS安装Anaconda

conda配置文件

位置
~/.condarc

配置zsh环境变量
export PATH=/Applications/anaconda3/bin:$PATH

conda镜像源

添加镜像指令(亦可以直接在配置文件中添加)
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/

设置搜索时显示通道地址
conda config --set show_channel_urls yes

shell虚拟环境

取消命令行前出现的(base)

  • 每次在命令行通过conda deactivate退出base环境回到系统自动的环境

  • conda config --set auto_activate_base false

安装TensorFlow环境

新建一个tf环境
conda create -n tensorflow python=3.7

进入tf环境
conda activate tensorflow

pip安装tf
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tensorflow

指定版本安装
conda install tensorflow=1.4
pip install tensorflow==1.4

卸载

安装clean包
conda install anaconda-clean

文件进行删除
anaconda-clean

删除Anaconda主文件包(默认路径)
/opt/anaconda3

矩阵连乘 (Multiplying a sequence of matrices)

我们想从两个以上矩阵连乘的式子中得到最佳的次序,使得计算时乘法运算最少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class DynamicProgramming {

private static final int N = 5;

public static void main(String[] args) {
int[][] C = new int[N][N];
int[][] S = new int[N][N];
int[] r = {4, 5, 3, 6, 4, 5};

for (int i = 0; i < N; i++) {// 初始化 最小乘运算数组
for (int j = 0; j < N; j++) {
C[i][j] = 0;
}
}

for (int i = 0; i < N; i++) {// 初始化 分割位置
for (int j = 0; j < N; j++) {
S[i][j] = 0;
}
}

for (int p = 2; p <= N; p++) {// P 为间隔
for (int i = 0, j; i < N - p + 1; i++) {// i 为起始值
j = i + p - 1;// j 为终止值
C[i][j] = C[i][i] + C[i + 1][j] + r[i] * r[i + 1] * r[j + 1];// 赋 C[i][j] 的初值方便下文比较取得最小值
S[i][j] = i;
for (int k = i; k< j; k++) {// k 为中间分割值
int temp = C[i][k] + C[k + 1][j] + r[i] * r[k + 1] * r[j + 1];

if (temp < C[i][j]) {// 取得每次分割后的最小值
C[i][j] = temp;
S[i][j] = k;
}
}
}
}

for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
System.out.print(C[i][j] + "\t");
}
System.out.println();
}

System.out.println();

for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (j > i) System.out.print(S[i][j] + 1 + "\t");
else System.out.print(0 + "\t");
}
System.out.println();
}
}
}

/* result
0 60 132 180 252
0 0 90 132 207
0 0 0 72 132
0 0 0 0 120
0 0 0 0 0

0 1 2 2 2
0 0 2 2 2
0 0 0 3 4
0 0 0 0 4
0 0 0 0 0

(A1 * A2) * ((A3 * A4) * A5)
*/

最长公共子序列 (Longest Common Subsequence)

给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,”ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

示例:

输入:text1 = “abcde”, text2 = “ace”
输出:3
解释:最长公共子序列是 “ace”,它的长度为 3。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class DynamicProgramming {

public static void main(String[] args) {
String A = "xzyzzyx";
String B = "zxyyzxz";
String res = "";

int[][] dp = new int[A.length() + 1][B.length() + 1];

for (int i = 0; i <= A.length(); i++) {
for (int j = 0; j <= B.length(); j++) {
if (i == 0 || j == 0) {
dp[i][j] = 0;
continue;
}

if (A.charAt(i - 1) == B.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
continue;
}

if (A.charAt(i - 1) != B.charAt(j - 1)) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
continue;
}
}
}

int row = A.length();
int col = B.length();

while (row > 0 && col > 0) {
if (dp[row - 1][col - 1] < dp[row][col]) {
res = res + A.charAt(row - 1);
row--; col--;
} else {
row--;
}
}

System.out.println("DP matrix is below: ");
for (int i = 0; i <= A.length(); i++) {
for (int j = 0; j <= B.length(); j++) {
System.out.print(dp[i][j] + " ");
}
System.out.println();
}

System.out.print("One of th LCS is: ");
for (int i = res.length() - 1; i >= 0; i--) {
System.out.print(res.charAt(i));
}
}
}

/* result

DP matrix is below:
0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1
0 1 1 1 1 2 2 2
0 1 1 2 2 2 2 2
0 1 1 2 2 3 3 3
0 1 1 2 2 3 3 4
0 1 1 2 3 3 3 4
0 1 2 2 3 3 4 4

One of th LCS is: xyzx

*/

典型的动态规划问题

首先声明一个以字符串A为行B为列的二维数组。

  • i=0j=0dp[i][j]=0
  • ai=bidp[i][j]=dp[i-1][j-1](对应位置相等的话公共子串在row-1,col-1的情况下加1.)
  • ai!=bidp[i][j]=max{dp[i-1][j], dp[i][j-1]}(不想等则取字符串A,B上一个相邻字符最长公共子串.)

最后显示一个最长子串时必须从后向前判断,顺序判断出的可能不是最长的子串。

Numpy训练实例

绘制Sigmoid函数与ReLU函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import numpy as np
import matplotlib.pyplot as plt

# 设置图片大小
plt.figure(figsize=(8, 3))

# x为1维数组
x = np.arange(-10, 10, 0.1)

# Sigmoid函数
s = 1. / (1 + np.exp(-x))

# ReLU函数
y = np.clip(x, a_min=0., a_max=None)

# 绘制Sigmoid函数
f = plt.subplot(121)
plt.plot(x, s, color='r')
plt.text(-5, 0.9, r'$y=\sigma(x)$', fontsize=13)
currentAxis = plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)

# 绘制ReLU函数
f = plt.subplot(122)
plt.plot(x, y, color='g')
plt.text(-3, 9, r'$y=ReLU(x)$', fontsize=13)
currentAxis = plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)

plt.show()