Pandas/Python中与R的复杂ifelse匹配相当的语句是什么?

3

我的目标是获得以下R代码的pandas等效代码:

df1$String_1_check = ifelse(df1$String_1 == df2[match(df1$String_2, df2$String_2), 1], TRUE, FALSE)

如果df1中第n行的String_1列的值等于df2的第一列,其中df1的第n行的String_2列与df2的String_2匹配,则在新的String_1_check列中为True,否则在String_1_check中为False。
df1中的String_1和String_2具有许多相同的值,而df2中的String_1只有一个可能的值。String_3不是唯一的。以下是示例数据框:
df1 = pd.DataFrame({'String_1': ['string 1', 'string 1', 'string 2', 'string 3', 'string 1'], 'String_2': ['string a', 'string a', 'string b', 'string a', 'string c']})
df2 = pd.DataFrame({'String_3': ['string 1', 'string 2', 'string 3'], 'String_2': ['string a', 'string b', 'string c']})

   String_1  String_2
0  string 1  string a
1  string 1  string a
2  string 2  string b
3  string 3  string a
4  string 1  string c

   String_3  String_2
0  string 1  string a
1  string 2  string b
2  string 3  string c

期望的输出结果将会是:
   String_1  String_2  String_1_check
0  string 1  string a  True
1  string 1  string a  True
2  string 2  string b  True
3  string 3  string a  False
4  string 1  string c  False

我尝试过np.whereisinpd.match(现已弃用),但都没有找到解决方案。

4个回答

5

将值重新赋回到df1中,就像原始的R一样,您可以这样做:

In []:
df1['String_1_check'] = df1.merge(df2, how='left')['String_3'] == df1['String_1']
df1

Out:
   String_1  String_2  String_1_check
0  string 1  string a            True
1  string 1  string a            True
2  string 2  string b            True
3  string 3  string a           False
4  string 1  string c           False

1

合并这两个数据框,检查字符串1和3是否匹配:

dfnew = df1.merge(df2, how='left')
dfnew["String_1_check"] = (dfnew.String_1 == dfnew.String_3)
del dfnew["String_3"]
print(dfnew)
#   String_1  String_2 String_1_check
#0  string 1  string a           True
#1  string 1  string a           True
#2  string 3  string a          False
#3  string 2  string b           True
#4  string 1  string c          False

我也尝试了使用merge,但注意到它会失去df1的顺序。不知道这是否重要。 - ayhan
行可以按任何顺序排序,如果需要的话。 - DYZ
1
如果您执行 df1.merge(df2, how='left'),则可以保持顺序,因此 df1['String_1_check'] == df1.merge(df2, how='left')['String_3'] == df1['String_1'] 等同于原始的 R 代码,该代码将结果分配回 df1 - AChampion

1
你可以使用 map 方法而不改变原始 df 的顺序。
df1['String_1_check']=list(zip(df1['String_1'],df1['String_2']))
df2.index=list(zip(df2['String_3'],df2['String_2']))
df2['Check']=True
df1['String_1_check']=df1['String_1_check'].map(df2['Check']).fillna(False)

Out[764]: 
   String_1  String_2  String_1_check
0  string 1  string a            True
1  string 1  string a            True
2  string 2  string b            True
3  string 3  string a           False
4  string 1  string c           False

1
这种方法对我的实际数据情况最有效。谢谢! - Andrew Russell

1
假设 df2.String_3 是唯一的,创建一个系列,并在 map 中使用它来与 df1.String_2 进行比较。考虑到 map 的常数时间查找相对于 merge 来说更快。
如果 df2.String_3 不是唯一的,则注意 OP 要求我们只关注从 df1.String_1 找到第一个匹配项的行。这意味着我们可以通过使用 drop_duplicates 使 df2.String_3 成为唯一的。
df1.String_1.map(df2.set_index('String_3').String_2).eq(df1.String_2)

0     True
1     True
2     True
3    False
4    False
dtype: bool

非唯一性修改版

df1.String_1.map(
    df2.drop_duplicates('String_3').set_index('String_3').String_2
).eq(df1.String_2)

使用pd.DataFrame.assign创建一个包含新列的df1副本。
df1.assign(
    String_1_check=df1.String_1.map(
        df2.drop_duplicates('String_3').set_index('String_3').String_2
    ).eq(df1.String_2)
)

   String_1  String_2  String_1_check
0  string 1  string a            True
1  string 1  string a            True
2  string 2  string b            True
3  string 3  string a           False
4  string 1  string c           False

时间
在这个模拟中,df2的大小是静态的。我不想建模独特的值。
下面是代码

enter image description here

pir = lambda df1, df2: df1.assign(String_1_check=df1.String_1.map(df2.drop_duplicates('String_3').set_index('String_3').String_2).eq(df1.String_2))
achamp = lambda df1, df2: df1.assign(String_1_check=df1.merge(df2, how='left').eval('String_3 == String_1'))

results = pd.DataFrame(
    index=pd.Index([10, 30, 100, 300, 1000, 3000, 10000, 30000]),
    columns='pir achamp'.split()
)

for i in results.index:
    d1 = pd.concat([df1] * i, ignore_index=True)
    for j in results.columns:
        stmt = '{}(d1, df2)'.format(j)
        setp = 'from __main__ import d1, df2, {}'.format(j)
        results.set_value(i, j, timeit(stmt, setp, number=20))

results.plot(loglog=True)

1
我知道 merge 不会很快 - 有趣的是我倒序构建了我的 map ;) df1['String_2'].map(df2.set_index('String_2')['String_3']) == df1['String_1'] - 相同的结果。+1 - AChampion
我还对你的函数进行了修改,并使用了 eval。这在处理较小数据时会影响性能,但在处理较大数据时则更有优势。不过,将其包装在 lambda 中会更美观。 - piRSquared
我真的很喜欢这个。不幸的是,String_3中的值不是唯一的。我已经编辑了我的问题以反映这一点。对于任何困惑,我感到抱歉。你有比“merge”更快的另一个解决方案吗? - Andrew Russell
实际上,由于您只关心与“df1”匹配的第一行构成的“df2”中的行进行比较...那么您可以删除重复项并使其唯一。 我会更新帖子。 - piRSquared
@AndrewRussell 还要注意,如果 String_3 不是唯一的话,使用 merge 也会出现奇怪的问题。 - piRSquared
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接