2 votes

Accélérer la correspondance des fichiers en fonction de leur nom

J'ai donc deux répertoires avec deux types de fichiers différents (par exemple .csv, .png) mais avec le même nom de base (par exemple 1001_12_15.csv, 1001_12_15.png). J'ai plusieurs milliers de fichiers dans chaque répertoire.

Ce que je veux faire, c'est obtenir les chemins complets des fichiers, après avoir fait correspondre les noms de base, puis FAIRE quelque chose avec le chemin complet des deux fichiers.

Je demande de l'aide pour accélérer la procédure.

Mon approche est la suivante :

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

de plus j'ai essayé fnmatch quelque chose comme :

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

Il semble que l'utilisation des boucles for imbriquées soit plus rapide. Mais je voudrais que ce soit encore plus rapide. Existe-t-il d'autres approches qui me permettraient d'accélérer mon code ?

1voto

Piakkaa Points 547

Tout d'abord, optimiser sur votre boucle existante comme ceci

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

les boucles imbriquées sont très lentes car il faut exécuter la boucle png n2 fois

Vous pouvez alors utiliser compréhension de la liste y indice de tableau pour l'accélérer davantage

## 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)
  • cette logique utilisant l'index de liste réduit le nombre de boucles de manière significative et il n'y a pas d'appels répétitifs à la librairie os.
  • La compréhension des listes est légèrement plus rapide que celle des boucles normales.

Vous pouvez parcourir la liste en boucle et faire quelque chose avec les valeurs. par exemple

for csv_base,png_base in csv_png_base:
    #do something

pandas fera le travail beaucoup plus rapidement car il exécutera la boucle en utilisant une bibliothèque C

0voto

MisterMiyagi Points 2734

Vous pouvez construire un index de recherche en O(n), puis rechercher des éléments dans cet index en O(1) chacun. Si vous avez des correspondances exactes comme votre question l'implique, une recherche à plat (flat lookup) dict suffit :

from os.path import basename, splitext

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

Cela vous permet de rechercher directement le fichier png correspondant à chaque fichier csv :

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

Au final, vous avez une construction de consultation O(n) et une itération séparée O(n) avec une consultation imbriquée O(1). La complexité totale est de O(n) par rapport à votre O(n^2) initial.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X