Анализ повторяемости инцидентов
Привет, Хабр!
Эта статья не будет полезна матерым дата-сатанистам, но может быть полезна менеджерам, которые хотят отследить повторяемость похожих задач, или как я — похожих технических неполадок.
Здесь начинаем разбирать анализ текстовых данных. По-разному «от руки» написанных отчетов о причинах возникновения инцидентов.
Незначительные ошибки в системе или нестабильное соединение от одного из провайдеров, могут на первый взгляд казаться мелочами. Однако, если такие «маленькие» проблемы возникают с завидной регулярностью, это уже тревожный сигнал. Постоянные технические неполадки со стороны провайдера или регулярные проблемы с банковскими партнерами — это повод задуматься и пересмотреть условия сотрудничества.
Сегодня о том, как я пытаюсь выявлять паттерны возникновения Инцидентов, другими словами, искать мелкие Проблемы.
Дано:
Есть BI-система, хранящая информацию обо всех отчетах.
Отчет довольно простой — форма с полями. Единственное интересующее нас поле называется »14. Причина молнии» и содержится в столбце 14 (как неожиданно) .xlsx файла.
Найти:
На выходе я хочу видеть три столбца:
1 столбец содержит порядковый номер проблемы;
2 столбец содержит название проблемы;
3 столбец содержит все номера схожих проблем.
Пускай Питончик будет возвращать мне result.csv файл, выводить на дашборд будем в другой раз.
Решение:
Поскольку нас интересует сущностное сравнение («проблемы сети» = «проблема с сетью»), шаги следующие:
Нам нужно убрать лишние слова, оставив лишь существительные, прилагательные, глаголы и наречия;
Срезать стоп-слова (иметь возможность убирать лишние слова, пускай хранятся в текстовом файле);
Срезать приставки, суффиксы — достаточно лемматизировать слова;
Есть еще пункт, который я пока не реализовал — добавить поиск по ключевым словам. Они будут лежать в том же репозитории, txt-файлом. Нам следует научить скрипт тому, что ключевые слова важнее остальных совпадений, чтобы была возможность влиять на агрегацию важных для нас инцидентов.
Теперь по порядку:
Нам потребуются библиотеки:
import os
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
Doc
)
»os» для работы с операционной системой.
»pandas» для работы с данными в формате DataFrame.
»TfidfVectorizer» и »cosine_similarity» из »sklearn» для обработки текста и вычисления схожести.
»natasha» для лемматизации текста на русском языке.
Загружаем явным образом стоп-слова:
def load_stopwords(filepath):
with open(filepath, 'r', encoding='utf-8') as file:
stopwords = file.read().splitlines()
return set(stopwords)
Эта функция открывает файл с заданным filepath, читает стоп-слова (одно на строку) и возвращает их в виде множества для быстрого поиска.
Вводим функцию лемматизации текста:
Эта функция принимает текст, сегментирует его на токены, тегирует их морфологическими тегами, лемматизирует каждый токен, исключает стоп-слова и возвращает лемматизированный текст.
def lemmatize_text(text, morph_vocab, segmenter, morph_tagger, stopwords):
doc = Doc(text)
doc.segment(segmenter)
doc.tag_morph(morph_tagger)
lemmas = []
for token in doc.tokens:
token.lemmatize(morph_vocab)
if token.lemma not in stopwords:
lemmas.append(token.lemma)
return ' '.join(lemmas)
Основная функция анализа схожих проблем (указанных причин инцидента):
def analyze_similar_problems(file_path, stopwords_path, output_path='result.csv'):
df = pd.read_excel(file_path)
df = df.dropna(subset=[df.columns[1], df.columns[13]])
Здесь данные загружаются из .xlsx файла с помощью pandas. Затем удаляются строки, в которых отсутствуют номера инцидентов или описания причин (столбцы 2 и 14). Во втором столбце у нас порядковый номер молнии, как правило, это четырехзначное число, в 14 — сам текст.
Загружаем стоп-слова из указанного файла с помощью функции load_stopwords.
stopwords = load_stopwords(stopwords_path)
Инициализируем необходимые компоненты Natasha для сегментации и лемматизации текста и Лемматизация текста в 14-м столбце.
segmenter = Segmenter()
morph_vocab = MorphVocab()
emb = NewsEmbedding()
morph_tagger = NewsMorphTagger(emb)
df['Лемматизированный текст'] = df[df.columns[13]].apply(
lambda text: lemmatize_text(str(text), morph_vocab, segmenter, morph_tagger, stopwords)
)
Теперь для каждого текста вызывается функция lemmatize_text, результат сохраняем в новом столбце Лемматизированный текст.
Основная магия: преобразование текста в векторы с использованием TF-IDF и вычисление косинусного расстояния между всеми парами записей.
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(df['Лемматизированный текст'])
cosine_similarities = cosine_similarity(tfidf_matrix, tfidf_matrix)
Создается объект «TfidfVectorizer», который преобразует лемматизированный текст в матрицу TF-IDF.
С помощью cosine_similarity вычисляется косинусное расстояние между всеми парами текстов в TF-IDF матрице.
Далее создаём пустой DataFrame для хранения результатов:
result_df = pd.DataFrame(columns=['Порядковый номер', 'Название проблемы', 'Схожие проблемы'])
Заполнение таблицы результатами:
Для каждой строки в исходном DataFrame сравниваются косинусные схожести с другими строками. Если схожесть выше заданного порогового значения (0.5), номер инцидента добавляется в список схожих проблем. Затем для каждой строки создается запись в »result_df» с указанием номера проблемы, названия проблемы и номеров схожих проблем.
for i in range(len(df)):
similarities = []
for j in range(len(df)):
if i != j and cosine_similarities[i][j] > 0.5:
similarities.append(df.iloc[j, 1])
result_df = pd.concat([result_df, pd.DataFrame({'Порядковый номер': [df.iloc[i, 1]],
'Название проблемы': [df.iloc[i, 13]],
'Схожие проблемы': [',
'.join(map(str, similarities))]})], ignore_index=True)
Полученный DataFrame »result_df» сохраняется в файл «result.csv»:
result_df.to_csv(output_path, index=False)
Этот блок кода запускает функцию »analyze_similar_problems», если скрипт выполняется напрямую. Путь к файлу данных и путь к файлу стоп-слов передаются в качестве аргументов функции:
if __name__ == '__main__':
file_path = 'your_data.xlsx'
stopwords_path = 'russian_stopwords.txt'
analyze_similar_problems(file_path, stopwords_path)
В результате получаем файл »result.csv» с корректным результатом. Прямо сейчас дальнейшая работа с ним устроена так: я копирую содержимое и добавляю в гугл-табличку, по которой и сужу о том, пора ли бить тревогу.
Ответ:
Данный скрипт позволяет довольно быстро, хоть и вручную, получить ответ на простой вопрос: «а такое у нас уже случалось?» при разборе очередного инцидента технического характера.
Следующим шагом станет анализ инцидентов и отклонений онлайн (раз в сутки подгружать из базы описания проблем) и выведением на дашборд таблицы, ранж в которой можно применять по «количеству» и «времени».
P.S.: Пока писал статью, понял, что еще хочу добавить: поиск по ключевым словам, которые будут считаться для скрипта важнее, чем простая повторяемость, чтобы инциденты с такими словами сразу попадали в верхнюю часть списка.