我有一个有两列的熊猫数据框架。我需要在不影响第二列的情况下改变第一列的值,并返回整个数据框架,只是第一列的值改变了。我如何在熊猫中使用apply()来做到这一点?
当前回答
如果你真的很关心你的apply函数的执行速度,并且你有一个巨大的数据集要处理,你可以使用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核心计算结果,因此它将比普通应用函数快得多。试着让我知道它是否对你有用。
其他回答
如果您需要修改列,请先复制您的数据框架
这里的许多回答建议修改一些列并将新值赋给旧列。通常会得到SettingWithCopyWarning:一个值正试图从数据帧(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梯子的语法差异非常小;只需要将条件和选项分离到单独的列表中。对于其他选项,请查看这个答案。
尽管给出的响应是正确的,但它们修改了初始数据帧,这并不总是可取的(并且,给定OP要求“使用apply”示例,可能它们想要一个返回新数据帧的版本,就像apply那样)。
这可以通过使用assign实现:如文档所述(重点是我的),对现有列进行赋值是有效的:
为数据框架分配新列。 返回一个包含所有原始列和新列的新对象。重新分配的现有列将被覆盖。
简而言之:
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中选择了正确的列。
给定一个样本数据帧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
如果你真的很关心你的apply函数的执行速度,并且你有一个巨大的数据集要处理,你可以使用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核心计算结果,因此它将比普通应用函数快得多。试着让我知道它是否对你有用。
对于单列最好使用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
推荐文章
- 如何在Python中进行热编码?
- 如何嵌入HTML到IPython输出?
- 在Python生成器上使用“send”函数的目的是什么?
- 是否可以将已编译的.pyc文件反编译为.py文件?
- Django模型表单对象的自动创建日期
- 在Python中包装长行
- 如何计算两个时间串之间的时间间隔
- 我如何才能找到一个Python函数的参数的数量?
- 您可以使用生成器函数来做什么?
- 将Python诗歌与Docker集成
- 提取和保存视频帧
- 使用请求包时出现SSL InsecurePlatform错误
- 如何检索Pandas数据帧中的列数?
- except:和except的区别:
- 错误:“字典更新序列元素#0的长度为1;2是必需的”