【问题标题】:How do I group my pandas columns to map and create a new column based on map values如何将我的 pandas 列分组以映射并根据映射值创建一个新列
【发布时间】:2020-09-14 05:35:22
【问题描述】:

我有一个学生 ID 数据框以及他们为每个科目参加的测试次数。我必须根据 ID 对它进行分组,其中包含主题和测试数量作为地图。

我有什么:

Id     Subject     Number_of_Tests
101    Maths       6
101    Science     8
101    History     10
102    History     5
102    Maths       4
102    Science     7

我想要什么:

Id     Tests                                  Grade
101    {Maths:6, Science:8, History:10}     A
102    {History:5, Maths:5, Science:7}      B

另外,像这样分组后,我想再添加一列,称为“等级”,它基于新创建的“测试”地图字段。 例如,如果数学中的测试次数大于 5 次,如果科学中的测试次数大于 5 次,并且如果历史中的测试次数大于 5 次,则评分为“A”,否则为“B”。

谁能帮帮我。

【问题讨论】:

  • 您的预期输出令人困惑,是列表列表还是您想要 dict 列表?
  • @Sociopath 我基本上想要它作为地图,就像在 scala 中一样。也想参考一下。

标签: python pandas dataframe dictionary


【解决方案1】:

我认为你需要:

# create a list of both columns on groupby
new_df = df.groupby('Id', as_index=False).aggregate({"Subject": lambda x: x.to_list(), "Number_Of_Tests": lambda x: x.to_list()})

# create a new column `Grade` based on condition
new_df["Grade"] = ["A" if all(j>5 for j in i) else "B" for i in new_df["Number_Of_Tests"]]

# create a column Tests using other 2 columns 
new_df["Tests"] = [{k:v for k,v in zip(i,j)} for i,j in zip(new_df["Subject"], new_df["Number_Of_Tests"])]

# drop unwanted columns
new_df.drop(["Subject","Number_Of_Tests"], axis=1, inplace=True)

输出:

   Id    Grade     Tests
0  101     A     {'Maths': 6, 'Science': 8, 'History': 10}
1  102     B     {'History': 5, 'Maths': 4, 'Science': 7}

编辑

mask1 = (df["Subject"] == "Maths") & (df["Number_Of_Tests"] > 3)
mask2 = (df["Subject"] == "Science") & (df["Number_Of_Tests"] > 5)
mask3 = (df["Subject"] == "History") & (df["Number_Of_Tests"] > 7)

df["Grades"] = np.select([mask1, mask2, mask3], ["A", "A", "A"], "B")


def func(x):
    if "B" in x.values:
        return "B"
    return "A"


new_df = df.groupby('Id', as_index=False).aggregate({"Subject": lambda x: x.to_list(),
                                                     "Number_Of_Tests": lambda x: x.to_list(),
                                                     "Grades": func})

new_df["Tests"] = [{k:v for k,v in zip(i,j)} for i,j in zip(new_df["Subject"], new_df["Number_Of_Tests"])]

new_df.drop(["Subject","Number_Of_Tests"], axis=1, inplace=True)

【讨论】:

  • 嗨@Sociopath,感谢您的回答。但是您能否让我知道如何从 dict(“Tests”列)访问而不是从 List(“Number_Of_Tests”列)访问
  • 对不起,我没听懂。你在这里尝试访问什么?整个dict 或特定主题的标记?
  • 成绩应该基于字典上的多个条件。例如,不是所有科目都将 5 作为阈值,如果数学 >3、科学 >5 和历史 >7 等不同科目的阈值不同,则为“A”,否则为“B”。
【解决方案2】:

您最初使用Tests 作为列表的输出提出了问题。这是提供该输出的代码。稍后我会尝试为 dict 创建另一个版本:

  1. Tests 列非常简单。首先,将相关列连接成一个字符串。稍后,在.groupby() 中,您会将这些值聚合为一个列表。
  2. 通过获取每个组的.min() 并返回AB,创建Grade 列。稍后,您可以将其重新合并到新的合并数据框中。

df['Tests'] = df['Subject'] + ': ' + df['Number_of_Tests'].astype(str)
df['Grade'] = (df.groupby(['Id'])['Number_of_Tests'].transform('min') > 5).replace([True,False], ['A','B'])
df = pd.merge(df.groupby(['Id'])['Tests'].agg(list).reset_index(),
              df[['Grade','Id']], on='Id').drop_duplicates(subset='Id')
df
Out[1]: 
    Id                                Tests Grade
0  101  [Maths: 6, Science: 8, History: 10]     A
3  102   [History: 5, Maths: 4, Science: 7]     B

【讨论】:

    【解决方案3】:

    更改数据框的结构,使其更易于操作,而不是像 dict 这样不易操作的格式。

    df = df.set_index(['Id', 'Subject']).unstack()
    
            Number_of_Tests              
    Subject         History Maths Science
    Id                                   
    101                  10     6       8
    102                   5     4       7
    

    现在只需根据所需条件添加一列。

    df['Grade'] = np.where((df['Number_of_Tests'] > 5).all(axis=1), 'A', 'B')
    

    输出

            Number_of_Tests               Grade
    Subject         History Maths Science      
    Id                                         
    101                  10     6       8     A
    102                   5     4       7     B
    

    如果你仍然想要dict

    df['Tests'] = df.groupby('Id').apply(lambda x: dict(zip(x.Subject, x.Number_of_Tests)))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-18
      • 2020-08-13
      • 2020-07-07
      • 2016-08-28
      • 2021-09-21
      • 2011-12-04
      • 1970-01-01
      相关资源
      最近更新 更多