Лучший способ или алгоритм для почти двойной проверки по огромному списку файлов? - PullRequest
1 голос
/ 29 февраля 2012

Я использую python, чтобы переписать огромный список файлов (более 20000). Всего около 300 МБ

Текущим способом является проверка почти дупла с использованием difflib SequenceMatcher и получение результата с использованием QuickRatio.

При 4 рабочих процессах работа выполняется 25 часов, что довольно медленно.

Я также попробовал Livenstheine, который дает проверку на почти полное дублирование базы С, но он даже медленнее и менее точен, чем difflib.

Проверка должна быть выполнена следующим образом: В папке 20000 файлов. каждый файл нужно сравнивать с 20000 файлами в папке на каждой итерации. поэтому будет 20000 * 20000 итераций.

Я думаю о том, чтобы проиндексировать все файлы и сравнить индексы, но я новичок в индексации, и я не уверен, что это сработает. Если это так, какие варианты индексации лучше?

Спасибо.

Ниже приведен код:

import os,sys,chardet, csv,operator,time,subprocess
from difflib import SequenceMatcher
import threading
#from threading import Timer
import multiprocessing
from multiprocessing import Pool

OrgFile = ""
mark = int(sys.argv[2])

def init_logger():
    print "Starting %s" % multiprocessing.current_process().name

#----Get_Near_DupeStatus--------#
def Get_Near_DupeStatus(score):
    if score > 30 and score <= 50:
        return "Low Inclusive"
    elif score > 50 and score <= 75:
        return "Inclusive"
    elif score > 75 and score <= 85:
        return "Most Inclusive"
    elif score > 85 and score <= 99:
        return "Near-Dupe"
    elif score == 100:
        return "Unique"
    else: return "No Inclusive"

#----Write_To_CSV --- ALL-------#
def Write_To_CSV_All(List):
    writer = csv.writer(open('./TableList.csv','wb'),delimiter=';', quotechar=' ', quoting=csv.QUOTE_MINIMAL)
    writer.writerow(['Path/FileName(Source);'+'ID;'+'NearDupeID;'+'Similarity Score;'+'Near_DupeStatus;'+'NearDupeProcess(Y/N);'+'Encoding'])
    for i,li in enumerate(sorted(List, key=operator.itemgetter("NearDupeID"))):
        writer.writerow([li['Path/FileName(Source)']+";"+'ID00'+str(i+1)+";"+str(li['NearDupeID'])+";"+str(li['Similarity Score'])+";"+li['Near_DupeStatus']+";"+li['NearDupeProcess(Y/N)']+";"+li['Encoding']])

#Get Finish File List
def Finish_Files(List,count,id):
    finish_files = []
    for i,li in enumerate(sorted(List, key=operator.itemgetter("Similarity Score"), reverse=True)):
        if i < count:
            li['NearDupeID'] = id
            finish_files.append(li)
        if count == 0:
            li['NearDupeID'] = id
#            if li['Similarity Score'] > 50:
            finish_files.append(li)
    return finish_files

#----Search Files in Dir--------#
def GetFileListFrom_Dir(dir):
    FileList = []
    for root,dirs,filenames in os.walk(dir):
        for filename in filenames:
            realpath = os.path.join(root, filename)
            FileList.append(realpath)
    return FileList

#----Matcher--------#
def Matcher(realpath):
    junk = ["\t","\n","\r"]
    score = 0
    dict = {}
    MatchFile = ""
    dupe_Process = 'N'
    if os.path.isfile(realpath):
        MatchFile =  open(realpath).read()
        matcher = SequenceMatcher(lambda x: x in junk,OrgFile, MatchFile)
        score = int(matcher.ratio()*100)
        if score >= mark:
            encoding = chardet.detect(MatchFile)['encoding']
            if encoding == None: encoding = 'None'
            if score > 85: dupe_Process = 'Y'
            dict = {'Path/FileName(Source)':realpath,'Similarity Score':score,'Near_DupeStatus':Get_Near_DupeStatus(score),'NearDupeProcess(Y/N)':dupe_Process,'Encoding':encoding}
            return dict

#-------------Pooling--------------------#
def MatcherPooling(FileList,orgFile,process):
    global OrgFile
    OrgFile = open(orgFile).read()
    pool_obj = Pool(processes=process)
    #pool_obj = Pool(processes=process,initializer=init_logger)
    dict = {}
    DictList = []
    dict = pool_obj.map(Matcher,FileList)
    DictList.append(dict)
    pool_obj.close()
    pool_obj.join()
    return DictList

def Progress():
    p = "/-\\|"
#    global t
    for s in p:
        time.sleep(0.1)
        sys.stdout.write("%c" % s)
        sys.stdout.flush()
        sys.stdout.write('\b')
    t2 = threading.Timer(0.1,Progress).start()
#    t.start()


#----Main--------#
def Main():
    Mainls = []
    dictList = []
    finish_List = []
    BLINK = '\033[05m'
    NOBLINK = '\033[25m'
    dir = sys.argv[1]
    process = int(sys.argv[3])
    Top_rec = int(sys.argv[4])
    Mainls = GetFileListFrom_Dir(dir)
    bar = "*"
    # setup toolbar
    sys.stdout.write("%s" % BLINK+"Processing...."+ NOBLINK + "With "+ str(process) + " Multi Process...")#+" \n")
    if Top_rec != 0:
        charwidth = len(Mainls)/Top_rec
    elif Top_rec == 0: charwidth = len(Mainls)
    t = threading.Timer(0.1,Progress)
    t.start()
#    sys.stdout.write("[%s]" % ("-" * charwidth))
#    sys.stdout.flush()
#    sys.stdout.write("\b" * (charwidth+1)) # return to start of line, after '['

    #----------------------------------------------------------#
    for id,orgFile in enumerate(sorted(Mainls)):
        for dl in MatcherPooling(sorted(Mainls),orgFile,process):
            for dict in dl:
                if dict != None:
                    dictList.append(dict)

            #Append Finish Files List For CSV ALL(Write Once)
            fl = Finish_Files(dictList,Top_rec,id+1)
            if Top_rec != 0:
                for del_List in fl:
                    Mainls.remove(del_List['Path/FileName(Source)'])
                    Mainls.sort()

            finish_List.extend(fl)
            dictList = []

        sys.stdout.write("%s" % bar)
        sys.stdout.flush()

        #Exit Loop
        if len(Mainls) == 0:
            break
    #----------------------------------------------------------#
    Write_To_CSV_All(finish_List)
    #print os.system('clear')
    sys.stdout.write("%s" % " ")
    print "Finished!"
    t.cancel()
    print os._exit(99)

if __name__ == '__main__':
    Main()

Ответы [ 4 ]

3 голосов
/ 29 февраля 2012

Частичный ответ, но одна очевидная оптимизация - сравнивать только файлы примерно одинакового размера. Также сравнение файлов a и b аналогично сравнению файлов b и a: 20000 файлов дают 20000 * (20000-1) / 2 сравнения. 300 МБ не так уж много, вы можете сначала прочитать все файлы.

При индексировании речь идет об описании каждого файла одним или несколькими числами. Размер один. Число непустых пробелов, пробелов или символов новой строки может быть другим. Если все файлы содержат данные одного типа, вы можете интерпретировать данные для создания более полезных чисел. Также полностью идентичные файлы будут иметь одинаковый хэш SHA-256. Это поможет, только если значительная часть файлов идентична.

import hashlib
m = hashlib.sha256()
m.update(file_contents)
this_files_hash_value=m.digest()

К сожалению, я не могу придумать ни одного метода для абсолютно точной и правильной факторизации (частей) того, что делает difflib.SequenceMatcher, поскольку он динамически сравнивает все возможные фрагменты входных файлов.

0 голосов
/ 07 апреля 2013

Если вы работаете с большими коллекциями и хотите найти не только дубликаты, но и почти дубликаты в текстовых файлах (PDF, DOC, HTML и т. Д.), Вам может понадобиться что-то вроде этого: http://softcorporation.com/products/neardup/ Он основан на Java, поэтому будет работать на разных платформах, и вы можете скачать его бесплатно.

0 голосов
/ 01 марта 2012

Прежде всего вы должны знать, насколько ваша процедура настолько быстра, насколько это возможно.

  • Время среднего времени выполнения, которое требуется для сравнения 2 файлов.Просто сравните их, не используйте многопроцессорность, не индексируйте ничего, не пишите в CSV ничего.
  • Умножьте это на 20000 x 10000 / 4 (так как у вас есть 4 рабочих)
  • Сравнитьрезультат, который вы получите с вашими текущими 25 часами.

Я считаю, что это очень важный тест, потому что он покажет, есть ли еще возможности для улучшения с вашим текущим алгоритмом сравнения.

Если есть возможности для улучшения, чем начать это делать!Потому что, как только у вас будет процедура optimezied, вы сможете двигаться дальше и начать искать лучшее решение для сравнения.

Если вы хотите обратиться за помощью, как это сделать, вам придетсяпредоставьте более читаемый пример кода (и короткий), потому что невозможно работать над тем, что у вас есть сейчас.

0 голосов
/ 29 февраля 2012

Я бы начал с того, что, как уже упоминалось, сравнивал только те файлы, которые относительно близки по размеру или похожи по типу MIME.

тогда я бы продолжил, сравнивая лишь небольшую часть данных, чтобы немедленно исключить большинство случаев. Хороший алгоритм должен начинаться с небольшого фрагмента файлов, сравнивать его, увеличивать размер в 2 раза (или больше, если хотите), если он достаточно похож, и так далее, пока вы не закончите сравнение всего или пока на любом этапе коэффициент подобия слишком низкий.

это означает, что, вероятно, первые 1-2 итерации исключат большинство файлов.

...