#python
#python
Вопрос:
У меня есть многомерный массив, в который я пытаюсь загрузить difflib.get_close_matches()
.
Мой массив выглядит следующим образом: array[(ORIGINAL, FILTERED)]
. ORIGINAL
— это строка, и FILTERED
это ORIGINAL
строка с отфильтрованными общими словами.
В настоящее время у меня создается новый массив, в который вводятся только FILTERED
слова difflib.get_close_matches()
. Затем я пытаюсь сопоставить результат из difflib
с array[(ORIGINAL, FILTERED)]
. Моя проблема в том, что часто у меня есть два или более FILTERED
слов, которые эквивалентны, и поэтому они не могут быть сопоставлены с использованием этого метода.
Есть ли способ, с помощью которого я могу передать весь array[(ORIGINAL,FILTERED)]
в difflib
, но при этом просмотреть только FILTERED
часть (все еще возвращая [(ORIGINAL,FILTERED)]
?)
Заранее спасибо!
import time
import csv
import difflib
import sys
import os.path
import datetime
### Filters out common words in an attempt to get better results ###
def ignoredWords (word):
filtered = word.lower()
#Common Full Words
## Majority of filters were edited out
#Common Abbreviations
if "univ" in filtered:
filtered = filtered.replace("univ","")
#Special Characters
if " " in filtered: #Two White Spaces
filtered = filtered.replace(" "," ")
if "-" in filtered:
filtered = filtered.replace("-"," ")
if "'" in filtered:
filtered = filtered.replace("'"," ")
if " amp; " in filtered:
filtered = filtered.replace(" amp;","")
if "("" in filtered:
filtered = filtered.replace("("","")
if "")" in filtered:
filtered = filtered.replace("")","")
if "t" in filtered:
filtered = filtered.replace("t"," ")
return filtered
### Takes in a list, then outputs a 2D list. array[Original, Filtered] ###
### For XXX: array[Original, Filtered, Account Number, Code] ###
def create2DArray (list):
array = []
for item in list:
clean = ignoredWords(item[2])
entry = (item[2].lower(), clean, item[0],item[1])
array.append(entry)
return array
def main(argv):
if(len(argv) < 3):
print "Not enough parameters. Please enter two file names"
sys.exit(2)
elif (not os.path.isfile(argv[1])):
print "%s is not found" %(argv[1])
sys.exit(2)
elif (not os.path.isfile(argv[2])):
print "%s is not found" %(argv[2])
sys.exit(2)
#Recode File ----- Not yet implemented
# if(len(argv) == 4):
# if(not os.path.isfile(argv[3])):
# print "%s is not found" %(argv[3])
# sys.exit(2)
#
# recode = open(argv[1], 'r')
# try:
# setRecode = c.readlines()
# finally:
# recode.close()
# setRecode.sort()
# print setRecode[0]
#Measure execution time
t0 = time.time()
cReader = csv.reader(open(argv[1], 'rb'), delimiter='|')
try:
setC = []
for row in cReader:
setC.append(row)
finally:
setC.sort()
aReader = csv.reader(open(argv[2], 'rb'), delimiter='|')
try:
setA = []
for row in aReader:
setA.append(row)
finally:
setA.sort()
#Put Set A and Set C into their own 2 dimmensional arrays.array[Original Word] [Cleaned Up Word]
arrayC = create2DArray(setC)
arrayA = create2DArray(setA)
#Create clean list versions for use with difflib
cleanListC = []
for item in arrayC:
cleanListC.append(item[1])
cleanListA = []
for item in arrayA:
cleanListA.append(item[1])
############OUTPUT FILENAME############
fMatch75 = open("Match75.csv", 'w')
Match75 = csv.writer(fMatch75, dialect='excel')
try:
header = "Fuzzy Matching Report. Generated: "
header = str(datetime.date.today())
Match75.writerow([header])
Match75.writerow(['C','A','C Cleaned','A Cleaned','C Account', 'C Group','A Account', 'A Group', 'Filtered Ratio %','Unfiltered Ratio %','Average Ratio %'])
for item in cleanListC:
match = difflib.get_close_matches(item,cleanListA,1,0.75)
if len(match) > 0:
filteredratio = difflib.SequenceMatcher(None,item,match[0]).ratio()
strfilteredratio = '%.2f' % (filteredratio*100)
found = 0
for group in arrayA:
if match[0] == group[1]:
origA = group[0]
acode = group[3]
aaccount = group[2]
found = found 1
for group in arrayC:
if item == group[1]:
origC = group[0]
ccode = group[3]
caccount = group[2]
found = found 2
if found == 3:
unfilteredratio = difflib.SequenceMatcher(None,origC,origA).ratio()
strunfilteredratio = '%.2f' % (unfilteredratio*100)
averageratio = (filteredratio unfilteredratio)/2
straverageratio = '%.2f' % (averageratio*100)
row = [origC.rstrip(),origA.rstrip(),item.rstrip(),match[0].rstrip(),caccount,ccode,aaccount,acode,strfilteredratio,strunfilteredratio,straverageratio]
Match75.writerow(row)
#These Else Ifs are for debugging. If NULL is found anywhere in the CSV, then an error has occurred
elif found == 2:
row = [origC.rstrip(),"NULL",item.rstrip(),match[0].rstrip(),caccount,ccode,"NULL","NULL",strfilteredratio,"NULL","NULL"]
Match75.writerow(row)
elif found == 1:
row = ["NULL",origA.rstrip(),item.rstrip(),match[0].rstrip(),"NULL","NULL",aaccount,acode,strfilteredratio,"NULL","NULL"]
Match75.writerow(row)
else:
row = ["NULL","NULL",item.rstrip(),match[0].rstrip(),"NULL","NULL","NULL","NULL",strfilteredratio,"NULL","NULL"]
Match75.writerow(row)
finally:
Match75.writerow(["A Proprietary and Confidential. Do Not Distribute"])
fMatch75.close()
print (time.time()-t0,"seconds")
if __name__ == "__main__":
main(argv=sys.argv)
Чего я пытаюсь достичь:
- Чтение входных файлов
- Отфильтруйте общие слова из имен, чтобы нечеткое сопоставление (‘difflib.get_close_matches()’) возвращало более точные результаты
- Сравните имена из fileA с именами в fileB, чтобы найти, какое из них, скорее всего, совпадает.
- Распечатайте исходные (не отфильтрованные) имена и процент совпадения.
Почему это сложно
Соглашения об именовании, используемые в двух входных файлах, значительно различаются. Некоторые имена частично сокращены (НАПРИМЕР: Файл A: Acme Company; Файл B: Acme Co). Поскольку соглашения об именовании не согласованы, я не могу выполнить ‘fileA.intersect(fileB)’, который был бы идеальным способом.
Где должно произойти изменение
for item in cleanListC:
match = difflib.get_close_matches(item,cleanListA,1,0.75)
CleanListA создается:
cleanListA = []
for item in arrayA:
cleanListA.append(item[1])
Таким образом теряется (ORIGINAL,FILTERED)
сопряжение.
Конечная цель
Я хотел бы передать arrayA в difflib.get_close_matches()
вместо cleanListA, чтобы сохранить (ORIGINAL,FILTERED)
сопряжение. difflib.get_close_matches()
при определении близких совпадений просматривалась бы только «ОТФИЛЬТРОВАННАЯ» часть сопряжения, но возвращалось бы все сопряжение.
Комментарии:
1. @MikeKusold Что вы имеете в виду под именем ‘array’? В Python я понимаю, что: ( docs.python.org/library/array.html#module-array )
2. Я использовал array [ORIGINAL, FILTERED] как средство для четкого описания моей переменной. Вы можете так же легко заменить слова[(Оригинал, отфильтрованный)].
3. Нам нужно, чтобы вы были более конкретными, пожалуйста. Является ли это
array.array
объектом? Или это на самом делеlist
? Или под «многомерным массивом» вы на самом деле подразумеваетеdict
? Все они различны в Python. Кроме того, пожалуйста, покажите нам, что вы пробовали (с кодом!). Чем больше деталей, тем лучше!!4. Я довольно новичок в Python, поэтому не совсем разбираюсь во всей терминологии. Я полагаю, что это список, но что вы называете списком в списке? Я опубликую некоторый код позже сегодня. К сожалению, это используется для генерации конфиденциальной информации для моего работодателя, поэтому мне потребуется некоторое время, чтобы отредактировать код, чтобы он был достаточно общим для совместного использования.
5. @MikeKusold Список в списке — это вложенный список (вложенный список — это английское выражение, а не термин в терминологии Python). Список списков — это список списков, нет питоновского термина, чтобы назвать это по-другому; некоторые называют список списков матрицей , но они ошибаются.
Ответ №1:
Поскольку вы уже используете SequenceMatcher
напрямую для получения коэффициента соответствия, вашим самым простым изменением, вероятно, было бы выполнить get_close_matches
операцию самостоятельно.
Сравните исходный код для get_close_matches() [например,http://svn.python.org/view/python/tags/r271/Lib/difflib.py?revision=86833amp;view=markup рядом со строкой 737]. Он возвращает список из n последовательностей с наибольшими коэффициентами. Поскольку вам нужно только наилучшее соответствие, вы могли бы отслеживать (ИСХОДНОЕ, ОТФИЛЬТРОВАННОЕ, соотношение), где соотношение на данный момент самое высокое, вместо того, heapq
чтобы исходный метод использовал для отслеживания n наибольших значений.
Например, вместо вашего основного цикла, что-то вроде:
seqm = difflib.SequenceMatcher()
for i in arrayC:
origC, cleanC, caccount, ccode = i
seqm.set_seq2(cleanC)
bestRatio = 0
for j in arrayA:
origA, cleanA = j[:2]
seqm.set_seq1(cleanA)
if (seqm.real_quick_ratio() >= bestRatio and
seqm.quick_ratio() >= bestRatio):
r = seqm.ratio()
if r >= bestRatio:
bestRatio = r
bestA = j
if bestRatio >= 0.75: # the cutoff from the original get_close_matches() call
origA, cleanA, aaccount, acode = bestA
filteredratio = bestRatio
strfilteredratio = '%.2f' % (filteredratio*100)
seqm.set_seqs( origC, origA )
unfilteredratio = seqm.ratio()
strunfilteredratio = '%.2f' % (unfilteredratio*100)
averageratio = (filteredratio unfilteredratio)/2
straverageratio = '%.2f' % (averageratio*100)
row = [origC.rstrip(),origA.rstrip(),cleanC.rstrip(),cleanA.rstrip(),caccount,ccode,aaccount,acode,strfilteredratio,strunfilteredratio,straverageratio]
else:
row = ["NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","0.00","NULL","NULL"]
Match75.writerow(row)
Комментарии:
1. В итоге я пошел по этому маршруту. Я надеялся, что есть способ передать список, такой как list[1] [i] или что-то в этом роде, но это тоже сработало.