Выполнил:
студент группы 851-М81
Меркулов А. Д.
ВВЕДЕНИЕ
ОПИСАНИЕ ДАННЫХ И МЕТОДИКИ АНАЛИЗА
2.1. Описание исходных данных
2.2. Методы анализа социальных сетей
РЕЗУЛЬТАТЫ ИССЛЕДОВАНИЯ
3.1. Основные свойства сети
3.2. Визуализация силового графа
3.3. Анализ сообществ лувенским методом
3.4. Анализ влиятельности PageRank
3.5. Сравнительный анализ мер центральности
ВЫВОДЫ
ЗАКЛЮЧЕНИЕ
ПРИЛОЖЕНИЕ А
Цель работы: Изучение методов анализа социальных сетей на примере данных каратэ-клуба Закари. Применение силового алгоритма, лувенского метода и алгоритма PageRank для анализа структуры сети.
Задачи исследования:
Актуальность исследования обусловлена возрастающей важностью анализа социальных сетей для понимания структуры взаимодействий в различных сообществах, организациях и социальных группах. Методы SNA находят применение в социологии, маркетинге, управлении и компьютерных науках.
Для анализа использовался классический набор данных — Граф каратэ-клуба Закари, который представляет собой социальная сеть взаимодействий между членами клуба каратэ в университетском городке
Характеристики графа:
Использованные методы анализа:
Программное обеспечение:
Основные свойства сети каратэ-клуба
| Параметр | Значение |
|---|---|
| Количество узлов | 34 |
| Количество ребер | 78 |
| Плотность сети | 0.1390 |
| Средняя степень | 4.59 |
| Средний коэффициент кластеризации | 0.5706 |
| Диаметр сети | 5 |
| Средняя длина пути | 2.41 |
| Модулярность (лувенский метод) | 0.4110 |
Рисунок: Силовой граф каратэ-клуба Закари с выделением сообществ
Анализ: На графе визуализированы социальные связи между членами клуба с использованием силового алгоритма. Разные цвета обозначают различные сообщества, выявленные лувенским методом. Позиционирование узлов отражает силу связей между ними.
Модулярность сети: 0.4110
Модулярность показывает качество разделения сети на сообщества. Значение выше 0.3 свидетельствует о хорошей кластерной структуре.
Состав основных сообществ:
Сообщество 1 (размер: 17 узлов):
Сообщество 2 (размер: 9 узлов):
Топ-10 наиболее влиятельных узлов по алгоритму PageRank
| Ранг | Узел | PageRank | Роль |
|---|---|---|---|
| 1 | Узел 33 | 0.0970 | John A |
| 2 | Узел 0 | 0.0885 | Mr. Hi |
| 3 | Узел 32 | 0.0759 | Член клуба |
| 4 | Узел 2 | 0.0628 | Член клуба |
| 5 | Узел 1 | 0.0574 | Член клуба |
| 6 | Узел 31 | 0.0420 | Член клуба |
| 7 | Узел 23 | 0.0411 | Член клуба |
| 8 | Узел 3 | 0.0372 | Член клуба |
| 9 | Узел 5 | 0.0338 | Член клуба |
| 10 | Узел 13 | 0.0335 | Член клуба |
Анализ: Алгоритм PageRank выявил ключевых членов клуба, которые оказывают наибольшее влияние на распространение информации в сети. Узлы с высоким PageRank обычно имеют много входящих связей от других влиятельных узлов.
Рисунок: Граф с размерами узлов по PageRank
Анализ: На графе размер узлов пропорционален их значению PageRank. Крупные узлы представляют наиболее влиятельных членов клуба.
Рисунок: Топ-10 наиболее влиятельных узлов по PageRank
Сравнение мер центральности для топ-5 узлов
| Узел | Степень | Посредничество | Близость | Собств. вектор | PageRank |
|---|---|---|---|---|---|
| Узел 33 | 0.515 | 0.304 | 0.550 | 0.373 | 0.097 |
| Узел 0 | 0.485 | 0.438 | 0.569 | 0.355 | 0.089 |
| Узел 32 | 0.364 | 0.145 | 0.516 | 0.309 | 0.076 |
| Узел 2 | 0.303 | 0.144 | 0.559 | 0.317 | 0.063 |
| Узел 1 | 0.273 | 0.054 | 0.485 | 0.266 | 0.057 |
Интерпретация мер центральности:
Рисунок: Сравнение мер центральности для топ-5 узлов
В ходе выполнения лабораторной работы успешно проведен комплексный анализ социальной сети каратэ-клуба Закари с применением современных методов SNA. Использование силового алгоритма, лувенского метода и алгоритма PageRank позволило получить многогранное представление о структуре и динамике социальных взаимодействий в клубе.
Научная и практическая значимость работы:
Перспективы дальнейших исследований:
Исходный код программы
# lab9_social_network_analysis.py
# -*- coding: utf-8 -*-
"""
Лабораторная работа: Анализ социальных сетей (Social Network Analysis)
Анализ структуры сети каратэ-клуба Закари методами SNA
"""
import os
import numpy as np
import pandas as pd
import datetime
from typing import List, Tuple, Dict, Any, Set
import sys
import matplotlib.pyplot as plt
import seaborn as sns
from collections import defaultdict
import networkx as nx
# Настройка стиля для академических графиков
plt.style.use('seaborn-v0_8-whitegrid')
sns.set_palette("husl")
# Попытка импортировать python-docx для генерации .docx отчёта
try:
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
PYDOCX_AVAILABLE = True
except Exception:
PYDOCX_AVAILABLE = False
# Попытка импортировать библиотеки для анализа сообществ
try:
import community as community_louvain
LOUVAIN_AVAILABLE = True
except Exception:
LOUVAIN_AVAILABLE = False
def read_own_source() -> Tuple[bool, str]:
"""
Пытается прочитать исходный код текущего файла и вернуть (успех, текст).
Возвращает (False, сообщение об ошибке) при неудаче.
"""
try:
current_file = os.path.abspath(__file__)
except Exception:
current_file = os.path.abspath(sys.argv[0]) if sys.argv and sys.argv[0] else None
if not current_file or not os.path.exists(current_file):
return False, "Не удалось определить путь к файлу скрипта"
try:
with open(current_file, "r", encoding="utf-8") as f:
src = f.read()
return True, src
except Exception as e:
return False, f"Ошибка при чтении исходного кода: {e}"
class SocialNetworkAnalyzer:
"""Класс для анализа социальных сетей методами SNA"""
def __init__(self):
self.graph = None
self.communities = None
self.pagerank_scores = None
self.centrality_measures = None
self.results = {}
def load_karate_club_graph(self):
"""Загрузка графа каратэ-клуба Закари"""
# Создаем граф каратэ-клуба Закари
self.graph = nx.karate_club_graph()
# Добавляем атрибуты узлов (реальные имена и роли из исследования Закари)
node_attributes = {
0: {'name': 'Mr. Hi', 'role': 'Instructor'},
33: {'name': 'John A', 'role': 'Officer'},
}
for node in self.graph.nodes():
if node in node_attributes:
self.graph.nodes[node].update(node_attributes[node])
else:
self.graph.nodes[node]['name'] = f'Member_{node}'
self.graph.nodes[node]['role'] = 'Member'
return self.graph
def force_directed_layout_analysis(self):
"""Анализ с использованием силового алгоритма (Force-directed algorithm)"""
# Используем spring layout (алгоритм Фрюхтермана-Рейнгольда)
pos = nx.spring_layout(self.graph, seed=42, k=1, iterations=50)
# Рассчитываем центральность по посредничеству для толщины ребер
edge_weights = nx.edge_betweenness_centrality(self.graph)
return {
'position': pos,
'edge_weights': edge_weights
}
def louvain_community_detection(self):
"""Выявление сообществ с помощью лувенского метода"""
if not LOUVAIN_AVAILABLE:
# Эмуляция если библиотека не установлена
return self._emulate_community_detection()
# Применяем лувенский метод
partition = community_louvain.best_partition(self.graph)
# Вычисляем модулярность
modularity = community_louvain.modularity(partition, self.graph)
# Группируем узлы по сообществам
communities = defaultdict(list)
for node, community_id in partition.items():
communities[community_id].append(node)
self.communities = dict(communities)
return {
'communities': self.communities,
'modularity': modularity,
'partition': partition
}
def _emulate_community_detection(self):
"""Эмуляция обнаружения сообществ если библиотека не установлена"""
# Используем встроенные методы networkx для обнаружения сообществ
from networkx.algorithms import community
# Используем greedy modularity communities
communities_list = list(community.greedy_modularity_communities(self.graph))
communities_dict = {}
for i, comm in enumerate(communities_list):
communities_dict[i] = list(comm)
# Вычисляем модулярность
modularity = community.modularity(self.graph, communities_list)
# Создаем partition для совместимости
partition = {}
for i, comm in enumerate(communities_list):
for node in comm:
partition[node] = i
self.communities = communities_dict
return {
'communities': self.communities,
'modularity': modularity,
'partition': partition
}
def pagerank_analysis(self):
"""Анализ влиятельности узлов с помощью алгоритма PageRank"""
# Вычисляем PageRank
pagerank = nx.pagerank(self.graph, alpha=0.85)
# Сортируем по убыванию влиятельности
sorted_pagerank = sorted(pagerank.items(), key=lambda x: x[1], reverse=True)
self.pagerank_scores = pagerank
return {
'pagerank_scores': pagerank,
'top_influential': sorted_pagerank[:10],
'sorted_pagerank': sorted_pagerank
}
def calculate_centrality_measures(self):
"""Расчет различных мер центральности"""
centrality_measures = {}
# Степень центральности
centrality_measures['degree'] = nx.degree_centrality(self.graph)
# Посредническая центральность
centrality_measures['betweenness'] = nx.betweenness_centrality(self.graph)
# Близостная центральность
centrality_measures['closeness'] = nx.closeness_centrality(self.graph)
# Векторная центральность
centrality_measures['eigenvector'] = nx.eigenvector_centrality(self.graph, max_iter=1000)
self.centrality_measures = centrality_measures
return centrality_measures
def analyze_network_properties(self):
"""Анализ основных свойств сети"""
properties = {}
# Основные метрики
properties['num_nodes'] = self.graph.number_of_nodes()
properties['num_edges'] = self.graph.number_of_edges()
properties['density'] = nx.density(self.graph)
properties['average_degree'] = sum(dict(self.graph.degree()).values()) / properties['num_nodes']
# Коэффициент кластеризации
properties['average_clustering'] = nx.average_clustering(self.graph)
# Диаметр и средний путь
if nx.is_connected(self.graph):
properties['diameter'] = nx.diameter(self.graph)
properties['average_path_length'] = nx.average_shortest_path_length(self.graph)
else:
# Для несвязных графов вычисляем для наибольшей компоненты
largest_cc = max(nx.connected_components(self.graph), key=len)
subgraph = self.graph.subgraph(largest_cc)
properties['diameter'] = nx.diameter(subgraph)
properties['average_path_length'] = nx.average_shortest_path_length(subgraph)
properties['connected_components'] = nx.number_connected_components(self.graph)
# Анализ распределения степеней
degrees = [deg for _, deg in self.graph.degree()]
properties['degree_distribution'] = {
'min_degree': min(degrees),
'max_degree': max(degrees),
'degree_histogram': np.histogram(degrees, bins=10)
}
return properties
def create_visualizations(self):
"""Создание визуализаций для отчета"""
# Создаем папку для изображений
os.makedirs("results/images", exist_ok=True)
visualization_paths = {}
try:
# 1. Силовой граф с выделением сообществ
plt.figure(figsize=(12, 8))
# Получаем разметку сообществ
community_results = self.louvain_community_detection()
partition = community_results['partition']
# Позиционирование
pos = nx.spring_layout(self.graph, seed=42, k=1, iterations=50)
# Цвета для сообществ
community_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
# Рисуем узлы с цветами по сообществам
for community_id in set(partition.values()):
nodes = [node for node in self.graph.nodes() if partition[node] == community_id]
nx.draw_networkx_nodes(self.graph, pos, nodelist=nodes,
node_color=community_colors[community_id % len(community_colors)],
node_size=300, alpha=0.9, edgecolors='black', linewidths=0.5)
# Рисуем ребра
nx.draw_networkx_edges(self.graph, pos, alpha=0.5, edge_color='gray', width=1)
# Подписи для ключевых узлов
labels = {}
pagerank_results = self.pagerank_analysis()
top_nodes = [node for node, _ in pagerank_results['top_influential'][:5]]
for node in top_nodes:
labels[node] = str(node)
nx.draw_networkx_labels(self.graph, pos, labels, font_size=10, font_weight='bold')
plt.title('Силовой граф каратэ-клуба Закари с выделением сообществ', fontsize=14, pad=20)
plt.axis('off')
plt.tight_layout()
force_graph_path = "results/images/force_directed_graph.png"
plt.savefig(force_graph_path, dpi=300, bbox_inches='tight')
plt.close()
visualization_paths['force_directed_graph'] = force_graph_path
# 2. Визуализация с размерами узлов по PageRank
plt.figure(figsize=(12, 8))
pagerank = pagerank_results['pagerank_scores']
node_sizes = [3000 * pagerank[node] for node in self.graph.nodes()]
# Рисуем граф с размерами узлов по PageRank
nx.draw_networkx_nodes(self.graph, pos, node_color='lightblue',
node_size=node_sizes, alpha=0.9, edgecolors='black', linewidths=0.5)
nx.draw_networkx_edges(self.graph, pos, alpha=0.3, edge_color='gray', width=1)
# Подписи для топ узлов по PageRank
labels = {}
for node in top_nodes:
labels[node] = f"{node}\n({pagerank[node]:.3f})"
nx.draw_networkx_labels(self.graph, pos, labels, font_size=8, font_weight='bold')
plt.title('Граф с размерами узлов по PageRank', fontsize=14, pad=20)
plt.axis('off')
plt.tight_layout()
pagerank_graph_path = "results/images/pagerank_graph.png"
plt.savefig(pagerank_graph_path, dpi=300, bbox_inches='tight')
plt.close()
visualization_paths['pagerank_graph'] = pagerank_graph_path
# 3. Топ-10 узлов по PageRank
plt.figure(figsize=(10, 6))
top_pagerank = pagerank_results['top_influential'][:10]
nodes = [f"Узел {node}" for node, _ in top_pagerank]
scores = [score for _, score in top_pagerank]
colors = plt.cm.viridis(np.linspace(0, 1, len(nodes)))
bars = plt.barh(nodes, scores, color=colors, alpha=0.8, edgecolor='black', linewidth=0.5)
plt.xlabel('PageRank Score', fontsize=12)
plt.title('Топ-10 наиболее влиятельных узлов по PageRank', fontsize=14, pad=20)
plt.grid(True, alpha=0.3, axis='x')
# Добавляем значения на график
for bar in bars:
width = bar.get_width()
plt.text(width + 0.001, bar.get_y() + bar.get_height()/2,
f'{width:.3f}', ha='left', va='center', fontsize=9)
plt.tight_layout()
top_pagerank_path = "results/images/top_pagerank.png"
plt.savefig(top_pagerank_path, dpi=300, bbox_inches='tight')
plt.close()
visualization_paths['top_pagerank'] = top_pagerank_path
# 4. Сравнение мер центральности
plt.figure(figsize=(12, 8))
centrality = self.calculate_centrality_measures()
top_nodes_pagerank = [node for node, _ in pagerank_results['top_influential'][:5]]
measures = ['degree', 'betweenness', 'closeness', 'eigenvector']
measure_names = ['Степень', 'Посредничество', 'Близость', 'Собственный вектор']
x = np.arange(len(top_nodes_pagerank))
width = 0.2
for i, (measure, name) in enumerate(zip(measures, measure_names)):
scores = [centrality[measure][node] for node in top_nodes_pagerank]
plt.bar(x + i * width, scores, width, label=name, alpha=0.8)
plt.xlabel('Узлы', fontsize=12)
plt.ylabel('Значение центральности', fontsize=12)
plt.title('Сравнение мер центральности для топ-5 узлов', fontsize=14, pad=20)
plt.xticks(x + width * 1.5, [f'Узел {node}' for node in top_nodes_pagerank])
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
centrality_comparison_path = "results/images/centrality_comparison.png"
plt.savefig(centrality_comparison_path, dpi=300, bbox_inches='tight')
plt.close()
visualization_paths['centrality_comparison'] = centrality_comparison_path
except Exception as e:
print(f"Ошибка при создании визуализаций: {e}")
return visualization_paths
def analyze(self):
"""Выполнение полного анализа социальной сети"""
print("Загрузка графа каратэ-клуба Закари...")
self.load_karate_club_graph()
print("Применение силового алгоритма...")
force_results = self.force_directed_layout_analysis()
print("Обнаружение сообществ лувенским методом...")
community_results = self.louvain_community_detection()
print("Анализ влиятельности PageRank...")
pagerank_results = self.pagerank_analysis()
print("Расчет мер центральности...")
centrality_results = self.calculate_centrality_measures()
print("Анализ свойств сети...")
network_properties = self.analyze_network_properties()
print("Создание визуализаций...")
visualizations = self.create_visualizations()
# Сохранение результатов
self.results = {
'network_properties': network_properties,
'force_results': force_results,
'community_results': community_results,
'pagerank_results': pagerank_results,
'centrality_results': centrality_results,
'visualizations': visualizations
}
return self.results
def get_analysis_description(self):
"""Возвращает описание анализа и методики"""
description = {
'title': "Анализ социальных сетей (Social Network Analysis)",
'discipline': "Методологические основы информационных процессов",
'topic': "Анализ социальных сетей",
'purpose': "Изучение методов анализа социальных сетей на примере данных каратэ-клуба Закари. Применение силового алгоритма, лувенского метода и алгоритма PageRank для анализа структуры сети.",
'tasks': [
"Построить силовой граф для визуализации сети",
"Применить лувенский метод для выявления кластеров/сообществ в сети",
"Использовать алгоритм PageRank для определения лидеров и наиболее активных членов",
"Выявить состав двух основных групп в сети",
"Провести сравнительный анализ полученных результатов"
],
'methodology': {
'force_directed': "Силовой алгоритм (Force-directed algorithm) - метод визуализации графов, который моделирует физические силы притяжения и отталкивания между узлами",
'louvain': "Лувенский метод - алгоритм обнаружения сообществ, основанный на оптимизации модулярности графа",
'pagerank': "Алгоритм PageRank - метод оценки важности узлов в ориентированном графе, учитывающий количество и качество входящих связей",
'centrality': "Меры центральности - количественные показатели, определяющие важность узлов в сети (степенная, посредническая, близостная, векторная)"
},
'dataset': {
'name': "Граф каратэ-клуба Закари",
'nodes': 34,
'edges': 78,
'description': "Социальная сеть взаимодействий между членами клуба каратэ в университетском городке"
}
}
return description
class SocialNetworkMarkdownReport:
"""Класс для генерации полного отчета в Markdown"""
def __init__(self, analyzer: SocialNetworkAnalyzer, analysis_results: Dict[str, Any]):
self.analyzer = analyzer
self.results = analysis_results
def add_header(self, title: str, level: int = 1) -> str:
return f"{'#' * level} {title}\n\n"
def add_table(self, data: List[List[Any]], headers: List[str], title: str = "") -> str:
table_str = ""
if title:
table_str += f"**{title}**\n\n"
table_str += "| " + " | ".join(headers) + " |\n"
table_str += "|" + "|".join(["---"] * len(headers)) + "|\n"
for row in data:
table_str += "| " + " | ".join(map(str, row)) + " |\n"
table_str += "\n"
return table_str
def add_image(self, image_path: str, caption: str = "") -> str:
if os.path.exists(image_path):
# Центрированное изображение с ограниченной шириной
return (f'<div align="center">\n\n'
f'<img src="{image_path}" alt="{caption}" style="max-width: 80%; height: auto; border: 1px solid #ddd; padding: 5px; background: #f8f9fa;">\n\n'
f'**Рисунок:** {caption}\n\n'
f'</div>\n\n')
else:
return f"*Изображение не найдено: {caption}*\n\n"
def add_code_section(self) -> str:
success, src = read_own_source()
section = self.add_header("ПРИЛОЖЕНИЕ А", 2)
section += "**Исходный код программы**\n\n"
if success:
section += "```python\n" + src
if not src.endswith("\n"):
section += "\n"
section += "```\n"
else:
section += f"*Не удалось прочитать исходный код: {src}*\n"
return section
def generate_report(self) -> str:
report = ""
# Титульная страница
report += '<div style="page-break-after: always;">\n\n'
report += "# Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования\n\n"
report += "### «Казанский национальный исследовательский технологический университет»\n\n"
report += "#### Институт: Институт управления, автоматизации и информационных технологий\n"
report += "#### Кафедра Информатики и прикладной математики\n\n"
report += "---\n\n"
report += "# ЛАБОРАТОРНАЯ РАБОТА\n\n"
description = self.analyzer.get_analysis_description()
report += f"### по дисциплине: «{description['discipline']}»\n\n"
report += f"### на тему: «{description['topic']}»\n\n"
report += "---\n\n"
report += "**Выполнил:** \n"
report += "студент группы 851-М81 \n"
report += "Меркулов А. Д. \n"
report += '</div>\n\n'
# Содержание
report += self.add_header("СОДЕРЖАНИЕ", 1)
toc = [
"1. ВВЕДЕНИЕ",
"2. ОПИСАНИЕ ДАННЫХ И МЕТОДИКИ АНАЛИЗА",
" 2.1. Описание исходных данных",
" 2.2. Методы анализа социальных сетей",
"3. РЕЗУЛЬТАТЫ ИССЛЕДОВАНИЯ",
" 3.1. Основные свойства сети",
" 3.2. Визуализация силового графа",
" 3.3. Анализ сообществ лувенским методом",
" 3.4. Анализ влиятельности PageRank",
" 3.5. Сравнительный анализ мер центральности",
"4. ВЫВОДЫ",
"5. ЗАКЛЮЧЕНИЕ",
"ПРИЛОЖЕНИЕ А"
]
for item in toc:
report += f"{item}\n\n"
report += "---\n\n"
# 1) Введение
report += self.add_header("1. ВВЕДЕНИЕ", 1)
report += f"**Цель работы:** {description['purpose']}\n\n"
report += "**Задачи исследования:**\n"
for i, task in enumerate(description['tasks'], 1):
report += f"{i}. {task}\n"
report += "\n"
report += "**Актуальность исследования** обусловлена возрастающей важностью анализа социальных сетей для понимания структуры взаимодействий в различных сообществах, организациях и социальных группах. Методы SNA находят применение в социологии, маркетинге, управлении и компьютерных науках.\n\n"
# 2) Описание данных и методики
report += self.add_header("2. ОПИСАНИЕ ДАННЫХ И МЕТОДИКИ АНАЛИЗА", 1)
report += self.add_header("2.1. Описание исходных данных", 2)
dataset = description['dataset']
report += f"Для анализа использовался классический набор данных — **{dataset['name']}**, который представляет собой {dataset['description'].lower()}\n\n"
report += "**Характеристики графа:**\n"
report += f"- Количество узлов (членов клуба): {dataset['nodes']}\n"
report += f"- Количество ребер (взаимодействий): {dataset['edges']}\n"
report += "- Граф представляет социальные связи между членами клуба каратэ\n"
report += "- Данные собраны в ходе наблюдения за взаимодействиями в клубе\n\n"
report += self.add_header("2.2. Методы анализа социальных сетей", 2)
methodology = description['methodology']
report += "**Использованные методы анализа:**\n"
for method, explanation in methodology.items():
report += f"- **{method.replace('_', ' ').title()}:** {explanation}\n"
report += "\n"
report += "**Программное обеспечение:**\n"
report += "- Python 3.x с библиотеками NetworkX, Matplotlib, Seaborn\n"
if LOUVAIN_AVAILABLE:
report += "- Библиотека python-louvain для обнаружения сообществ\n"
else:
report += "- Встроенные алгоритмы NetworkX для обнаружения сообществ\n"
report += "\n"
# 3) Результаты исследования
report += self.add_header("3. РЕЗУЛЬТАТЫ ИССЛЕДОВАНИЯ", 1)
report += self.add_header("3.1. Основные свойства сети", 2)
properties = self.results['network_properties']
properties_data = [
["Количество узлов", properties['num_nodes']],
["Количество ребер", properties['num_edges']],
["Плотность сети", f"{properties['density']:.4f}"],
["Средняя степень", f"{properties['average_degree']:.2f}"],
["Средний коэффициент кластеризации", f"{properties['average_clustering']:.4f}"],
["Диаметр сети", properties.get('diameter', 'N/A')],
["Средняя длина пути", f"{properties.get('average_path_length', 'N/A'):.2f}"],
["Модулярность (лувенский метод)", f"{self.results['community_results']['modularity']:.4f}"]
]
report += self.add_table(properties_data, ["Параметр", "Значение"], "Основные свойства сети каратэ-клуба")
report += self.add_header("3.2. Визуализация силового графа", 2)
visualizations = self.results['visualizations']
if 'force_directed_graph' in visualizations:
report += self.add_image(visualizations['force_directed_graph'],
"Силовой граф каратэ-клуба Закари с выделением сообществ")
report += "**Анализ:** На графе визуализированы социальные связи между членами клуба с использованием силового алгоритма. Разные цвета обозначают различные сообщества, выявленные лувенским методом. Позиционирование узлов отражает силу связей между ними.\n\n"
report += self.add_header("3.3. Анализ сообществ лувенским методом", 2)
community_results = self.results['community_results']
report += f"**Модулярность сети:** {community_results['modularity']:.4f}\n\n"
report += "Модулярность показывает качество разделения сети на сообщества. Значение выше 0.3 свидетельствует о хорошей кластерной структуре.\n\n"
report += "**Состав основных сообществ:**\n\n"
communities = community_results['communities']
# Сортируем сообщества по размеру
sorted_communities = sorted(communities.items(), key=lambda x: len(x[1]), reverse=True)
for i, (comm_id, nodes) in enumerate(sorted_communities[:2], 1):
report += f"**Сообщество {i}** (размер: {len(nodes)} узлов):\n"
report += f"- Узлы: {', '.join(map(str, sorted(nodes)))}\n"
# Анализ ключевых узлов в сообществе
pagerank = self.results['pagerank_results']['pagerank_scores']
community_pagerank = [(node, pagerank[node]) for node in nodes]
community_pagerank.sort(key=lambda x: x[1], reverse=True)
if community_pagerank:
top_node, top_score = community_pagerank[0]
report += f"- Наиболее влиятельный узел: {top_node} (PageRank: {top_score:.3f})\n"
report += "\n"
report += self.add_header("3.4. Анализ влиятельности PageRank", 2)
pagerank_results = self.results['pagerank_results']
# Таблица топ-10 узлов по PageRank
top_pagerank_data = []
for i, (node, score) in enumerate(pagerank_results['top_influential'][:10], 1):
top_pagerank_data.append([
f"{i}",
f"Узел {node}",
f"{score:.4f}",
"Mr. Hi" if node == 0 else "John A" if node == 33 else "Член клуба"
])
report += self.add_table(top_pagerank_data, ["Ранг", "Узел", "PageRank", "Роль"],
"Топ-10 наиболее влиятельных узлов по алгоритму PageRank")
report += "**Анализ:** Алгоритм PageRank выявил ключевых членов клуба, которые оказывают наибольшее влияние на распространение информации в сети. Узлы с высоким PageRank обычно имеют много входящих связей от других влиятельных узлов.\n\n"
if 'pagerank_graph' in visualizations:
report += self.add_image(visualizations['pagerank_graph'],
"Граф с размерами узлов по PageRank")
report += "**Анализ:** На графе размер узлов пропорционален их значению PageRank. Крупные узлы представляют наиболее влиятельных членов клуба.\n\n"
if 'top_pagerank' in visualizations:
report += self.add_image(visualizations['top_pagerank'],
"Топ-10 наиболее влиятельных узлов по PageRank")
report += self.add_header("3.5. Сравнительный анализ мер центральности", 2)
centrality = self.results['centrality_results']
# Сравнительная таблица для топ-5 узлов
top_nodes = [node for node, _ in pagerank_results['top_influential'][:5]]
centrality_data = []
for node in top_nodes:
centrality_data.append([
f"Узел {node}",
f"{centrality['degree'][node]:.3f}",
f"{centrality['betweenness'][node]:.3f}",
f"{centrality['closeness'][node]:.3f}",
f"{centrality['eigenvector'][node]:.3f}",
f"{pagerank_results['pagerank_scores'][node]:.3f}"
])
report += self.add_table(centrality_data,
["Узел", "Степень", "Посредничество", "Близость", "Собств. вектор", "PageRank"],
"Сравнение мер центральности для топ-5 узлов")
report += "**Интерпретация мер центральности:**\n"
report += "- **Степень:** Количество непосредственных связей узла\n"
report += "- **Посредничество:** Частота нахождения узла на кратчайших путях между другими узлами\n"
report += "- **Близость:** Средняя длина кратчайшего пути от узла до всех других узлов\n"
report += "- **Собственный вектор:** Влиятельность узла с учетом влиятельности его соседей\n"
report += "- **PageRank:** Вероятность нахождения в узле при случайном блуждании по сети\n\n"
if 'centrality_comparison' in visualizations:
report += self.add_image(visualizations['centrality_comparison'],
"Сравнение мер центральности для топ-5 узлов")
# 4) Выводы
report += self.add_header("4. ВЫВОДЫ", 1)
conclusions = [
"Социальная сеть каратэ-клуба Закари демонстрирует четкую кластерную структуру с двумя основными сообществами, что соответствует реальному разделению в клубе",
"Лувенский метод эффективно выявил естественные сообщества в сети с высокой модулярностью",
"Алгоритм PageRank идентифицировал ключевых лидеров в каждом сообществе, которые играют важную роль в распространении информации",
"Сравнительный анализ различных мер центральности показал различные аспекты влиятельности узлов в сети",
"Узлы с высокой посреднической центральностью играют роль \"мостов\" между различными сообществами",
"Структура сети отражает реальные социальные взаимодействия и иерархические отношения в клубе",
"Методы анализа социальных сетей предоставляют мощный инструментарий для изучения структуры и динамики социальных взаимодействий"
]
for i, conclusion in enumerate(conclusions, 1):
report += f"{i}. {conclusion}\n"
report += "\n"
# 5) Заключение
report += self.add_header("5. ЗАКЛЮЧЕНИЕ", 1)
report += "В ходе выполнения лабораторной работы успешно проведен комплексный анализ социальной сети каратэ-клуба Закари с применением современных методов SNA. Использование силового алгоритма, лувенского метода и алгоритма PageRank позволило получить многогранное представление о структуре и динамике социальных взаимодействий в клубе.\n\n"
report += "**Научная и практическая значимость работы:**\n"
report += "- Продемонстрирована эффективность методов SNA для анализа реальных социальных сетей\n"
report += "- Получены количественные характеристики социальной структуры клуба\n"
report += "- Выявлены ключевые узлы и сообщества, определяющие динамику взаимодействий\n"
report += "- Разработан методический подход к комплексному анализу социальных сетей\n\n"
report += "**Перспективы дальнейших исследований:**\n"
report += "- Анализ временной динамики социальной сети\n"
report += "- Исследование устойчивости сети к удалению ключевых узлов\n"
report += "- Применение методов машинного обучения для прогнозирования связей\n"
report += "- Сравнительный анализ различных социальных сетей\n"
report += "- Разработка рекомендательных систем на основе структуры сети\n\n"
# Приложение
report += self.add_code_section()
return report
class SocialNetworkDocxReport:
"""Класс для генерации отчета в формате DOCX"""
def __init__(self, analyzer: SocialNetworkAnalyzer, analysis_results: Dict[str, Any]):
self.analyzer = analyzer
self.results = analysis_results
def setup_document_styles(self, doc):
"""Настройка стилей документа"""
# Настройка стилей для русского текста
try:
doc.styles['Normal'].font.name = 'Times New Roman'
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), 'Times New Roman')
doc.styles['Normal'].font.size = Pt(12)
except:
pass
def add_table_to_docx(self, doc, data: List[List[Any]], headers: List[str], title: str = ""):
"""Добавляет таблицу в DOCX документ"""
if title:
p = doc.add_paragraph()
p.add_run(title).bold = True
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.add_paragraph()
table = doc.add_table(rows=len(data)+1, cols=len(headers))
table.style = 'Table Grid'
# Заголовки
hdr_cells = table.rows[0].cells
for i, header in enumerate(headers):
hdr_cells[i].text = str(header)
hdr_cells[i].paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
for paragraph in hdr_cells[i].paragraphs:
for run in paragraph.runs:
run.bold = True
# Данные
for i, row in enumerate(data, 1):
row_cells = table.rows[i].cells
for j, cell in enumerate(row):
row_cells[j].text = str(cell)
if j > 0: # Выравнивание числовых данных по центру
row_cells[j].paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.add_paragraph()
def add_image_to_docx(self, doc, image_path: str, caption: str = ""):
"""Добавляет изображение в DOCX документ"""
if os.path.exists(image_path):
try:
# Центрируем изображение
paragraph = doc.add_paragraph()
paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = paragraph.add_run()
run.add_picture(image_path, width=Inches(5.0))
# Добавляем подпись
if caption:
caption_paragraph = doc.add_paragraph()
caption_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
caption_run = caption_paragraph.add_run(f"Рисунок: {caption}")
caption_run.italic = True
doc.add_paragraph()
except Exception as e:
doc.add_paragraph(f"Ошибка при добавлении изображения: {e}")
else:
doc.add_paragraph(f"Изображение не найдено: {caption}")
def generate_docx_report(self, filename: str = "social_network_analysis_report.docx") -> str:
"""
Генерация отчёта в формате DOCX для анализа социальных сетей
"""
out_path = os.path.join("results", filename)
if not PYDOCX_AVAILABLE:
print("python-docx не установлен — DOCX отчёт не будет создан")
return out_path
try:
doc = Document()
self.setup_document_styles(doc)
# Титульная страница
title_paragraph = doc.add_paragraph()
title_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
title_run = title_paragraph.add_run("Федеральное государственное бюджетное образовательное учреждение\высшего профессионального образования\n«Казанский национальный исследовательский технологический университет»")
title_run.bold = True
title_run.font.size = Pt(14)
doc.add_paragraph()
university_paragraph = doc.add_paragraph()
university_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
university_paragraph.add_run("Институт: Институт управления, автоматизации и информационных технологий\nКафедра Информатики и прикладной математики").bold = True
doc.add_paragraph("\n" * 3)
# Название работы
work_paragraph = doc.add_paragraph()
work_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
work_run = work_paragraph.add_run("ЛАБОРАТОРНАЯ РАБОТА")
work_run.bold = True
work_run.font.size = Pt(16)
description = self.analyzer.get_analysis_description()
doc.add_paragraph()
discipline_paragraph = doc.add_paragraph()
discipline_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
discipline_paragraph.add_run(f"по дисциплине: «{description['discipline']}»").bold = True
doc.add_paragraph()
topic_paragraph = doc.add_paragraph()
topic_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
topic_paragraph.add_run(f"на тему: «{description['topic']}»").bold = True
doc.add_paragraph("\n" * 4)
# Информация о студенте и преподавателе
info_table = doc.add_table(rows=4, cols=2)
info_table.style = 'Table Grid'
info_table.cell(0, 0).text = "Выполнил:"
info_table.cell(0, 1).text = "студент группы 851-М81\nМеркулов А. Д."
for row in info_table.rows:
for cell in row.cells:
cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT
doc.add_page_break()
# Содержание
title = doc.add_paragraph()
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
title.add_run("СОДЕРЖАНИЕ").bold = True
title.runs[0].font.size = Pt(14)
doc.add_paragraph()
toc_items = [
"1. ВВЕДЕНИЕ",
"2. ОПИСАНИЕ ДАННЫХ И МЕТОДИКИ АНАЛИЗА",
"2.1. Описание исходных данных",
"2.2. Методы анализа социальных сетей",
"3. РЕЗУЛЬТАТЫ ИССЛЕДОВАНИЯ",
"3.1. Основные свойства сети",
"3.2. Визуализация силового графа",
"3.3. Анализ сообществ лувенским методом",
"3.4. Анализ влиятельности PageRank",
"3.5. Сравнительный анализ мер центральности",
"4. ВЫВОДЫ",
"5. ЗАКЛЮЧЕНИЕ",
"ПРИЛОЖЕНИЕ А"
]
for item in toc_items:
p = doc.add_paragraph()
p.add_run(item)
doc.add_page_break()
# 1) Введение
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("1. ВВЕДЕНИЕ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
doc.add_paragraph(f"Цель работы: {description['purpose']}")
doc.add_paragraph("Задачи исследования:")
for task in description['tasks']:
p = doc.add_paragraph(task, style='List Bullet')
doc.add_paragraph("Актуальность исследования обусловлена возрастающей важностью анализа социальных сетей для понимания структуры взаимодействий в различных сообществах, организациях и социальных группах.")
# 2) Описание данных и методики
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("2. ОПИСАНИЕ ДАННЫХ И МЕТОДИКИ АНАЛИЗА").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
# 2.1 Описание данных
subheading = doc.add_paragraph()
subheading.add_run("2.1. Описание исходных данных").bold = True
dataset = description['dataset']
doc.add_paragraph(f"Для анализа использовался классический набор данных — {dataset['name']}, который представляет собой {dataset['description'].lower()}")
doc.add_paragraph("Характеристики графа:")
doc.add_paragraph(f"• Количество узлов (членов клуба): {dataset['nodes']}", style='List Bullet')
doc.add_paragraph(f"• Количество ребер (взаимодействий): {dataset['edges']}", style='List Bullet')
# 2.2 Методика анализа
subheading = doc.add_paragraph()
subheading.add_run("2.2. Методы анализа социальных сетей").bold = True
methodology = description['methodology']
doc.add_paragraph("Использованные методы анализа:")
for method, explanation in methodology.items():
doc.add_paragraph(f"• {method.replace('_', ' ').title()}: {explanation}", style='List Bullet')
# 3) Результаты исследования
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("3. РЕЗУЛЬТАТЫ ИССЛЕДОВАНИЯ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
# 3.1 Основные свойства
subheading = doc.add_paragraph()
subheading.add_run("3.1. Основные свойства сети").bold = True
properties = self.results['network_properties']
properties_data = [
["Количество узлов", properties['num_nodes']],
["Количество ребер", properties['num_edges']],
["Плотность сети", f"{properties['density']:.4f}"],
["Средняя степень", f"{properties['average_degree']:.2f}"],
["Средний коэффициент кластеризации", f"{properties['average_clustering']:.4f}"]
]
self.add_table_to_docx(doc, properties_data, ["Параметр", "Значение"], "Основные свойства сети")
# 3.2 Визуализация
subheading = doc.add_paragraph()
subheading.add_run("3.2. Визуализация силового графа").bold = True
visualizations = self.results['visualizations']
if 'force_directed_graph' in visualizations:
self.add_image_to_docx(doc, visualizations['force_directed_graph'],
"Силовой граф каратэ-клуба Закари с выделением сообществ")
doc.add_paragraph("Анализ: На графе визуализированы социальные связи между членами клуба с использованием силового алгоритма. Разные цвета обозначают различные сообщества.")
# 3.3 Сообщества
subheading = doc.add_paragraph()
subheading.add_run("3.3. Анализ сообществ лувенским методом").bold = True
community_results = self.results['community_results']
doc.add_paragraph(f"Модулярность сети: {community_results['modularity']:.4f}")
communities = community_results['communities']
sorted_communities = sorted(communities.items(), key=lambda x: len(x[1]), reverse=True)
for i, (comm_id, nodes) in enumerate(sorted_communities[:2], 1):
doc.add_paragraph(f"Сообщество {i} (размер: {len(nodes)} узлов): {', '.join(map(str, sorted(nodes)))}")
# 3.4 PageRank
subheading = doc.add_paragraph()
subheading.add_run("3.4. Анализ влиятельности PageRank").bold = True
pagerank_results = self.results['pagerank_results']
top_pagerank_data = []
for i, (node, score) in enumerate(pagerank_results['top_influential'][:8], 1):
top_pagerank_data.append([
f"{i}",
f"Узел {node}",
f"{score:.4f}"
])
self.add_table_to_docx(doc, top_pagerank_data, ["Ранг", "Узел", "PageRank"],
"Топ-8 наиболее влиятельных узлов")
if 'top_pagerank' in visualizations:
doc.add_page_break()
self.add_image_to_docx(doc, visualizations['top_pagerank'],
"Топ-10 наиболее влиятельных узлов по PageRank")
# 4) Выводы
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("4. ВЫВОДЫ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
conclusions = [
"Социальная сеть каратэ-клуба Закари демонстрирует четкую кластерную структуру с двумя основными сообществами",
"Лувенский метод эффективно выявил естественные сообщества в сети с высокой модулярностью",
"Алгоритм PageRank идентифицировал ключевых лидеров в каждом сообществе",
"Сравнительный анализ различных мер центральности показал различные аспекты влиятельности узлов",
"Структура сети отражает реальные социальные взаимодействия в клубе",
"Методы анализа социальных сетей предоставляют мощный инструментарий для изучения социальных структур"
]
for i, conclusion in enumerate(conclusions, 1):
doc.add_paragraph(f"{i}. {conclusion}", style='List Number')
# 5) Заключение
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("5. ЗАКЛЮЧЕНИЕ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
doc.add_paragraph("В ходе выполнения лабораторной работы успешно проведен комплексный анализ социальной сети каратэ-клуба Закари с применением современных методов SNA.")
doc.add_paragraph("Научная и практическая значимость работы:")
doc.add_paragraph("• Продемонстрирована эффективность методов SNA для анализа реальных социальных сетей", style='List Bullet')
doc.add_paragraph("• Получены количественные характеристики социальной структуры клуба", style='List Bullet')
doc.add_paragraph("• Выявлены ключевые узлы и сообщества, определяющие динамику взаимодействий", style='List Bullet')
doc.add_paragraph("Перспективы дальнейших исследований:")
doc.add_paragraph("• Анализ временной динамики социальной сети", style='List Bullet')
doc.add_paragraph("• Исследование устойчивости сети к удалению ключевых узлов", style='List Bullet')
doc.add_paragraph("• Применение методов машинного обучения для прогнозирования связей", style='List Bullet')
# Приложение
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("ПРИЛОЖЕНИЕ А").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
subheading = doc.add_paragraph()
subheading.add_run("Исходный код программы").bold = True
success_src, src_text = read_own_source()
if success_src:
# Добавляем код с моноширинным шрифтом
for line in src_text.splitlines():
p = doc.add_paragraph()
run = p.add_run(line)
try:
run.font.name = 'Courier New'
run.font.size = Pt(9)
except Exception:
pass
else:
doc.add_paragraph("Не удалось прочитать исходный код скрипта: " + src_text)
# Сохранение файла
doc.save(out_path)
print(f"DOCX-отчёт сохранён: {out_path}")
except Exception as e:
print(f"Ошибка при генерации DOCX-отчёта: {e}")
raise e
return out_path
def main():
"""Основная функция для выполнения лабораторной работы по анализу социальных сетей"""
# Проверка наличия необходимых библиотек
if not LOUVAIN_AVAILABLE:
print("ВНИМАНИЕ: Библиотека python-louvain не установлена. Будут использованы встроенные методы NetworkX.")
print("Для использования лувенского метода установите: pip install python-louvain")
# Создаем анализатор
analyzer = SocialNetworkAnalyzer()
# Выполняем анализ
print("Выполняется анализ социальной сети каратэ-клуба Закари...")
results = analyzer.analyze()
# Создаем папку для результатов
os.makedirs("results", exist_ok=True)
os.makedirs("results/images", exist_ok=True)
# Генерируем Markdown отчет
md_reporter = SocialNetworkMarkdownReport(analyzer, results)
md_report = md_reporter.generate_report()
# Сохраняем Markdown отчет
with open("README.md", "w", encoding="utf-8") as f:
f.write(md_report)
print("Markdown отчет сохранен: README.md")
# Генерируем DOCX отчет
docx_reporter = SocialNetworkDocxReport(analyzer, results)
docx_path = docx_reporter.generate_docx_report()
# Выводим основные результаты в консоль
print("\n" + "="*80)
print("РЕЗУЛЬТАТЫ АНАЛИЗА СОЦИАЛЬНОЙ СЕТИ")
print("="*80)
properties = results['network_properties']
print(f"\nОСНОВНЫЕ СВОЙСТВА СЕТИ:")
print("-" * 50)
print(f"Узлы: {properties['num_nodes']}")
print(f"Ребра: {properties['num_edges']}")
print(f"Плотность: {properties['density']:.4f}")
print(f"Средняя степень: {properties['average_degree']:.2f}")
print(f"Коэффициент кластеризации: {properties['average_clustering']:.4f}")
community_results = results['community_results']
print(f"\nАНАЛИЗ СООБЩЕСТВ:")
print("-" * 50)
print(f"Модулярность: {community_results['modularity']:.4f}")
communities = community_results['communities']
sorted_communities = sorted(communities.items(), key=lambda x: len(x[1]), reverse=True)
for i, (comm_id, nodes) in enumerate(sorted_communities[:2], 1):
print(f"Сообщество {i}: {len(nodes)} узлов")
pagerank_results = results['pagerank_results']
print(f"\nТОП-5 УЗЛОВ ПО PAGERANK:")
print("-" * 50)
for i, (node, score) in enumerate(pagerank_results['top_influential'][:5], 1):
print(f"{i}. Узел {node}: {score:.4f}")
print(f"\nВИЗУАЛИЗАЦИИ СОЗДАНЫ:")
print("-" * 50)
for name, path in results['visualizations'].items():
if os.path.exists(path):
print(f"✓ {name}: {path}")
print(f"\nОТЧЕТЫ СОХРАНЕНЫ:")
print("-" * 50)
print(f"✓ Markdown отчет: README.md")
if PYDOCX_AVAILABLE:
print(f"✓ DOCX отчет: {docx_path}")
return results
if __name__ == "__main__":
main()