在具有500e6行的hdf5 pytable中查找副本

时间:2013-12-23 12:02:03

标签: python hdf5 pytables

问题

我有一个大的(> 500e6行)数据集,我已经放入pytables数据库。

假设第一列是ID,第二列是每个ID的计数器。每个ID计数器组合必须是唯一的。我想在500e6行中找到一条非唯一的行。

作为首发,我做过类似的事情:

index1 = db.cols.id.create_index()
index2 = db.cols.counts.create_index()
for row in db:
    query = '(id == %d) & (counts == %d)' % (row['id'],  row['counts'])
    result = th.readWhere(query)
    if len(result) > 1:
        print row

我承认这是一种蛮力方法。有关改进的建议吗?

更新

目前的强力运行时间为8421分钟。

溶液 感谢大家的意见和建议。我设法使用以下方法将运行时间缩短到2364.7秒:

ex = tb.Expr('(x * 65536) + y', uservars = {"x":th.cols.id, "y":th.cols.counts})
ex = tb.Expr(expr)
ex.setOutput(th.cols.hash)
ex.eval()
indexrows = th.cols.hash.create_csindex(filters=filters)

ref = None
dups = []
for row in th.itersorted(sortby=th.cols.hash):
  if row['hash'] == ref:
    dups.append(row['hash'] )
  ref = row['hash']

print("ids: ", np.right_shift(np.array(dups, dtype=np.int64), 16))
print("counts: ", np.array(dups, dtype=np.int64) & 65536-1)

我可以生成一个完美的哈希,因为我的最大值小于2 ^ 16。我有效地将两列打包成32位int。

一旦生成了csindex,迭代排序的值并对重复进行邻居测试是相当简单的。

这种方法可能会稍微调整一下,但我正在测试一些可能提供更自然解决方案的替代方法。

2 个答案:

答案 0 :(得分:4)

我想到了两种明显的技术:散列和排序。

A)定义一个哈希函数,将ID和Counter组合成一个紧凑的值。

B)计算每个哈希码发生的频率

C)从您的数据中选择所有有哈希冲突的数据(这应该是一个''''''''''&lt ;;

D)对此数据集进行排序以查找重复项。

需要选择A)中的散列函数,使其适合主存储器,同时提供足够的选择性。也许为此使用两个2 ^ 30大小的位集。你可以承受5-10%的冲突,这仍然应该减少数据集的大小,以便以后快速进行内存中的排序。

这实际上是一个布隆过滤器。

答案 1 :(得分:1)

您采取的暴力方法似乎要求您执行500e6查询,每个表对应一行。虽然我认为在另一个答案中提出的散列和排序方法基本上是正确的,但值得注意的是pytables已经被认为是为了速度而构建的,并且应该已经预期将这些技术有效地包括在“引擎盖下”,所以说话。

我认为你编写的简单代码很可能还没有充分利用pytables已经提供给你的功能。

create_index()的文档中,它说默认设置为optlevel=6kind='medium'。它提到您可以通过降低索引的熵来提高每个500e6查询的速度,并且可以通过选择非默认值{{1来将索引的熵减少到其最小可能值(零)通过调用create_csindex()来生成索引,或等效地,通过调用merge sort来生成索引。根据文档,您需要花费更长的时间来预先创建一个更好的优化索引,然后通过节省您在重复500e6的一系列查询上的时间来支付您的费用。次。

如果优化你的pytables列索引无法充分加速你的代码,并且你只想简单地对所有行执行大规模排序,然后只需通过查找相邻排序行中的匹配项来搜索重复项,就有可能通过以块为单位对数据进行排序,然后将块保存在磁盘上的临时文件中,使用相对适度的内存量在O(N log(N))时间内执行here。示例here和{{3}}原则上演示了如何在Python中专门执行此操作。但是你应该首先尝试优化你的pytables索引,因为这可能会在你的特定情况下提供一个更简单,更自然的解决方案。