J'ai deux bases de données de pandas qui ont quelques lignes en commun.
Supposons que dataframe2 est un sous-ensemble de dataframe1.
Comment puis-je obtenir les lignes de dataframe1 qui ne sont pas dans dataframe2?
df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]})
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]})
Une méthode consisterait à stocker le résultat d'une fusion interne sous les deux dfs. Nous pouvons simplement sélectionner les lignes lorsque les valeurs d'une colonne ne sont pas dans ce commun
In [119]:
common = df1.merge(df2,on=['col1','col2'])
print(common)
df1[(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))]
col1 col2
0 1 10
1 2 11
2 3 12
Out[119]:
col1 col2
3 4 13
4 5 14
MODIFIER
Une autre méthode que vous avez trouvée consiste à utiliser isin
qui produira des lignes NaN
que vous pouvez supprimer:
In [138]:
df1[~df1.isin(df2)].dropna()
Out[138]:
col1 col2
3 4 13
4 5 14
Cependant, si df2 ne commence pas les lignes de la même manière, cela ne fonctionnera pas:
df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11, 12,13]})
produira le df entier:
In [140]:
df1[~df1.isin(df2)].dropna()
Out[140]:
col1 col2
0 1 10
1 2 11
2 3 12
3 4 13
4 5 14
La solution actuellement sélectionnée produit des résultats incorrects. Pour résoudre correctement ce problème, nous pouvons effectuer une jointure à gauche de df1
à df2
, en veillant à ne récupérer que les lignes uniques de df2
.
Tout d'abord, nous devons modifier le DataFrame d'origine pour ajouter la ligne avec data [3, 10].
df1 = pd.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3],
'col2' : [10, 11, 12, 13, 14, 10]})
df2 = pd.DataFrame(data = {'col1' : [1, 2, 3],
'col2' : [10, 11, 12]})
df1
col1 col2
0 1 10
1 2 11
2 3 12
3 4 13
4 5 14
5 3 10
df2
col1 col2
0 1 10
1 2 11
2 3 12
Effectuez une jointure à gauche en éliminant les doublons dans df2
, de sorte que chaque ligne de df1
se joint à exactement une ligne de df2
. Utilisez le paramètre indicator
pour renvoyer une colonne supplémentaire indiquant la table d'origine.
df_all = df1.merge(df2.drop_duplicates(), on=['col1','col2'],
how='left', indicator=True)
df_all
col1 col2 _merge
0 1 10 both
1 2 11 both
2 3 12 both
3 4 13 left_only
4 5 14 left_only
5 3 10 left_only
Créer une condition booléenne:
df_all['_merge'] == 'left_only'
0 False
1 False
2 False
3 True
4 True
5 True
Name: _merge, dtype: bool
Quelques solutions font la même erreur - elles vérifient seulement que chaque valeur est indépendamment dans chaque colonne, pas ensemble dans la même ligne. L'ajout de la dernière ligne, unique mais contenant les valeurs des deux colonnes de df2
, expose l'erreur:
common = df1.merge(df2,on=['col1','col2'])
(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))
0 False
1 False
2 False
3 True
4 True
5 False
dtype: bool
Cette solution donne le même résultat faux:
df1.isin(df2.to_dict('l')).all(1)
En supposant que les index soient cohérents dans les cadres de données (sans tenir compte des valeurs de col réelles):
df1[~df1.index.isin(df2.index)]
Comme déjà indiqué, isin requiert des colonnes et des index identiques pour une correspondance. Si correspondance ne doit concerner que le contenu des lignes, une méthode pour obtenir le masque de filtrage des lignes présentes consiste à convertir les lignes en un (Multi) Index:
In [77]: df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 'col2' : [10, 11, 12, 13, 14, 10]})
In [78]: df2 = pandas.DataFrame(data = {'col1' : [1, 3, 4], 'col2' : [10, 12, 13]})
In [79]: df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)]
Out[79]:
col1 col2
1 2 11
4 5 14
5 3 10
Si index doit être pris en compte, set_index a l'argument de mot clé append pour ajouter des colonnes à l'index existant. Si les colonnes ne sont pas alignées, liste (df.columns) peut être remplacée par des spécifications de colonne pour aligner les données.
pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())
pourrait également être utilisé pour créer les indices, bien que je doute que cela soit plus efficace.
Supposons que vous avez deux images, df_1 et df_2 ayant plusieurs champs (nom_colonne) et que vous souhaitez rechercher les seules entrées de df_1 qui ne se trouvent pas dans df_2 sur la base de certains champs (par exemple, champs_x, champs_y), suivez les étapes suivantes.
Étape1.Ajoutez une colonne clé1 et clé2 à df_1 et à df_2 respectivement.
Step2.Merge les images comme indiqué ci-dessous. field_x et field_y sont nos colonnes souhaitées.
Step3.Sélectionnez uniquement les lignes de df_1 où key1 n'est pas égal à key2.
Step4.Drop key1 et key2.
Cette méthode résoudra votre problème et fonctionnera rapidement, même avec des ensembles de données volumineux. Je l'ai essayé pour les cadres de données avec plus de 1 000 000 de lignes.
df_1['key1'] = 1
df_2['key2'] = 1
df_1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'left')
df_1 = df_1[~(df_1.key2 == df_1.key1)]
df_1 = df_1.drop(['key1','key2'], axis=1)
Vous pouvez également concatrer df1
, df2
:
x = pd.concat([df1, df2])
puis supprimez tous les doublons:
y = x.drop_duplicates(keep=False, inplace=False)
un peu tard, mais il peut être intéressant de vérifier le paramètre "indicateur" de pd.merge.
Voir cette autre question pour un exemple: Comparez PandaS DataFrames et renvoie les lignes manquantes dans la première
vous pouvez le faire en utilisant isin (dict) method:
In [74]: df1[~df1.isin(df2.to_dict('l')).all(1)]
Out[74]:
col1 col2
3 4 13
4 5 14
Explication:
In [75]: df2.to_dict('l')
Out[75]: {'col1': [1, 2, 3], 'col2': [10, 11, 12]}
In [76]: df1.isin(df2.to_dict('l'))
Out[76]:
col1 col2
0 True True
1 True True
2 True True
3 False False
4 False False
In [77]: df1.isin(df2.to_dict('l')).all(1)
Out[77]:
0 True
1 True
2 True
3 False
4 False
dtype: bool
Voici une autre façon de résoudre ce problème:
df1[~df1.index.isin(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]
Ou:
df1.loc[df1.index.difference(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]
Que dis-tu de ça:
df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5],
'col2' : [10, 11, 12, 13, 14]})
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3],
'col2' : [10, 11, 12]})
records_df2 = set([Tuple(row) for row in df2.values])
in_df2_mask = np.array([Tuple(row) in records_df2 for row in df1.values])
result = df1[~in_df2_mask]
Pour ce faire, j'ajoute une nouvelle colonne propre à un seul cadre de données et l'utilise pour choisir de conserver ou non une entrée.
df2[col3] = 1
df1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'outer')
df1['Empt'].fillna(0, inplace=True)
Cela fait que chaque entrée de df1 a un code - 0 s'il est unique à df1, 1 s'il se trouve dans les deux cadres de données. Vous utilisez ensuite ceci pour limiter à ce que vous voulez
answer = nonuni[nonuni['Empt'] == 0]