楔子
笔者曾经遇到过两种形式的数据。 那时,我确实不好住了。 最后解决了,方法不优雅,而且效率也不高。 如果想提高效率,就必须使用pandas提供的方法。 pandas一定可以作为一个强大的库优雅地解决。 当时用自己的方法解决后,就没有了。 但是最近又遇到了当时的情况,所以决定优雅地解决。 最后努力,总算找到了解决办法。 首先,让我们来看看当时难以居住的笔者的两种格式的数据和需求。
需求1 :
这些格式的数据包括:
科目名成绩
kwdhc国语90
kwdhc数学90
kwdhc英语90
dddlf国语91
dddlf数学91
dddlf英语91
xndfj国语92
xndfj数学92
xndfj英语92
我们会变成下面这样
姓名、国语、数学、英语
kwdhc 90 90 90
dddlf 91 91 91
xndfj 92 92 92
需求2 :
年龄兴趣
kwdhc 18舞蹈、歌曲、钢琴
dddlf 20歌曲、跳跃、说唱、篮球
xndfj 19古筝,翻译
我们会变成下面这样
年龄兴趣
kwdhc 18舞蹈
kwdhc 18歌曲
kwdhc 18钢琴
dddlf 20演唱
dddlf 20跳跃
dddlf 20 rap
dddlf 20篮球
xndfj 19古筝
xndfj 19翻译
解决需求1
未堆叠
是打印(df )
''''
科目分数
0 kwdhc国语90
1 kwdhc数学90
2 kwdhc英语90
3 dddlf国语91
4 dddlf数学91
5 dddlf英语91
6 xndfj国语92
7 xndfj数学92
8 xndfj英语92
''''
#为名称和科目设置索引,只提取“分数”,得到对应的二次索引Series对象
df=df.set_index([ '名称','科目' ] ) )
two_level_index_series=df[ '分数' ]
#这里得到的是具有辅助索引的series
打印(two _ level _ index _ series ) )
''''
科目名称
kwdhc国语90
数学90
英语90
dddlf国语91
数学91
英语91
xndfj国语92
数学92
英语92
Name:分数,dtype: int64
''''
#调用辅助索引的unstack方法可以得到DataFrame
#然后,主索引自动成为数据帧的索引,而辅助索引成为数据帧的列
new _ df=two _ level _ index _ series.un stack (
打印(new _ df )是
''''
科目数学英语语文
姓名
kwdhc 90 90 90
dddlf 91 91 91
xndfj 92 92 92
''''
#你怎么回来的? 但是有一个不完整的地方
#那就是这个new_df的索引和columns有名字
# index的名称为“名称”,columns的名称为“科目”。 因为原始series的两个索引被称为“名称”和“分数”
# rename_axis表示要重命名坐标轴
#这里首先清空columns的名称,继续查看索引不为空的原因
new _ df=new _ df.rename _ axis (columns=none )
打印(new _ df )是
''''
数学英语
姓名
kwdhc 90 90 90
dddlf 91 91 91
xndfj 92 92 92
''''
new_df=new_df.reset_index (
打印(new _ df )是
''''
姓名、数学、英文
0 kwdhc 90 90 90
1滴滴涕
lf 91 91 912 xndfj 92 92 92
"""
# 大功告成,如果index变为空的话,那么在reset_index之后,列名会变成index
# 但是如果原来索引有名字,reset_index,列名就是原来的索引名
pivot
pivot相当于是我们上面方法的一个化简,我们是把姓名作为索引、科目作为列、分数作为值
print(df)
"""
姓名 科目 分数
0 kwdhc 语文 90
1 kwdhc 数学 90
2 kwdhc 英语 90
3 dddlf 语文 91
4 dddlf 数学 91
5 dddlf 英语 91
6 xndfj 语文 92
7 xndfj 数学 92
8 xndfj 英语 92
"""
df = pd.pivot(df, index="姓名", columns="科目", values="分数")
print(df)
"""
科目 数学 英语 语文
姓名
kwdhc 90 90 90
dddlf 91 91 91
xndfj 92 92 92
"""
# 可以看到上面这一步,就直接相当于df.set_index(["姓名", "科目"])["分数"].unstack()
df = df.rename_axis(columns=None).reset_index()
print(df)
"""
姓名 数学 英语 语文
0 kwdhc 90 90 90
1 dddlf 91 91 91
2 xndfj 92 92 92
"""
解决需求二:
print(df)
"""
姓名 年龄 爱好
kwdhc 18 跳舞,唱歌,钢琴
dddlf 20 唱,跳,rap,篮球
xndfj 19 古筝,翻译
"""
df = df.set_index(["姓名", "年龄"])["爱好"].str.split(",", expand=True).stack().reset_index(drop=True, level=-1).reset_index().rename(columns={0: "爱好"})
print(df)
"""
姓名 年龄 爱好
0 kwdhc 18 跳舞
1 kwdhc 18 唱歌
2 kwdhc 18 钢琴
3 dddlf 20 唱
4 dddlf 20 跳
5 dddlf 20 rap
6 dddlf 20 篮球
7 xndfj 19 古筝
8 xndfj 19 翻译
"""
估计有人会懵逼,别急我们来一步一步拆解,不过在此之前我们先来介绍一下unstack和stack
unstack和stack
首先Series只有unstack,DataFrame既有unstack又有stack。对于Series来说,我们刚才说了,unstack是把该Series变成一个DataFrame,并且会把当前的一级索引变成DataFrame的对应索引、二级索引变成DataFrame的对应列,但如果不止二级呢?假设这个Series有8级索引呢?其实不管有多少级,假设n级,unstack不加参数的话,那么默认是把最后一级索引变成DataFrame的列,前面的n-1个索引则依旧会变成DataFrame的索引,当然也是n-1个。
为了和DataFrame做对比,我们就假设为2级索引。对于Series来说,unstack是把1级索引变成对应DataFrame的索引,2级索引是变成对应DataFrame的列。如果对DataFrame调用unstack,那么会把这个DataFrame转成一个具有二级索引的Series(如果这个DataFrame的索引只有一级的话),对应的索引变成具有二级索引的Series的二级索引,对应的列变成具有二级索引Series的一级索引。如果是stack的话,那么和Series正好是相反的,DataFrame的索引变成具有二级索引Series的一级索引,列变成具有二级索引Series的二级索引。
文字不好懂的话,看一张图
下面我们就来分析一下上面的那一长串
print(df)
"""
姓名 年龄 爱好
kwdhc 18 跳舞,唱歌,钢琴
dddlf 20 唱,跳,rap,篮球
xndfj 19 古筝,翻译
"""
# 我们是对"爱好"这个字段进行分解
# 那么将除了"爱好"之外的其它字段设置为索引
df = df.set_index(["姓名", "年龄"])
print(df)
"""
爱好
姓名 年龄
kwdhc 18 跳舞,唱歌,钢琴
dddlf 20 唱,跳,rap,篮球
xndfj 19 古筝,翻译
"""
# 筛选出"爱好"这个字段,此时得到的是一个具有二级索引的Series
# 索引的名字叫 "姓名"和"年龄"
s = df["爱好"]
print(s)
"""
姓名 年龄
kwdhc 18 跳舞,唱歌,钢琴
dddlf 20 唱,跳,rap,篮球
xndfj 19 古筝,翻译
Name: 爱好, dtype: object
"""
# 那么下面就对期望的字段进行分解
# 我们这个例子都是以逗号为分隔符,至于具体是什么则以实际数据为准
# 显然这里得到一个具有二级索引的DataFrame
df = s.str.split(",", expand=True)
print(df)
"""
0 1 2 3
姓名 年龄
kwdhc 18 跳舞 唱歌 钢琴 None
dddlf 20 唱 跳 rap 篮球
xndfj 19 古筝 翻译 None None
"""
# 调用stack,按照前面说的,会变成一个Series,索引就是DataFrame的索引再加上这个列变成的索引,显然列变成的索引就是三级索引了
# 可以看成是把DataFrame的索引看成一个整体作为对应Series的一级索引了
s = df.stack()
# 此时的数据已经像那么回事了
print(s)
"""
姓名 年龄
kwdhc 18 0 跳舞
1 唱歌
2 钢琴
dddlf 20 0 唱
1 跳
2 rap
3 篮球
xndfj 19 0 古筝
1 翻译
dtype: object
"""
# 然后调用reset_index,但是我们发现索引有三级,那么这样做就会导致,0 1 2 0 1 2..这些也变成了一列,当然可以之后drop掉
# 但是我们也可以直接删掉
# 于是我们可以加上一个drop=True,但是这样又把所有的index都删掉了,于是我们可以指定一个level
# 由于三级索引,那么最后一级就是2,当然可以直接指定为-1,表示最后一级,表示把最后一级索引删掉
s = s.reset_index(drop=True, level=-1)
print(s)
"""
姓名 年龄
kwdhc 18 跳舞
18 唱歌
18 钢琴
dddlf 20 唱
20 跳
20 rap
20 篮球
xndfj 19 古筝
19 翻译
dtype: object
"""
# 但是我们发现,上面的reset_index(drop=True, level=-1)并没有把前面的索引变成列
# 这是因为我们指定了level,如果不指定level,那么drop=True会把所有的索引都删掉
# 但指定了level只会删除对应级别的索引,而不会同时对前面的索引进行reset,于是需要再调用一次reset_index,此时就什么也不需要指定了
df = s.reset_index()
# 会自动进行笛卡尔乘积
print(df)
"""
姓名 年龄 0
0 kwdhc 18 跳舞
1 kwdhc 18 唱歌
2 kwdhc 18 钢琴
3 dddlf 20 唱
4 dddlf 20 跳
5 dddlf 20 rap
6 dddlf 20 篮球
7 xndfj 19 古筝
8 xndfj 19 翻译
"""
# 但是我们发现列名,是自动生成的0,于是再进行rename
df = df.rename(columns={0: "爱好"})
print(df)
"""
姓名 年龄 爱好
0 kwdhc 18 跳舞
1 kwdhc 18 唱歌
2 kwdhc 18 钢琴
3 dddlf 20 唱
4 dddlf 20 跳
5 dddlf 20 rap
6 dddlf 20 篮球
7 xndfj 19 古筝
8 xndfj 19 翻译
"""
# 此时就大功告成啦