如何融化熊猫数据框?
Posted
技术标签:
【中文标题】如何融化熊猫数据框?【英文标题】:How do I melt a pandas dataframe? 【发布时间】:2022-01-19 02:28:28 【问题描述】:在pandas 标签上,我经常看到用户询问有关在 pandas 中融化数据框的问题。我将尝试对此主题进行规范的问答(自我回答)。
我要澄清一下:
什么是融化?
如何使用融化?
什么时候使用melt?
我看到一些关于融化的热门问题,例如:
pandas convert some columns into rows : 这个其实还可以,但多解释一下会更好。
Pandas Melt Function : 很好的问题答案是好的,但它有点太模糊了,没有太多的解释。
Melting a pandas dataframe:也是一个不错的答案!但这只是针对那种特殊情况,很简单,只有pd.melt(df)
Pandas dataframe use columns as rows (melt):非常整洁!但问题是它只针对 OP 提出的特定问题,也需要使用pivot_table
。
因此,我将尝试对该主题进行规范的问答。
数据集:
我将在这个随机年龄的随机分数数据集上获得所有答案(更容易解释答案:D):
import pandas as pd
df = pd.DataFrame('Name': ['Bob', 'John', 'Foo', 'Bar', 'Alex', 'Tom'],
'Math': ['A+', 'B', 'A', 'F', 'D', 'C'],
'English': ['C', 'B', 'B', 'A+', 'F', 'A'],
'Age': [13, 16, 16, 15, 15, 13])
>>> df
Name Math English Age
0 Bob A+ C 13
1 John B B 16
2 Foo A B 16
3 Bar F A+ 15
4 Alex D F 15
5 Tom C A 13
>>>
问题:
我会遇到一些问题,它们将在下面的自我回答中得到解决。
问题一:
我如何融合一个数据框,使原来的数据框变成:
Name Age Subject Grade
0 Bob 13 English C
1 John 16 English B
2 Foo 14 English B
3 Bar 15 English A+
4 Alex 17 English F
5 Tom 12 English A
6 Bob 13 Math A+
7 John 16 Math B
8 Foo 14 Math A
9 Bar 15 Math F
10 Alex 17 Math D
11 Tom 12 Math C
我想将其转置,以便一列是每个科目,另一列是学生的重复姓名以及年龄和分数。
问题2:
这和问题1类似,但是这次我想让问题1输出Subject
列只有Math
,我想过滤掉English
列:
Name Age Subject Grades
0 Bob 13 Math A+
1 John 16 Math B
2 Foo 16 Math A
3 Bar 15 Math F
4 Alex 15 Math D
5 Tom 13 Math C
我希望输出像上面那样。
问题3:
如果我要对熔体进行分组并按分数排序学生,我将如何做到这一点,以获得如下所示的所需输出:
value Name Subjects
0 A Foo, Tom Math, English
1 A+ Bob, Bar Math, English
2 B John, John, Foo Math, English, English
3 C Tom, Bob Math, English
4 D Alex Math
5 F Bar, Alex Math, English
我需要对它进行排序,名称用逗号分隔,Subjects
分别以相同的顺序用逗号分隔
问题 4:
我将如何unmelt 融化的数据框?假设我已经融化了这个数据框:
print(df.melt(id_vars=['Name', 'Age'], var_name='Subject', value_name='Grades'))
变成:
Name Age Subject Grades
0 Bob 13 Math A+
1 John 16 Math B
2 Foo 16 Math A
3 Bar 15 Math F
4 Alex 15 Math D
5 Tom 13 Math C
6 Bob 13 English C
7 John 16 English B
8 Foo 16 English B
9 Bar 15 English A+
10 Alex 15 English F
11 Tom 13 English A
那么我将如何将其翻译回原始数据框,如下:
Name Math English Age
0 Bob A+ C 13
1 John B B 16
2 Foo A B 16
3 Bar F A+ 15
4 Alex D F 15
5 Tom C A 13
我该怎么做呢?
问题 5:
如果我要按学生姓名分组,用逗号分隔科目和成绩,我会怎么做?
Name Subject Grades
0 Alex Math, English D, F
1 Bar Math, English F, A+
2 Bob Math, English A+, C
3 Foo Math, English A, B
4 John Math, English B, B
5 Tom Math, English C, A
我想要一个像上面这样的数据框。
问题 6:
如果我要完全融化我的数据框,所有列都是值,我该怎么做?
Column Value
0 Name Bob
1 Name John
2 Name Foo
3 Name Bar
4 Name Alex
5 Name Tom
6 Math A+
7 Math B
8 Math A
9 Math F
10 Math D
11 Math C
12 English C
13 English B
14 English B
15 English A+
16 English F
17 English A
18 Age 13
19 Age 16
20 Age 16
21 Age 15
22 Age 15
23 Age 13
我想要一个像上面这样的数据框。所有列都作为值。
请在下面查看我的自我回答:)
【问题讨论】:
【参考方案1】:pandas 版本低于 0.20.0 的用户请注意,我将使用df.melt(...)
进行示例,但您的版本对于df.melt
来说太低了,您需要改用pd.melt(df, ...)
。 H2>
文档参考:
这里的大部分解决方案都会和melt
一起使用,所以要知道melt
的方法,请看documentaion的解释
将 DataFrame 从宽格式转为长格式,可选择保留 标识符集。
此函数对于将 DataFrame 转换为一种格式非常有用 或更多列是标识符变量(id_vars),而所有其他列 列,被认为是测量变量 (value_vars),是“未透视的” 到行轴,只留下两个非标识符列,“变量” 和“价值”。
而参数是:
参数
id_vars : 元组、列表或 ndarray,可选
用作标识符变量的列。
value_vars : 元组、列表或 ndarray,可选
要取消透视的列。如果未指定,则使用所有未设置为 id_vars 的列。
var_name : 标量
用于“变量”列的名称。如果 None 它使用 frame.columns.name 或“变量”。
value_name : 标量,默认“值”
用于“值”列的名称。
col_level : int 或 str,可选
如果列是 MultiIndex,则使用此级别进行融合。
ignore_index : bool,默认为 True
如果为 True,则忽略原始索引。如果为 False,则保留原始索引。索引标签将重复 根据需要。
1.1.0 版中的新功能。
融化的逻辑:
Melting合并多列并将dataframe由宽转长,对于问题1的解决方案(见下),步骤为:
首先我们得到原始数据帧。
然后melt首先合并Math
和English
列,并使数据帧复制(更长)。
最后添加Subject
列,分别是Grades
列值的主题。
这是melt
函数的简单逻辑。
解决方案:
我会解决我自己的问题。
问题一:
问题 1 可以使用 pd.DataFrame.melt
解决,代码如下:
print(df.melt(id_vars=['Name', 'Age'], var_name='Subject', value_name='Grades'))
此代码将 id_vars
参数传递给 ['Name', 'Age']
,然后自动将 value_vars
设置为其他列 (['Math', 'English']
),然后转换为该格式。
您也可以使用stack
解决问题 1,如下所示:
print(
df.set_index(["Name", "Age"])
.stack()
.reset_index(name="Grade")
.rename(columns="level_2": "Subject")
.sort_values("Subject")
.reset_index(drop=True)
)
此代码将Name
和Age
列设置为索引并堆叠其余列Math
和English
,并重置索引并将Grade
分配为列名,然后重命名其他列level_2
到Subject
然后按Subject
列排序,最后再次重置索引。
这两种解决方案的输出:
Name Age Subject Grade
0 Bob 13 English C
1 John 16 English B
2 Foo 14 English B
3 Bar 15 English A+
4 Alex 17 English F
5 Tom 12 English A
6 Bob 13 Math A+
7 John 16 Math B
8 Foo 14 Math A
9 Bar 15 Math F
10 Alex 17 Math D
11 Tom 12 Math C
问题2:
这和我的第一个问题类似,但是这个我只有一个在Math
列中过滤,这一次value_vars
参数可以使用,如下所示:
print(
df.melt(
id_vars=["Name", "Age"],
value_vars="Math",
var_name="Subject",
value_name="Grades",
)
)
或者我们也可以将stack
与列规范一起使用:
print(
df.set_index(["Name", "Age"])[["Math"]]
.stack()
.reset_index(name="Grade")
.rename(columns="level_2": "Subject")
.sort_values("Subject")
.reset_index(drop=True)
)
这两种解决方案都给出:
Name Age Subject Grade
0 Bob 13 Math A+
1 John 16 Math B
2 Foo 16 Math A
3 Bar 15 Math F
4 Alex 15 Math D
5 Tom 13 Math C
问题3:
问题 3 可以通过 melt
和 groupby
解决,使用 agg
函数和 ', '.join
,如下所示:
print(
df.melt(id_vars=["Name", "Age"])
.groupby("value", as_index=False)
.agg(", ".join)
)
它会融化数据框,然后按等级分组并聚合它们并用逗号连接它们。
stack
也可以用来解决这个问题,stack
和 groupby
如下所示:
print(
df.set_index(["Name", "Age"])
.stack()
.reset_index()
.rename(columns="level_2": "Subjects", 0: "Grade")
.groupby("Grade", as_index=False)
.agg(", ".join)
)
这个stack
函数只是以等效于melt
的方式转置数据帧,然后重置索引,重命名列和组以及聚合。
两种解决方案的输出:
Grade Name Subjects
0 A Foo, Tom Math, English
1 A+ Bob, Bar Math, English
2 B John, John, Foo Math, English, English
3 C Bob, Tom English, Math
4 D Alex Math
5 F Bar, Alex Math, English
问题 4:
我们首先为输入数据融合数据框:
df = df.melt(id_vars=['Name', 'Age'], var_name='Subject', value_name='Grades')
那么现在我们可以开始解决这个问题 4。
问题 4 可以用 pivot_table
解决,我们必须指定 pivot_table
参数、values
、index
、columns
和 aggfunc
。
我们可以用下面的代码解决它:
print(
df.pivot_table("Grades", ["Name", "Age"], "Subject", aggfunc="first")
.reset_index()
.rename_axis(columns=None)
)
输出:
Name Age English Math
0 Alex 15 F D
1 Bar 15 A+ F
2 Bob 13 C A+
3 Foo 16 B A
4 John 16 B B
5 Tom 13 A C
融化的数据帧被转换回与原始数据帧完全相同的格式。
我们首先旋转融化的数据框,然后重置索引并删除列轴名称。
问题 5:
问题 5 可以通过 melt
和 groupby
解决,如下所示:
print(
df.melt(id_vars=["Name", "Age"], var_name="Subject", value_name="Grades")
.groupby("Name", as_index=False)
.agg(", ".join)
)
Name
融化并分组。
或者你可以stack
:
print(
df.set_index(["Name", "Age"])
.stack()
.reset_index()
.groupby("Name", as_index=False)
.agg(", ".join)
.rename("level_2": "Subjects", 0: "Grades", axis=1)
)
两个代码输出:
Name Subjects Grades
0 Alex Math, English D, F
1 Bar Math, English F, A+
2 Bob Math, English A+, C
3 Foo Math, English A, B
4 John Math, English B, B
5 Tom Math, English C, A
问题 6:
问题 6 可以用melt
解决,不需要指定列,只需指定预期的列名:
print(df.melt(var_name='Column', value_name='Value'))
这会融化整个数据框
或者你可以stack
:
print(
df.stack()
.reset_index(level=1)
.sort_values("level_1")
.reset_index(drop=True)
.set_axis(["Column", "Value"], axis=1)
)
两个代码输出:
Column Value
0 Age 16
1 Age 15
2 Age 15
3 Age 16
4 Age 13
5 Age 13
6 English A+
7 English B
8 English B
9 English A
10 English F
11 English C
12 Math C
13 Math A+
14 Math D
15 Math B
16 Math F
17 Math A
18 Name Alex
19 Name Bar
20 Name Tom
21 Name Foo
22 Name John
23 Name Bob
结论:
melt
是一个非常好用的功能,经常需要用到,一旦遇到这类问题,别忘了试试melt
,它可能会很好地解决你的问题。
请记住,对于 pandas 版本低于 0.20.0 的用户,您必须使用 pd.melt(df, ...)
而不是 df.melt(...)
。
【讨论】:
我用black
编辑了代码块以避免横向滚动代码块。如果更改不正确,请随时恢复。以上是关于如何融化熊猫数据框?的主要内容,如果未能解决你的问题,请参考以下文章