【问题标题】:How do I melt a pandas dataframe?如何融化熊猫数据框?
【发布时间】:2021-10-27 21:19:27
【问题描述】:

标签上,我经常看到用户询问有关在 pandas 中融化数据框的问题。我将尝试对此主题进行规范的问答(自我回答)。

我要澄清一下:

  1. 什么是融化?

  2. 如何使用融化?

  3. 什么时候使用melt?

我看到一些关于融化的热门问题,例如:

因此,我将尝试对该主题进行规范的问答。



数据集:

我将在这个随机年龄的随机分数数据集上获得所有答案(更容易解释答案: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

我想要一个像上面这样的数据框。所有列都作为值。

请在下面查看我的自我回答:)

【问题讨论】:

    标签: pandas python pandas dataframe melt pandas-melt


    【解决方案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的解决方案(见下),步骤为:

    1. 首先我们得到原始数据帧。

    2. 然后melt首先合并MathEnglish列,并使数据帧复制(更长)。

    3. 最后添加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 编辑了代码块以避免横向滚动代码块。如果更改不正确,请随时恢复。
    猜你喜欢
    相关资源
    最近更新 更多