使用条件在熊猫数据框中生成新列
Posted
技术标签:
【中文标题】使用条件在熊猫数据框中生成新列【英文标题】:Using conditional to generate new column in pandas dataframe 【发布时间】:2015-01-18 10:33:20 【问题描述】:我有一个看起来像这样的熊猫数据框:
portion used
0 1 1.0
1 2 0.3
2 3 0.0
3 4 0.8
我想基于used
列创建一个新列,使df
看起来像这样:
portion used alert
0 1 1.0 Full
1 2 0.3 Partial
2 3 0.0 Empty
3 4 0.8 Partial
创建一个新的alert
列基于
如果used
是1.0
,则alert
应该是Full
。
如果used
是0.0
,则alert
应该是Empty
。
否则,alert
应该是 Partial
。
最好的方法是什么?
【问题讨论】:
Pandas conditional creation of a series/dataframe column 的可能重复项 【参考方案1】:您可以定义一个返回不同状态“Full”、“Partial”、“Empty”等的函数,然后使用df.apply
将该函数应用于每一行。请注意,您必须传递关键字参数 axis=1
以确保它将函数应用于行。
import pandas as pd
def alert(row):
if row['used'] == 1.0:
return 'Full'
elif row['used'] == 0.0:
return 'Empty'
elif 0.0 < row['used'] < 1.0:
return 'Partial'
else:
return 'Undefined'
df = pd.DataFrame(data='portion':[1, 2, 3, 4], 'used':[1.0, 0.3, 0.0, 0.8])
df['alert'] = df.apply(alert, axis=1)
# portion used alert
# 0 1 1.0 Full
# 1 2 0.3 Partial
# 2 3 0.0 Empty
# 3 4 0.8 Partial
【讨论】:
很好的例子。为了使代码更清晰一点(并且由于您使用的是axis=1
),您可以将参数c
重命名为row
,这样很明显您可以访问行中的所有值函数。【参考方案2】:
或者你可以这样做:
import pandas as pd
import numpy as np
df = pd.DataFrame(data='portion':np.arange(10000), 'used':np.random.rand(10000))
%%timeit
df.loc[df['used'] == 1.0, 'alert'] = 'Full'
df.loc[df['used'] == 0.0, 'alert'] = 'Empty'
df.loc[(df['used'] >0.0) & (df['used'] < 1.0), 'alert'] = 'Partial'
它提供相同的输出,但在 10000 行上运行速度大约快 100 倍:
100 loops, best of 3: 2.91 ms per loop
然后使用应用:
%timeit df['alert'] = df.apply(alert, axis=1)
1 loops, best of 3: 287 ms per loop
我想选择取决于您的数据框有多大。
【讨论】:
关于 %timeit 的问题:如果第一个循环 100 次 @2.91 秒,这是否意味着总时间为 291 毫秒,略长于警报功能完成 1 次循环的 287 毫秒时间? 本例中的 1 个循环在%%timeit
之后运行 3 行代码。 timeit
程序会自动选择循环数(在这种情况下为 100),以便在一些合理的“超时”内提供更稳健的测量(即,如果运行 1 个循环比此“超时”长,则只有 1 个循环,例如在“使用应用”的情况下)。 timeit
的结果应该在“每 1 个循环”的基础上进行比较。这就是为什么有“运行速度大约快 100 倍”短语的原因:耗时 2.91 毫秒的 1 个循环比耗时 287 毫秒的 1 个循环快大约 100 倍。【参考方案3】:
使用np.where
,通常很快
In [845]: df['alert'] = np.where(df.used == 1, 'Full',
np.where(df.used == 0, 'Empty', 'Partial'))
In [846]: df
Out[846]:
portion used alert
0 1 1.0 Full
1 2 0.3 Partial
2 3 0.0 Empty
3 4 0.8 Partial
时间
In [848]: df.shape
Out[848]: (100000, 3)
In [849]: %timeit df['alert'] = np.where(df.used == 1, 'Full', np.where(df.used == 0, 'Empty', 'Partial'))
100 loops, best of 3: 6.17 ms per loop
In [850]: %%timeit
...: df.loc[df['used'] == 1.0, 'alert'] = 'Full'
...: df.loc[df['used'] == 0.0, 'alert'] = 'Empty'
...: df.loc[(df['used'] >0.0) & (df['used'] < 1.0), 'alert'] = 'Partial'
...:
10 loops, best of 3: 21.9 ms per loop
In [851]: %timeit df['alert'] = df.apply(alert, axis=1)
1 loop, best of 3: 2.79 s per loop
【讨论】:
如果您的情况不太复杂,这应该是公认的答案。【参考方案4】:无法发表评论,因此提出新的答案:改进 Ffisegydd 的方法,您可以使用字典和 dict.get()
方法使传递给 .apply()
的函数更易于管理:
import pandas as pd
def alert(c):
mapping = 1.0: 'Full', 0.0: 'Empty'
return mapping.get(c['used'], 'Partial')
df = pd.DataFrame(data='portion':[1, 2, 3, 4], 'used':[1.0, 0.3, 0.0, 0.8])
df['alert'] = df.apply(alert, axis=1)
根据用例,您可能还想在函数定义之外定义字典。
【讨论】:
【参考方案5】:df['TaxStatus'] = np.where(df.Public == 1, True, np.where(df.Public == 2, False))
这似乎可行,但 ValueError 除外:应给出 x 和 y 两者或都不给出
【讨论】:
【参考方案6】:将np.select()
用于>2 个条件
鉴于像 OP 的示例这样的 >2 个条件,np.select()
比嵌套多个级别的 np.where()
干净得多(并且同样快)。
将条件/选择定义为两个列表(按元素配对),带有可选的默认值(“else”情况):
conditions = [
df.used.eq(0),
df.used.eq(1),
]
choices = [
'Empty',
'Full',
]
df['alert'] = np.select(conditions, choices, default='Partial')
或将条件/选择定义为可维护性的字典(在进行添加/修订时更容易使它们正确配对):
conditions =
'Empty': df.used.eq(0),
'Full': df.used.eq(1),
df['alert'] = np.select(conditions.values(), conditions.keys(), default='Partial')
np.select()
非常快
具有 5 个条件(满、高、中、低、空)的时序:
df = pd.DataFrame('used': np.random.randint(10 + 1, size=10)).div(10)
【讨论】:
您是否有代码或示例说明您如何为此答案创建此图表?我很想把这个展示给一些人。 那是perfplot @scarebear以上是关于使用条件在熊猫数据框中生成新列的主要内容,如果未能解决你的问题,请参考以下文章