2017-10-12 34 views
1

我有一个检查从一个DataFrame更改为另一个的记录的要求。它必须匹配全部列。大熊猫检查平等太慢使用

一个是excel文件(new_df),一个是SQL查询(sql_df)。形状是〜20,000行×39列。我认为这将是df.equals(other_df)

目前我使用下面的工作:

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和执行其它任务与他们。

有很多循环,我希望避免...因为这可能是我的性能问题的原因。目前,20,000个记录需要5分钟以上(硬件上有所不同),这是糟糕的表现。我想加入/连接所有列成一个长字符串来比较,但这似乎是另一种低效率的方式。解决这个问题的更好方法是什么?我怎样才能避免将这种混乱添加到空列表解决方案中?

+0

是什么让你觉得'等于'是罪魁祸首? – 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

@MaxU,我想目标是产生类似于“差异”的东西。append([unique_id,bad_columns])'(参见问题示例代码的最后一部分),它看起来像带有“ID”的序列和具有差异的列名称列表。 – 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 

请注意:apply是接近for循环,所以第二部分可能会花费更多的时间比第一。

+0

好答案。我也会测试这个! – MattR

+0

我在第一行代码new_df中得到了一个sytax错误。[new ...是否应该有.loc?我也面临'ValueError:只能比较标记相同的DataFrame对象,就像我在现实世界的例子中那样,两个数据框没有相同数量的记录。即使添加一个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