16机器学习开放基础课程--使用 Pandas 进行数据探索

使用 Pandas 进行数据探索

Pandas 的主要方法

import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
df = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/telecom_churn.csv')
df.head()

上图中的每行对应一位客户,每列对应客户的一个特征。
查看一下该数据库的维度

df.shape

打印列名

df.columns

输出 DataFrame 的一些总体信息

df.info()

bool、int64、float64 和 object 是该数据库特征的数据类型。这一方法同时也会显示是否有缺失值,上述结果表明在该数据集中不存在缺失值,因为每列都包含 3333 个观测,和我们之前使用 shape 方法得到的数字是一致的。
astype() 方法可以更改列的类型,下列公式将 Churn 离网率 特征修改为 int64 类型。

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

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

df.describe()

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

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

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

df['Churn'].value_counts()

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

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

排序

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

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

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

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

索引和获取数据

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

df['Churn'].mean()

对一家公司而言,14.5% 的离网率是一个很糟糕的数据,这么高的离网率可能导致公司破产。
布尔值索引同样很方便,语法是 df[P(df['Name'])],P 是在检查 Name 列每个元素时所使用的逻辑条件。这一索引的输出是 DataFrame 的 Name 列中满足 P 条件的行。
使用布尔值索引来回答这样以下问题:离网用户的数值变量的均值是多少?

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

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

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

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

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

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

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

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

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

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

df[-1:]

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

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

df.apply(np.max)

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

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

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

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

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

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

分组(Groupby)

Pandas 下分组数据的一般形式为:

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

上述函数的解释:
groupby() 方法根据 grouping_columns 的值进行分组。
接着,选中感兴趣的列(columns_to_show)。若不包括这一项,那么就会选中所有非 groupby 列(即除 grouping_colums 外的所有列)。
最后,应用一个或多个函数(function)。

根据 Churn 离网率 变量的值对数据进行分组,显示每组的统计数据。

columns_to_show = ['Total day minutes', 'Total eve minutes',
                   'Total night minutes']

df.groupby(['Churn'])[columns_to_show].describe(percentiles=[])

将一些函数传给 agg(),通过 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])

汇总表

透视表(Pivot Table)是电子表格程序和其他数据探索软件中一种常见的数据汇总工具。它根据一个或多个键对数据进行聚合,并根据行和列上的分组将数据分配到各个矩形区域中。
通过 pivot_table() 方法可以建立透视表,其参数如下:
values 表示需要计算的统计数据的变量列表
index 表示分组数据的变量列表
aggfunc 表示需要计算哪些统计数据,例如,总和、均值、最大值、最小值等。

通过 pivot_table() 方法查看不同区号下白天、夜晚、深夜的电话量的均值。

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

交叉表(Cross Tabulation)是一种用于计算分组频率的特殊透视表,在 Pandas 中一般使用 crosstab() 方法构建交叉表。
构建一个交叉表查看样本的 Churn 离网率 和 International plan 国际套餐 的分布情况。

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

构建一个交叉表查看 Churn 离网率 和 Voice mail plan 语音邮件套餐 的分布情况。

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

上述结果表明,大部分用户是忠实用户,同时他们并不使用额外的服务(国际套餐、语音邮件)。

增减 DataFrame 的行列

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

total_calls = df['Total day calls'] + df['Total eve calls'] + \
    df['Total night calls'] + df['Total intl calls']
# loc 参数是插入 Series 对象后选择的列数
# 设置为 len(df.columns)以便将计算后的 Total calls 粘贴到最后一列
df.insert(loc=len(df.columns), column='Total calls', value=total_calls)

df.head()

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

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

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

# 移除先前创捷的列
df.drop(['Total charge', 'Total calls'], axis=1, inplace=True)
# 删除行
df.drop([1, 2]).head()

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

预测离网率

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

# 加载模块,配置绘图
import matplotlib.pyplot as plt
import seaborn as sns

sns.countplot(x='International plan', hue='Churn', data=df)

上图表明,开通了国际套餐的用户的离网率要高很多,这是一个很有趣的观测结果。也许,国际电话高昂的话费让客户很不满意。
同理,查看 Customer service calls 客服呼叫 变量与 Chunrn 离网率 的相关性,并可视化结果。

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

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

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'])

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


image.png

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

人口收入普查数据探索

import warnings
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
warnings.filterwarnings('ignore')

data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
data.head()

数据集中有多少男性和女性?

data['sex'].value_counts()

数据集中女性的平均年龄是多少?

data[data['sex'] == 'Female']['age'].mean()

数据集中德国公民的比例是多少?

float((data['native-country'] == 'Germany').sum()) / data.shape[0]

年收入超过 50K 和低于 50K 人群年龄的平均值和标准差是多少?

ages1 = data[data['salary'] == '>50K']['age']
ages2 = data[data['salary'] == '<=50K']['age']
print("The average age of the rich: {0} +- {1} years, poor - {2} +- {3} years.".format(
    round(ages1.mean()), round(ages1.std(), 1),
    round(ages2.mean()), round(ages2.std(), 1)))

年收入超过 50K 的人群是否都接受过高中以上教育?

data[data['salary'] == '>50K']['education'].unique()  # No

使用 groupby 和 describe 统计不同种族和性别人群的年龄分布数据。

for (race, sex), sub_df in data.groupby(['race', 'sex']):
    print("Race: {0}, sex: {1}".format(race, sex))
    print(sub_df['age'].describe())

统计男性高收入人群中已婚和未婚(包含离婚和分居)人群各自所占数量。

data[(data['sex'] == 'Male') &
     (data['marital-status'].isin(['Never-married',
                                   'Separated', 'Divorced']))]['salary'].value_counts()
data[(data['sex'] == 'Male') &
     (data['marital-status'].str.startswith('Married'))]['salary'].value_counts()
data['marital-status'].value_counts()

统计数据集中最长周工作小时数及对应的人数,并计算该群体中收入超过 50K 的比例。

max_load = data['hours-per-week'].max()
print("Max time - {0} hours./week.".format(max_load))

num_workaholics = data[data['hours-per-week'] == max_load].shape[0]
print("Total number of such hard workers {0}".format(num_workaholics))

rich_share = float(data[(data['hours-per-week'] == max_load)
                        & (data['salary'] == '>50K')].shape[0]) / num_workaholics
print("Percentage of rich among them {0}%".format(int(100 * rich_share)))

计算各国超过和低于 50K 人群各自的平均周工作时长。

# 方法一
for (country, salary), sub_df in data.groupby(['native-country', 'salary']):
    print(country, salary, round(sub_df['hours-per-week'].mean(), 2))
# 方法二
pd.crosstab(data['native-country'], data['salary'],
            values=data['hours-per-week'], aggfunc=np.mean).T

你可能感兴趣的:(16机器学习开放基础课程--使用 Pandas 进行数据探索)