import streamlit as st # Конфигурация страницы ДО ЛЮБЫХ ДРУГИХ КОМАНД Streamlit st.set_page_config( page_title="Анализ текстовых данных", page_icon="📊", layout="wide", initial_sidebar_state="expanded" ) # Теперь импортируем остальные библиотеки import json import pandas as pd import plotly.express as px import plotly.graph_objects as go from collections import Counter import nltk import os import warnings import tempfile # Игнорируем предупреждения warnings.filterwarnings("ignore") # Настройка NLTK для работы в ограниченной среде @st.cache_resource def setup_nltk(): # Создаем временную директорию для NLTK данных nltk_data_path = os.path.join(tempfile.gettempdir(), 'nltk_data') os.makedirs(nltk_data_path, exist_ok=True) # Устанавливаем путь для NLTK данных nltk.data.path.append(nltk_data_path) # Скачиваем необходимые данные try: nltk.data.find('tokenizers/punkt') except LookupError: nltk.download('punkt', download_dir=nltk_data_path, quiet=True) # Инициализация NLTK setup_nltk() from nltk.tokenize import word_tokenize from razdel import tokenize as razdel_tokenize from nltk.stem import SnowballStemmer # Пример данных в памяти SAMPLE_DATA = [ {"text": "Привет, как дела? Это пример текста для анализа.", "preprocessed_text": "Привет, как дела? Это пример текста для анализа."}, {"text": "Natural language processing is fascinating.", "preprocessed_text": "Natural language processing is fascinating."}, {"text": "Машинное обучение и обработка естественного языка.", "preprocessed_text": "Машинное обучение и обработка естественного языка."}, {"text": "Hello world! This is a test sentence.", "preprocessed_text": "Hello world! This is a test sentence."}, {"text": "Токенизация важна для обработки текста.", "preprocessed_text": "Токенизация важна для обработки текста."}, {"text": "The quick brown fox jumps over the lazy dog.", "preprocessed_text": "The quick brown fox jumps over the lazy dog."}, {"text": "Нейронные сети используются в современных NLP системах.", "preprocessed_text": "Нейронные сети используются в современных NLP системах."}, {"text": "Python is a great programming language for data science.", "preprocessed_text": "Python is a great programming language for data science."}, {"text": "Анализ текста помогает понять его структуру и содержание.", "preprocessed_text": "Анализ текста помогает понять его структуру и содержание."}, {"text": "Machine learning models require large amounts of data.", "preprocessed_text": "Machine learning models require large amounts of data."} ] # Функции токенизации def nltk_tokenize(text, language): try: lang = 'russian' if language == 'Русский' else 'english' tokens = word_tokenize(text, language=lang) return [t for t in tokens if t.strip()] except Exception: # Fallback на простой split return [t for t in text.split() if t.strip()] def razdel_tokenize_func(text, language): if language == 'Русский': try: tokens = [token.text for token in razdel_tokenize(text) if token.text.strip()] return tokens except Exception: return [t for t in text.split() if t.strip()] return [t for t in text.split() if t.strip()] def snowball_stem(tokens, language): try: lang = 'russian' if language == 'Русский' else 'english' stemmer = SnowballStemmer(lang) return [stemmer.stem(token) for token in tokens if token.strip()] except Exception: return tokens def compute_metrics(tokens_list, vocab): if not tokens_list: return {'token_lengths': [], 'oov_percentage': 0, 'token_freq': {}, 'vocab_size': 0} token_lengths = [len(token) for tokens in tokens_list for token in tokens] total_tokens = len(token_lengths) oov_tokens = sum(1 for tokens in tokens_list for token in tokens if token not in vocab) oov_percentage = oov_tokens / total_tokens * 100 if total_tokens > 0 else 0 token_freq = Counter(token for tokens in tokens_list for token in tokens) top_tokens = dict(sorted(token_freq.items(), key=lambda x: x[1], reverse=True)[:10]) return { 'token_lengths': token_lengths, 'oov_percentage': oov_percentage, 'token_freq': top_tokens, 'vocab_size': len(set(token for tokens in tokens_list for token in tokens)) } def read_uploaded_file(uploaded_file): """Чтение загруженного файла в память""" texts = [] try: content = uploaded_file.getvalue().decode('utf-8') for line in content.splitlines(): try: article = json.loads(line.strip()) text = article.get('preprocessed_text', article.get('cleaned_text', article.get('text', ''))) if text and len(text.strip()) > 0: texts.append(text.strip()) except: continue return texts except Exception as e: st.error(f"Ошибка чтения файла: {str(e)}") return [] def main(): st.title("📊 Интерактивная обработка текста") st.markdown("Анализ текстовых данных с различными методами токенизации") # Сайдбар с настройками with st.sidebar: st.header("⚙️ Настройки") language = st.selectbox("Выберите язык", ["Русский", "Английский"]) st.header("📁 Данные") use_sample = st.checkbox("Использовать пример датасета", value=True) uploaded_file = st.file_uploader("Или загрузите JSONL", type=["jsonl"]) st.header("🔧 Методы") method = st.selectbox("Выберите метод", ['nltk', 'razdel', 'nltk_snowball']) st.header("ℹ️ О приложении") st.info(""" Это приложение анализирует текстовые данные с помощью: - **NLTK**: Стандартная токенизация - **Razdel**: Для русского языка - **Snowball**: Токенизация + стемминг """) # Определяем какие данные использовать texts = [] if use_sample: texts = [item['preprocessed_text'] for item in SAMPLE_DATA] st.success(f"✅ Используется пример датасета ({len(texts)} текстов)") if uploaded_file is not None: uploaded_texts = read_uploaded_file(uploaded_file) if uploaded_texts: texts = uploaded_texts st.success(f"✅ Загружен файл: {uploaded_file.name} ({len(texts)} текстов)") else: st.error("❌ Не удалось прочитать загруженный файл") if not use_sample: return if not texts: st.info("👆 Пожалуйста, выберите пример датасета или загрузите свой файл") return # Ограничиваем для демо if len(texts) > 50: st.info(f"ℹ️ Будут обработаны первые 50 текстов из {len(texts)}") texts = texts[:50] # Показываем пример текстов with st.expander("📝 Просмотреть данные"): for i, text in enumerate(texts[:5]): st.write(f"**Текст {i+1}:** {text}") if len(texts) > 5: st.write(f"... и еще {len(texts) - 5} текстов") # Обработка if st.button("🚀 Запустить обработку", type="primary", use_container_width=True): with st.spinner("Обработка текстов..."): tokens_list = [] vocab = set() progress_bar = st.progress(0) status_text = st.empty() for i, text in enumerate(texts): status_text.text(f"Обработка текста {i+1}/{len(texts)}") # Выбор метода if method == 'nltk': tokens = nltk_tokenize(text, language) elif method == 'razdel': tokens = razdel_tokenize_func(text, language) elif method == 'nltk_snowball': base_tokens = nltk_tokenize(text, language) tokens = snowball_stem(base_tokens, language) else: tokens = [t for t in text.split() if t.strip()] if tokens: tokens_list.append(tokens) vocab.update(tokens) progress_bar.progress((i + 1) / len(texts)) status_text.text("✅ Обработка завершена!") if not tokens_list: st.error("❌ Не удалось получить токены!") return # Вычисление метрик metrics = compute_metrics(tokens_list, vocab) # Результаты st.subheader("📈 Результаты анализа") # Основные метрики col1, col2, col3, col4 = st.columns(4) with col1: st.metric("Размер словаря", metrics['vocab_size']) with col2: st.metric("Доля OOV", f"{metrics['oov_percentage']:.2f}%") with col3: st.metric("Текстов", len(tokens_list)) with col4: total_tokens = sum(len(tokens) for tokens in tokens_list) st.metric("Всего токенов", total_tokens) # Визуализация tab1, tab2, tab3 = st.tabs(["📏 Длины токенов", "📊 Частотность", "📋 Топ токены"]) with tab1: if metrics['token_lengths']: fig1 = px.histogram( metrics['token_lengths'], nbins=15, title="Распределение длин токенов", labels={'value': 'Длина токена', 'count': 'Количество'}, color_discrete_sequence=['#FF4B4B'] ) fig1.update_layout( showlegend=False, xaxis_title="Длина токена", yaxis_title="Количество" ) st.plotly_chart(fig1, use_container_width=True) else: st.info("Нет данных для визуализации длин токенов") with tab2: if metrics['token_freq']: token_df = pd.DataFrame( metrics['token_freq'].items(), columns=['Токен', 'Частота'] ) fig2 = px.bar( token_df, x='Токен', y='Частота', title="Топ-10 самых частых токенов", color='Частота', color_continuous_scale='Blues' ) fig2.update_layout( xaxis_title="Токен", yaxis_title="Частота" ) st.plotly_chart(fig2, use_container_width=True) else: st.info("Нет данных для визуализации частотности") with tab3: if metrics['token_freq']: token_df = pd.DataFrame( metrics['token_freq'].items(), columns=['Токен', 'Частота'] ) token_df['Доля (%)'] = (token_df['Частота'] / token_df['Частота'].sum() * 100).round(2) st.dataframe( token_df, use_container_width=True, hide_index=True ) # Скачивание результатов csv = token_df.to_csv(index=False) st.download_button( "💾 Скачать результаты (CSV)", csv, file_name="token_analysis.csv", mime="text/csv", use_container_width=True ) else: st.info("Нет данных о токенах") # Запуск приложения if __name__ == '__main__': main()