动手学数据分析(3)——数据重构

本文为Datawhale8月组队学习——动手学数据分析课程的系列学习笔记。

Datawhale-动手学数据分析

数据来源

Kaggle小白入门首选练手项目——Kaggle-泰坦尼克号存活率

Ch2-2 数据重构

数据重构部分主要介绍了数据的合并变形分组

数据合并与拼接

数据的合并与拼接一般涉及四个方法/函数:

函数/方法 使用场景
concat() 可以在两个维度上拼接(外连接)
append() DataFrame/Series的纵向拼接
merge() 两个DataFrame的横向拼接(基于任意共有列)
join() 两个DataFrame的横向拼接(基于索引)

其中,concat和append方法倾向于简单的连接,merge和join则类似数据库中的连接。

连接方式

这里回顾一下数据库的几种连接方式:

连接方式 含义
内连接 两个表的交集,左表和右表都有的才显示出来
外连接(全连接) 两个表的并集,有的都显示,连接后没有的值默认为空值
左连接(左外连接) 以左表为基准,左表有的右表也有就显示,左表有的右表没有填空值
右连接(右外连接) 以右表为基准,右表有的左表也有就显示,右表有的左表没有填空值
交叉连接(笛卡尔积) 左表每行依此连接右表各行,左表有m行,右表有n行,则连接后有m×n行

concat

pd.concat以列表(List)形式传入需要连接的DataFrame,并可以设置连接的方向(axis),支持DataFrame/Series的组合,拼接方法默认为外连接(并集)。

1
2
3
# keys参数可以方便辨别索引相同的行来源
frames = [df1, df2, df3]
result = pd.concat(frames, keys=['x', 'y', 'z'])

append

即纵向拼接,补充部分样本。支持列数不同的表拼接,没有数据将默认为NaN。ignore_index参数可以用于重置拼接后的数字索引。

merge/join

一般用于表的横向连接,二者都支持上面提到的几种连接方式how='left'/'right'/'outer'/'inner'。主要差异在于join是靠索引进行连接的,而merge可以使用on指定连接的键(基准列)

merge也使用index作为键进行连接的方式:将left_indexright_index均置为True。

此外,二者默认的连接方式不同,merge默认为内连接,join默认为左连接。当二者遇到基准列重复项时会使用笛卡尔积(交叉连接)

merge函数有两种,一种是DataFrame的方法,一种是pandas自带的函数,二者的效果是一致的。

尽管join也有on参数,但只是更改了传入表中要连接的准则列。


关于几个问题的思考

问题1:任务四和五中需要用到表的纵向连接,为什么都要求使用DataFrame的append方法,如果只要求使用merge或者join可不可以完成任务四和任务五呢?

思考:merge和join很少用于表的纵向连接,一般认为它无法用于纵向连接,但经过探索,在实际操作中,可以借助外连接实现表的纵向连接,只需传入的on参数为所有列,即基准列为所有列。

1
2
3
4
5
6
7
8
9
10
11
# 尝试1
result_try = pd.merge(result_up,result_down,on='PassengerId',how = 'outer')
result_try
# 失败
# 因此,除了连接基准列其余列会直接横向合并,不管列名是否一致,同名自动加上_x/_y

# 尝试2
columns = list(result.columns)
result_try = pd.merge(result_up,result_down,on=columns,how = 'outer')
result_try
# 成功

这样的操作是有限制的,倘若出现重复的行,就会出现只保留一行的情况,并不建议使用。

问题2:如何实现交叉连接?(来自Datawhale成员的提问)

思路:用到此前提到的遇到基准列重复项时会使用笛卡尔积(交叉连接)的兴致,为两个表构造一个数值相同的列作为key。

1
2
3
df1['key']=1
df2['key']=1
pd.merge(df1,df2,on='key')

数据变形

主要用到stack()unstack()方法。

  • stack:最基础的变形函数,把Dataframe堆叠为二级索引的Series,可以看作将横向的索引放到纵向,参数level可指定变化的列索引是哪一层(或哪几层,需要列表)
  • unstack:stack的逆函数,可以把Series解回为DataFrame

二者都具有将表转置(横索引 ↔ 纵索引)的作用,更高级的方法参见pivot()

数据分组

GroupBy运行机制

pandas中的数据分组基于GroupBy,既可用于Series也可用于DataFrame,groupby()方法返回一个对应的没有经过计算的DataFrameGroupBy对象或SeriesGroupBy对象,该对象本身不会返回任何东西,只有当相应的方法被调用才会起作用。

基本使用方式:在通过groupby()方法得到GroupBy对象后,可以使用其自带的各种统计量计算方法,将返回各组(列)的相应统计量(DataFrame/Series形式)

其运行机制如下:

即:根据输入的分组依据,运行groupby进行分组,后续使用统计函数时,同时对各个组应用该统计函数,并返回相应的统计量再聚合到一起以DataFrame形式返回。

对分组对象使用head函数,返回的是每个组的前几行,而不是数据集前几行。

groupby支持依据多列进行分组,分组后使用统计函数时会出现多级索引,可以使用多次[]操作器进行提取

基于列的聚合操作——agg

agg()提供基于列的聚合操作。而groupby()可以看做是基于行,或者说index的聚合操作。

  • 可以传入列表
    • 可以直接输入完整的函数名:[np.sum, np.count]
    • 可以只输入统计函数名(字符串):[‘mean’, ‘sum’]
  • 可以传入dict——{列1:统计函数1,列2:统计函数2}
    • dict的values还可以为多个统计函数的列表
  • 结合rename使用,方便识别统计量进行后续分析
1
2
3
4
5
6
7
8
9
10
# 第一次尝试,可以使用numpy的函数
df.groupby('Sex').agg([np.mean,np.sum])['Survived'].rename(columns={'mean':'mean_sex','sum':'survived_sex'})

# 参考资料后第二次尝试
df.groupby('Pclass').agg(['mean','sum'])['Survived'].rename(columns={'mean':'mean_pclass','sum':'survived_pclass'})# 这里调用的方法应该是?

# 第三次尝试,参考课程答案,提示了agg可以传入dict以选择对其他每个特征的统计函数
df.groupby('Sex').agg({'Survived':'sum','Fare':'mean'})
# 再试试字典的value为func列表时
df.groupby('Sex').agg({'Survived':['sum','count'],'Fare':'mean'})

图片来源

PANDAS 数据合并与重塑

Datawhale 动手学数据分析答疑群

评论