我有一个有两列的熊猫数据框架。我需要在不影响第二列的情况下改变第一列的值,并返回整个数据框架,只是第一列的值改变了。我如何在熊猫中使用apply()来做到这一点?
当前回答
让我使用datetime并考虑null或空格来尝试一个复杂的计算。我在一个datetime列上减少30年,并使用apply方法以及lambda和转换datetime格式。行if x != "否则x将相应地处理所有空格或null。
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)
其他回答
尽管给出的响应是正确的,但它们修改了初始数据帧,这并不总是可取的(并且,给定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中选择了正确的列。
对于单列最好使用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
让我使用datetime并考虑null或空格来尝试一个复杂的计算。我在一个datetime列上减少30年,并使用apply方法以及lambda和转换datetime格式。行if x != "否则x将相应地处理所有空格或null。
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)
如果您需要修改列,请先复制您的数据框架
这里的许多回答建议修改一些列并将新值赋给旧列。通常会得到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梯子的语法差异非常小;只需要将条件和选项分离到单独的列表中。对于其他选项,请查看这个答案。
给定一个样本数据帧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
推荐文章
- 将Pandas或Numpy Nan替换为None以用于MysqlDB
- 使用pandas对同一列进行多个聚合
- 使用Python解析HTML
- django MultiValueDictKeyError错误,我如何处理它
- 如何在for循环期间修改列表条目?
- 我如何在Django中创建一个鼻涕虫?
- 没有名为'django.core.urlresolvers'的模块
- 蟒蛇导出环境文件
- Django - makemigrations -未检测到任何更改
- SQLAlchemy:引擎、连接和会话差异
- 在Python Pandas中删除多个列中的所有重复行
- 更改pandas DataFrame中的特定列名
- 将Pandas多索引转换为列
- 熊猫在每组中获得最高的n个记录
- 熊猫数据帧得到每组的第一行