2017-08-15 1 views
3

Mon but est d'obtenir l'équivalent de pandas géants du dessous Code R:Pandas/Python équivalent de match ifelse complexe R

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

Si la valeur de la n-ième rangée de la colonne String_1 de DF1 est égale à la première colonne de df2 où la nième ligne de la colonne String_2 de df1 correspond à String_2 de df2, puis True dans une nouvelle colonne String_1_check, sinon False dans String_1_check. Df1 a plusieurs instances des mêmes valeurs dans String_1 et String_2, et df2 n'a qu'une instance de chaque valeur possible dans String_1. String_3 n'est pas unique. Avec ces exemples dataframes:

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 

La sortie souhaitée serait:

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 

J'ai essayé np.where, isin, pd.match (désapprouvée maintenant), mais ne l'ai pas trouvé une solution.

Répondre

1

Vous pouvez à l'aide map sans changer l'ordre de votre original 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

Cela a fini par fonctionner le mieux pour mes données réelles. Merci! . –

1

fusionnera les deux dataframes et vérifier si les chaînes 1 et 3 match (modifiés pour intégrer une suggestion AChampion):

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 
+0

Je tente également de fusion mais notez qu'il perd l'ordre de DF1. Je ne sais pas si c'est important. – ayhan

+0

Les lignes peuvent être triées dans n'importe quel ordre, si nécessaire. – DyZ

+1

Vous pouvez garder l'ordre si vous le faites 'df1.merge (DF2, comment = 'left')', donc 'DF1 [ 'String_1_check'] == df1.merge (DF2, comment = 'left') [ 'STRING_3' ] == df1 ['String_1'] ', est équivalent au code' R' original qui a assigné le résultat à 'df1' – AChampion

5

Attribution de la valeur à df1 comme le R d'origine que vous pouvez faire:

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

En supposant df2.String_3 est unique, créer une série de df2 et de l'utiliser dans une map de comparer contre df1.String_2. Ce sera rapide compte tenu map est des recherches à temps constant par rapport à merge.

Dans le cas où df2.String_3 est pas unique remarquer que OP exige que nous ne nous préoccuper de la ligne dans laquelle nous trouvons le premier match de df1.String_1. Cela signifie que nous pouvons faire df2.String_3 unique en utilisant drop_duplicates

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 

Version modifiée pour la non-unicité

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

Utilisez pd.DataFrame.assign pour créer une copie de df1 qui comprend une nouvelle colonne.

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 

timing
Dans cette simulation, la taille de df2 est statique. Je n'avais pas envie de modéliser des valeurs uniques.
code ci-dessous

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

Je savais que 'merge' ne va être rapide - fait intéressant que je construit mon' map' en sens inverse;) 'DF1 [ 'STRING_2'] carte (df2.set_index ('STRING_2') [ 'STRING_3']) = = df1 ['String_1'] '- même résultat. +1 – AChampion

+0

J'ai aussi pris des libertés avec votre fonction et j'ai utilisé 'eval'. C'est un handicap de performance avec des données plus petites et avantageux avec des données plus importantes. Mais c'est plus joli quand enveloppé dans un 'lambda'. – piRSquared

+0

J'aime vraiment ça. Malheureusement, les valeurs de String_3 ne sont pas uniques. J'ai édité ma question pour refléter cela. Désolé pour toute confusion. Avez-vous une autre solution plus rapide que 'merge'? –