Ускорение сопоставления файлов на основе имен файлов

Поэтому у меня есть 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 быстрее. Но я хочу, чтобы это было еще быстрее. Есть ли другие подходы, которые я мог бы ускорить мой код?

3 ответа

Прежде всего, оптимизируйте синтаксис в существующем цикле, как это

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 раза

Тогда вы можете использовать списки и индекс массива, чтобы ускорить его

## 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)
  • эта логика, использующая индекс списка, значительно уменьшает количество циклов, и нет повторяющихся вызовов ОС lib
  • понимание списка немного быстрее, чем в обычном цикле

Вы можете перебрать список и сделать что-то со значениями, например

for csv_base,png_base in csv_png_base:
    #do something

хотяpandas сделает работу намного быстрее, потому что она будет запускать цикл с использованием библиотеки C

Вы можете создать поисковый индекс в 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
}

Это позволяет вам непосредственно искать png-файл, соответствующий каждому 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

В конце концов, у вас есть конструкция поиска O (n) и отдельная итерация O (n) с вложенным поиском O(1). Общая сложность O (n) по сравнению с вашей первоначальной O(n^2).

Я предложу следующий алгоритм с использованием панд

1.create a pandas data frame with columns as "file name" and "file path csv" file path png"

2.use glob/os.walk to loop over the files in the directory. - 

    2.1 for every file name insert the path and name to the data frame.

3. use groupby method to group by the file name

   3.1 use the sum method on the groupby to create one row which have file name and 
      correspond file path and png path.
      # df = df.groupby(df[['file_name']].sum(1)).sum().reset_index()

как только вы загрузите пример, предоставим код решения

удачи

Другие вопросы по тегам