【问题标题】:Fastest way to compare two huge csv files in python(numpy)在python(numpy)中比较两个巨大的csv文件的最快方法
【发布时间】:2017-11-10 20:10:54
【问题描述】:

我正在尝试在两个相当大的 csv 文件之间找到 intesect 子集 电话号码(一个有 60 万行,另一个有 3 亿行)。我目前正在使用 pandas 打开两个文件,然后将所需的列转换为 1d numpy 数组,然后使用 numpy intersect 来获取相交。有没有更好的方法来做到这一点,无论是使用 python 还是任何其他方法。感谢您的帮助

import pandas as pd
import numpy as np

df_dnc = pd.read_csv('dncTest.csv', names = ['phone'])
df_test = pd.read_csv('phoneTest.csv', names = ['phone'])

dnc_phone = df_dnc['phone']
test_phone = df_test['phone']

np.intersect1d(dnc_phone, test_phone)

【问题讨论】:

  • 您的 CSV 文件的结构是否相同? (即您是在寻找相同的行还是相同的 CSV 字段)?
  • 目前每个步骤需要多少时间?哪一步是瓶颈?当前的总运行时间是多少,您的目标总运行时间是多少?顺便说一句,您可以设置squeeze=True 以立即获得系列并跳过dnc_phone = df_dnc['phone'] 部分。
  • @zwer,是的,有相同的结构并比较相同的 csv 字段。这是电话号码
  • 如果这是其中之一,我会坚持使用 pandas,如果您需要至少执行几次或需要进行其他比较,我会将其加载到数据库中跨度>
  • pd.read_csv 被誉为最快的 Python csv 阅读器。 numpy intersect 可能与合并、排序和检查重复项结合使用。如果数字已经排序并且是唯一的,您可以简化它。

标签: python pandas csv numpy bigdata


【解决方案1】:

我会给你一些 Python 伪代码的通用解决方案。您在这里要解决的是"Programming Pearls" by Jon Bentley 书中的经典问题。

只需一个简单的位数组就可以非常有效地解决这个问题,因此我的评论是,电话号码有多长(有多少位数)。

假设电话号码的长度最多为 10 位,而您可以拥有的最大电话号码为:9 999 999 999(空格用于提高可读性)。这里我们可以使用每个数字 1bit 来识别该数字是否在设置中(分别设置或未设置位),因此我们将使用 9 999 999 999 位来识别每个数字,即:

  • bits[0]标识号码0 000 000 000
  • bits[193]标识号码0 000 000 193
  • 号码为 659 234-4567 将由bits[6592344567] 处理

这样做,我们需要预先分配最初设置为 09 999 999 999 位,即:9 999 999 999 / 8 / 1024 / 1024 = 大约 1.2 GB 的内存。

我认为最后保存数字的交集将比位表示使用更多的空间 => 最多将存储 600k 个整数 => 64bit * 600k = 大约 4.6 GB (actually int is not stored that efficiently and might use much more),如果这些是字符串你可能会以更多的内存要求结束。

从 CSV 文件(逐行或缓冲文件阅读器)解析电话号码字符串,将其转换为数字,而不是进行恒定时间的内存查找,IMO 将比处理字符串并合并它们更快。很遗憾,我没有要测试的这些电话号码文件,但很想听听您的发现。

from bitstring import BitArray


max_number = 9999999999

found_phone_numbers = BitArray(length=max_number+1)


# replace this function with the file open function and retrieving
#  the next found phone number
def number_from_file_iteator(dummy_data):
    for number in dummy_data:
        yield number


def calculate_intersect():
    # should be open a file1 and getting the generator with numbers from it
    #  we use dummy data here
    for number in number_from_file_iteator([1, 25, 77, 224322323, 8292, 1232422]):
        found_phone_numbers[number] = True

    # open second file and check if the number is there
    for number in number_from_file_iteator([4, 24, 224322323, 1232422, max_number]):
        if found_phone_numbers[number]:
            yield number


number_intersection = set(calculate_intersect())

print number_intersection

我使用了bitstring pip package 中的BitArray,它需要大约2 秒来初始化整个位串。之后,扫描文件将使用常量内存。最后我使用set 来存储项目。

注意 1: 该算法可以修改为只使用list。在这种情况下,一旦位号与该位匹配,就必须重置第二个循环,这样重复项就不会再次匹配。

注意 2: 存储在 set/list 是惰性的,因为我们在第二个 for 循环中使用了生成器。运行时复杂度是线性的,即O(N)

【讨论】:

    【解决方案2】:
    1. 将 600k 电话号码读入set
    2. 逐行输入较大的文件,对照集合检查每一行。
    3. 立即将匹配项写入输出文件。

    这样您就不必一次将所有数据加载到内存中。

    【讨论】:

    • 与使用 Pandas 和 NumPy 中提供的编译代码工具相比,这将产生灾难性的性能。
    • 其他条件相同,当然可以。但它最大限度地减少了内存占用。如果 OP 已经获得不错的性能,则无需尝试,我同意。 (很遗憾,这个问题没有说明需要解决什么问题。)
    猜你喜欢
    • 2012-10-28
    • 1970-01-01
    • 2016-11-12
    • 2015-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    相关资源
    最近更新 更多