使用 Python 有效地查找部分字符串匹配 --> 从 5 GB 文件中的值列表开始的值

Posted

技术标签:

【中文标题】使用 Python 有效地查找部分字符串匹配 --> 从 5 GB 文件中的值列表开始的值【英文标题】:Efficiently Find Partial String Match --> Values Starting From List of Values in 5 GB file with Python 【发布时间】:2015-11-02 06:51:02 【问题描述】:

我有一个 5GB 的企业文件,我正在尝试提取其业务类型代码 (SNACODE) 以对应于杂货店的 SNACODE 开头的所有企业。例如,某些企业的 SNACODE 可能是 42443013、44511003、44419041、44512001、44522004,我想要所有代码以我的杂货 SNACODES 代码列表开头的所有企业 = [4451,4452,447,772,45299,45291,45212]。在这种情况下,我会得到 44511003、44512001 和 44522004 的行

根据我搜索的内容,读取文件的最有效方式似乎是一次一行(如果不是 SQL 路由)。然后我使用了一个 for 循环并检查了我的 SNACODE 列是否以我的任何代码开头(这可能是一个坏主意,但我可以开始工作的唯一方法)。

我不知道文件中有多少行,但有 84 列。我的电脑运行了很长时间,所以我问了一个朋友,他说完成这个任务应该只需要 10-20 分钟。我的朋友编辑了代码,但我认为他误解了我正在尝试做的事情,因为他的结果没有返回任何内容。

我现在正试图找到一种比重复我的 9.5 小时并让我的笔记本电脑运行未知时间更有效的方法。我能找到的最接近的东西是most efficient way to find partial string matches in large file of strings (python),但它似乎不是我要找的东西。

问题:

最好的方法是什么?这需要多长时间? 有什么方法可以让我从停止的地方开始? (我不知道我读取了多少行 5gb 文件,但我有最后保存的数据行 - 是否有一种快速/简单的方法可以在文件中找到与唯一 ID 对应的行而无需读取每个行?)

这是我尝试过的——在 9.5 小时内,它输出了一个 72MB 的文件(200k+ 行)的杂货店

    codes = [4451,4452,447,772,45299,45291,45212]  #codes for grocery stores
    for df in pd.read_csv('infogroup_bus_2010.csv',sep=',', chunksize=1):
        data = np.asarray(df)
        data = pd.DataFrame(data, columns = headers)
        for code in codes:
            if np.char.startswith(str(data["SNACODE"][0]), str(code)):
                with open("grocery.csv", "a") as myfile:
                    data.to_csv(myfile, header = False)
                    print code
                break  #break code for loop if match

    grocery.to_csv("grocery.csv", sep = '\t')

这是我朋友编辑的。我很确定 x = df[df.SNACODE.isin(codes)] 只匹配完美匹配,因此什么也不返回。

    codes = [4451,4452,447,772,45299,45291,45212]  
    matched = []
    for df in pd.read_csv('infogroup_bus_2010.csv',sep=',', chunksize=1024*1024, dtype = str, low_memory=False):
        x = df[df.SNACODE.isin(codes)]
        if len(x):
            matched.append(x)
    print "Processed chunk and found  matches".format(len(x))
  
    output = pd.concat(matched, axis=0)
    output.to_csv("grocery.csv", index = False)

谢谢!

【问题讨论】:

你应该创建一个测试文件,它只会运行一两分钟,然后你可以测试算法,看看哪个最有效。 对于简单的字符串匹配,您可以通过从需求中删除 Pandas 来简化很多。 如果这是一次性的,我认为不值得探索如何从中间的某个地方继续。只需从头到尾重新运行。 ***.com/questions/17957890/… 【参考方案1】:

为了提高速度,您可以预先构建一个与您需要的行匹配的正则表达式并读取原始文件行(无 csv 解析)并使用正则表达式检查它们...

codes = [4451,4452,447,772,45299,45291,45212]
col_number = 4 # Column number of SNACODE
expr = re.compile("[^,]*," * col_num +
                  "|".join(map(str, codes)) +
                  ".*")
for L in open('infogroup_bus_2010.csv'):
    if expr.match(L):
        print L

请注意,这只是一个简单的草图,因为没有考虑转义...如果 SNACODE 列不是第一个并且前面的字段可能包含逗号,您需要更复杂的正则表达式,例如:

...
'([^"][^,]*,|"([^"]|"")*",)' * col_num +
...

忽略双引号内的逗号

【讨论】:

对不起,我对您的最后一条评论感到困惑:“请注意,这只是一个简单的草图,因为不考虑转义......如果 SNACODE 列不是第一个并且前面的字段可能包含逗号,您需要更复杂的正则表达式。”能详细点吗? @user3768258:此代码正在构建以跳过 SNACODE 字段之前的字段的正则表达式是 [^,]*,[^,]*,...,即零个或多个非逗号后跟逗号的序列。但是,如果字段内容是例如"hey, dude",则逗号被引用并且不应被视为字段分隔符。当然可以编写一个跳过引号的正则表达式,但它比 [^,]* 更复杂。 一般来说,将 L 保存为一个新的数据帧然后在最后将它写为一个大的 df 或者写下每一行是否更快?谢谢! Python 缓冲没问题...这种处理(读一行,写一行)通常相当快,而且所有耗时的东西都是用 C 完成的。我不会期待很长时间5Gb 输入的计算时间(肯定不是几个小时)。为了获得最大速度,但是我会选择使用 mmap 访问文件内容的 C++ 版本......对于这种处理,我希望它的速度与复制 5Gb 的速度基本相同文件。如果需要对同一数据进行许多不同的搜索,那么构建索引可能是一种更好的方法......【参考方案2】:

您或许可以让您的 pandas 解决方案更快:

codes = [4451, 4452, 447, 772, 45299, 45291, 45212]
codes = [str(code) for code in codes]

sna = pd.read_csv('infogroup_bus_2010.csv', usecols=['SNACODE'], 
                  chunksize=int(1e6), dtype='SNACODE': str)

with open('grocery.csv', 'w') as fout:
    for chunk in sna:
        for code in chunk['SNACODE']:
            for target_code in codes:
                if code.startswith(target_code):
                    fout.write('\n'.format(code))

使用usecols=['SNACODE'] 只读所需的列。您可以使用chunksize=int(1e6) 调整块大小。根据您的 RAM,您可能会做得更大。

【讨论】:

以上是关于使用 Python 有效地查找部分字符串匹配 --> 从 5 GB 文件中的值列表开始的值的主要内容,如果未能解决你的问题,请参考以下文章

[算法总结] 13 道题搞定 BAT 面试——字符串

如何有效地将大字符串从 Python 传递到 C++ 扩展方法?

python与正则表达式

C ++有效查找向量中第一个最近的匹配值?

preg_grep 输出部分匹配

如何更有效地计算 n 个字符串之间的不匹配分数?