查看原文
其他

开启机器学习的第一课:用Pandas进行数据分析

2018-02-24 新年新气象 AI科技大本营


翻译 | AI科技大本营

参与 | 林椿眄


本课程的目的并不是要开发一门全新的机器学习或数据分析的综合入门课程,也不是想借此来取代基础教育、在线/离线课程或一些专业和书籍。我们推出本系列文章是为了让初学者快速地回顾一些基础知识,并帮助你找到进一步学习的方向。

 

首先用简短通俗的语言回顾数学和机器学习的基础知识,并引用了一些其他资源,这种教学的方法与一些深度学习书籍类似。

 

此外,课程不单单注重理论的讲解,更强调完美地平衡理论与实践。因此,每个理论主题讲解后都有一个对应的练习专题,方便初学者通过自己动手来巩固所学的知识。此外,你还可以在参加学习课程期间,参加Kaggle举办的一些Inclass比赛。

 

 

我们的课程将会在OpenDataScience Slack社区提供一个讨论专栏。你需要填写此表格,通过我们的审核并得到邀请。

 

课程大纲如下:


1. 关于本课程

2. 课程作业

3. Pandas的一些主要方法演示

4. 用Pandas尝试预测电信客户流失率

5. 课程作业#1

6. 有用的一些资源

 

1. 关于课程


教学大纲

1. 用Pandas进行数据分析

2. 用Python进行数据的可视化

3. 分类,决策树和k最近邻算法

4. 线性分类和回归算法

5. Bagging算法和随机森林

6. 特征工程和特征选择

7. 无监督学习:主成分分析和聚类分析

8. Vowpal Wabbit:学习千兆字节的数据

9. 用Python进行时间序列分析

10. 梯度提升(gradient boosting)

 

社区

 

参加课程后,你将被邀请加入OpenDataScience Slack社区,有专业的老师在线指导,为你解答课程及作业上的问题。 你可以通过填写此表格来获得进入社区的资格,我们主要想了解每个人的一些关于数学背景和技能的问题。


 

预备知识


你需要具备的一些基础知识包括:微积分,线性代数,概率论和统计学的基础概念,以及Python编程技巧。


此外,“深度学习”一书中的第一部分知识也是不错的选择。你还可以在线学习各种相关的数学和Python在线课程(对于Python CodeAcademy来说)。Wiki上也能为你提供更多的帮助。

 

软件配置

你只需要安装Anaconda(针对Python 3.6)就能重现课程中的代码。在本课程的后续学习中,你还将需要安装其他类似Xgboost和Vowpal Wabbit这些依赖库。

 

你也可以使用Docker容器,它能帮助你自动安装所需的其他软件。有关更多信息可在相应的Wiki页面上找到。

 

2. 作业


  • 每篇作业都以Jupyter notebook的形式出现。作业任务就是填写缺少的代码片段,并在Google测验表单中回答相应的问题;

  • 每个作业任务都要在一个星期内完成的。

  • 请在OpenDataScience Slack社区中的#eng_mlcourse_open频道讨论课程相关的内容,包括课程文章及相应的作业,也可以在评论区进行讨论。

  • 作业的答案将以相应的Google的形式提交并最终发送。

 

3. Pandas主要方法演示

 

Pandas和数据可视化分析有许多很棒的教程。如果你想了解相应的主题,那么可以等待本系列的第3篇文章,我们将详细介绍机器学习的知识。

 

你可以用jupyter notebook来阅读下面的材料,你也可以使用Jupyter在本地复现文章中的代码。

 

Pandas是一个Python库,提供了丰富的数据分析方法。数据科学家经常使用Pandas来分析处理.csv,.tsv或.xlsx等表格数据。因此,诸如SQL数据库数据,使用Pandas来加载、处理并分析这样的表格数据将变得非常方便。此外,结合Matplotlib和Seaborn,Pandas提供了强大的数据可视化分析功能。

 

Pandas中的主要数据结构包括Series和DataFrame类。前者是针对一些特定数据类型的一种一维索引数组格式。后者是对相同类型的表格数据的一种二维数据结构,可以看作Series instances的字典形式。DataFrame类是非常贴近实际数据形式的一种数据结构:它的行对应于实例(对象,观察等),它的列对应于每个实例的特征。

 

我们将通过分析一个关于电信运营商客户流失率的数据集来演示Pandas数据分析的主要方法。首先,我们使用read_csv读取数据,并通过head方法来查看前5行:


import pandas as pd
import numpy as np

df = pd.read_csv('../../data/telecom_churn.csv')
df.head()



其中,每行对应于一个研究的对象,列是对象所对应的特征。

 

下面,我们来看看数据维度,要素名称和要素类型。

 

print(df.shape)


(3333, 20)


从输出中我们可以看到,该表格数据包含3333行和20列。


现在我们尝试使用columns打印出列名称:


print(df.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数据结构的一些通用信息:


print(df.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
None


其中bool,int64,float64和object是我们数据的类型特征。我们可以看到bool型(布尔)特征有1个,objec型特征有3个,数字类型特征(包括int64和float64)共有16个。同样地,我们还可以很容易地查看数据中是否存在缺失值。由于每列包含3333个观测值,这与我们之前得到的数据的维度是一样的,因此这里不存在缺失数据。

 

我们还可以用astype()方法来改变数据的列类型。应用这种方法,我们可以将churn的数据特征转化为int64类型:


df['Churn'] = df['Churn'].astype('int64')


 describe()方法用来描述每个数字特征(int64和float64类型)的基本统计信息:包括非缺失值的数量,均值,标准差,范围,中位数,0.25和0.75四分位数。

 

此外,要查看数据的非数字特征的统计信息,还必须在include参数中明确指出感兴趣的数据类型。

 

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


对于分类(object型)特征和布尔(bool型)特征,我们可以使用value_counts()方法来查看。下面我们查看下客户流失率Churn的分布情况:

 

df[‘Churn’].value_counts()


0    2850
1     483
Name: Churn, dtype: int64


可以看到,3333位用户中有2850位有忠诚用户,其对应的Churn为0。如果要计算相应用户所占的比例,可以将value_counts函数中的normalize参数设置为True即可:


df[‘Churn’'].value_counts(normalize=True)


0    0.855086
1    0.144914
Name: Churn, dtype: float64


Sorting


DataFrame数据结构可以对一个特定变量的值(如列)进行排序。例如,我们可以按日总费用排序(设置参数ascending= False,按降序排序):


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



此外,我们还可以对多列进行排序:


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


 

Indexing、Retrieving data


DataFrame数据结构可以通过不同的方式对数据进行索引。


你可以使用DataFrame ['Name']的结构来获得单个列,我们可以通过这个方法获得数据中用户流失率Churn所占的比例。


df[‘Churn’].mean()


0.14491449144914492


我们可以看出,客户流失率Churn达到14.5%,这对一家公司来说确实是非常糟糕的结果,因为高流失率会使公司破产。


用Bool值对列数据进行索引也是非常简便的,具体的实现为df [P(df 

['Name'])],其中P表示对Name这个列的每个元素进行检查所需某个逻辑条件。这种索引结果是获取DataFrame数据中对应列只满足P条件的行。

 

同样地,我们用这个方法可以获得以下问题的答案:


获得流失用户数值变量的平均值?


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


用户白天在电话上花费的时间(平均)是多少?


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


206.91407867494814


忠实用户(Churn == 0)中没有国际计划的国际长途电话的最大长度是多少?


df[(df ['Churn'] == 0) & (df ['International plan'] =='No')] ['Total intlminutes']. max()


18.899999999999999

 

DataFrame数据结构不仅可以按列名(标签)或行名(索引)进行索引,还可以通过序列号或行进行索引。


其中,loc()方法是用于按名称进行索引,我们假定“索引从0到5(包含索引值)的行以及从State到Area code标记(包含索引值)的列的值”,代码如下:


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


 

而iloc ()方法可以用于按数字进行索引。我们会假定“索引得到前三列中前五行的值,这种索引方式和Python切片方式是一样的,不会包含索引的最大值对应的项,代码如下:


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



如果想索引DataFrame数据中的第一行和最后一行,我们可以使用df [:1]或df [-1:]的形式。

 

将函数应用于数据中的单元格,列和行


使用apply()方法,将相应的函数应用于数据中的每列:


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


置参数axis=1,apply()方法也可以将函数应用到数据的每一行。在这种情况下,还可以结合Lambda函数一起使用将实现更强大的搜索功能。例如,如果我们需要选择以W开头的State项,我们可以这样做:


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



以{old_value:new_value}的字典形式作为参数,map ()方法可以实现替换数据每列中的值:


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



同样地,replace()方法也能实现同样地效果:


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


 

Grouping


Pandas中,对数据进行分组显示操作一般可以这样做:


df.groupby(by=grouping_columns)[columns_to_show]. function()


1. 首先,groupby()方法将以grouping_columns的值来划分数据,得到的结果将作为DataFrame新的索引。


2. 然后,选择感兴趣的列columns_to_show。如果不包含columns_to_show列的话,则将包含所有非groupby的子句。


3. 最后,将一个或多个函数应用于每个选定列来获取我们想要分组的结果。

 

我们根据流失率Churn的值对数据进行分组,并显示每个组中的三列的统计信息:


columns_to_show= ['Total day minutes', 'Total eve minutes', 'Total night minutes']
df.groupby(['Churn'])[columns_to_show].describe(percentiles=[])


同样地,我们可以用一个列表形式,将参数传递给agg()方法来实现同样的效果:


columns_to_show= ['Total day minutes', 'Total eve minutes', 'Total night minutes']
df.groupby(['Churn'])[columns_to_show].agg([np.mean,np.std, np.min, np.max])

 


Summarytables


如果想查看数据样本中Churn和International plan两个变量是如何分布的,我们可以使用crosstab()方法来构建一个简单的表格查看我们想要的内容:


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



pd.crosstab(df['Churn'], df['Voice mail plan'], normalize=True)



我们可以看到,忠实用户中的大多数人并没有使用额外的服务,如International plan或Vioce mail等。


这种表格形式有点类似于Excel的数据透视表。当然,Pandas中同样能实现数据透视表。你可以应用pivot_table()方法,设置如下参数来对你的数据进行进一步的分析:


  • value:用于数据透视的变量列表

  • index:用于数据分组的变量列表

  • aggfunc:用于数据透视的指标,如按数据的总和,平均数,最大值,最小值或其他值等进行数据透视分析


我们来看看area code平均每天白天和晚上的电话呼叫情况:


df.pivot_table(['Total day calls', 'Total eve calls', 'Total night calls'], ['Area code'], aggfunc='mean')


 

DataFrame数据转换


正如Pandas中许多方法一样,我们可以通过多种方式为DataFrame的列添加数据。


例如,如果想要计算所有用户的总呼叫次数,那么我们可以创建total_calls 的Series数据形式并将其粘贴到DataFrame中:


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', value=total_calls)
df.head()



我们还可以在不创建Series实例形式的情况下,为每列添加数据:


df['Totalcharge'] = df['Total day charge'] + df['Total eve charge'] + \ df['Total nightcharge']+
df['Total intlcharge']
df.head()


 

如果想要删除数据的某列或某行的话,我们可以使用drop()方法,并制定方法中必需的index和axis参数。如果要删除列,则设置参数axis=1;反之,删除行的话则axis=0。此外,inplace参数将决定是否更改原始的DataFrame数据:使用inplace = False时,drop方法不会更改现有DataFrame数据结构,并返回删除行或列后的新数据框。当inplace = True时,DataFrame的数据结构也将随之改变。


# get rid of just created columns
df.drop(['Total charge', 'Total calls'], axis=1, inplace=True)
# and here’s how you can delete rows
df.drop([1, 2]).head()


 

4.  实战项目:预测电信客户的流失率


首先,我们查看电信客户流失率churn与International plan变量之间的相关性。我们将使用crosstab()方法以及Seaborn进行可视化分析(下一篇文章将更详细地介绍可视化分析)。



# some imports and "magic" commands to set up plotting
%matplotlib inline import matplotlib.pyplot as plt
# pip install seaborn
import seaborn as sns
plt.rcParams['figure.figsize'] = (8, 6)
sns.countplot(x='International plan', hue='Churn', data=df);



我们看到,带International plan的客户流失率churn更高。这个结果产生的原因可能是国际长途电话费用的大幅度控制和管理不善所引起的,并导致电信客户的不满。

 

接下来,我们将查看另一个重要指标--客服电话的呼叫次数。同样地,我们还是通过crosstab来可视化地分析:


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



sns.countplot(x='Customerservice calls', hue='Churn', data=df)



在表中我们无法清楚地看到二者之间的关系,但从图中我们可以发现,客服呼叫次数达到4次时会导致客户流失率churn大幅地增加。


现在我们为DataFrame数据添加一个二元属性,令customer service calls > 3,来再次查看它与流失率churn之间的关系。


df['Many_service_calls']= (df['Customer service calls'] > 3).astype('int')
pd.crosstab(df['Many_service_calls'],df['Churn'], margins=True)
sns.countplot(x='Many_service_calls',hue='Churn', data=df)


 

下面,我们构建另一个表格,来查看Churn与International plan和新创建的Many_service_calls之间的相关性。


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


 

因此,我们可以看到当客服的呼叫数量少于4并且添加了International plan时,可以预测该客户是电信的忠诚用户,即Churn = 0。预测结果的可能期望值为85.8%,即我们预测错误的次数只有464 + 9次。我们通过非常简单的推理来得到这样一个好的结果(85.8%),这将作为下一步机器学习模型构建的基础。

 

在机器学习出现之前,数据分析看起来是多么复杂和繁琐的工作。而在我们的课程中,通过机器学习的方法我们可以对数据进行非常简单有效的分析,下面让我们回顾一下以上课程所涵盖的内容:


  • 样本中忠诚客户所占的比例为85.5%,换句话说,我们所构建的最简单的模型总是预测“忠实客户”在所有样本中所占比例约为85.5%。更进一步地说,后续模型所预测的准确性结果应该不低于这个数字,我们希望改善后的模型所得到的结果将会更高;


  • 这样一个简单模型的预测结果,可以用下面的公式表示:“International plan=True& Customer Service calls > 3 => Churn = 1, else Churn = 0”,我们得到的85.8%准确性刚刚超过85.5%水平。随后,我们将进一步讨论决策树,并找出如何仅仅基于输入数据来自动找到数据之间的相关性;


  • 没有应用机器学习方法,我们就已经可以得到这两个基准,这将成为我们构建后续模型的起点。如果后续的模型只能将预测结果小幅地提高(如只提高0.5%),那么我们还是选择用这种最简单的方式来构建我们的预测模型。


  • 在训练复杂模型之前,我们建议对数据进行适当的预处理,可视化地生成一些图来查看数据的结构并逐步验证我们的假设。此外,在实际应用中,我们通常都是尝试从最简单的机器学习模型开始的,然后进一步构建更复杂的解决方案。

 

5. 作业#1


在这次作业中,你将分析美国居民的UCI成人数据集,统计人口的信息。我们建议在完成Jupyter notebook中的任务,再回答Google表单中的10个问题。此外,在提交表单后,你也可以修改你的答案。任务的截止日期:2月11日23:59 CET

 

6. 有用的资源


  • 首先,也是最重要的资源,当然是Pandas的官方文档

  • 10分钟掌握Pandas

  • Pandas的cheatsheet (PDF版)

  • GitHub repos:“Pandas练习”和“有效使用Pandas”

  • scipy-lectures.org中关于pandas,numpy,matplotlib和scikit-learn的使用教程

 

作者信息:Yury Kashnitsky,Mail.Ru Group的数据科学家;Ekaterina Demidova,Segmento的数据科学家。文章的翻译和编辑包括:Yuanyuan Pao,Christina Butsko, Anastasia Manokhina, Sergey Isaev, and Artem Trunov.

 

原文链接:

https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-1-exploratory-data-analysis-with-pandas-de57880f1a68

 

招聘

新一年,AI科技大本营的目标更加明确,有更多的想法需要落地,不过目前对于营长来说是“现实跟不上灵魂的脚步”,因为缺人~~


所以,AI科技大本营要壮大队伍了,现招聘AI记者和资深编译,有意者请将简历投至:gulei@csdn.net,期待你的加入!


如果你暂时不能加入营长的队伍,也欢迎与营长分享你的精彩文章,投稿邮箱:suiling@csdn.net


AI科技大本营读者群(计算机视觉、NLP、Python、AI+金融方向)正在招募中,后台回复:读者群,联系营长,添加营长请备注姓名,研究方向。



☟☟☟点击 | 阅读原文 | 查看更多精彩内容

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存