如何融化熊猫数据框?

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首先合并MathEnglish列,并使数据帧复制(更长)。

    最后添加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)
)

此代码将NameAge 列设置为索引并堆叠其余列MathEnglish,并重置索引并将Grade 分配为列名,然后重命名其他列level_2Subject 然后按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 可以通过 meltgroupby 解决,使用 agg 函数和 ', '.join,如下所示:

print(
    df.melt(id_vars=["Name", "Age"])
    .groupby("value", as_index=False)
    .agg(", ".join)
)

它会融化数据框,然后按等级分组并聚合它们并用逗号连接它们。

stack 也可以用来解决这个问题,stackgroupby 如下所示:

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 参数、valuesindexcolumnsaggfunc

我们可以用下面的代码解决它:

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 可以通过 meltgroupby 解决,如下所示:

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 编辑了代码块以避免横向滚动代码块。如果更改不正确,请随时恢复。

以上是关于如何融化熊猫数据框?的主要内容,如果未能解决你的问题,请参考以下文章

融化包含字典列的熊猫数据框,以便字典值也被融化

熊猫,融化,未融化保留指数

熊猫数据框使用列作为行[重复]

如何使用熊猫拆分数据框?

如何按天聚合熊猫数据框

如何在熊猫数据框中展平数组