Модуль 7: Текстовий аналіз

УРОК 7

Час60-90 хвилин
ПотрібноУроки 1-6
РезультатАналіз текстових повідомлень

Задача

Перехоплення, повідомлення, документи — це текст. Типові задачі:

Нові терміни

NLPNatural Language Processing — обробка природної мови. Алгоритми для роботи з текстом.
ТокенізаціяРозбиття тексту на окремі слова (токени). "Привіт світ" → ["Привіт", "світ"]
Стоп-словаЧасті слова без значення: і, в, на, що. Зазвичай фільтруються.
ЛематизаціяЗведення слова до базової форми: "позиції" → "позиція".
N-грамиПослідовності з N слів. Біграми: "вогневий контакт", "точка збору".

Частина 1: Базова обробка тексту

Очищення тексту

import re text = "Група на позиції 48.4567, 35.0234! Зв'язок встановлено..." # Нижній регістр text_lower = text.lower() # Видалення пунктуації text_clean = re.sub(r'[^\w\s]', '', text_lower) # Видалення чисел text_no_nums = re.sub(r'\d+', '', text_clean) # Видалення зайвих пробілів text_final = ' '.join(text_no_nums.split()) print(f"Оригінал: {text}") print(f"Очищено: {text_final}")

Токенізація

# Простий спосіб — split() text = "група на позиції зв'язок встановлено" tokens = text.split() print(f"Токени: {tokens}") # Результат: ['група', 'на', 'позиції', "зв'язок", 'встановлено']

Фільтрація стоп-слів

# Українські стоп-слова (базовий набір) STOP_WORDS = { 'і', 'в', 'на', 'що', 'це', 'до', 'з', 'не', 'та', 'як', 'від', 'за', 'по', 'для', 'але', 'або', 'ще', 'вже', 'так', 'був', 'буде', 'є', 'їх', 'його', 'її', 'ми', 'ви', 'вони' } text = "група на позиції і зв'язок вже встановлено" tokens = text.lower().split() # Фільтруємо filtered = [word for word in tokens if word not in STOP_WORDS] print(f"Без стоп-слів: {filtered}") # Результат: ['група', 'позиції', "зв'язок", 'встановлено']

Частина 2: Частота слів

from collections import Counter import re # Тестові повідомлення messages = [ "Група на позиції, зв'язок встановлено", "Переміщення до нової позиції", "Вогневий контакт на позиції", "Зв'язок втрачено, потрібна підтримка", "Група вийшла на позицію спостереження", "Підтримка надана, зв'язок відновлено" ] # Об'єднуємо та очищаємо all_text = ' '.join(messages).lower() all_text = re.sub(r'[^\w\s]', '', all_text) tokens = all_text.split() # Фільтруємо стоп-слова STOP_WORDS = {'на', 'до', 'і', 'в', 'з'} tokens = [t for t in tokens if t not in STOP_WORDS] # Підраховуємо word_counts = Counter(tokens) print("Найчастіші слова:") for word, count in word_counts.most_common(10): print(f" {word}: {count}")

N-грами (біграми)

from collections import Counter def get_ngrams(tokens, n=2): """Генерує n-грами зі списку токенів""" return [' '.join(tokens[i:i+n]) for i in range(len(tokens)-n+1)] text = "вогневий контакт на позиції спостереження" tokens = text.split() bigrams = get_ngrams(tokens, 2) print(f"Біграми: {bigrams}") # ['вогневий контакт', 'контакт на', 'на позиції', 'позиції спостереження'] trigrams = get_ngrams(tokens, 3) print(f"Триграми: {trigrams}") # ['вогневий контакт на', 'контакт на позиції', 'на позиції спостереження']

Частина 3: Пошук патернів

Пошук ключових слів

import re # Словник категорій CATEGORIES = { 'рух': ['переміщення', 'рух', 'вийшл', 'прибу', 'відбу'], 'контакт': ['вогневий', 'контакт', 'обстріл', 'бій'], 'зв\'язок': ['зв\'язок', 'радіо', 'сигнал', 'частот'], 'техніка': ['танк', 'бтр', 'бмп', 'гармат', 'машин'] } def categorize(text): """Визначає категорії повідомлення""" text_lower = text.lower() found = [] for category, keywords in CATEGORIES.items(): for keyword in keywords: if keyword in text_lower: found.append(category) break return found if found else ['інше'] # Тест messages = [ "Група здійснює переміщення", "Вогневий контакт, потрібна підтримка", "Зв'язок втрачено", "Виявлено 2 танки на позиції" ] for msg in messages: cats = categorize(msg) print(f"{msg[:40]}... → {cats}")

Витягування сутностей

import re text = """ Сокіл-1 доповідає: група Орел на позиції 48.4567, 35.0234. Контакт з Беркутом о 14:30. Підтримка від Грім-2 очікується. """ # Позивні (слово-число або слово з великої літери) callsigns = re.findall(r'\b[А-ЯІЇЄҐA-Z][а-яіїєґa-z]+(?:-\d+)?\b', text) print(f"Можливі позивні: {set(callsigns)}") # Координати coords = re.findall(r'\d+\.\d+,?\s*\d+\.\d+', text) print(f"Координати: {coords}") # Час times = re.findall(r'\b\d{1,2}:\d{2}\b', text) print(f"Час: {times}")

Частина 4: Схожість текстів

Знайти схожі повідомлення — корисно для виявлення дублікатів або пов'язаних подій.

from collections import Counter import math def text_to_vector(text): """Перетворює текст у вектор частот слів""" words = text.lower().split() return Counter(words) def cosine_similarity(vec1, vec2): """Косинусна схожість між двома векторами""" # Спільні слова common = set(vec1.keys()) & set(vec2.keys()) # Скалярний добуток dot_product = sum(vec1[w] * vec2[w] for w in common) # Норми norm1 = math.sqrt(sum(v**2 for v in vec1.values())) norm2 = math.sqrt(sum(v**2 for v in vec2.values())) if norm1 == 0 or norm2 == 0: return 0 return dot_product / (norm1 * norm2) # Тест msg1 = "група на позиції зв'язок встановлено" msg2 = "зв'язок з групою на позиції підтверджено" msg3 = "вогневий контакт потрібна підтримка" vec1 = text_to_vector(msg1) vec2 = text_to_vector(msg2) vec3 = text_to_vector(msg3) print(f"Схожість 1-2: {cosine_similarity(vec1, vec2):.2f}") # Висока print(f"Схожість 1-3: {cosine_similarity(vec1, vec3):.2f}") # Низька
💡 Косинусна схожість: 1.0 = ідентичні, 0.0 = нічого спільного. Значення > 0.5 зазвичай означає схожі тексти.

Практична задача

Аналіз перехоплених повідомлень. Створи text_analysis.py:

import re from collections import Counter # --- ДАНІ --- messages = [ "[08:23] Сокіл-1: Група на позиції, зв'язок встановлено", "[08:45] Орел-2: Переміщення до точки Альфа", "[09:12] Сокіл-1: Вогневий контакт, потрібна підтримка", "[09:30] Грім-3: Підтримка надана, рухаюсь до позиції", "[10:15] Орел-2: Прибув на точку Альфа, зв'язок стабільний", "[10:45] Сокіл-1: Контакт завершено, втрат немає", "[11:00] Беркут-1: Спостереження з позиції, рух противника", "[11:30] Грім-3: На позиції, готовий до підтримки", ] # --- ОЧИЩЕННЯ --- def clean_text(text): # Видаляємо час та позивні на початку text = re.sub(r'^\[.*?\]\s*\S+:\s*', '', text) # Нижній регістр, без пунктуації text = re.sub(r'[^\w\s]', '', text.lower()) return text # --- АНАЛІЗ --- STOP_WORDS = {'на', 'до', 'з', 'і', 'в', 'є', 'що'} all_tokens = [] for msg in messages: clean = clean_text(msg) tokens = [t for t in clean.split() if t not in STOP_WORDS] all_tokens.extend(tokens) # Частота слів word_freq = Counter(all_tokens) print("=== ТОП-10 СЛІВ ===") for word, count in word_freq.most_common(10): print(f" {word}: {count}") # --- ПОЗИВНІ --- callsigns = [] for msg in messages: match = re.search(r'\]\s*(\S+-\d+):', msg) if match: callsigns.append(match.group(1)) callsign_freq = Counter(callsigns) print("\n=== АКТИВНІСТЬ ПОЗИВНИХ ===") for cs, count in callsign_freq.most_common(): print(f" {cs}: {count} повідомлень") # --- КАТЕГОРИЗАЦІЯ --- CATEGORIES = { 'рух': ['переміщення', 'рух', 'прибув', 'рухаюсь'], 'контакт': ['вогневий', 'контакт', 'противник'], 'зв\'язок': ['зв\'язок', 'стабільний'], 'підтримка': ['підтримка', 'готовий'] } print("\n=== КАТЕГОРІЇ ПОВІДОМЛЕНЬ ===") for msg in messages: text_lower = msg.lower() cats = [] for cat, keywords in CATEGORIES.items(): if any(kw in text_lower for kw in keywords): cats.append(cat) if cats: print(f" {msg[:50]}... → {cats}")

Очікуваний результат

=== ТОП-10 СЛІВ === позиції: 4 звязок: 3 підтримка: 3 контакт: 2 ... === АКТИВНІСТЬ ПОЗИВНИХ === Сокіл-1: 3 повідомлень Орел-2: 2 повідомлень Грім-3: 2 повідомлень Беркут-1: 1 повідомлень === КАТЕГОРІЇ ПОВІДОМЛЕНЬ === [08:23] Сокіл-1: Група на позиції, зв'язок встано... → ['зв'язок'] [08:45] Орел-2: Переміщення до точки Альфа... → ['рух'] ...

Якщо не працює

ПомилкаРішення
UnicodeDecodeErrorПроблема з кодуванням. Читай файли з encoding='utf-8'
re.errorПомилка в регулярному виразі. Перевір екранування спецсимволів
KeyError в CounterСлово не знайдено. Використовуй .get() або перевіряй наявність

Робота з AI

Є текстовий файл з перехопленими повідомленнями. Формат: [час] Позивний: текст повідомлення Напиши скрипт для аналізу: 1. Витягни всі унікальні позивні та порахуй їх активність 2. Знайди найчастіші слова (без стоп-слів) 3. Витягни координати, час, назви місць 4. Категоризуй повідомлення: рух, контакт, зв'язок, інше 5. Знайди схожі повідомлення (косинусна схожість > 0.5) 6. Збережи звіт у CSV Українська мова, коментарі українською.

Додатково: бібліотеки NLP

Для складніших задач є спеціалізовані бібліотеки:

spaCyШвидкий NLP: токенізація, NER, залежності
NLTKКласична бібліотека NLP, багато інструментів
pymorphy2Морфологія для української/російської
langdetectВизначення мови тексту
sentence-transformersСемантична схожість (нейромережі)
pip install pymorphy2
import pymorphy2 morph = pymorphy2.MorphAnalyzer(lang='uk') words = ['позиції', 'переміщення', 'встановлено'] for word in words: parsed = morph.parse(word)[0] print(f"{word} → {parsed.normal_form} ({parsed.tag})")

Чек-лист

☐ Очищення тексту працює ☐ Частота слів рахується ☐ Стоп-слова фільтруються ☐ Позивні витягуються regex ☐ Категоризація працює ☐ text_analysis.py виконується

Самостійна практика

Наступний урок

Урок 8: Автоматизація та звіти

Створення автоматичних звітів, планування задач, пайплайни обробки.