我有来自2个来源的大型数据集,一个是巨大的csv文件,另一个来自数据库查询。我正在编写验证脚本来比较两个来源的数据并记录/打印差异。我认为值得一提的一件事是来自两个来源的数据不是完全相同的格式或顺序。例如:
来源1(CSV文件):
email1@gmail.com,key1,1
email2@gmail.com,key1,3
email1@gmail.com,key2,1
email1@gmail.com,key3,5
email2@gmail.com,key3,2
email2@gmail.com,key3,2
email3@gmail.com,key2,3
email3@gmail.com,key3,1
来源2(数据库):
email key1 key2 key3
email1@gmail.com 1 1 5
email2@gmail.com 3 2 <null>
email4@gmail.com 1 1 5
我想要的脚本输出如下:
source1 - source2 (or csv - db): 2 rows total with differences
email2@gmail.com 3 2 2
email3@gmail.com <null> 3 1
source2 - source1 (or db-csv): 2 rows total with differences
email2@gmail.com 3 2 <null>
email4@gmail.com 1 1 5
输出格式可能略有不同,可以更清楚地显示更多差异(来自数千/数百万条记录)。
我开始编写脚本以将来自两个源的数据保存到两个词典中,并循环遍历词典或从词典创建集合,但这似乎是一个非常低效的过程。我考虑过使用pandas,但是pandas似乎没有办法对数据帧进行这种类型的比较。
请告诉我是否有更好/更有效的方法。提前谢谢!
答案 0 :(得分:1)
你是在正确的道路上。你想要的是快速匹配2个表。熊猫可能有点过分了。
您可能想要遍历第一个表并创建字典。您不想要做的事情是为每个元素交互两个列表。即使是小名单也需要进行大量搜索。
ReadCsv模块是从磁盘读取数据的好模块。对于每一行,您将把它放在一个字典中,其中键是电子邮件,值是完整的行。在普通的台式计算机中,您可以在一秒钟内迭代1000万行。
现在您将迭代抛出第二行,对于每一行,您将使用电子邮件从字典中获取数据。这样看,因为dict是一个数据结构,你可以在O(1)中得到键值,你将通过N + M行进行交互。在几秒钟内,您应该能够比较两个表。这很简单。以下是示例代码:
import csv
firstTable = {}
with open('firstTable.csv', 'r') as csvfile:
reader = csv.reader(csvfile, delimiter=',')
for row in reader:
firstTable[row[0]] = row #email is in row[0]
for row2 in get_db_table2():
email = row2[0]
row1 = firstTable[email] #this is a hash. The access is very quick
my_complex_comparison_func(row1, row2)
如果没有足够的RAM内存来容纳内存中第一个字典的所有键,则可以使用Shelve module作为firstTable变量。这将在磁盘中创建一个非常快速访问的索引。
由于您的某个表已经在数据库中,我可能首先要使用您的数据库将磁盘中的数据加载到临时表中。创建索引,并在表上进行内部联接(如果需要知道哪些行在另一个表中没有数据,则进行外连接)。数据库针对此类操作进行了优化。然后,您可以从python中进行选择以获取连接的行,并将python用于复杂的比较逻辑。
答案 1 :(得分:0)
您可以使用pivot
转换df,drop_duplicates
之后使用concat
df2=df2.applymap(lambda x : pd.to_numeric(x,errors='ignore')
pd.concat([df.pivot(*df.columns).reset_index(),df2)],keys=['db','csv']).\
drop_duplicates(keep=False).\
reset_index(level=0).\
rename(columns={'level_0':'source'})
Out[261]:
key source email key1 key2 key3
1 db email2@gmail.com 3 2 2
1 csv email2@gmail.com 3 2 <null>
注意,我在这里使用to_numeric
转换为df2的数字