【问题标题】:Locating multiple files in large dataset in python在python中定位大型数据集中的多个文件
【发布时间】:2023-04-21 14:25:01
【问题描述】:

我有一个大型图像文件存储库(约 200 万个,.jpg),各个 id 分布在多个子目录中,我正在尝试在包含约 1,000 个子集的列表中查找和复制每个图像身份证。

我对 Python 还是很陌生,所以我的第一个想法是使用 os.walk 遍历每个文件的 1k 子集,以查看子集中是否有任何匹配 id。这至少在理论上是可行的,但在每秒 3-5 张图像的情况下,它似乎非常慢。一次查找一个 id 的所有文件似乎也是如此。

import shutil
import os
import csv

# Wander to Folder, Identify Files
for root, dirs, files in os.walk(ImgFolder):
    for file in files:
        fileName = ImgFolder + str(file)
# For each file, check dictionary for match
        with open(DictFolder, 'r') as data1:
            csv_dict_reader = csv.DictReader(data1)
            for row in csv.DictReader(data1):
                img_id_line = row['id_line']
                isIdentified = (img_id_line in fileName) and ('.jpg' in fileName)
# If id_line == file ID, copy file
                if isIdentified:
                    src = fileName + '.jpg'
                    dst = dstFolder + '.jpg'
                    shutil.copyfile(src,dst)
                else:
                    continue

我一直在考虑尝试自动执行查询搜索,但数据包含在 NAS 上,我没有简单的方法来索引文件以加快查询速度。我运行代码的机器是 W10,因此我不能使用 Ubuntu Find 方法,我收集到的方法在此任务上要好得多。

任何加快这个过程的方法都将不胜感激!

【问题讨论】:

  • 您需要利用多处理来加快速度
  • 补充@gold_cy 所说的内容,建立某种索引以加快未来的搜索速度可能会有所帮助
  • 您应该为所有可以查询的文件创建索引/查找
  • @Alex 我很欣赏索引会加快进程,但是 afaik,这将需要管理员访问驱动器,我很遗憾无法获得
  • 如果您无权确定文件的位置,您打算如何复制文件?

标签: python python-3.x os.walk


【解决方案1】:

这里有几个脚本可以满足您的需求。

index.py

此脚本使用pathlib 遍历目录以搜索具有给定扩展名的文件。它将写入一个包含两列文件名和文件路径的 TSV 文件。

import argparse
from pathlib import Path


def main(args):
    for arg, val in vars(args).items():
        print(f"{arg} = {val}")

    ext = "*." + args.ext
    index = {}
    with open(args.output, "w") as fh:
        for file in Path(args.input).rglob(ext):
            index[file.name] = file.resolve()
            fh.write(f"{file.name}\t{file.resolve()}\n")


if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument(
        "input",
        help="Top level folder which will be recursively "
        " searched for files ending with the value "
        "provided to `--ext`",
    )
    p.add_argument("output", help="Output file name for the index tsv file")
    p.add_argument(
        "--ext",
        default="jpg",
        help="Extension to search for. Don't include `*` or `.`",
    )
    main(p.parse_args())

search.py

此脚本会将索引(index.py 的输出)加载到字典中,然后将 CSV 文件加载到字典中,然后对于每个 id_line,它将在索引中查找文件名并尝试复制它到输出文件夹。

import argparse
import csv
import shutil
from collections import defaultdict
from pathlib import Path


def main(args):
    for arg, val in vars(args).items():
        print(f"{arg} = {val}")

    if not Path(args.dest).is_dir():
        Path(args.dest).mkdir(parents=True)

    with open(args.index) as fh:
        index = dict(l.strip().split("\t", 1) for l in fh)
    print(f"Loaded {len(index):,} records")

    csv_dict = defaultdict(list)

    with open(args.csv) as fh:
        reader = csv.DictReader(fh)
        for row in reader:
            for (k, v) in row.items():
                csv_dict[k].append(v)

    print(f"Searching for {len(csv_dict['id_line']):,} files")
    copied = 0
    for file in csv_dict["id_line"]:
        if file in index:
            shutil.copy2(index[file], args.dest)
            copied += 1
        else:
            print(f"!! File {file!r} not found in index")
    print(f"Copied {copied} files to {args.dest}")


if __name__ == "__main__":
    p = argparse.ArgumentParser()
    p.add_argument("index", help="Index file from `index.py`")
    p.add_argument("csv", help="CSV file with target filenames")
    p.add_argument("dest", help="Target folder to copy files to")
    main(p.parse_args())

如何运行:

python index.py --ext "jpg" "C:\path\to\image\folder" "index.tsv"
python search.py "index.tsv" "targets.csv" "C:\path\to\output\folder"

我会先在一个/两个文件夹上尝试这个,以检查它是否具有预期的结果。

【讨论】:

    【解决方案2】:

    在文件名唯一且文件位置不变的假设下,可以创建一个字典,以O(1) 时间复杂度 搜索文件路径。字典创建过程需要一些时间,可以在你的电脑上pickle它,所以你只需要运行一次。

    创建字典的简单脚本:

    from pathlib import Path
    import pickle
    
    
    root = Path('path/to/root/folder')
    # files extensions to index
    extensions = {'.jpg', '.png'}
    # iterating over whole `root` directory tree and indexing by file name
    image = {file.stem: file for file in root.rglob('*.*') if file.suffix in extensions}
    # saving the index on your computer for further use
    index_path = Path('path/to/index.pickle')
    with index_path.open('wb') as file:
        pickle.dump(image, file, pickle.HIGHEST_PROTOCOL)
    

    加载字典的示例:

    from pathlib import Path
    import pickle
    
    
    index_path = Path('path/to/index.pickle')
    with index_path.open('rb') as file:
        image = pickle.load(file)
    

    【讨论】:

      最近更新 更多