Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| from collections import Counter | |
| import numpy as np | |
| import os | |
| import warnings | |
| warnings.filterwarnings('ignore') | |
| # Настройка отображения | |
| plt.style.use('default') | |
| plt.rcParams['font.family'] = 'DejaVu Sans' | |
| def load_and_analyze_data(): | |
| """Загрузка и базовый анализ данных""" | |
| file_path = 'small.csv' | |
| try: | |
| df = pd.read_csv(file_path, encoding='utf-8', delimiter=';') | |
| print("Файл загружен с разделителем ';' и кодировкой utf-8") | |
| except: | |
| try: | |
| df = pd.read_csv(file_path, encoding='cp1251', delimiter=';') | |
| print("Файл загружен с разделителем ';' и кодировкой cp1251") | |
| except: | |
| try: | |
| df = pd.read_csv(file_path, encoding='utf-8', delimiter=',') | |
| print("Файл загружен с разделителем ',' и кодировкой utf-8") | |
| except: | |
| try: | |
| df = pd.read_csv(file_path, encoding='cp1251', delimiter=',') | |
| print("Файл загружен с разделителем ',' и кодировкой cp1251") | |
| except Exception as e: | |
| print(f"Ошибка загрузки файла: {e}") | |
| return None | |
| print("=" * 60) | |
| print("АНАЛИЗ РЕЗУЛЬТАТОВ АВТОМАТИЧЕСКОЙ ОЦЕНКИ") | |
| print("=" * 60) | |
| print(f"Размер данных: {df.shape[0]} строк, {df.shape[1]} колонок") | |
| print(f"Колонки: {list(df.columns)}") | |
| return df | |
| def basic_statistics(df): | |
| """Базовая статистика по оценкам""" | |
| print("\n" + "=" * 40) | |
| print("БАЗОВАЯ СТАТИСТИКА") | |
| print("=" * 40) | |
| # Статистика по AI оценкам | |
| print("AI оценки (pred_score):") | |
| print(f" Среднее: {df['pred_score'].mean():.3f}") | |
| print(f" Медиана: {df['pred_score'].median():.3f}") | |
| print(f" Стандартное отклонение: {df['pred_score'].std():.3f}") | |
| print(f" Минимум: {df['pred_score'].min():.3f}") | |
| print(f" Максимум: {df['pred_score'].max():.3f}") | |
| # Статистика по человеческим оценкам | |
| print("\nОценки экзаменатора:") | |
| print(f" Среднее: {df['Оценка экзаменатора'].mean():.3f}") | |
| print(f" Медиана: {df['Оценка экзаменатора'].median():.3f}") | |
| print(f" Стандартное отклонение: {df['Оценка экзаменатора'].std():.3f}") | |
| # Распределение оценок | |
| print("\nРаспределение оценок экзаменатора:") | |
| распределение = df['Оценка экзаменатора'].value_counts().sort_index() | |
| for оценка, count in распределение.items(): | |
| print(f" {оценка}: {count} ответов ({count / len(df) * 100:.1f}%)") | |
| def calculate_correlations(df): | |
| """Расчет корреляций и разниц""" | |
| print("\n" + "=" * 40) | |
| print("КОРРЕЛЯЦИИ И РАСХОЖДЕНИЯ") | |
| print("=" * 40) | |
| # Корреляция | |
| correlation = df[['Оценка экзаменатора', 'pred_score']].corr().iloc[0, 1] | |
| print(f"Корреляция между оценками: {correlation:.3f}") | |
| # Разницы между оценками | |
| df['разница'] = df['pred_score'] - df['Оценка экзаменатора'] | |
| df['abs_разница'] = abs(df['разница']) | |
| print(f"Средняя абсолютная разница: {df['abs_разница'].mean():.3f}") | |
| print(f"Максимальная разница: {df['abs_разница'].max():.3f}") | |
| print(f"Минимальная разница: {df['abs_разница'].min():.3f}") | |
| # Анализ согласованности | |
| print("\nСОГЛАСОВАННОСТЬ ОЦЕНОК:") | |
| for порог in [0.1, 0.3, 0.5, 1.0]: | |
| согласованные = df[df['abs_разница'] < порог].shape[0] | |
| процент = (согласованные / len(df)) * 100 | |
| print(f" Разница < {порог}: {согласованные} ответов ({процент:.1f}%)") | |
| # Направление разниц | |
| завышение = len(df[df['разница'] > 0]) | |
| занижение = len(df[df['разница'] < 0]) | |
| совпадение = len(df[df['разница'] == 0]) | |
| print(f"\nНАПРАВЛЕНИЕ РАЗНИЦ:") | |
| print(f" AI завышает: {завышение} ({завышение / len(df) * 100:.1f}%)") | |
| print(f" AI занижает: {занижение} ({занижение / len(df) * 100:.1f}%)") | |
| print(f" Полное совпадение: {совпадение} ({совпадение / len(df) * 100:.1f}%)") | |
| def create_visualizations(df): | |
| """Создание визуализаций""" | |
| print("\n" + "=" * 40) | |
| print("СОЗДАНИЕ ВИЗУАЛИЗАЦИЙ") | |
| print("=" * 40) | |
| # Создаем папку для графиков | |
| os.makedirs('graphs', exist_ok=True) | |
| # 1. Scatter plot сравнения оценок | |
| plt.figure(figsize=(12, 8)) | |
| scatter = plt.scatter(df['Оценка экзаменатора'], df['pred_score'], | |
| c=df['abs_разница'], cmap='viridis', alpha=0.7, s=80) | |
| plt.colorbar(scatter, label='Абсолютная разница') | |
| plt.plot([0, 2], [0, 2], 'r--', alpha=0.5, label='Идеальное соответствие') | |
| plt.xlabel('Оценка экзаменатора', fontsize=12) | |
| plt.ylabel('AI оценка (pred_score)', fontsize=12) | |
| plt.title('Сравнение человеческой и AI оценки', fontsize=14) | |
| plt.legend() | |
| plt.grid(True, alpha=0.3) | |
| plt.xticks([0, 1, 2]) | |
| plt.yticks(np.arange(0, 2.5, 0.5)) | |
| plt.savefig('graphs/scatter_comparison_pro.png', dpi=300, bbox_inches='tight') | |
| plt.close() | |
| # 2. Гистограмма разниц | |
| plt.figure(figsize=(12, 6)) | |
| n, bins, patches = plt.hist(df['разница'], bins=30, alpha=0.7, | |
| edgecolor='black', color='skyblue') | |
| plt.xlabel('Разница оценок (AI - Человек)', fontsize=12) | |
| plt.ylabel('Количество ответов', fontsize=12) | |
| plt.title('Распределение разниц между AI и человеческими оценками', fontsize=14) | |
| plt.grid(True, alpha=0.3) | |
| plt.axvline(x=0, color='red', linestyle='--', alpha=0.8, linewidth=2, label='Нулевая разница') | |
| plt.axvline(x=df['разница'].mean(), color='orange', linestyle='--', | |
| alpha=0.8, linewidth=2, label=f'Средняя разница: {df["разница"].mean():.3f}') | |
| plt.legend() | |
| plt.savefig('graphs/difference_histogram_pro.png', dpi=300, bbox_inches='tight') | |
| plt.close() | |
| # 3. Box plot по типам вопросов | |
| plt.figure(figsize=(14, 8)) | |
| box_data = [df[df['№ вопроса'] == question]['pred_score'].values | |
| for question in sorted(df['№ вопроса'].unique())] | |
| box_plot = plt.boxplot(box_data, labels=sorted(df['№ вопроса'].unique()), | |
| patch_artist=True) | |
| # Раскрашиваем boxplot | |
| colors = ['lightblue', 'lightgreen', 'lightcoral', 'lightyellow'] | |
| for patch, color in zip(box_plot['boxes'], colors): | |
| patch.set_facecolor(color) | |
| plt.title('Распределение AI оценок по номерам вопросов', fontsize=14) | |
| plt.xlabel('Номер вопроса', fontsize=12) | |
| plt.ylabel('AI оценка (pred_score)', fontsize=12) | |
| plt.grid(True, alpha=0.3) | |
| plt.savefig('graphs/question_boxplot_pro.png', dpi=300, bbox_inches='tight') | |
| plt.close() | |
| print("Графики сохранены в папку 'graphs/'") | |
| def analyze_extreme_cases(df): | |
| """Анализ крайних случаев""" | |
| print("\n" + "=" * 40) | |
| print("АНАЛИЗ КРАЙНИХ СЛУЧАЕВ") | |
| print("=" * 40) | |
| # Наибольшие расхождения | |
| большие_расхождения = df.nlargest(8, 'abs_разница')[ | |
| ['Id экзамена', '№ вопроса', 'Оценка экзаменатора', 'pred_score', | |
| 'abs_разница', 'разница'] | |
| ] | |
| print("Топ-8 наибольших расхождений:") | |
| print("-" * 80) | |
| for idx, row in большие_расхождения.iterrows(): | |
| направление = "ЗАВЫШЕНИЕ" if row['разница'] > 0 else "ЗАНИЖЕНИЕ" | |
| print(f"\nЭкзамен {row['Id экзамена']}, Вопрос {row['№ вопроса']} ({направление}):") | |
| print(f" Человек: {row['Оценка экзаменатора']} | AI: {row['pred_score']:.3f}") | |
| print(f" Разница: {row['abs_разница']:.3f} ({row['разница']:+.3f})") | |
| print("-" * 60) | |
| def analyze_explanations(df): | |
| """Анализ объяснений оценок""" | |
| print("\n" + "=" * 40) | |
| print("АНАЛИЗ ОБЪЯСНЕНИЙ ОЦЕНОК") | |
| print("=" * 40) | |
| explanation_columns = ['объяснение_оценки', 'explanation', 'объяснение'] | |
| explanation_col = None | |
| for col in explanation_columns: | |
| if col in df.columns: | |
| explanation_col = col | |
| break | |
| if not explanation_col: | |
| print("Колонка с объяснениями оценок не найдена") | |
| return | |
| # Собираем все объяснения | |
| все_объяснения = ' '.join(df[explanation_col].dropna().astype(str)) | |
| # Разбиваем на слова и фильтруем | |
| слова = [word.strip() for word in все_объяснения.split() if len(word.strip()) > 2] | |
| # Анализ частотности | |
| частотность = Counter(слова) | |
| print("Топ-15 наиболее частых характеристик в объяснениях:") | |
| for слово, count in частотность.most_common(15): | |
| print(f" {слово}: {count}") | |
| # Анализ по ключевым категориям | |
| категории = { | |
| 'Развернутый': 'Развернутый ответ', | |
| 'смысловое': 'Смысловое соответствие', | |
| 'соответствие': 'Смысловое соответствие', | |
| 'Хорошая': 'Хорошая структура', | |
| 'структура': 'Хорошая структура', | |
| 'лексика': 'Разнообразная лексика', | |
| 'Высокий': 'Высокий балл', | |
| 'балл': 'Высокий балл', | |
| 'описание': 'Подробное описание', | |
| 'личный': 'Личный опыт', | |
| 'покрытие': 'Покрытие вопросов' | |
| } | |
| print(f"\nСТАТИСТИКА ПО КАТЕГОРИЯМ:") | |
| for ключ, описание in категориями.items(): | |
| count = sum(1 for слово in слова if ключ in слово) | |
| if count > 0: | |
| print(f" {описание}: {count}") | |
| def performance_by_question_type(df): | |
| """Анализ производительности по типам вопросов""" | |
| print("\n" + "=" * 40) | |
| print("АНАЛИЗ ПО ТИПАМ ВОПРОСОВ") | |
| print("=" * 40) | |
| вопросы_статистика = df.groupby('№ вопроса').agg({ | |
| 'Оценка экзаменатора': ['mean', 'std', 'count'], | |
| 'pred_score': ['mean', 'std'], | |
| 'abs_разница': 'mean', | |
| 'разница': 'mean' | |
| }).round(3) | |
| # Переименовываем колонки для удобства | |
| вопросы_статистика.columns = ['чел_среднее', 'чел_стд', 'количество', | |
| 'ai_среднее', 'ai_стд', 'ср_абс_разница', 'ср_разница'] | |
| вопросы_статистика['расхождение'] = abs(вопросы_статистика['ср_разница']) | |
| print("СТАТИСТИКА ПО ВОПРОСАМ:") | |
| print("-" * 80) | |
| print(f"{'Вопрос':<6} {'Чел.ср':<8} {'AI ср':<8} {'Разн.':<8} {'Кол-во':<8} {'Описание'}") | |
| print("-" * 80) | |
| for вопрос, row in вопросы_статистика.iterrows(): | |
| разница_знак = "+" if row['ср_разница'] > 0 else "" | |
| print(f"{вопрос:<6} {row['чел_среднее']:<8} {row['ai_среднее']:<8} " | |
| f"{разница_знак}{row['ср_разница']:<7} {int(row['количество']):<8} ", end="") | |
| if row['расхождение'] > 0.3: | |
| print("ВНИМАНИЕ: большое расхождение") | |
| elif row['расхождение'] > 0.1: | |
| print("Умеренное расхождение") | |
| else: | |
| print("Хорошее соответствие") | |
| def save_detailed_analysis(df): | |
| """Сохранение детального анализа в файл""" | |
| print("\n" + "=" * 40) | |
| print("СОХРАНЕНИЕ РЕЗУЛЬТАТОВ") | |
| print("=" * 40) | |
| # Создаем копию с анализом | |
| df_analysis = df.copy() | |
| df_analysis['разница_ai_человек'] = df_analysis['pred_score'] - df_analysis['Оценка экзаменатора'] | |
| df_analysis['abs_разница'] = abs(df_analysis['разница_ai_человек']) | |
| # Добавляем категоризацию расхождений | |
| условия = [ | |
| df_analysis['abs_разница'] < 0.1, | |
| df_analysis['abs_разница'] < 0.3, | |
| df_analysis['abs_разница'] < 0.5, | |
| df_analysis['abs_разница'] >= 0.5 | |
| ] | |
| категории = ['Отличное', 'Хорошее', 'Умеренное', 'Низкое'] | |
| df_analysis['качество_согласования'] = np.select(условия, категории, default='Низкое') | |
| # Сортируем по наибольшим расхождениям | |
| df_analysis = df_analysis.sort_values('abs_разница', ascending=False) | |
| try: | |
| # Сохраняем в Excel | |
| with pd.ExcelWriter('detailed_analysis_pro.xlsx', engine='openpyxl') as writer: | |
| # Все данные | |
| df_analysis.to_excel(writer, sheet_name='Все_данные_с_анализом', index=False) | |
| # Сводная таблица по вопросам | |
| сводная = df_analysis.groupby('№ вопроса').agg({ | |
| 'Оценка экзаменатора': ['mean', 'std', 'min', 'max'], | |
| 'pred_score': ['mean', 'std', 'min', 'max'], | |
| 'abs_разница': ['mean', 'max'], | |
| 'разница_ai_человек': 'mean', | |
| 'Id экзамена': 'count' | |
| }).round(3) | |
| сводная.to_excel(writer, sheet_name='Сводка_по_вопросам') | |
| # Наибольшие расхождения | |
| большие_расхождения = df_analysis.nlargest(20, 'abs_разница')[ | |
| ['Id экзамена', '№ вопроса', 'Оценка экзаменатора', | |
| 'pred_score', 'разница_ai_человек', 'abs_разница'] | |
| ] | |
| большие_расхождения.to_excel(writer, sheet_name='Наибольшие_расхождения', index=False) | |
| # Статистика по качеству согласования | |
| качество_стат = df_analysis['качество_согласования'].value_counts() | |
| качество_стат.to_excel(writer, sheet_name='Качество_согласования') | |
| print("Детальный анализ сохранен в 'detailed_analysis_pro.xlsx'") | |
| except Exception as e: | |
| print(f"Не удалось сохранить Excel, сохраняем в CSV: {e}") | |
| df_analysis.to_csv('detailed_analysis_pro.csv', index=False, encoding='utf-8') | |
| print("Детальный анализ сохранен в 'detailed_analysis_pro.csv'") | |
| def generate_summary_report(df): | |
| """Генерация итогового отчета""" | |
| print("\n" + "=" * 60) | |
| print("ИТОГОВЫЙ ОТЧЕТ") | |
| print("=" * 60) | |
| корреляция = df[['Оценка экзаменатора', 'pred_score']].corr().iloc[0, 1] | |
| ср_разница = df['abs_разница'].mean() | |
| print(f"\nОБЩАЯ СТАТИСТИКА:") | |
| print(f" Всего ответов: {len(df)}") | |
| print(f" Корреляция AI-Человек: {корреляция:.3f}") | |
| print(f" Средняя абсолютная разница: {ср_разница:.3f}") | |
| # Оценка качества | |
| if корреляция > 0.8 and ср_разница < 0.2: | |
| оценка = "ОТЛИЧНОЕ" | |
| elif корреляция > 0.6 and ср_разница < 0.3: | |
| оценка = "ХОРОШЕЕ" | |
| elif корреляция > 0.4 and ср_разница < 0.4: | |
| оценка = "УДОВЛЕТВОРИТЕЛЬНОЕ" | |
| else: | |
| оценка = "НИЗКОЕ" | |
| print(f"\nОЦЕНКА КАЧЕСТВА СИСТЕМЫ: {оценка}") | |
| # Рекомендации | |
| print(f"\nРЕКОМЕНДАЦИИ:") | |
| if ср_разница > 0.3: | |
| print(" Проанализировать систематические ошибки в оценках") | |
| if корреляция < 0.6: | |
| print(" Улучшить согласованность с человеческими оценками") | |
| # Лучшие и худшие вопросы | |
| вопросы_стат = df.groupby('№ вопроса')['abs_разница'].mean().sort_values() | |
| лучший_вопрос = вопросы_стат.index[0] | |
| худший_вопрос = вопросы_стат.index[-1] | |
| print(f"\nЛУЧШИЙ ВОПРОС ПО СОГЛАСОВАННОСТИ: №{лучший_вопрос} (разница: {вопросы_стат.iloc[0]:.3f})") | |
| print(f"ХУДШИЙ ВОПРОС ПО СОГЛАСОВАННОСТИ: №{худший_вопрос} (разница: {вопросы_стат.iloc[-1]:.3f})") | |
| def main(): | |
| """Основная функция""" | |
| try: | |
| # Загрузка данных | |
| df = load_and_analyze_data() | |
| if df is None: | |
| return | |
| # Проверка необходимых колонок | |
| required_columns = ['Оценка экзаменатора', 'pred_score', '№ вопроса'] | |
| missing_columns = [col for col in required_columns if col not in df.columns] | |
| if missing_columns: | |
| print(f"ОШИБКА: Отсутствуют колонки: {missing_columns}") | |
| return | |
| # Выполнение анализа | |
| basic_statistics(df) | |
| calculate_correlations(df) | |
| create_visualizations(df) | |
| analyze_extreme_cases(df) | |
| analyze_explanations(df) | |
| performance_by_question_type(df) | |
| save_detailed_analysis(df) | |
| generate_summary_report(df) | |
| print("\n" + "=" * 60) | |
| print("АНАЛИЗ ЗАВЕРШЕН!") | |
| print("=" * 60) | |
| print("\nСОЗДАННЫЕ ФАЙЛЫ:") | |
| print(" graphs/scatter_comparison_pro.png - сравнение оценок") | |
| print(" graphs/difference_histogram_pro.png - распределение разниц") | |
| print(" graphs/question_boxplot_pro.png - оценки по вопросам") | |
| print(" detailed_analysis_pro.xlsx - детальный отчет") | |
| except FileNotFoundError: | |
| print("ОШИБКА: Файл 'small.csv' не найден в текущей директории") | |
| except Exception as e: | |
| print(f"ОШИБКА при выполнении анализа: {str(e)}") | |
| if __name__ == "__main__": | |
| main() |