【问题标题】:pandas create new column based on values from other columns / apply a function of multiple columns, row-wise熊猫根据其他列的值创建新列/逐行应用多列的函数
【发布时间】:2015-01-09 06:52:18
【问题描述】:

我想将我的自定义函数(它使用 if-else 阶梯)应用于这六列(ERI_HispanicERI_AmerInd_AKNatvERI_AsianERI_Black_Afr.AmerERI_HI_PacIslERI_White)我的数据框的行。

我尝试了与其他问题不同的方法,但似乎仍然无法为我的问题找到正确的答案。这其中的关键部分是,如果这个人被算作西班牙裔,他们就不能算作其他任何人。即使他们在另一个种族列中有“1”,他们仍然被视为西班牙裔,而不是两个或更多种族。同样,如果所有 ERI 列的总和大于 1,则它们被计为两个或多个种族,不能算作一个独特的种族(西班牙裔除外)。希望这是有道理的。任何帮助将不胜感激。

这几乎就像对每一行进行 for 循环,如果每条记录满足一个标准,它们就会被添加到一个列表中并从原始列表中删除。

从下面的数据框中,我需要根据 SQL 中的以下规范计算一个新列:

==========================标准===================== ==========

IF [ERI_Hispanic] = 1 THEN RETURN “Hispanic”
ELSE IF SUM([ERI_AmerInd_AKNatv] + [ERI_Asian] + [ERI_Black_Afr.Amer] + [ERI_HI_PacIsl] + [ERI_White]) > 1 THEN RETURN “Two or More”
ELSE IF [ERI_AmerInd_AKNatv] = 1 THEN RETURN “A/I AK Native”
ELSE IF [ERI_Asian] = 1 THEN RETURN “Asian”
ELSE IF [ERI_Black_Afr.Amer] = 1 THEN RETURN “Black/AA”
ELSE IF [ERI_HI_PacIsl] = 1 THEN RETURN “Haw/Pac Isl.”
ELSE IF [ERI_White] = 1 THEN RETURN “White”

评论:如果西班牙裔的 ERI 标志为真 (1),则员工被归类为“西班牙裔”

注释:如果超过 1 个非西班牙裔 ERI 标志为真,则返回“两个或更多”

====================== DATAFRAME ======================== ===

     lname          fname       rno_cd  eri_afr_amer    eri_asian   eri_hawaiian    eri_hispanic    eri_nat_amer    eri_white   rno_defined
0    MOST           JEFF        E       0               0           0               0               0               1           White
1    CRUISE         TOM         E       0               0           0               1               0               0           White
2    DEPP           JOHNNY              0               0           0               0               0               1           Unknown
3    DICAP          LEO                 0               0           0               0               0               1           Unknown
4    BRANDO         MARLON      E       0               0           0               0               0               0           White
5    HANKS          TOM         0                       0           0               0               0               1           Unknown
6    DENIRO         ROBERT      E       0               1           0               0               0               1           White
7    PACINO         AL          E       0               0           0               0               0               1           White
8    WILLIAMS       ROBIN       E       0               0           1               0               0               0           White
9    EASTWOOD       CLINT       E       0               0           0               0               0               1           White

【问题讨论】:

  • 您的特定函数只是一个长 if-else 阶梯,其中某些变量的值优先于其他变量。在硬件工程术语中,它被称为 priority-decoder

标签: python pandas numpy apply


【解决方案1】:

另一种(易于推广的)方法,其基石是pandas.DataFrame.idxmax。首先,易于概括的序言。

# Indeed, all your conditions boils down to the following
_gt_1_key = 'two_or_more'
_lt_1_key = 'other'

# The "dictionary-based" if-else statements
labels = {
    _gt_1_key     : 'Two Or More',
    'eri_hispanic': 'Hispanic',
    'eri_nat_amer': 'A/I AK Native',
    'eri_asian'   : 'Asian',
    'eri_afr_amer': 'Black/AA',
    'eri_hawaiian': 'Haw/Pac Isl.',
    'eri_white'   : 'White',  
    _lt_1_key     : 'Other',
}

# The output-driving 1-0 matrix
mat = df.filter(regex='^eri_').copy()  # `~.copy` to avoid `SettingWithCopyWarning`

...最后,在 vectorized fashion

mat[_gt_1_key] = gt1 = mat.sum(axis=1)
mat[_lt_1_key] = gt1.eq(0).astype(int)
race_label     = mat.idxmax(axis=1).map(labels)

在哪里

>>> race_label
0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White
dtype: object

这是一个pandas.Series 实例,您可以轻松地在df 中托管,即执行df['race_label'] = race_label

【讨论】:

    【解决方案2】:

    正如@user3483203 指出的那样,numpy.select 是最好的方法

    将条件语句和相应的操作存储在两个列表中

    conds = [(df['eri_hispanic'] == 1),(df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1)),(df['eri_nat_amer'] == 1),(df['eri_asian'] == 1),(df['eri_afr_amer'] == 1),(df['eri_hawaiian'] == 1),(df['eri_white'] == 1,])
    
    actions = ['Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White']
    

    您现在可以使用这些列表作为参数来使用 np.select

    df['label_race'] = np.select(conds,actions,default='Other')
    

    参考:https://numpy.org/doc/stable/reference/generated/numpy.select.html

    【讨论】:

      【解决方案3】:

      试试这个,

      df.loc[df['eri_white']==1,'race_label'] = 'White'
      df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
      df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
      df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
      df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
      df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
      df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
      df['race_label'].fillna('Other', inplace=True)
      

      O/P:

           lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian  \
      0      MOST    JEFF      E             0          0             0   
      1    CRUISE     TOM      E             0          0             0   
      2      DEPP  JOHNNY    NaN             0          0             0   
      3     DICAP     LEO    NaN             0          0             0   
      4    BRANDO  MARLON      E             0          0             0   
      5     HANKS     TOM    NaN             0          0             0   
      6    DENIRO  ROBERT      E             0          1             0   
      7    PACINO      AL      E             0          0             0   
      8  WILLIAMS   ROBIN      E             0          0             1   
      9  EASTWOOD   CLINT      E             0          0             0   
      
         eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label  
      0             0             0          1       White         White  
      1             1             0          0       White      Hispanic  
      2             0             0          1     Unknown         White  
      3             0             0          1     Unknown         White  
      4             0             0          0       White         Other  
      5             0             0          1     Unknown         White  
      6             0             0          1       White   Two Or More  
      7             0             0          1       White         White  
      8             0             0          0       White  Haw/Pac Isl.  
      9             0             0          1       White         White 
      

      使用.loc 代替apply

      它改进了矢量化。

      .loc 以简单的方式工作,根据条件屏蔽行,将值应用于冻结行。

      更多详情请访问 .loc docs

      性能指标:

      接受的答案:

      def label_race (row):
         if row['eri_hispanic'] == 1 :
            return 'Hispanic'
         if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
            return 'Two Or More'
         if row['eri_nat_amer'] == 1 :
            return 'A/I AK Native'
         if row['eri_asian'] == 1:
            return 'Asian'
         if row['eri_afr_amer']  == 1:
            return 'Black/AA'
         if row['eri_hawaiian'] == 1:
            return 'Haw/Pac Isl.'
         if row['eri_white'] == 1:
            return 'White'
         return 'Other'
      
      df=pd.read_csv('dataser.csv')
      df = pd.concat([df]*1000)
      
      %timeit df.apply(lambda row: label_race(row), axis=1)
      

      每个循环 1.15 秒 ± 46.5 毫秒(7 次运行的平均值 ± 标准偏差,每次 1 个循环)

      我的建议答案:

      def label_race(df):
          df.loc[df['eri_white']==1,'race_label'] = 'White'
          df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
          df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
          df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
          df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
          df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
          df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
          df['race_label'].fillna('Other', inplace=True)
      df=pd.read_csv('s22.csv')
      df = pd.concat([df]*1000)
      
      %timeit label_race(df)
      

      每个循环 24.7 毫秒 ± 1.7 毫秒(7 次运行的平均值 ± 标准偏差,每次 10 次循环)

      【讨论】:

        【解决方案4】:

        好的,有两个步骤 - 首先是编写一个执行您想要的翻译的函数 - 我根据您的伪代码将一个示例放在一起:

        def label_race (row):
           if row['eri_hispanic'] == 1 :
              return 'Hispanic'
           if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1 :
              return 'Two Or More'
           if row['eri_nat_amer'] == 1 :
              return 'A/I AK Native'
           if row['eri_asian'] == 1:
              return 'Asian'
           if row['eri_afr_amer']  == 1:
              return 'Black/AA'
           if row['eri_hawaiian'] == 1:
              return 'Haw/Pac Isl.'
           if row['eri_white'] == 1:
              return 'White'
           return 'Other'
        

        您可能想回顾一下,但它似乎可以解决问题 - 请注意,进入函数的参数被认为是一个标有“行”的 Series 对象。

        接下来,使用 pandas 中的 apply 函数来应用函数——例如

        df.apply (lambda row: label_race(row), axis=1)
        

        注意 axis=1 说明符,这意味着应用程序是在行级别而不是列级别完成的。结果在这里:

        0           White
        1        Hispanic
        2           White
        3           White
        4           Other
        5           White
        6     Two Or More
        7           White
        8    Haw/Pac Isl.
        9           White
        

        如果您对这些结果感到满意,请再次运行它,将结果保存到原始数据框中的新列中。

        df['race_label'] = df.apply (lambda row: label_race(row), axis=1)
        

        生成的数据框如下所示(向右滚动查看新列):

              lname   fname rno_cd  eri_afr_amer  eri_asian  eri_hawaiian   eri_hispanic  eri_nat_amer  eri_white rno_defined    race_label
        0      MOST    JEFF      E             0          0             0              0             0          1       White         White
        1    CRUISE     TOM      E             0          0             0              1             0          0       White      Hispanic
        2      DEPP  JOHNNY    NaN             0          0             0              0             0          1     Unknown         White
        3     DICAP     LEO    NaN             0          0             0              0             0          1     Unknown         White
        4    BRANDO  MARLON      E             0          0             0              0             0          0       White         Other
        5     HANKS     TOM    NaN             0          0             0              0             0          1     Unknown         White
        6    DENIRO  ROBERT      E             0          1             0              0             0          1       White   Two Or More
        7    PACINO      AL      E             0          0             0              0             0          1       White         White
        8  WILLIAMS   ROBIN      E             0          0             1              0             0          0       White  Haw/Pac Isl.
        9  EASTWOOD   CLINT      E             0          0             0              0             0          1       White         White
        

        【讨论】:

        • 请注意:如果您只是将行输入到您的函数中,您可以这样做:df.apply(label_race, axis=1)
        • 如果我想对另一行做类似的事情,我可以使用相同的功能吗?例如,从结果来看,如果 ['race_label'] == "White" 返回 'White' 等等。但是如果 ['race_label'] == 'Unknown' 从 ['rno_defined'] 列返回值。我假设相同的功能会起作用,但我似乎无法弄清楚如何从另一列获取值。
        • 您可以编写一个新函数,查看“race_label”字段,并将结果发送到一个新字段,或者 - 我认为在这种情况下这可能会更好,编辑原始函数,将最后的 return 'Other' 行更改为 return row['rno_defined'] 在 if/then 语句集未找到匹配项的情况下(即当前,您看到“其他”),它应该替换该列中的值。
        • 您可以简化:df.apply(lambda row: label_race (row),axis=1)df.apply(label_race, axis=1)
        • 在较新的版本中,如果您收到“SettingWithCopyWarning”,您应该查看“assign”方法。见:stackoverflow.com/a/12555510/3015186
        【解决方案5】:

        由于这是“pandas new column from others”的第一个 Google 搜索结果,下面是一个简单的示例:

        import pandas as pd
        
        # make a simple dataframe
        df = pd.DataFrame({'a':[1,2], 'b':[3,4]})
        df
        #    a  b
        # 0  1  3
        # 1  2  4
        
        # create an unattached column with an index
        df.apply(lambda row: row.a + row.b, axis=1)
        # 0    4
        # 1    6
        
        # do same but attach it to the dataframe
        df['c'] = df.apply(lambda row: row.a + row.b, axis=1)
        df
        #    a  b  c
        # 0  1  3  4
        # 1  2  4  6
        

        如果你得到SettingWithCopyWarning,你也可以这样做:

        fn = lambda row: row.a + row.b # define a function for the new column
        col = df.apply(fn, axis=1) # get column data with an index
        df = df.assign(c=col.values) # assign values to column 'c'
        

        来源:https://stackoverflow.com/a/12555510/243392

        如果您的列名包含空格,您可以使用如下语法:

        df = df.assign(**{'some column name': col.values})
        

        这是applyassign 的文档。

        【讨论】:

        • 简短的回答,提炼到本质!
        • 当我执行df['c'] = df.apply(lambda row: row.a + row.b, axis=1) 时收到SettingWithCopyWarning 这是一个真正的问题,还是我不应该担心?
        • @Nate 我从来没有收到过这个警告——也许这取决于数据框中的数据?但我根据 2017 年的另一个答案修改了答案。
        • @Brian Burns 是否可以在数据框中执行此操作,但不是添加 row.a + row.b 我想从第 0 行中减去第 b 列第 1 行?
        【解决方案6】:

        上述答案完全有效,但存在矢量化解决方案,形式为numpy.select。这允许您定义条件,然后为这些条件定义输出,比使用 apply 更有效:


        首先,定义条件:

        conditions = [
            df['eri_hispanic'] == 1,
            df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
            df['eri_nat_amer'] == 1,
            df['eri_asian'] == 1,
            df['eri_afr_amer'] == 1,
            df['eri_hawaiian'] == 1,
            df['eri_white'] == 1,
        ]
        

        现在,定义相应的输出:

        outputs = [
            'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
        ]
        

        最后,使用numpy.select

        res = np.select(conditions, outputs, 'Other')
        pd.Series(res)
        

        0           White
        1        Hispanic
        2           White
        3           White
        4           Other
        5           White
        6     Two Or More
        7           White
        8    Haw/Pac Isl.
        9           White
        dtype: object
        

        为什么应该使用numpy.select 而不是apply?以下是一些性能检查:

        df = pd.concat([df]*1000)
        
        In [42]: %timeit df.apply(lambda row: label_race(row), axis=1)
        1.07 s ± 4.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
        
        In [44]: %%timeit
            ...: conditions = [
            ...:     df['eri_hispanic'] == 1,
            ...:     df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
            ...:     df['eri_nat_amer'] == 1,
            ...:     df['eri_asian'] == 1,
            ...:     df['eri_afr_amer'] == 1,
            ...:     df['eri_hawaiian'] == 1,
            ...:     df['eri_white'] == 1,
            ...: ]
            ...:
            ...: outputs = [
            ...:     'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
            ...: ]
            ...:
            ...: np.select(conditions, outputs, 'Other')
            ...:
            ...:
        3.09 ms ± 17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
        

        使用numpy.select 给我们极大地提高了性能,并且差异只会随着数据的增长而增加。

        【讨论】:

        • 这个解决方案被低估了。我知道我可以用 apply 做类似的事情,但我正在寻找替代方案,因为我必须对数千个文件执行该操作。很高兴我找到了你的帖子。
        • 我在创建类似内容时遇到了麻烦。我收到“系列的真值不明确......”错误消息。我的代码是 Kansas_City = ['ND', 'SD', 'NE', 'KS', 'MN', 'IA', 'MO'] 条件 = [df_merge['state_alpha'] in Kansas_City] 输出 = [' Kansas City'] df_merge['Region'] = np.select(conditions, outputs, 'Other') 有什么帮助吗?
        • 这应该是公认的答案。其他的都很好,但是一旦你处理更大的数据,这个是唯一有效的,而且运行速度非常快。
        • 我们可以使用 np.select 应用函数吗?例如:如果 df['col1']==x , reverse(df['col2'])
        • 我同意@mix。我仍然在学习 python、pandas 和 numpy,但这个解决方案被低估了。我有一组相当复杂的数据框需要更新,看起来就是这样。
        【解决方案7】:

        .apply() 接受一个函数作为第一个参数;像这样传入label_race函数:

        df['race_label'] = df.apply(label_race, axis=1)
        

        您无需制作 lambda 函数即可传入函数。

        【讨论】:

          猜你喜欢
          • 2021-08-01
          • 2017-03-11
          • 1970-01-01
          • 2022-06-10
          • 2021-12-24
          • 2019-07-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多