Как расслабиться инженеру на работе при помощи Python?
Всем привет! Меня зовут Владимир Ганзюк
Работаю инженером НСИ и изучаю для себя C#, но не сталкиваясь с Python, наткнулся я как-то случайно на одну очень интересную библиотеку Pymorhp.
Pymorph — морфологический анализатор для русского языка, использует словари из OpenCorpora. Исходный код можно получить на github. Документация к библиотеке написана достаточно хорошо.
Предыстория:
Попивая чаек в поте лица, написал мне руководитель, что на следующие два спринта мне необходимо проверить и унифицировать один из атрибутов на миллиард позиций.
Ладно, на самом деле попивал я кофе, а позиций было и вправду огромное количество. К сожалению, все это можно сделать только вручную (так думали все).
Суть:
В выгрузке из нашей системы имеется рабочая среда, естественно, таких сред может быть несколько. Перечисление сред изначально было через »;», через какое-то время кто-то подумал, что неплохо будет всё-таки использовать »,». Это исправить можно и в Excel, но наименование рабочей среды должно начинаться с существительного + прилагательного. Вот несколько примеров:
Топливо дизельное;
Газ углеводородный, фракция пропан-пропиленовая, газ природный;
Фракция бутан-бутиленовая.
В системе рабочие среды были внесены некорректно, к примеру: «Бутан бутиленовая фракция», «Бензин; вода», «Газ углеводородный, сухой газ».
Как мы помним правило: существительное должно стоять на первом месте, и запятая должна разделять элементы перечисления. Поэтому необходимо было произвести унификацию.
За вечер написал костыль (функцию) sorting_dictionary:
def sorting_dictionary(dictionary):
sorted_dic, dic, result_words = [], [], []
for index in dictionary.keys():
sentence_in_cell = dictionary[index].split(", ")
for words in sentence_in_cell:
words = words.split()
for word in words:
p = morph.parse(word)[0]
if p.tag.POS == "NOUN":
sorted_dic.append(word)
for word in words:
p = morph.parse(word)[0]
if p.tag.POS == "PRED":
sorted_dic.append(word)
elif p.tag.POS == "PREP":
sorted_dic.append(word)
for word in words:
p = morph.parse(word)[0]
if p.tag.POS == "ADJF":
sorted_dic.append(word.lower())
for word in words:
p = morph.parse(word)[0]
item_list = ['CONJ', 'PRTF']
for item in item_list:
if item == p.tag.POS:
sorted_dic.append(word)
for word in words:
p = morph.parse(word)[0]
item_list = ["VERB", "INFN"]
for item in item_list:
if item == p.tag.POS:
sorted_dic.append(word)
for word in words:
p = morph.parse(word)[0]
if p.tag.POS == None:
sorted_dic.append(word)
words2 = " ".join(sorted_dic)
result_words.append(words2)
words2 = " "
sorted_dic.clear()
res_join = ", ".join(result_words)
dic.append(upcase_first_letter(res_join))
result_words.clear()
return dic
Вкратце, функция принимает на вход словарь, в который уже заранее были внесены все значения из файла Excel с использованием библиотеки openpyxl и »;» была заменена на »,».
Часть речи слова получаем через атрибут POS: p.tag.POS. Если запрашиваемая характеристика для данного тега не определена, то возвращается None. Обозначения для граммем, можно получить тут:
Эта функция возвращает уже отсортированный вариант словаря.
Результаты:
В выгрузке по цеху было 2193 позиций, которые необходимо было каждую вручную проверить.
Функция изменила 565 позиций, а это значит, что 1628 позиций уже отсеялись как правильные. В основном это легкие по типу: «Азот», «Бензин нестабильный», которая Pymorph определяет без проблем.
Из 565 измененных позиций 121 оказалась некорректной, например: «Раствор свежий щелочи», хотя правильный вариант — «Раствор щелочи свежий». Также есть проблема со скобками, к примеру: «Смесь газопродуктовая (бензин, ВСГ)», функция возвращает, как «Смесь (бензин газопродуктовая, ВСГ)».
Скорость работы алгоритма составила 29 секунд. Неплохо, да? Но у данной библиотеки иногда возникают сложности с определением части речи.
К примеру, если отойти от сред и разобрать предложение по словам «Мама мыла раму», то именно слово «мыла» оно определит, как существительное единственного числа с более высокой вероятностью, чем глагол. Да, у данной библиотеки ещё есть score — оценка вероятности того, что данный разбор правильный. Цитата из документации к библиотеке «то, как нужно разбирать слово, зависит от соседних слов; pymorphy2 работает только на уровне отдельных слов»
Также очень много позиций «Вакуумный газойль; Дизельное топливо», где «газойль» и «топливо» должны стоять на первом месте, функция вернула, как «Газойль вакуумный, топливо дизельное». Даже на удивление нашел такую среду из выгрузки:»27% водный раствор амина, Н2S — до 10% масс., азот», функция вернула абсолютно правильный вариант «Раствор амина водный 27%, Н2S — до 10% масс., азот»
Данная библиотека также в состоянии изменять падеж слова. Например, в библиотеке Natasha, которую я тоже пробовал, определить падеж можно, а вот изменить его, к сожалению, не получилось. Да и в отличие от Pymorph, Natasha очень медленная, т.к. Yargy реализует алгоритм Early parser, а его сложность , код написан больше на читаемость, а не оптимизацию. 1000 позиций Natasha обрабатывала около 1 минуты, в то время как Pymorph справился с объемом в два раза больше за двое меньшее время. Это так, небольшое отступление, если кто столкнется с подобной ситуацией с выбором библиотеки.
Изменение падежа слова, например, может понадобиться для переноса транспортируемой среды в формат «Транспортировка» + среда для другого атрибута.
Вывод:
Интересно узнать мнение других специалистов. Возможно, кто-то воспользовался искусственным интеллектом в подобной ситуации. Надеюсь, кому-то поможет данный способ.
Ганзюк Владимир, инженер нормативно-справочной информации (НСИ)