Ускорение сопоставления файлов на основе имен файлов
Поэтому у меня есть 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()
как только вы загрузите пример, предоставим код решения
удачи