[英]How to conditionally concat 2 columns in Python Pandas Dataframe
我有以下 dataframe:
d = {'col1': [1, "Avoid", 3, "Avoid"], 'col2': ["AA", "BB", "Avoid", "Avoid"]}
df = pd.DataFrame(data=d)
df
col1 col2
0 1 AA
1 Avoid BB
2 3 Avoid
3 Avoid Avoid
我必须有条件地将col1和col2连接到col3中。 条件:
最终结果应该如下所示:
col1 col2 col3
0 1 AA 1 & AA
1 Avoid BB Avoid
2 3 Avoid Avoid
3 Avoid Avoid Avoid
在不处理 for 循环的情况下如何做到这一点?
不是使用 pandas 的有效方法,但如果您无法更改数据结构,这些是解决方案:
解决方案1:
def custom_merge(cols):
if cols["col1"]=="Avoid" or cols["col2"]=="Avoid":
return "Avoid"
else:
return f"{cols['col1']} & {cols['col2']}"
df['col3'] = df.apply(custom_merge, axis=1)
解决方案2:
df['col3'] = (df["col1"].astype(str) + " & " + df["col2"].astype(str)).apply(lambda x: "Avoid" if 'Avoid' in x else x)
两种解决方案都会导致以下结果:
col1 col2 col3
0 1 AA 1 & AA
1 Avoid BB Avoid
2 3 Avoid Avoid
3 Avoid Avoid Avoid
执行时间比较
在本节中,我将计算提出的解决方案的执行时间。 @mozway 在他的回答中提出了 2 个非常棘手的解决方案,我将其称为解决方案 3a 和解决方案 3b。 另一个有趣的解决方案是@sammywemmy 的解决方案,它使用列表推导,然后将列表添加到 dataframe,我将此解决方案称为解决方案 4
实验实例将具有以下结构:
import pandas as pd
n = 100000
d = {'col1': [1, "Avoid", 3, "Avoid"] * n, 'col2': ["AA", "BB", "Avoid", "Avoid"] * n}
df = pd.DataFrame(data=d)
执行时间处理时间:
解决方案1:
%timeit 3.56 s ± 71.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
解决方案2:
%timeit 140 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
解决方案 3a:
%timeit 3.44 s ± 77.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
解决方案 3b:
%timeit 893 ms ± 13.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
解决方案4:
%timeit 191 ms ± 5.71 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
解决方案 1 和解决方案 3a 在所考虑的实例上具有相似的执行时间。 解决方案 3b 的运行速度比解决方案 1 和 3a 快 4 倍。 最快的解决方案是解决方案 2,它比解决方案 2 快约 7 倍,比解决方案 1 和 3a 快约 25 倍。 这是因为解决方案 2 利用了pandas 矢量化。 解决方案 4 具有与解决方案 2 类似的运行时间,利用列表理解来进行合并操作(不使用 pandas)。
提示:如果您可以更改数据格式,建议格式化数据,以便您可以使用本机 pandas 函数进行连接操作,或者如果您无法更改数据格式并且可以不使用 pandas,您可能会有使用字典或列表稍微加快解决方案 2,使用列表理解进行合并操作。
对普通 python 中的字符串运行列表推导:
out = [f"{l}&{r}"
if 'Avoid' not in {l, r}
else 'Avoid'
for l, r in zip(df.col1, df.col2)]
df.assign(col3 = out)
col1 col2 col3
0 1 AA 1&AA
1 Avoid BB Avoid
2 3 Avoid Avoid
3 Avoid Avoid Avoid
尝试这个:
df["col3"]=df.apply(lambda x:"Avoid" if x["col1"]=="Avoid" or x["col2"]=="Avoid" else f"{x['col1']} & {x['col2']}",axis=1)
使用 boolean 操作,这使您可以使用任意数量的列:
# is any value in the row "Avoid"?
m = df.eq('Avoid').any(1)
# concatenate all columns unless there was a "Avoid"
df['col3'] = df.astype(str).agg(' & '.join, axis=1).mask(m, 'Avoid')
如果您有很多行而使用“避免”的行数很少,那么替代方案应该会更快:
m = df.ne('Avoid').all(1)
df.loc[m, 'col3'] = df[m].astype(str).agg(' & '.join, axis=1)
df['col3'] = df['col3'].fillna('Avoid')
output:
col1 col2 col3
0 1 AA 1 & AA
1 Avoid BB Avoid
2 3 Avoid Avoid
3 Avoid Avoid Avoid
df["col3"] = df["col1"] + " & " + df["col2"]
df["col3"] = df["col3"].apply(lambda x: "Avoid" if x.contains("Avoid") else x)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.