pandas根据其他列的值创建新列

Dav*_*ave 251 python numpy apply pandas

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

它几乎就像在每行中执行for循环一样,如果每条记录符合条件,它们将被添加到一个列表中并从原始列表中删除.

从下面的数据框中,我需要根据以下内容计算新列:

========================= CRITERIA ======================== =======

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”
Run Code Online (Sandbox Code Playgroud)

评论:如果西班牙裔美国人的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
Run Code Online (Sandbox Code Playgroud)

Tho*_*ber 315

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

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'
Run Code Online (Sandbox Code Playgroud)

您可能想要讨论这个问题,但似乎可以解决这个问题 - 注意进入函数的参数被认为是标记为"row"的Series对象.

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

df.apply (lambda row: label_race(row), axis=1)
Run Code Online (Sandbox Code Playgroud)

注意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
Run Code Online (Sandbox Code Playgroud)

如果您对这些结果感到满意,请再次运行,将结果发布到原始数据框中的新列中.

df['race_label'] = df.apply (lambda row: label_race(row), axis=1)
Run Code Online (Sandbox Code Playgroud)

结果数据框看起来像这样(向右滚动以查看新列):

      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
Run Code Online (Sandbox Code Playgroud)

  • 只是一个注意事项:如果你只是将行输入你的函数,你可以这样做:`df.apply(label_race,axis = 1)` (58认同)
  • 你可以简化:`df.apply(lambda row:label_race(row),axis = 1)`到`df.apply(label_race,axis = 1)` (9认同)
  • 不需要将函数包装在 lambda 中,这应该也可以工作:`df['race_label'] = df.apply (label_race, axis=1)` (8认同)
  • 在较新的版本中,如果你得到'SettingWithCopyWarning',你应该看看'assign'方法.请参阅:http://stackoverflow.com/a/12555510/3015186 (4认同)
  • 您可以编写一个新函数,查看'race_label'字段,并将结果发送到一个新字段,或者 - 我认为这可能更好,在这种情况下,编辑原始函数,更改最终`return'其他'`line to`返回row ['rno_defined']`,在if/then语句集找不到匹配的情况下(即当前,你看到'Other'),它应该替换该列中的值. (2认同)

Bri*_*rns 172

由于这是"其他人的pandas新专栏"的第一个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
Run Code Online (Sandbox Code Playgroud)

如果你得到了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'
Run Code Online (Sandbox Code Playgroud)

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

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

df = df.assign(**{'some column name': col.values})
Run Code Online (Sandbox Code Playgroud)

这里是申请分配的文件.

  • 简短的回答,精炼至本质! (3认同)
  • 当我执行 `df['c'] = df.apply(lambda row: row.a + row.b, axis=1)` 时,我得到了 `SettingWithCopyWarning` 这是一个真正的问题,还是我不应该担心吗? (2认同)
  • @Nate我从来没有得到过这样的警告 - 也许这取决于数据框中的数据?但我根据2017年的另一个答案给出了答案. (2认同)
  • 这如何回答这个问题?这个函数根本不使用任何条件(`if`)**加上**当你可以只执行`df['a'] + df['b'时,你甚至不应该在这里使用`.apply` ]`。 (2认同)

use*_*203 32

上面的答案是完全有效的,但存在一个矢量化的解决方案,形式为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,
]
Run Code Online (Sandbox Code Playgroud)

现在,定义相应的输出:

outputs = [
    'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
]
Run Code Online (Sandbox Code Playgroud)

最后,使用numpy.select:

res = np.select(conditions, outputs, 'Other')
pd.Series(res)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

为什么要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)
Run Code Online (Sandbox Code Playgroud)

使用numpy.select为我们提供了极大改进的性能,并且差异只会随着数据的增长而增加.

  • 这应该是公认的答案。其他的都很好,但一旦你处理更大的数据,这个是唯一有效的,而且它的速度快得惊人。 (5认同)
  • 这个解决方案被低估了。我知道我可以对apply执行类似的操作,但是由于我必须对数千个文件执行该操作,因此正在寻找替代方法。很高兴我找到了您的帖子。 (2认同)

Moh*_* ah 26

尝试这个,

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)
Run Code Online (Sandbox Code Playgroud)

开/关:

     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 
Run Code Online (Sandbox Code Playgroud)

使用.loc代替apply.

它改进了矢量化。

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

有关更多详细信息,请访问.loc 文档

性能指标:

接受的答案:

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)
Run Code Online (Sandbox Code Playgroud)

每个循环 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)
Run Code Online (Sandbox Code Playgroud)

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


Gab*_*ore 25

.apply()将函数作为第一个参数; 传递label_race函数如下:

df['race_label'] = df.apply(label_race, axis=1)
Run Code Online (Sandbox Code Playgroud)

您不需要使用lambda函数来传递函数.


cot*_*ail 9

如果我们检查它的源代码apply()它是 Python for 循环的语法糖(通过类apply_series_generator()的方法FrameApply)。因为它有 pandas 开销,所以它通常比 Python 循环慢。

\n

尽可能使用优化(矢量化)方法。如果必须使用循环,请使用@numba.jit装饰器。

\n

1. 不要用于apply()if-else 梯子

\n

df.apply()这几乎是 pandas 中执行此操作最慢的方法。如user3483203Mohamed Thasin ah的答案所示,根据数据帧的大小,np.select()可能比产生相同输出df.loc快 50-300 倍。df.apply()

\n

事实上,使用模块中的装饰器的循环实现(与 类似apply())比和快(大约 50-60%)。1@jitnumbadf.locnp.select

\n

Numba 适用于 numpy 数组,因此在使用jit装饰器之前,需要将数据帧转换为 numpy 数组。然后通过检查循环中的条件将值填充到预初始化的空数组中。由于 numpy 数组没有列名,因此您必须通过循环中的索引来访问列。jitted 函数中 if-else 梯子中最不方便的部分apply()是通过索引访问列。否则它的实现几乎相同。

\n
import numpy as np\nimport numba as nb\n@nb.jit(nopython=True)\ndef conditional_assignment(arr, res):    \n    length = len(arr)\n    for i in range(length):\n        if arr[i][3] == 1:\n            res[i] = \'Hispanic\'\n        elif arr[i][0] + arr[i][1] + arr[i][2] + arr[i][4] + arr[i][5] > 1:\n            res[i] = \'Two Or More\'\n        elif arr[i][0]  == 1:\n            res[i] = \'Black/AA\'\n        elif arr[i][1] == 1:\n            res[i] = \'Asian\'\n        elif arr[i][2] == 1:\n            res[i] = \'Haw/Pac Isl.\'\n        elif arr[i][4] == 1:\n            res[i] = \'A/I AK Native\'\n        elif arr[i][5] == 1:\n            res[i] = \'White\'\n        else:\n            res[i] = \'Other\'\n    return res\n\n# the columns with the boolean data\ncols = [c for c in df.columns if c.startswith(\'eri_\')]\n# initialize an empty array to be filled in a loop\n# for string dtype arrays, we need to know the length of the longest string\n# and use it to set the dtype\nres = np.empty(len(df), dtype=f"<U{len(\'A/I AK Native\')}")\n# pass the underlying numpy array of `df[cols]` into the jitted function\ndf[\'rno_defined\'] = conditional_assignment(df[cols].values, res)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

2.不要用于apply()数字运算

\n

如果您需要通过添加两列来添加新行,您的第一直觉可能是编写

\n
df[\'c\'] = df.apply(lambda row: row[\'a\'] + row[\'b\'], axis=1)\n
Run Code Online (Sandbox Code Playgroud)\n

但取而代之的是,使用sum(axis=1)方法逐行添加(+如果只有几列,则使用运算符):

\n
df[\'c\'] = df[[\'a\',\'b\']].sum(axis=1)\n# equivalently\ndf[\'c\'] = df[\'a\'] + df[\'b\']\n
Run Code Online (Sandbox Code Playgroud)\n

根据数据帧的大小,sum(1)可能比apply().

\n

事实上,您几乎不需要apply()对 pandas 数据框进行数值运算,因为它为大多数运算提供了优化方法:加法 ( ) sum(1)、减法 (sub()diff())、乘法 ( ) prod(1)、除法 (div()或) 、/幂 ( ) 、、、、、、、等都可以在整个数据帧上执行,而无需.pow()>>===%//&|apply()

\n

例如,假设您想使用以下规则创建一个新列:

\n
IF [colC] > 0 THEN RETURN [colA] * [colB]\nELSE RETURN [colA] / [colB]\n
Run Code Online (Sandbox Code Playgroud)\n

使用优化的 pandas 方法,可以写为

\n
df[\'new\'] = df[[\'colA\',\'colB\']].prod(1).where(df[\'colC\']>0, df[\'colA\'] / df[\'colB\'])\n
Run Code Online (Sandbox Code Playgroud)\n

等效的apply()解决方案是:

\n
df[\'new\'] = df.apply(lambda row: row.colA * row.colB if row.colC > 0 else row.colA / row.colB, axis=1)\n
Run Code Online (Sandbox Code Playgroud)\n

使用优化方法的方法比apply()20k 行数据帧的等效方法快 250 倍。这种差距只会随着数据大小的增加而增加(对于具有 100 万行的数据帧,速度快了 365 倍),并且时间差异将变得越来越明显。2

\n
\n
\n 1:在下面的结果中,我使用具有 2400 万行的数据帧(这是我可以在我的计算机上构建的最大帧)展示了三种方法的性能。对于较小的帧,numba-jitted 函数的运行速度始终比其他两个函数快至少 50%(您可以自己检查)。\n
def pd_loc(df):\n    df[\'rno_defined\'] = \'Other\'\n    df.loc[df[\'eri_nat_amer\'] == 1, \'rno_defined\'] = \'A/I AK Native\'\n    df.loc[df[\'eri_asian\'] == 1, \'rno_defined\'] = \'Asian\'\n    df.loc[df[\'eri_afr_amer\'] == 1, \'rno_defined\'] = \'Black/AA\'\n    df.loc[df[\'eri_hawaiian\'] == 1, \'rno_defined\'] = \'Haw/Pac Isl.\'\n    df.loc[df[\'eri_white\'] == 1, \'rno_defined\'] = \'White\'\n    df.loc[df[[\'eri_afr_amer\', \'eri_asian\', \'eri_hawaiian\', \'eri_nat_amer\', \'eri_white\']].sum(1) > 1, \'rno_defined\'] = \'Two Or More\'\n    df.loc[df[\'eri_hispanic\'] == 1, \'rno_defined\'] = \'Hispanic\'\n    return df\n\ndef np_select(df):\n    conditions = [df[\'eri_hispanic\'] == 1,\n                  df[[\'eri_afr_amer\', \'eri_asian\', \'eri_hawaiian\', \'eri_nat_amer\', \'eri_white\']].sum(1).gt(1),\n                  df[\'eri_nat_amer\'] == 1,\n                  df[\'eri_asian\'] == 1,\n                  df[\'eri_afr_amer\'] == 1,\n                  df[\'eri_hawaiian\'] == 1,\n                  df[\'eri_white\'] == 1]\n    outputs = [\'Hispanic\', \'Two Or More\', \'A/I AK Native\', \'Asian\', \'Black/AA\', \'Haw/Pac Isl.\', \'White\']\n    df[\'rno_defined\'] = np.select(conditions, outputs, \'Other\')\n    return df\n\n\n@nb.jit(nopython=True)\ndef conditional_assignment(arr, res):\n    \n    length = len(arr)\n    for i in range(length):\n        if arr[i][3] == 1 :\n            res[i] = \'Hispanic\'\n        elif arr[i][0] + arr[i][1] + arr[i][2] + arr[i][4] + arr[i][5] > 1 :\n            res[i] = \'Two Or More\'\n        elif arr[i][0]  == 1:\n            res[i] = \'Black/AA\'\n        elif arr[i][1] == 1:\n            res[i] = \'Asian\'\n        elif arr[i][2] == 1:\n            res[i] = \'Haw/Pac Isl.\'\n        elif arr[i][4] == 1 :\n            res[i] = \'A/I AK Native\'\n        elif arr[i][5] == 1:\n            res[i] = \'White\'\n        else:\n            res[i] = \'Other\'\n            \n    return res\n\ndef nb_loop(df):\n    cols = [c for c in df.columns if c.startswith(\'eri_\')]\n    res = np.empty(len(df), dtype=f"<U{len(\'A/I AK Native\')}")\n    df[\'rno_defined\'] = conditional_assignment(df[cols].values, res)\n    return df\n\n# df with 24mil rows\nn = 4_000_000\ndf = pd.DataFrame({\n    \'eri_afr_amer\': [0, 0, 0, 0, 0, 0]*n, \n    \'eri_asian\': [1, 0, 0, 0, 0, 0]*n, \n    \'eri_hawaiian\': [0, 0, 0, 1, 0, 0]*n, \n    \'eri_hispanic\': [0, 1, 0, 0, 1, 0]*n, \n    \'eri_nat_amer\': [0, 0, 0, 0, 1, 0]*n, \n    \'eri_white\': [0, 0, 1, 1, 0, 0]*n\n}, dtype=\'int8\')\ndf.insert(0, \'name\', [\'MOST\', \'CRUISE\', \'DEPP\', \'DICAP\', \'BRANDO\', \'HANKS\']*n)\n\n%timeit nb_loop(df)\n# 5.23 s \xc2\xb1 45.2 ms per loop (mean \xc2\xb1 std. dev. of 10 runs, 10 loops each)\n\n%timeit pd_loc(df)\n# 7.97 s \xc2\xb1 28.8 ms per loop (mean \xc2\xb1 std. dev. of 10 runs, 10 loops each)\n\n%timeit np_select(df)\n# 8.5 s \xc2\xb1 39.6 ms per loop (mean \xc2\xb1 std. dev. of 10 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

2:在下面的结果中,我使用 20k 行和 100 万行的数据帧展示了两种方法的性能。对于较小的帧,间隙较小,因为优化方法有开销 whileapply()是循环。随着帧大小的增加,矢量化开销成本会随着代码的整体运行时间而减少,同时apply()仍然在帧上循环。

\n
n = 20_000 # 1_000_000\ndf = pd.DataFrame(np.random.rand(n,3)-0.5, columns=[\'colA\',\'colB\',\'colC\'])\n\n%timeit df[[\'colA\',\'colB\']].prod(1).where(df[\'colC\']>0, df[\'colA\'] / df[\'colB\'])\n# n = 20000: 2.69 ms \xc2\xb1 23.5 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n# n = 1000000: 86.2 ms \xc2\xb1 441 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n\n%timeit df.apply(lambda row: row.colA * row.colB if row.colC > 0 else row.colA / row.colB, axis=1)\n# n = 20000: 679 ms \xc2\xb1 33.8 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n# n = 1000000: 31.5 s \xc2\xb1 587 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n


Kar*_*tel 5

根据标准的复杂程度选择方法

对于下面的示例 - 为了显示新列的多种类型的规则 - 我们将假设一个包含 columns 和 的 DataFrame 'red''green'其中'blue'包含 0 到 1 范围内的浮点值。

一般情况:.apply

只要计算新值的必要逻辑可以写成同一行中其他值的函数,我们就可以使用的方法来获得所需的结果。编写该函数,使其接受单个参数,即单行输入:.applyDataFrame

def as_hex(value):
    # clamp to avoid rounding errors etc.
    return min(max(0, int(value * 256)), 255)

def hex_color(row):
    r, g, b = as_hex(row['red']), as_hex(row['green']), as_hex(row['blue'])
    return f'#{r:02x}{g:02x}{b:02x}'
Run Code Online (Sandbox Code Playgroud)

函数本身不要在名称后写括号)传递给.apply,并指定(意味着向分类函数axis=1提供行,以便计算列-而不是相反)。因此:

df['hex_color'] = df.apply(hex_color, axis=1)
Run Code Online (Sandbox Code Playgroud)

请注意,包装lambda不是必需,因为我们没有绑定任何参数或以其他方式修改函数。

.apply步骤是必要的,因为转换函数本身不是矢量化的。因此,像这样的天真的方法df['color'] = hex_color(df)是行不通的(示例问题)。

这个工具功能强大,但效率低下。为了获得最佳性能,请在适用的情况下使用更具体的方法

带条件的多项选择:或用ornumpy.select重复赋值df.locdf.where

假设我们对颜色值进行阈值处理,并计算粗略的颜色名称,如下所示:

def additive_color(row):
    # Insert here: logic that takes values from the `row` and computes
    # the desired cell value for the new column in that row.
    # The `row` is an ordinary `Series` object representing a row of the
    # original `DataFrame`; it can be indexed with column names, thus:
    if row['red'] > 0.5:
        if row['green'] > 0.5:
            return 'white' if row['blue'] > 0.5 else 'yellow'
        else:
            return 'magenta' if row['blue'] > 0.5 else 'red'
    elif row['green'] > 0.5:
        return 'cyan' if row['blue'] > 0.5 else 'green'
    else:
        return 'blue' if row['blue'] > 0.5 else 'black'
Run Code Online (Sandbox Code Playgroud)

在这种情况下 -在 3.10 及更高版本中,分类函数将是if/else梯子或match/ - 我们可能会使用 获得更快的性能。casenumpy.select

这种方法的工作原理非常不同。首先,计算每个条件适用的数据掩码:

black = (df['red'] <= 0.5) & (df['green'] <= 0.5) & (df['blue'] <= 0.5)
white = (df['red'] > 0.5) & (df['green'] > 0.5) & (df['blue'] > 0.5)
Run Code Online (Sandbox Code Playgroud)

要调用numpy.select,我们需要两个并行序列 - 一个条件,另一个对应的值:

df['color'] = np.select(
    [white, black],
    ['white', 'black'],
    'colorful'
)
Run Code Online (Sandbox Code Playgroud)

可选的第三个参数指定在不满足任何条件时要使用的值。(作为练习:填写其余条件,并在没有第三个参数的情况下尝试。)

类似的方法是根据每个条件进行重复分配。首先分配默认值,然后使用df.loc为每个条件分配特定值:

df['color'] = 'colorful'
df.loc[white, 'color'] = 'white'
df.loc[black, 'color'] = 'black'
Run Code Online (Sandbox Code Playgroud)

或者,df.where可以用来做作业。然而,像这样使用,会在不满足df.where条件的地方分配指定的值,因此必须反转条件:

df['color'] = 'colorful'
df['color'] = df['color'].where(~white, 'white').where(~black, 'black')
Run Code Online (Sandbox Code Playgroud)

简单的数学运算:内置数学运算符和广播

例如,apply基于 - 的方法如下:

def brightness(row):
    return row['red'] * .299 + row['green'] * .587 + row['blue'] * .114

df['brightness'] = df.apply(brightness, axis=1)
Run Code Online (Sandbox Code Playgroud)

可以通过广播运算符来编写,以获得更好的性能(并且也更简单):

df['brightness'] = df['red'] * .299 + df['green'] * .587 + df['blue'] * .114
Run Code Online (Sandbox Code Playgroud)

作为练习,这是第一个以这种方式重做的示例:

def as_hex(column):
    scaled = (column * 256).astype(int)
    clamped = scaled.where(scaled >= 0, 0).where(scaled <= 255, 255)
    return clamped.apply(lambda i: f'{i:02x}')

df['hex_color'] = '#' + as_hex(df['red']) + as_hex(df['green']) + as_hex(df['blue'])
Run Code Online (Sandbox Code Playgroud)

我无法找到将整数值格式化为十六进制字符串的矢量化等效项,因此.apply仍然在内部使用 - 这意味着全速损失仍然发挥作用。尽管如此,这还是展示了一些通用技术。

有关更多详细信息和示例,请参阅cottontail的回答