def olga_test(): ''' проверка по искусственным ошибкам ''' mistakes = {} start_time = time.time() with open('true_mistakes.dic') as m_file: lines = m_file.readlines() for line in lines: m, corr = line.split('\t') mistakes[m] = corr disable_print('error dict in memory, took %s sec' % (time.time() - start_time)) with open('test-set.txt') as f_in: words = f_in.readlines() for w in words: disable_print('-'*20) w = w.strip() print(w) w_test = w.strip().lower() # process(ws) is_mistake, aspell_suggests = aspell(w_test) if is_mistake: disable_print('ASPELL') disable_print('mistake', '\n'.join(aspell_suggests)) try: disable_print('RULES') print(mistakes[w_test]) except: disable_print('NOT IN THE DICTIONARY')
def heritage_rules(word, prev_w, next_w, accent_mistakes, multiword=True): ''' все наши правила вызываются здесь ''' # границы mistake = [] if multiword: mistake += check_boundaries(word, prev_w, next_w) # disable_print('1', mistake) # небольшой контекст mistake += context_rules(word) # disable_print('2', mistake) try: correction = accent_mistakes[word] disable_print('accent rules:', correction) mistake.append(correction) except KeyError: disable_print('not in the dict of artificial mistakes') # try: # disable_print('3', mistake) bigram_rules = rules_back(word, ()) #bigram_rules = rules_back(word, (), all_combinations=True) for x in bigram_rules: is_wrong, asp = aspell(x) if not is_wrong: mistake.append(x) elif len(asp) >= 2: mistake += asp[:2] disable_print('bigram rules:', x, '-> asp (is_mistake, vars):', is_wrong, asp[:2]) # except: # disable_print('ALARM', sys.exc_info()[0], 'word:', word) # disable_print('4', mistake) return mistake
def check_boundaries(word, prev_w, next_w): ''' слепить с предыдущим, следующим, пред и след и проверить всё аспеллом если такое слово есть - выдать его, если нет — первые два предложения аспелла ''' disable_print('CHECK BOUNDARIES: prev word next:', [prev_w, word, next_w]) new = [] boundaries = list() boundaries.append(prev_w + word) boundaries.append('-'.join((prev_w, word))) boundaries.append(word + next_w) boundaries.append('-'.join((word, next_w))) boundaries.append(prev_w + word + next_w) boundaries.append('-'.join((prev_w, word, next_w))) disable_print('bound vars:', boundaries) for x in boundaries: is_mistake, suggestions = aspell(x) # disable_print('boundaries::', x, is_mistake, suggestions) if not is_mistake: new.append(x) else: new += suggestions[:2] # print(x, '->aspell', suggestions) for p in 'что или ли же бы'.split(' '): if word.endswith(p) and len(p) > 0: x = word.replace(p, '') new.append(x+' '+p) disable_print('boundaries:', new) return list(set(new))
def context_rules(word): ''' выдать список уникальных исправлений слова согласно правилам, которые формулируются на небольшом контексте ''' new = [] # начало слова, пара (верно, ошибка) begin = (('е', 'э'), ('э', 'е'), ('c', 'з'), ('чт', 'ш')) for pair in begin: if word.startswith(pair[1]): new.append(pair[0] + word[1:]) # конец слова, пара (верно, ошибка) final = (('к', 'г'), ('й', 'и'), ('з', 'с'), ('й', 'ы'), ('аю', 'у'), ('ие', 'и'), ('ся', 'а')) minus3 = (('его', 'ево'), ('ого', 'ово'), ('ние', 'нье'), ('жом', 'жем'), ('цом', 'цем'), ('чом', 'чем')) tsya = ('ца', 'ця', 'ться', 'тса', 'стя', 'ста') for pair in final: if word.endswith(pair[1]): new.append(word[:-1] + pair[0]) try: for pair in minus3: if word.endswith(pair[1]): new.append(word[:-3] + pair[0]) except: pass for x in tsya: if word.endswith(x): new.append(re.sub(x, 'тся', word)) new.append(re.sub(x, 'ться', word)) try: if word.startswith('по') and word[2] != '-': for x in 'ому ему цки ски ьи'.split(' '): if word.endswith(x): new.append('по-' + word[2:]) except: pass try: if word.startswith('кое') and word[3] != '-': new.append('кое-' + word[3:]) except: pass try: if word.endswith('нибудь') and word[-7] != '-': new.append(word[:-6] + '-нибудь') except: pass new = set(new) suggest = [] for x in new: is_wrong, asp = aspell(x) if not is_wrong: suggest.append(x) elif len(asp) >= 2: suggest += asp[:2] # print('context rules:', set(suggest)) return set(suggest)
def check_word(word, prev_w, next_w, accent_mistakes, big_ru, multiword): ''' проверка и исправление кирилического слова :param word: слово, которое сейчас проверяем :param prev_w: предыдущий токен; для нулевого токена ' '; :param next_w: следующий токен; для последнего токена ' '; :param accent_mistakes: то же, что и в check_text :param big_ru: то же, что и в check_text :return: словарь {'correct': [...], 'mistake': [...], 'aspell': [...]} — см. check_text ''' disable_print('check word (prev - word - next):', [prev_w, word, next_w]) mistake = [] result_dict = {'correct': [], 'mistake': [], 'aspell': []} # проверяем аспеллом is_mistake, aspell_suggests = aspell(word) if not is_mistake: # ok или real word mistake disable_print('aspell: OK') result_dict['correct'].append(word) print(freq_filter(word, big_ru)) if freq_filter(word, big_ru)[1]: pass else: # редкое correct => даём свои варианты mistake = heritage_rules(word, prev_w, next_w, accent_mistakes, multiword) result_dict['mistake'] = sort_heritage_suggestions(mistake, big_ru) else: # аспелл говорит, что есть ошибка # result_dict['correct'] = [] disable_print('aspell: is mistake;', aspell_suggests) # предложения аспелла result_dict['aspell'] = aspell_suggests freq_asp = [freq_filter(s, big_ru) for s in aspell_suggests[2:]] # freq_asp = [] # for s in aspell_suggests: # print(s) # freq_asp.append(freq_filter(s, big_ru)) disable_print('freq asp:', freq_asp) # freq_from_aspell = [w[0] for w in filter(lambda x: x[1], freq_asp)] asp_sorted = [x[0] for x in sorted(freq_asp, key=lambda x: float(x[2]), reverse=True)] disable_print('ASPELL sorted', asp_sorted) if len(asp_sorted) > 5: asp_sorted = asp_sorted[:5] # asp_to_mistake - это то, что будем объединять с нашими предложениями и писать в ответ asp_to_mistake = aspell_suggests[:2] + asp_sorted disable_print('asp_to_mistake', asp_to_mistake) # теперь наши дополнения mistake = [] mistake += heritage_rules(word, prev_w, next_w, accent_mistakes, multiword) disable_print('heritage_rules', mistake) # сортируем наши исправления disable_print('sort_heritage_suggestions(mistake, big_ru)', sort_heritage_suggestions(mistake, big_ru)) # ответ result_dict['mistake'] = sort_heritage_suggestions(mistake, big_ru) + asp_to_mistake return result_dict
def check_text(in_arg, out_arg='fout.txt', accent_mistakes={}, big_ru={}, multiword=False): ''' проверка файла или строковой переменной :param in_arg: имя файла (.txt, utf-8), :param out_arg: имя файла (.txt, utf-8), куда будем писать результаты :param accent_mistakes: словарь искусственных ошибок на безударные :param big_ru: частотный словарь русского языка (леммы) :param multiword: режим проверки одного слова (используется в evaluation) :return: список словарей, соответствующих токенам текста. ключи словаря: 'correct' – если слово написано верно, 'aspell' – исправления аспелла, 'mistake' – исправления нашей системы ''' # читаем текст и удаляем в конце пробелы, таблуляцию, переносы строк if in_arg.endswith('.txt'): with open(in_arg, encoding='utf-8') as f_in: text = f_in.read().strip() else: text = in_arg.strip() disable_print('='*30+'\n'+'START:') checked_text = [] # здесь будет скапливаться ответ -- список словарей (по словарю на слово) # загружаем частотник '''if big_ru == {}: disable_print('preparing freq dict...') big_ru = {} with open('freqrnc2011.csv') as rus: ru = rus.readlines()[1:] for line in ru: lemma, pos, ipm, r, d, doc = line.split('\t') big_ru[lemma + ',' + pos] = ipm # слово идентифицируется по лемме и части речи, потому что бывают омонимы # загружаем словарь искусственных ошибок if accent_mistakes == {}: disable_print('preparing accent mistakes...') accent_mistakes = prepare_accent_mistakes()''' # загружаем правила перевода латиницы в кириллицу lat_table = csv2transform('латиница.csv') if not multiword: tokens = [text] # в режиме multiword=False список токенов состоит только из одного слова # вставляем пробелы перед знаками пунктуации, чтобы после разбиения по пробелам не потерять знаки препинания punct = list('.,:;!?') # в дальнейшем нужен список знаков препинания text = re.sub('\n', ' ', text) text_punct = re.sub('\.', ' . ', text) text_punct = re.sub(',', ' , ', text_punct) text_punct = re.sub('!', ' ! ', text_punct) text_punct = re.sub('\?', ' ? ', text_punct) # токенизируем tokens = text_punct.split(' ') disable_print('%d word(s)' % len(tokens)) # для нулевого токена предыдущий - пробел, для последнего следующий - пробел tokens.insert(0, ' ') tokens.insert(len(tokens), ' ') # disable_print('TOKENS:', tokens[1:-1]) for i, token in enumerate(tokens[1:-1]): # не анализируем пробелы, которые вставили только что k = i+1 # настоящие индексы массива tokens disable_print('\ntoken:', k, token) suggestions = {'correct': [], 'mistake': [], 'aspell': []} # результаты для этого токена word = token.strip().lower() # к нижнему регистру word = re.sub('ё', 'е', word) # замена ё на е if len(word) > 0: # если не пустая строка is_en, num_lat = is_english(word) # есть ли в слове латиница? # disable_print('# lat =', num_lat) if num_lat == len(word) or word in punct: # английское слово или знак пунктуации, не проверяем disable_print('english or punct') checked_text.append({'correct': [], 'aspell': [], 'mistake': []}) elif num_lat == 0: # всё слово кириллицей disable_print('all cyr') disable_print('prev-w-next:', [tokens[k-1], tokens[k], tokens[k+1]]) suggestions = check_word(word, tokens[k-1], tokens[k+1], accent_mistakes, big_ru, multiword) else: # есть латиница disable_print('cyr and lat') suggestions['aspell'] = aspell(word)[1] # правим латиницу temp_vars = rules_back(word, lat_table, all_combinations=False) disable_print('temp_cyr', temp_vars) cyr_vars = [] for replaced in temp_vars: if not is_english(replaced)[0]: # только кириллица cyr_vars.append(replaced) # disable_print('== CYR ==') disable_print('cyr', cyr_vars) correct_vars, mistake_vars = [], [] # обработка каждого из кириллических вариантов for cyr_var in cyr_vars: result = check_word(cyr_var, tokens[k-1], tokens[k+1], accent_mistakes, big_ru, multiword) mistake_vars += result['mistake'] # suggestions['aspell'] += result['aspell'] correct_vars += result['correct'] # disable_print('cyr_k suggestions:', suggestions) # только уникальные mistake_vars = list(set(mistake_vars)) correct_vars = list(set(correct_vars)) # находим ipm для исправлений freq_mistake_vars = [freq_filter(s, big_ru) for s in mistake_vars] # сортируем по частотности suggestions['mistake'] = [x[0] for x in sorted(freq_mistake_vars, key=lambda x: float(x[2]), reverse=True)] # «правильные» слова сортируем по расстоянию Левенштейна до исходного (ещё с латиницей) suggestions['correct'] = sorted(correct_vars, key=lambda x: edit_distance(x, word)) checked_text.append(suggestions) disable_print('===RESULTS===') # пишем результаты в файл '''if '.txt' not in out_arg: out_arg = 'fout.txt' with open(out_arg, 'w') as f_out: f_out.write('token\taspell_correct\tfull_aspell\ther')''' for t, r in zip(tokens[1:-1], checked_text): r['mistake'] = list(set(r['mistake'])) # disable_pdisable_print('\t'.join([t, ','.join(r['correct']), ','.join(r['mistake'])])) f_out.write('\n' + t + '\t' + ','.join(r['correct']) + '\t' + ','.join(r['aspell']) + '\t' + ','.join(r['mistake'])) disable_pprint(checked_text) disable_print('='*30) if not multiword: return checked_text[0] else: return checked_text
def old_process(test): ''' в этой версии есть проверка частотности, aspell, hunspell, mystem ''' # загружаем частотник big_ru = {} with open('./Freq2011/freqrnc2011.csv') as rus: ru = rus.readlines()[1:] for line in ru: lemma, pos, ipm, r, d, doc = line.split('\t') big_ru[lemma + ',' + pos] = ipm disable_print('START:', test) disable_print('aspell') disable_pprint(aspell(test)) # проверяем hunspell is_wrong, correct = hunspell(test) if not is_wrong: disable_print('hunspell says OK') # говорит, что ошибки нет. # проверим на редкость: лемматизируем и найдём в частотнике disable_print('check if rare') mystemmed = mystem.analyze(test) lemma_mystem = mystemmed[0]['analysis'][0]['lex'] pos_mystem = mystemmed[0]['analysis'][0]['gr'].split('=')[0].split(',')[0].lower() try: corr_ipm = big_ru[lemma_mystem + ',' + pos_mystem] print(test, corr_ipm) except: # нет в частотном словаре, наверное, редкое. disable_print('not in the freq_dict', test) else: disable_print('hunspell says mistake') # hunspell говорит, что есть ошибка, и даёт варианты if len(correct) > 0: ans = [] disable_print('check if rare') for corr in correct: # проверить редкость предложенных вариантов # disable_print('check if rare') mystemmed = mystem.analyze(corr) lemma_mystem = mystemmed[0]['analysis'][0]['lex'] pos_mystem = mystemmed[0]['analysis'][0]['gr'].split('=')[0].split(',')[0].lower() try: corr_ipm = big_ru[lemma_mystem + ',' + pos_mystem] # print(corr, corr_ipm, 'ipm') ans.append((corr, float(corr_ipm))) except: # disable_print('not in the dictionary', corr) pass disable_pprint(sorted(ans, key=lambda x: x[1], reverse=True)) else: disable_print('hunspell gives nothing') # hunspell говорит, что есть ошибка, вариантов нет # проверить майстемом is_wrong_mystem = lookup_pymystem(test) if not is_wrong_mystem: correct.append(test) else: # действительно ошибка, предложить варианты disable_print('\tno suggestions by mystem') disable_print('final corrections') disable_pprint(correct)