У меня есть многомерный массив, который я пытаюсь передать в difflib.get_close_matches()
.
Мой массив выглядит так: array[(ORIGINAL, FILTERED)]
. ORIGINAL
- это строка, а FILTERED
- строка ORIGINAL
с отфильтрованными общими словами.
В настоящее время я создаю новый массив, в котором в difflib.get_close_matches()
подаются только слова FILTERED
. Затем я попытаюсь сопоставить результат с 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 " & " in filtered:
filtered = filtered.replace(" &","")
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)
То, что я пытаюсь достичь:
Почему это сложно
Соглашения об именах, используемые в двух входных файлах, значительно различаются. Некоторые имена частично сокращены (EX: File 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()
будет рассматривать только "ФИЛЬТРИРОВАННУЮ" часть спаривания при определении совпадений, но возвращает полное спаривание.
Поскольку вы уже используете SequenceMatcher
напрямую, чтобы получить коэффициент соответствия, ваши самые простые изменения, вероятно, состоят в том, чтобы выполнить операцию get_close_matches
самостоятельно.
Сравните источник для get_close_matches(), http://svn.python.org/view/python/tags/r271/Lib/difflib.py?revision=86833&view=markup рядом с линией 737]. Он возвращает список из n последовательностей с наивысшими коэффициентами. Поскольку вы хотите только лучшее совпадение, вы можете отслеживать соотношение (ОРИГИНАЛЬНОЕ, ФИЛЬТРИРОВАННОЕ), где отношение является самым высоким до сих пор, вместо heapq
исходный метод использует для отслеживания наивысшего значения.
Например, вместо вашего основного цикла, например:
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)