stack()和unstack()

pandas行转列, 列转行, 以及一行生成多行中, 我们主要要用到 stack() 和 unstack() 操作.

DataFrame.stack(self, level=- 1)
stack是将DataFram变为Series, DataFrame的列变成具有多级索引Series的最后一级索引, DataFrame的索引变成具有多级索引Series的其他索引.

DataFrame.unstack(self, level=- 1) / Series.unstack(self, level=- 1)
对于Series来说, unstack()是把最后一级索引变成对应DataFrame的列, 其他索引变成对应DataFrame的索引; 对于DataFrame来说, unstack()是将DataFram变为Series, DataFrame的列变成具有多级索引Series的一级索引, DataFrame的索引变成具有多级索引Series的其他索引.

例如:

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
import pandas as pd


df0 = pd.DataFrame([[80, 85], [92, 90], [88, 78]], index=['小红', '小华', '小明'], columns=['数学', '语文'])
'''df0:
数学 语文
小红 80 85
小华 92 90
小明 88 78
'''

# =========================分割线=========================

se1 = df0.unstack(level=-1)
'''se1:
数学 小红 80
小华 92
小明 88
语文 小红 85
小华 90
小明 78
dtype: int64
'''
# 可以看到df1的列学科变成了se1的一级索引, 索引则会变成se1的其他索引.

df1 = se1.unstack(level=-1)
'''df1
小红 小华 小明
数学 80 92 88
语文 85 90 78
'''
# 可以看到se1的最后一级索引姓名变成了df1的列, 其它索引则会变成df1的索引.

# =========================分割线=========================

se2 = df0.stack(level=-1)
'''se2:
小红 数学 80
语文 85
小华 数学 92
语文 90
小明 数学 88
语文 78
dtype: int64
'''
# 可以看到df1的列索引分数变成了se1的最后一级索引, 其它索引则会变成se1的索引.


df2 = se2.unstack(level=-1)
'''df2:
数学 语文
小红 80 85
小华 92 90
小明 88 78
'''
# 可以看到se2的最后一级索引学科变成了df2的列, 其它索引则会变成df2的索引.

pivot()

DataFrame.pivot(self, index=None, columns=None, values=None)
用来调整DataFrame的索引, 列, 值.

例如:

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
import pandas as pd


df = pd.DataFrame({ '姓名':['小红', '小红', '小华', '小华', '小明', '小明'],
'科目':['数学', '语文', '数学', '语文', '数学', '语文'],
'成绩':[88, 85, 92, 90, 88, 78] })
'''df:
姓名 科目 成绩
0 小红 数学 88
1 小红 语文 85
2 小华 数学 92
3 小华 语文 90
4 小明 数学 88
5 小明 语文 78
'''

df = df.pivot(index='姓名', columns)
'''df:
科目 数学 语文
姓名
小华 92 90
小明 88 78
小红 88 85
'''

# 将列名置空, rename_axis()方法是给坐标轴重命名
df = df.rename_axis(columns=None)
'''df:
数学 语文
姓名
小华 92 90
小明 88 78
小红 88 85
'''

# 重新设置索引, 若index变为空的话,那么在reset_index之后,列名会变成index
df = df.reset_index()
'''df:
姓名 数学 语文
0 小华 92 90
1 小明 88 78
2 小红 88 85
'''

分割value问题

有这样一组数据:

姓名 年龄 爱好
小红 16 跳舞,唱歌,钢琴
小华 18 唱,跳,rap,篮球
小明 20 古筝,翻译

我们想要把它变成下面这样:

姓名 年龄 爱好
小红 16 跳舞
小红 16 唱歌
小红 16 钢琴
小华 18
小华 18
小华 18 rap
小华 18 篮球
小明 20 古筝
小明 20 翻译

先导入数据:

1
2
3
4
5
6
7
8
9
df = pd.DataFrame({ '姓名':['小红', '小华', '小明'],
'年龄':[16, 18, 20],
'爱好':['跳舞,唱歌,钢琴', '唱,跳,rap,篮球', '古筝,翻译'] })
'''df:
姓名 年龄 爱好
0 小红 16 跳舞,唱歌,钢琴
1 小华 18 唱,跳,rap,篮球
2 小明 20 古筝,翻译
'''

第一种方法

首先将df的姓名和年龄设置为索引(当前为列名):

1
2
3
4
5
6
7
8
df = df.set_index(["姓名", "年龄"])
'''df:
爱好
姓名 年龄
小红 16 跳舞,唱歌,钢琴
小华 18 唱,跳,rap,篮球
小明 20 古筝,翻译
'''

此时df只有爱好这一列, 再将爱好这列分割:

1
2
3
4
5
6
7
8
df = df["爱好"].str.split(",", expand=True)
'''df:
0 1 2 3
姓名 年龄
小红 16 跳舞 唱歌 钢琴 None
小华 18 唱 跳 rap 篮球
小明 20 古筝 翻译 None None
'''

此时字段已经被分割开来, 我们需要将这多列变为一列, 此时我们可以调用 stack(), 将列转变为Series的最后一级索引:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
se = df.stack()
'''se:
姓名 年龄
小红 16 0 跳舞
1 唱歌
2 钢琴
小华 18 0 唱
1 跳
2 rap
3 篮球
小明 20 0 古筝
1 翻译
dtype: object
'''

此时这个df已经转变为Series, 而此Series的最后一级索引一级没有了作用, 可以直接删掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
se = se.reset_index(drop=True, level=-1)
# 这个reset_index因为指定了删除level=-1, 所以只是删除了该Series的最后一级索引, 并没有将该Series转变为DataFrame
'''se:
姓名 年龄
小红 16 跳舞
16 唱歌
16 钢琴
小华 18 唱
18 跳
18 rap
18 篮球
小明 20 古筝
20 翻译
dtype: object
'''

我们再调用一次reset_index(), 将Series的索引转变为DataFrame的列:

1
2
3
4
5
6
7
8
9
10
11
12
13
df = se.reset_index()
'''df:
姓名 年龄 0
0 小红 16 跳舞
1 小红 16 唱歌
2 小红 16 钢琴
3 小华 18 唱
4 小华 18 跳
5 小华 18 rap
6 小华 18 篮球
7 小明 20 古筝
8 小明 20 翻译
'''

最后再更改一下列名就完成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
df = df.rename(columns={0: "爱好"})
'''python
姓名 年龄 爱好
0 小红 16 跳舞
1 小红 16 唱歌
2 小红 16 钢琴
3 小华 18 唱
4 小华 18 跳
5 小华 18 rap
6 小华 18 篮球
7 小明 20 古筝
8 小明 20 翻译
'''

第二种方法

使用下面方法, 可以迅速使列表炸裂开.

DataFrame.explode(self, column)

首先我们将数据的”爱好”列转换为列表

1
2
3
4
5
6
7
df["爱好"] = df["爱好"].str.split(",")
'''df:
姓名 年龄 爱好
0 小红 16 [跳舞, 唱歌, 钢琴]
1 小华 18 [唱, 跳, rap, 篮球]
2 小明 20 [古筝, 翻译]
'''

再使用该方法, 并指定”爱好”列, 就完成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
df = df.explode("爱好")
'''df:
姓名 年龄 爱好
0 小红 16 跳舞
0 小红 16 唱歌
0 小红 16 钢琴
1 小华 18 唱
1 小华 18 跳
1 小华 18 rap
1 小华 18 篮球
2 小明 20 古筝
2 小明 20 翻译
'''

参考文献

[1] pandas行转列、列转行、以及一行生成多行 - 古明地盆 - 博客园