Pandas 有条件地创建系列/数据框列
Posted
技术标签:
【中文标题】Pandas 有条件地创建系列/数据框列【英文标题】:Pandas conditional creation of a series/dataframe column 【发布时间】:2021-12-28 02:59:29 【问题描述】:我有一个如下所示的数据框:
Type Set
1 A Z
2 B Z
3 B X
4 C Y
我想将另一列添加到数据帧(或生成一系列),其长度与数据帧相同(记录/行数相等),如果Set == 'Z'
设置颜色'green'
,如果@'red'
987654325@ 等于其他任何值。
最好的方法是什么?
【问题讨论】:
【参考方案1】:如果您只有两个选择:
df['color'] = np.where(df['Set']=='Z', 'green', 'red')
例如,
import pandas as pd
import numpy as np
df = pd.DataFrame('Type':list('ABBC'), 'Set':list('ZZXY'))
df['color'] = np.where(df['Set']=='Z', 'green', 'red')
print(df)
产量
Set Type color
0 Z A green
1 Z B green
2 X B red
3 Y C red
如果您有两个以上的条件,请使用np.select
。例如,如果您希望 color
是
yellow
当(df['Set'] == 'Z') & (df['Type'] == 'A')
否则blue
当(df['Set'] == 'Z') & (df['Type'] == 'B')
否则purple
(df['Type'] == 'B')
否则black
,
然后使用
df = pd.DataFrame('Type':list('ABBC'), 'Set':list('ZZXY'))
conditions = [
(df['Set'] == 'Z') & (df['Type'] == 'A'),
(df['Set'] == 'Z') & (df['Type'] == 'B'),
(df['Type'] == 'B')]
choices = ['yellow', 'blue', 'purple']
df['color'] = np.select(conditions, choices, default='black')
print(df)
产生
Set Type color
0 Z A yellow
1 Z B blue
2 X B purple
3 Y C black
【讨论】:
必须使用numpy的原因是什么? 这是pandas
构建的库。由于它已经具有此功能,因此无需 pandas
来实现它。
此代码现在(2022 年 1 月)返回 A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead
【参考方案2】:
列表推导是另一种有条件地创建另一列的方法。如果您在列中使用对象 dtype,例如在您的示例中,列表推导通常优于大多数其他方法。
示例列表理解:
df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit 测试:
import pandas as pd
import numpy as np
df = pd.DataFrame('Type':list('ABBC'), 'Set':list('ZZXY'))
%timeit df['color'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit df['color'] = np.where(df['Set']=='Z', 'green', 'red')
%timeit df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')
1000 loops, best of 3: 239 µs per loop
1000 loops, best of 3: 523 µs per loop
1000 loops, best of 3: 263 µs per loop
【讨论】:
请注意,对于更大的数据帧(想想pd.DataFrame('Type':list('ABBC')*100000, 'Set':list('ZZXY')*100000)
-size),numpy.where
超过 map
,但列表理解是王道(比 numpy.where
快大约 50%)。
如果条件需要多列信息,是否可以使用列表推导法?我正在寻找这样的东西(这不起作用):df['color'] = ['red' if (x['Set'] == 'Z') & (x['Type'] == 'B') else 'green' for x in df]
将 iterrows 添加到数据框,然后您可以通过 row 访问多个列: ['red' if (row['Set'] == 'Z') & (row['Type'] = = 'B') else 'green' 用于索引,在 df.iterrows() 中的行]
请注意,如果您需要从数据框中的另一个系列中获取替换值,例如df['color_type'] = np.where(df['Set']=='Z', 'green', df['Type'])
,这个不错的解决方案将不起作用
@cheekybastard 或者不要,因为.iterrows()
是出了名的迟钝,在迭代时不应该修改 DataFrame。【参考方案3】:
实现这一目标的另一种方法是
df['color'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green')
【讨论】:
【参考方案4】:以下比定时here的方法要慢,但是我们可以根据多列的内容计算额外的列,并且可以为额外的列计算两个以上的值。
仅使用“Set”列的简单示例:
def set_color(row):
if row["Set"] == "Z":
return "red"
else:
return "green"
df = df.assign(color=df.apply(set_color, axis=1))
print(df)
Set Type color
0 Z A red
1 Z B red
2 X B green
3 Y C green
考虑更多颜色和更多列的示例:
def set_color(row):
if row["Set"] == "Z":
return "red"
elif row["Type"] == "C":
return "blue"
else:
return "green"
df = df.assign(color=df.apply(set_color, axis=1))
print(df)
Set Type color
0 Z A red
1 Z B red
2 X B green
3 Y C blue
编辑(21/06/2019):使用 plydata
也可以使用plydata 来做这种事情(不过这似乎比使用assign
和apply
还要慢)。
from plydata import define, if_else
简单if_else
:
df = define(df, color=if_else('Set=="Z"', '"red"', '"green"'))
print(df)
Set Type color
0 Z A red
1 Z B red
2 X B green
3 Y C green
嵌套if_else
:
df = define(df, color=if_else(
'Set=="Z"',
'"red"',
if_else('Type=="C"', '"green"', '"blue"')))
print(df)
Set Type color
0 Z A red
1 Z B red
2 X B blue
3 Y C green
【讨论】:
我们如何使用这种函数引用其他行?例如。if row["Set"].shift(1) == "Z":
,但这不起作用
@ChrisDixon 据我所知,apply
只能看到一行或一列(取决于选择的轴),但不能看到当前处理的行或列之外的其他行或列。跨度>
【参考方案5】:
这是给这只猫换皮的另一种方法,使用字典将新值映射到列表中的键:
def map_values(row, values_dict):
return values_dict[row]
values_dict = 'A': 1, 'B': 2, 'C': 3, 'D': 4
df = pd.DataFrame('INDICATOR': ['A', 'B', 'C', 'D'], 'VALUE': [10, 9, 8, 7])
df['NEW_VALUE'] = df['INDICATOR'].apply(map_values, args = (values_dict,))
它是什么样子的:
df
Out[2]:
INDICATOR VALUE NEW_VALUE
0 A 10 1
1 B 9 2
2 C 8 3
3 D 7 4
当您要创建许多 ifelse
-type 语句(即要替换许多唯一值)时,这种方法会非常强大。
当然你也可以这样做:
df['NEW_VALUE'] = df['INDICATOR'].map(values_dict)
但在我的机器上,这种方法比上面的 apply
方法慢三倍以上。
你也可以这样做,使用dict.get
:
df['NEW_VALUE'] = [values_dict.get(v, None) for v in df['INDICATOR']]
【讨论】:
我喜欢这个答案,因为它展示了如何对值进行多次替换 但在我的机器上,这种方法比上面的 apply 方法慢三倍以上。 你是如何对这些进行基准测试的?根据我的快速测量,.map()
解决方案比.apply()
快约 10 倍。
更新:在 100,000,000 行,52 个字符串值上,.apply()
需要 47 秒,而 .map()
只需 5.91 秒。【参考方案6】:
您可以简单地使用强大的.loc
方法并根据需要使用一个或多个条件(使用pandas=1.0.5 测试)。
代码摘要:
df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
df['Color'] = "red"
df.loc[(df['Set']=="Z"), 'Color'] = "green"
#practice!
df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"
解释:
df=pd.DataFrame(dict(Type='A B B C'.split(), Set='Z Z X Y'.split()))
# df so far:
Type Set
0 A Z
1 B Z
2 B X
3 C Y
添加一个“颜色”列并将所有值设置为“红色”
df['Color'] = "red"
应用你的单一条件:
df.loc[(df['Set']=="Z"), 'Color'] = "green"
# df:
Type Set Color
0 A Z green
1 B Z green
2 B X red
3 C Y red
或多个条件,如果你想:
df.loc[(df['Set']=="Z")&(df['Type']=="B")|(df['Type']=="C"), 'Color'] = "purple"
您可以在此处阅读 Pandas 逻辑运算符和条件选择: Logical operators for boolean indexing in Pandas
【讨论】:
【参考方案7】:你可以使用pandas方法where
和mask
:
df['color'] = 'green'
df['color'] = df['color'].where(df['Set']=='Z', other='red')
# Replace values where the condition is False
或
df['color'] = 'red'
df['color'] = df['color'].mask(df['Set']=='Z', other='green')
# Replace values where the condition is True
或者,您可以将方法 transform
与 lambda 函数一起使用:
df['color'] = df['Set'].transform(lambda x: 'green' if x == 'Z' else 'red')
输出:
Type Set color
1 A Z green
2 B Z green
3 B X red
4 C Y red
来自@chai 的性能比较:
import pandas as pd
import numpy as np
df = pd.DataFrame('Type':list('ABBC')*1000000, 'Set':list('ZZXY')*1000000)
%timeit df['color1'] = 'red'; df['color1'].where(df['Set']=='Z','green')
%timeit df['color2'] = ['red' if x == 'Z' else 'green' for x in df['Set']]
%timeit df['color3'] = np.where(df['Set']=='Z', 'red', 'green')
%timeit df['color4'] = df.Set.map(lambda x: 'red' if x == 'Z' else 'green')
397 ms ± 101 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
976 ms ± 241 ms per loop
673 ms ± 139 ms per loop
796 ms ± 182 ms per loop
【讨论】:
也更快: import pandas as pd import numpy as np df = pd.DataFrame('Type':list('ABBC')*1000000, 'Set':list('ZZXY' )*1000000) %timeit df['color1'] = 'red'; df['color1'].where(df['Set']=='Z','green') %timeit df['color2'] = ['red' if x == 'Z' else 'green' for x in df['Set']] %timeit df['color3'] = np.where(df['Set']=='Z', 'red', 'green') %timeit df['color4'] = df.Set.map( lambda x: 'red' if x == 'Z' else 'green') 397 ms ± 101 ms 每个循环(平均值±标准差。7 次运行,每个循环 1 个循环)976 ms ±每个循环 241 毫秒 每个循环 673 毫秒 ± 139 毫秒 每个循环 796 毫秒 ± 182 毫秒 @chai 将您的评价添加到我的回答中。谢谢!【参考方案8】:.apply()
方法的一个班轮如下:
df['color'] = df['Set'].apply(lambda set_: 'green' if set_=='Z' else 'red')
之后,df
数据框如下所示:
>>> print(df)
Type Set color
0 A Z green
1 B Z green
2 B X red
3 C Y red
【讨论】:
【参考方案9】:如果您只有 2 个选择,请使用 np.where()
df = pd.DataFrame('A':range(3))
df['B'] = np.where(df.A>2, 'yes', 'no')
如果您有超过 2 个选择,也许apply()
可以工作
输入
arr = pd.DataFrame('A':list('abc'), 'B':range(3), 'C':range(3,6), 'D':range(6, 9))
而arr是
A B C D
0 a 0 3 6
1 b 1 4 7
2 c 2 5 8
如果您希望 E 列成为 if arr.A =='a' then arr.B elif arr.A=='b' then arr.C elif arr.A == 'c' then arr.D else something_else
arr['E'] = arr.apply(lambda x: x['B'] if x['A']=='a' else(x['C'] if x['A']=='b' else(x['D'] if x['A']=='c' else 1234)), axis=1)
最后 arr 是
A B C D E
0 a 0 3 6 0
1 b 1 4 7 4
2 c 2 5 8 8
【讨论】:
【参考方案10】:如果您要处理海量数据,最好采用记忆化方法:
# First create a dictionary of manually stored values
color_dict = 'Z':'red'
# Second, build a dictionary of "other" values
color_dict_other = x:'green' for x in df['Set'].unique() if x not in color_dict.keys()
# Next, merge the two
color_dict.update(color_dict_other)
# Finally, map it to your column
df['color'] = df['Set'].map(color_dict)
当你有很多重复值时,这种方法最快。我的一般经验法则是在以下情况下记忆:data_size
> 10**4
& n_distinct
data_size/4
E.x.在一个案例中记忆 10,000 行具有 2,500 个或更少的不同值。
【讨论】:
好吧,所以只有 2 个不同的值要映射,100,000,000 行,没有“记忆”的情况下运行需要 6.67 秒,而有 9.86 秒。 100,000,000 行,52 个不同的值,其中 1 个映射到第一个输出值,其他 51 个都对应另一个:没有记忆的 7.99 秒,有记忆的 11.1 秒。 你的值是随机排列的吗?还是他们背靠背?大熊猫的高速可能是由于缓存@AMC 你的值是随机排列的吗?还是它们是背靠背的? 值是随机的,使用random.choices()
选择。【参考方案11】:
来自pyjanitor 的case_when 函数是pd.Series.mask
的包装器,并为多种条件提供了可链接/方便的形式:
对于单个条件:
df.case_when(
df.col1 == "Z", # condition
"green", # value if True
"red", # value if False
column_name = "color"
)
Type Set color
1 A Z green
2 B Z green
3 B X red
4 C Y red
对于多个条件:
df.case_when(
df.Set.eq('Z') & df.Type.eq('A'), 'yellow', # condition, result
df.Set.eq('Z') & df.Type.eq('B'), 'blue', # condition, result
df.Type.eq('B'), 'purple', # condition, result
'black', # default if none of the conditions evaluate to True
column_name = 'color'
)
Type Set color
1 A Z yellow
2 B Z blue
3 B X purple
4 C Y black
更多例子可以在here找到
【讨论】:
以上是关于Pandas 有条件地创建系列/数据框列的主要内容,如果未能解决你的问题,请参考以下文章