繁体   English   中英

如何有条件地连接 Python Pandas Dataframe 中的 2 列

[英]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

我必须有条件地将col1col2连接到col3中。 条件:

  • 只有 concat 2 列,只要它们都不是Avoid
  • 如果col1col2中的任何一个为 Avoid,则col3也将等于Avoid
  • 执行连接时,需要在col3的列值之间添加“&” 例如, col3的第一行将是"1 & AA"

最终结果应该如下所示:

    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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM