2017-10-12 2 views
1

私は、あるDataFrameから別のものに変更されたレコードをチェックする必要があります。 すべての列に一致する必要があります。パンダで平等のチェックが遅すぎる

1つはExcelファイル(new_df)、1つはSQLクエリ(sql_df)です。形状は〜20,000行×39列です。

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]) 

私は後でdifferencesbad_columnsを取り、彼らと他のタスクを実行します。私は、これはdf.equals(other_df)

は現在、私は次のように使用していますのために良い仕事だと思いました。

これは私のパフォーマンス上の問題の原因である可能性がありますので避けることを望む多くのループがあります。現在、2万レコードで5分以上かかる(ハードウェアによって異なる)、これは性能が悪いことです。私は、すべての列を1つの長い文字列に追加/連結して比較することを考えていましたが、それは別の非効率的な方法のようです。これを解決するためのより良い方法は何でしょうか/空のリストソリューションにこのような不都合な追加を避けるにはどうすればよいですか?

+0

「equals」とは何が原因だと思いますか? – user2357112

+0

@ user2357112 - 有効なポイント。 *簡単にルーピングの量にすることができました - 私はこのことについてあまり誤解を与えないようにタイトルを更新しました – MattR

+1

'new_df'と' sql_df'(または類似しているもの)の両方のサンプルは、実際の解決策を提供するのに大いに役立ちます。 – FabienP

答えて

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 

ショーの異なる列:

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

ショーの異なる行:

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 

UPDATE:

示す、異種細胞:

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

ドキュメンテーションを検索していてもわからなかったこれらの秘密の小さなパンダの方法はいつもあります。この仕事をすることができるかどうかがわかります。これは技術的に私の元の質問に答えるので、私はSOの健康を受け入れます。異種の列/行のIDまたは 'loc'を取得する方法は何でしょうか? – MattR

+0

@MattR、異種のセルだけを表示したいのですか、または異なる行/列全体を表示したいですか? – MaxU

+1

@マックス、私は、違いが似ているものを作ることを目標にしていると思う。'ID'を持つシーケンスのように見えるappend([unique_id、bad_columns])'(質問のサンプルコードの最後の部分を参照)と、違いのあるカラムの名前のリスト。 – FabienP

2

は差がある行のみを示すフィルタデータフレームを取得します。

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 

はあなたが作るしようとして出力される違いがあるIDと列の名前のタプルを取得します。 同じ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 

applyforループに近いので、2番目の部分はおそらく最初よりも時間がかかりますのでご了承ください。

+0

偉大な答え。私もこれをテストします! – MattR

+0

コードの最初の行、new_dfにsytaxエラーが発生しています。[新規... .locになっていますか?実際の例のように、ValueError:同じラベルのDataFrameオブジェクトのみを比較することもできます.2つのデータフレームには同じ量のレコードがありません。 reset_indexを追加しても、そこにある「ValueError」の問題は解決されません。 – MattR

+0

更新:Sytnaxエラーを取り除くために '.'を削除し、両方のデータフレームを両方とも同じIDを持つ場所にフィルタリングしました。これは美しく動作します。 'result_df.set_axis'がどのように動いているのかを説明することは可能でしょうか?[docs](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.set_axis.html)はとても説明していませんまあ? – MattR

関連する問題