如何对单个列使用apply()函数?

2024-12-13 08:36:00
admin
原创
172
摘要:问题描述:我有一个包含多列的 pandas 数据框。我想仅更改第一列的值而不影响其他列。如何apply()在 pandas 中做到这一点?解决方案 1:给定一个示例数据框df: a b 0 1 2 1 2 3 2 3 4 3 4 5 你想要的是:df['a'] = df['a'].app...

问题描述:

我有一个包含多列的 pandas 数据框。我想仅更改第一列的值而不影响其他列。如何apply()在 pandas 中做到这一点?


解决方案 1:

给定一个示例数据框df

   a  b
0  1  2
1  2  3
2  3  4
3  4  5

你想要的是:

df['a'] = df['a'].apply(lambda x: x + 1)

返回:

   a  b
0  2  2
1  3  3
2  4  4
3  5  5

解决方案 2:

对于单个列最好使用map(),如下所示:

df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9



df['a'] = df['a'].map(lambda a: a / 2.)

      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9

解决方案 3:

给定以下数据框df和函数complex_function

import pandas as pd

def complex_function(x, y=0):
    if x > 5 and x > y:
        return 1
    else:
        return 2

df = pd.DataFrame(data={'col1': [1, 4, 6, 2, 7], 'col2': [6, 7, 1, 2, 8]})
   col1  col2
0     1     6
1     4     7
2     6     1
3     2     2
4     7     8

有几种解决方案可以apply()只在一列上使用。下面我将详细解释它们。

一、简单解决方案

最直接的解决方案是来自@Fabio Lamanna 的解决方案:

df['col1'] = df['col1'].apply(complex_function)

输出:

   col1  col2
0     2     6
1     2     7
2     1     1
3     2     2
4     1     8

只修改了第一列,第二列保持不变。解决方案非常漂亮。它只有一行代码,读起来几乎像英文:“取‘col1’并对其应用函数 complex_function。

但是,如果您需要另一列的数据,例如“col2”,则它将不起作用。如果您想将“col2”的值传递给变量ycomplex_function则需要其他东西。

II. 使用整个数据框的解决方案

或者,您可以使用本SO帖子或本帖子中所述的整个数据框:

df['col1'] = df.apply(lambda x: complex_function(x['col1']), axis=1)

或者如果你(像我一样)更喜欢不使用 lambda 函数的解决方案:

def apply_complex_function(x):
    return complex_function(x['col1'])
df['col1'] = df.apply(apply_complex_function, axis=1) 

此解决方案中有很多内容需要解释。该apply()函数适用于pd.Series pd.DataFrame。但您不能使用df['col1'] = df.apply(complex_function).loc[:, 'col1'],因为它会抛出ValueError

因此,您需要提供要使用哪一列的信息。更复杂的是,该apply()函数只接受可调用函数。为了解决这个问题,您需要定义一个以列为参数的 (lambda) 函数x['col1'];即,我们将列信息包装在另一个函数中。

不幸的是,axis 参数的默认值为零 ( axis=0),这意味着它将尝试按列执行而不是按行执行。这在第一个解决方案中不是问题,因为我们给出了apply()pd.Series但现在输入是一个数据框,我们必须明确 ( axis=1)。(我很奇怪我经常忘记这一点。)

您是否喜欢带有 lambda 函数的版本是主观的。在我看来,即使没有引入 lambda 函数,这行代码也足够复杂,难以阅读。您只需要 (lambda) 函数作为包装器。它只是样板代码。读者不应该为此烦恼。

现在,您可以轻松修改此解决方案以考虑第二列:

def apply_complex_function(x):
    return complex_function(x['col1'], x['col2'])
df['col1'] = df.apply(apply_complex_function, axis=1)

输出:

   col1  col2
0     2     6
1     2     7
2     1     1
3     2     2
4     2     8

在索引 4 处,值从 1 变为 2,因为第一个条件 7 > 5为真,但第二个条件7 > 8为假。

请注意,您只需要更改第一行代码(即函数),而不需要更改第二行。


附注

切勿将列信息放入你的函数中。

def bad_idea(x):
    return x['col1'] ** 2

通过这样做,您可以使通用函数依赖于列名!这是一个坏主意,因为下次您想使用这个函数时,您就不能使用了。更糟糕的是:也许您会重命名不同数据框中的列,只是为了让它与现有函数一起工作。(我经历过,做过。这是一个危险的局面!)


III. 不使用的替代解决方案apply()

尽管 OP 明确要求使用 解决方案apply(),但也提出了其他解决方案。例如,@George Petrov 的答案建议使用map();@Thibaut Dubernet 的答案建议使用assign()

我完全同意这很少apply()是最好的解决方案,因为apply()不是矢量化的。它是一个元素级操作,具有昂贵的函数调用和开销pd.Series

其中一个原因apply()是你想使用现有函数,而性能不是问题。或者你的函数太复杂,没有矢量化版本。

另一个使用原因apply()是与 groupby() 结合使用。请注意DataFrame.apply()GroupBy.apply()是不同的函数。

因此考虑一些替代方案是有意义的:

  • map()仅适用于pd.Series,但接受字典和pd.Series作为输入。使用map()函数几乎可以与使用 互换apply()。它比 更快apply()。有关更多详细信息,请参阅此 SO 帖子。

df['col1'] = df['col1'].map(complex_function)
  • applymap()对于数据帧来说几乎是相同的。它不支持pd.Series并且总是返回数据帧。但是,它可以更快。文档指出:“在当前实现中,在第一列/行上applymap调用func两次以决定是否可以采用快速或慢速代码路径。 ”但如果性能真的很重要,你应该寻找替代路线。

df['col1'] = df.applymap(complex_function).loc[:, 'col1']
  • assign()不是 的可行替代品apply()。它仅在最基本的用例中具有类似的行为。它不适用于complex_function。您仍然需要,apply()如下面的示例所示。的主要用例assign()是方法链,因为它返回数据框而不更改原始数据框。

df['col1'] = df.assign(col1=df.col1.apply(complex_function))

附件:如何加速apply()

我在这里只提到它,因为它是由其他答案建议的,例如@durjoy。该列表并不详尽:

  1. 不要使用apply()这不是开玩笑。对于大多数数字运算,pandas 中都存在矢量化方法。通常可以使用布尔索引和的组合来重构 if/else 块.loc。我的示例complex_function可以以这种方式重构。

  2. 重构为 Cython。如果您有一个复杂的方程式,并且方程式的参数位于数据框中,这可能是一个好主意。查看官方 pandas 用户指南了解更多信息。

  3. 使用raw=True参数。理论上,apply() 如果您只是应用 NumPy 缩减函数,这应该会提高的性能,因为的开销pd.Series被消除了。当然,您的函数必须接受 ndarray。您必须将函数重构为 NumPy。通过这样做,您将获得巨大的性能提升。

  4. 使用第三方软件包。您应该尝试的第一件事是Numba。我不知道@durjoy 提到的swifter;可能还有很多其他软件包值得在这里提及。

  5. 尝试/失败/重复。如上所述,map()并且applymap()可以更快 - 取决于用例。只需对不同版本进行计时并选择最快的版本。这种方法是最繁琐的,性能提升最少。

解决方案 4:

你根本不需要函数。你可以直接对整个列进行操作。

示例数据:

>>> df = pd.DataFrame({'a': [100, 1000], 'b': [200, 2000], 'c': [300, 3000]})
>>> df

      a     b     c
0   100   200   300
1  1000  2000  3000

列中所有值的一半a

>>> df.a = df.a / 2
>>> df

     a     b     c
0   50   200   300
1  500  2000  3000

解决方案 5:

尽管给出的响应是正确的,但它们会修改初始数据框,这并不总是可取的(并且,鉴于 OP 要求“使用apply”的示例,可能是他们想要一个返回新数据框的版本,就像apply这样)。

这可以使用assign:它对现有列有效assign,如文档所述(重点是我的):

为 DataFrame 分配新列。

返回一个新对象,其中包含所有原始列和新列。重新分配的现有列将被覆盖

简而言之:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame([{'a': 15, 'b': 15, 'c': 5}, {'a': 20, 'b': 10, 'c': 7}, {'a': 25, 'b': 30, 'c': 9}])

In [3]: df.assign(a=lambda df: df.a / 2)
Out[3]: 
      a   b  c
0   7.5  15  5
1  10.0  10  7
2  12.5  30  9

In [4]: df
Out[4]: 
    a   b  c
0  15  15  5
1  20  10  7
2  25  30  9

请注意,该函数将传递整个数据框,而不仅仅是您要修改的列,因此您需要确保在 lambda 中选择正确的列。

解决方案 6:

如果你真的关心你的应用函数的执行速度,并且你有一个庞大的数据集需要处理,那么你可以使用 swifter 来加快执行速度,下面是在 pandas 数据框上使用 swifter 的一个示例:

import pandas as pd
import swifter

def fnc(m):
    return m*3+4

df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})

# apply a self created function to a single column in pandas
df["y"] = df.m.swifter.apply(fnc)

这将使您的所有 CPU 核心都能计算结果,因此它将比普通的应用函数快得多。尝试一下,如果它对您有用,请告诉我。

解决方案 7:

让我尝试使用日期时间进行复杂的计算,并考虑空值或空格。我正在减少日期时间列上的 30 年,并使用apply方法以及lambda转换日期时间格式。Lineif x != '' else x将相应地处理所有空格或空值。

df['Date'] = df['Date'].fillna('')
df['Date'] = df['Date'].apply(lambda x : ((datetime.datetime.strptime(str(x), '%m/%d/%Y') - datetime.timedelta(days=30*365)).strftime('%Y%m%d')) if x != '' else x)

解决方案 8:

如果需要修改列,请先复制数据框

这里的许多答案都建议修改某些列并将新值分配给旧列。收到SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.警告是很常见的。当您的数据框是从另一个数据框创建的但不是正确副本时,就会发生这种情况。

为了消除此警告,请复印一份并重新分配。

df = df.copy()
df['a'] = df['a'].apply('add', other=1)
apply()只需要函数名称

只需将函数名称传递给 即可调用该函数apply()(无需lambda)。如果函数需要其他参数,则可以将它们作为关键字参数传递,也可以将位置参数作为 传递args=。例如,假设您的数据框中有文件路径,并且您需要读取这些路径中的文件。

def read_data(path, sep=',', usecols=[0]):
    return pd.read_csv(path, sep=sep, usecols=usecols)

df = pd.DataFrame({'paths': ['../x/yz.txt', '../u/vw.txt']})

df['paths'].apply(read_data)                            # you don't need lambda

df['paths'].apply(read_data, args=(',', [0, 1]))        # pass the positional arguments to `args=`

df['paths'].apply(read_data, sep=',', usecols=[0, 1])   # pass as keyword arguments
不要应用函数,直接调用适当的方法

通过 将自定义函数应用于列几乎从来都不是理想的选择apply()。由于apply()是具有 pandas 开销的 Python 循环的语法糖,因此它通常比在列表推导中调用相同函数要慢,更不用说调用优化的 pandas 方法了。几乎所有数字运算符都可以直接应用于列,并且它们都有相应的方法。

# add 1 to every element in column `a`
df['a'] += 1

# for every row, subtract column `a` value from column `b` value
df['c'] = df['b'] - df['a']

如果您想应用具有 if-else 块的函数,那么您可能应该使用numpy.where()或numpy.select()。它要快得多。如果您有超过 10k 行的数据,您会立即注意到差异。

例如,如果您有一个类似于func()下面的自定义函数,那么您不必将其应用于列,而是可以直接对列进行操作并使用返回值numpy.select()

def func(row):
    if row == 'a':
        return 1
    elif row == 'b':
        return 2
    else:
        return -999

# instead of applying a `func` to each row of a column, use `numpy.select` as below

import numpy as np
conditions = [df['col'] == 'a', df['col'] == 'b']
choices = [1, 2]
df['new'] = np.select(conditions, choices, default=-999)

如您所见,numpy.select()与 if-else 阶梯式结构相比,语法差异非常小;只需将条件和选择分成单独的列表即可。有关其他选项,请查看此答案。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   4027  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2755  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   86  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   99  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   87  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用