pandas 算法慢:for 循环和 lambda

pandas algorithm slow: for loops and lambda

总结:我正在搜索一堆数据之间的拼写错误,这需要很长时间

我正在遍历几个 CSV 文件(总共一百万行?),在每个文件中我都在遍历一个 json 子值,该子值可能有 200 个要搜索的字符串。对于每个循环或 json 值,我向每个数据框添加一列,然后使用 lambdas 函数使用 Levenshtein 的搜索算法查找拼写错误。然后我输出包含潜在拼写错误的任何行的结果 代码:

for file in file_list:  #20+ files             
df = pd.read_csv(file, usecols=["search column","a bunch of other columns...") #50k lines each-ish
for v in json_data.values():  #30 ish json values
    for row in v["json_search_string"]:    #200 ish substrings
        df_temp = df
        df_temp['jelly'] = row
        df_temp['difference'] = df_temp.apply(lambda x: jellyfish.levenshtein_distance(x['search column'],x['jelly']), axis=1)
        df_agg = df_temp[df_temp['difference'] <3]
        if os.path.isfile(filepath+"levenshtein.csv"):
            with open(filepath+"levenshtein.csv", 'a') as f:
                df_agg.to_csv(f, header=False)
        else:
            df_agg.to_csv(filtered_filepath+"levenshtein.csv") 

我之前尝试过相同的算法,但为了保持简短,我没有遍历每个 CSV 的所有 JSON 值,而是只做了一个 JSON 值,如下所示:

for file in file_list:  #20+ files             
    df = pd.read_csv(file, usecols=["search column","a bunch of other columns...") #50k lines each-ish
    for row in data['z']['json_search_string']:
        #levenshtein algorithm above

上面的循环花了大约 100 分钟 到 运行 完成! (编辑:lambda 函数每次 运行 大约需要 1-3 秒)并且 JSON 文件中大约有 30 个。关于如何压缩算法并使其更快的任何想法?我想也许我可以获取所有 200ish json 子字符串并将它们作为一个列添加到每个 df 并以​​某种方式 运行 一次搜索所有列的 lambda 函数,但我不确定如何做到这一点呢。这样我只会将 20 个文件每个迭代 30 次,而不是第三层 for 循环添加的数千次迭代。想法?

备注: 以下是数据的示例: JSON数据

{
"A": {
    "email": "blah",
    "name": "Joe Blah",
    "json_search_string": [
        "Company A",
        "Some random company name",
        "Company B",
        "etc",
        "..."

和 csv 列:

ID, Search Column,            Other Columns
1,  Clompany A,               XYZ
2,  Company A,                XYZ
3,  Some misspelled company,  XYZ
etc

嗯,性能增强的问题真的很难回答。 根据努力和表现,这里有一些建议。

  1. 通过重新安排代码逻辑进行小幅调整。努力:小。预期增强:小。通过查看您的代码,我知道您要将来自 File(数字 20)的单词与固定的 JSON File(只有一个)进行比较。与其为每个 File 读取 JSON File,为什么不首先从 JSON File 准备固定单词列表,并将其用于所有后续比较?逻辑是这样的:

    # prepare fixed words from JSON DATA
    fixed_words = [] 
    for v in json_data.values():
        fixed_words += v["json_search_string"]
    # looping over each file, and compare them with word from fixed words
    for f in file_list:
        # do the comparison and save.
    
  2. 使用多处理。努力:小。预期增强:中位数。既然你所有的工作都是相似的,为什么不尝试多处理呢?您可以在每个文件上应用多处理,或者在执行 时应用。有很多多处理的源代码,请看一下。很容易为您的案例实施。

  3. 使用其他语言实现编辑距离。您的代码的瓶颈是 Levenshtein 距离的计算。你使用了jellyfish python 包,它是一个纯粹的python(当然,对于大集来说性能不好)。以下是一些其他选项:

    一个。已经存在具有 C/C++ 实现的 python 包。努力:小。预期增强:高。感谢@Corley Brigman 的评论,editdistance 是您可以使用的一个选项。

    b。 Cyphon 自行实现。努力:中位数。增强:中值或高。检查 pandas 文档 Performance

    c。 C/C++ 作为包装器自行实现。努力:高;预期增强:高。检查 Wrapping with C/C++

您可以使用我的一些建议来获得更高的性能。 希望这会有所帮助。

您可以将代码更改为:

for file in file_list:  #20+ files             
    df = pd.read_csv(file, usecols=["search column","a bunch of other columns...") #50k lines each-ish
    x_search  = x['search column']
    for v in json_data.values():  #30 ish json values
        for row in v["json_search_string"]:    #200 ish substrings
            mask = [jellyfish.levenshtein_distance(s1,s2) < 3  for s1,s2 in zip(x_search, row) ] 
            df_agg = df_temp[mask]
            if os.path.isfile(filepath+"levenshtein.csv"):
                with open(filepath+"levenshtein.csv", 'a') as f:
                    df_agg.to_csv(f, header=False)
            else:
                df_agg.to_csv(filtered_filepath+"levenshtein.csv") 

apply return 可能更贵的系列副本:

a = range(10**4)
b = range(10**4,2*(10**4))
%timeit [ (x*y) <3 for x,y in zip(a,b)]
%timeit pd.DataFrame([a,b]).apply(lambda x: x[0]*x[1] < 3 )

1000 次循环,3 次循环最佳:每次循环 1.23 毫秒

1 个循环,3 个循环中的最佳:每个循环 668 毫秒