Как расслабиться инженеру на работе при помощи Python?

aabf13802b5f2fa47049df121b19d089.JPG

Всем привет! Меня зовут Владимир Ганзюк

Работаю инженером НСИ и изучаю для себя C#, но не сталкиваясь с Python, наткнулся я как-то случайно на одну очень интересную библиотеку Pymorhp.

Pymorph — морфологический анализатор для русского языка, использует словари из OpenCorpora. Исходный код можно получить на github. Документация к библиотеке написана достаточно хорошо.

Предыстория:

Попивая чаек в поте лица, написал мне руководитель, что на следующие два спринта мне необходимо проверить и унифицировать один из атрибутов на миллиард позиций.

61aa15e7d59439b3f193027536abd885.jpg

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

Суть:

В выгрузке из нашей системы имеется рабочая среда, естественно, таких сред может быть несколько. Перечисление сред изначально было через »;», через какое-то время кто-то подумал, что неплохо будет всё-таки использовать »,». Это исправить можно и в 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, а его сложность О(n^3), код написан больше на читаемость, а не оптимизацию. 1000 позиций Natasha обрабатывала около 1 минуты, в то время как Pymorph справился с объемом в два раза больше за двое меньшее время. Это так, небольшое отступление, если кто столкнется с подобной ситуацией с выбором библиотеки.

Изменение падежа слова, например, может понадобиться для переноса транспортируемой среды в формат «Транспортировка» + среда для другого атрибута.

92cf282898c592e530ba570eca7c53f4.JPG

Вывод:

Интересно узнать мнение других специалистов. Возможно, кто-то воспользовался искусственным интеллектом в подобной ситуации. Надеюсь, кому-то поможет данный способ.


Ганзюк Владимир, инженер нормативно-справочной информации (НСИ)

© Habrahabr.ru