【问题标题】:Speed up file matching based on names of files根据文件名加快文件匹配
【发布时间】:2019-11-29 15:21:58
【问题描述】:

所以我有 2 个具有 2 种不同文件类型(例如 .csv、.png)但具有相同基本名称(例如 1001_12_15.csv、1001_12_15.png)的目录。我在每个目录中有数千个文件。

我想要做的是在匹配基本名称之后获取文件的完整路径,然后对两个文件的完整路径执行一些操作。

我正在寻求有关如何加快该过程的帮助。

我的做法是:

csvList=[a list with the full path of each .csv file]
pngList=[a list with the full path of each .png file]



for i in range(0,len(csvlist)):
    csv_base = os.path.basename(csvList[i])
    #eg 1001
    csv_id = os.path.splitext(fits_base)[0].split("_")[0]

    for j in range(0, len(pngList)):
        png_base = os.path.basename(pngList[j])
        png_id = os.path.splitext(png_base)[0].split("_")[0]
        if float(png_id) == float(csv_id):
            DO SOMETHING

我试过 fnmatch 之类的东西:

for csv_file in csvList:
    try:
        csv_base = os.path.basename(csv_file)

        csv_id = os.path.splitext(csv_base)[0].split("_")[0]

        rel_path = "/path/to/file"
        pattern = "*" + csv_id + "*.png"

        reg_match = fnmatch.filter(pngList, pattern)
        reg_match=" ".join(str(x) for x in reg_match)
        if reg_match:
            DO something

似乎使用嵌套的 for 循环更快。但我希望它更快。还有其他方法可以加快我的代码速度吗?

【问题讨论】:

  • png = glob.glob(".png") , csv = glob.glob(".csv")
  • 有一些示例数据会很高兴
  • 列表推导通常比循环更简洁、更快
  • 您不必要地搜索完全匹配。为什么不检查给定 csv 的 png 是否确实存在?如果您想在内存中执行此操作,为什么不使用集合进行 O(1) 查找?
  • 您的描述提到文件匹配整个名称,但扩展名。您的代码仅在下划线之前比较基本名称的开头。你在找什么样的比赛?

标签: python performance for-loop match fnmatch


【解决方案1】:

首先,像这样优化现有循环的语法

for csv in csvlist:
    csv_base = os.path.basename(csv)
    csv_id = os.path.splitext(csv_base)[0].split("_")[0]

    for png in pnglist:
        png_base = os.path.basename(png)
        png_id = os.path.splitext(png_base)[0].split("_")[0]
        if float(png_id) == float(csv_id):
            #do something here

嵌套循环非常慢,因为您需要运行 png 循环 n2 次​​p>

然后你可以使用 list comprehensionarray index 来加快速度

## create lists of processed values 
## so you dont have to keep running the os library
sv_base_list=[os.path.basename(csv) for csv in csvlist]
csv_id_list=[os.path.splitext(csv_base)[0].split("_")[0] for csv_base in csv_base_list]
png_base_list=[os.path.basename(png) for png in pnglist]
png_id_list=[os.path.splitext(png_base)[0].split("_")[0] for png_base in png_base_list]


## run a single loop with list.index to find matching pair and record base values array

csv_png_base=[(csv_base_list[csv_id_list.index(png_id)], png_base)\
                   for png_id,png_base in zip(png_id_list,png_base_list)\
                   if png_id in csv_id_list]

## csv_png_base contains a tuple contianing (csv_base,png_base)
  • 这种使用列表索引的逻辑显着减少了循环次数,并且没有重复的 os lib 调用
  • 列表理解比普通循环稍快

您可以遍历列表并对这些值做一些事情 例如

for csv_base,png_base in csv_png_base:
    #do something

pandas 会更快地完成这项工作,因为它将使用 C 库运行循环

【讨论】:

  • 根据我的经验,在 Python 中编写一个 csv 文件解析器会更快,它会返回一个 dict,然后您可以将其转换为 pandas df。你试过吗?
  • 列表推导在性能上与 for 循环相当。此外,它们比多个列表理解的代码块更具可读性。
  • 好吧,只是想坚持他的方法。加上数组索引会节省很多循环计数不是吗
  • 你可以自己测试一下
  • list.index 运行一个内部循环来查找该项目。它仍然是 O(n)。
【解决方案2】:

您可以在 O(n) 中建立一个搜索索引,然后在每个 O(1) 中查找其中的项目。如果您有问题所暗示的完全匹配,则平面查找 dict 就足够了:

from os.path import basename, splitext

png_lookup = {
    splitext(basename(png_path))[0] : png_path
    for png_path in pngList
}

这样可以直接查找每个csv文件对应的png文件:

for csv_file in csvList:
    csv_id = splitext(basename(csv_file)[0]
    try:
        png_file = png_lookup[csv_id]
    except KeyError:
        pass
    else:
        # do something

最后,您有一个 O(n) 查找构造和一个带有嵌套 O(1) 查找的单独 O(n) 迭代。与最初的 O(n^2) 相比,总复杂度为 O(n)。

【讨论】:

    猜你喜欢
    • 2018-10-23
    • 2017-08-14
    • 2019-04-14
    • 2019-10-05
    • 2021-10-28
    • 1970-01-01
    • 1970-01-01
    • 2014-09-22
    • 2012-12-31
    相关资源
    最近更新 更多