2017-10-12 5 views
1

J'ai besoin de vérifier les enregistrements qui changent d'un DataFrame à l'autre. Il doit correspondre sur toutes les colonnes.Pandas Vérification de l'égalité Trop lent pour l'utilisation

l'un est un fichier Excel (new_df), l'un est une requête SQL (sql_df). La forme est ~ 20 000 lignes par 39 colonnes. Je pensais que ce serait un bon travail pour df.equals(other_df)

Actuellement, je me sers de ce qui suit:

import pandas as pd 
import numpy as np 
new_df = pd.DataFrame({'ID' : [0 ,1, 2, 3, 4, 5, 6, 7, 8, 9], 
        'B' : [1,0,3,5,0,0,np.NaN,9,0,0], 
        'C' : [10,0,30,50,0,0,4,10,1,3], 
        'D' : [1,0,3,4,0,0,7,8,0,1], 
        'E' : ['Universtiy of New York','New Hampshire University','JMU','Oklahoma State','Penn State', 
          'New Mexico Univ','Rutgers','Indiana State','JMU','University of South Carolina']}) 

sql_df= pd.DataFrame({'ID' : [0 ,1, 2, 3, 4, 5, 6, 7, 8, 9], 
        'B' : [1,0,3,5,0,0,np.NaN,9,0,0], 
        'C' : [10,0,30,50,0,0,4,10,1,0], 
        'D' : [5,0,3,4,0,0,7,8,0,1], 
        'E' : ['Universtiy of New York','New Hampshire University','NYU','Oklahoma State','Penn State', 
          'New Mexico Univ','Rutgers','Indiana State','NYU','University of South Carolina']}) 

# creates an empty list to append to 
differences = [] 
# for all the IDs in the dataframe that should not change check if this record is the same in the database 
# must use reset_index() so the equals() will work as I expect it to 
# if it is not the same, append to a list which has the Aspn ID that is failing, along with the columns that changed 
for unique_id in new_df['ID'].tolist(): 
# get the id from the list, and filter both sql and new dfs to this record 
    if new_df.loc[new_df['ID'] == unique_id].reset_index(drop=True).equals(sql_df.loc[sql_df['ID'] == unique_id].reset_index(drop=True)) is False: 
     bad_columns = [] 
     for column in new_df.columns.tolist(): 
     # if not the same above, check which column using the same logic 
      if new_df.loc[new_df['ID'] == unique_id][column].reset_index(drop=True).equals(sql_df.loc[sql_df['ID'] == unique_id][column].reset_index(drop=True)) is False: 
       bad_columns.append(column)        
     differences.append([unique_id, bad_columns]) 

Je prends plus tard differences et bad_columns et d'autres tâches avec eux.

Il y a beaucoup de boucles que j'espère éviter ... comme ceci peut être la cause de mon problème de performance. Il prend actuellement plus de 5 minutes pour 20 000 enregistrements (varie selon le matériel), ce qui est une performance abyssale. Je pensais ajouter/concaténer toutes les colonnes dans une longue chaîne à comparer à la place, mais cela semble être un autre moyen inefficace. Quelle serait une meilleure façon de résoudre ceci/comment puis-je éviter ce désordre d'ajouter à une solution de liste vide?

+0

Qu'est-ce qui vous fait penser que «l'égal» est le coupable? – user2357112

+0

@ user2357112 - point valide. Pourrait * facilement * être le montant de la boucle - j'ai mis à jour le titre pour être moins trompeur à ce sujet – MattR

+1

Un exemple de 'new_df' et' sql_df' (ou quelque chose de semblable) aiderait grandement à fournir une solution de travail. – FabienP

Répondre

4
In [26]: new_df.ne(sql_df) 
Out[26]: 
     B  C  D  E  ID 
0 False False True False False 
1 False False False False False 
2 False False False True False 
3 False False False False False 
4 False False False False False 
5 False False False False False 
6 True False False False False 
7 False False False False False 
8 False False False True False 
9 False True False False False 

Afficher les colonnes différentes:

In [27]: new_df.ne(sql_df).any(axis=0) 
Out[27]: 
B  True 
C  True 
D  True 
E  True 
ID False 
dtype: bool 

Afficher les lignes différentes:

In [28]: new_df.ne(sql_df).any(axis=1) 
Out[28]: 
0  True 
1 False 
2  True 
3 False 
4 False 
5 False 
6  True 
7 False 
8  True 
9  True 
dtype: bool 

MISE À JOUR:

montrant des cellules différentes:

In [86]: x = new_df.ne(sql_df) 

In [87]: new_df[x].loc[x.any(1)] 
Out[87]: 
    B C D E ID 
0 NaN NaN 1.0 NaN NaN 
2 NaN NaN NaN JMU NaN 
6 NaN NaN NaN NaN NaN 
8 NaN NaN NaN JMU NaN 
9 NaN 3.0 NaN NaN NaN 

In [88]: sql_df[x].loc[x.any(1)] 
Out[88]: 
    B C D E ID 
0 NaN NaN 5.0 NaN NaN 
2 NaN NaN NaN NYU NaN 
6 NaN NaN NaN NaN NaN 
8 NaN NaN NaN NYU NaN 
9 NaN 0.0 NaN NaN NaN 
+0

Il y a toujours ces petites méthodes secrètes de pandas dont je ne savais même pas qu'elles existaient même en cherchant dans la documentation. Je vais voir si je peux faire ce travail. Cela répond techniquement à ma question initiale, donc je vais accepter pour SO santé. Quel serait le moyen d'obtenir l'ID ou «loc» des colonnes/lignes dissemblables? – MattR

+0

@MattR, voulez-vous afficher uniquement des cellules dissemblables ou toutes les lignes/colonnes dissemblables? – MaxU

+1

@MaxU, je suppose que le but est de produire quelque chose de similaire à 'différences.append ([unique_id, bad_columns]) '(voir la dernière partie de l'exemple de code de la question) qui ressemble à une séquence avec' ID' et une liste des noms des colonnes avec une différence. – FabienP

2

filtrés dataframe montrant uniquement les lignes avec des différences:

result_df = new_df[new_df != sql_df].dropna(how='all') 

>>> result_df 
Out[]: 
    B C D E ID 
0 NaN NaN 1.0 NaN NaN 
2 NaN NaN NaN JMU NaN 
8 NaN NaN NaN JMU NaN 
9 NaN 3.0 NaN NaN NaN 

Obtenez tuples de ID et les colonnes où il y a une différence, qui est la sortie vous où essayer de produire. Cela devrait fonctionner même si vous avez plusieurs colonnes avec différence pour le même ID.

result_df.set_axis(labels=new_df.ID[result_df.index], axis=0) 

>>> result_df.apply(lambda x: (x.name, result_df.columns[x.notnull()]), axis=1) 
Out[]: 
ID 
0 (0, [D]) 
2 (2, [E]) 
8 (8, [E]) 
9 (9, [C]) 
dtype: object 

S'il vous plaît noter que apply est proche d'une boucle for, de sorte que la deuxième partie prendra probablement plus de temps que la première.

+0

Bonne réponse. Je vais tester cela aussi! – MattR

+0

Je reçois une erreur de syntaxe dans la première ligne de code, new_df. [New ... est-ce qu'il y a un .loc? Je suis également confronté 'ValueError: ne peut comparer que des objets DataFrame étiquetés de façon identique', car dans mon exemple réel, les deux dataframes n'ont pas la même quantité d'enregistrements. Même ajouter un reset_index ne résout pas le 'ValueError' des conseils là-bas? – MattR

+0

Mise à jour: J'ai supprimé le '.' pour supprimer l'erreur Sytnax ainsi que filtré les deux Dataframes à l'endroit où ils ont tous les deux le même ID. Cela fonctionne magnifiquement. Serait-il possible d'expliquer comment 'result_df.set_axis' fonctionne? Les [docs] (https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.set_axis.html) n'expliquent pas très bien? – MattR