201 lines
No EOL
7.5 KiB
Python
201 lines
No EOL
7.5 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import pandas as pd
|
|
import numpy as np
|
|
import networkx as nx
|
|
import matplotlib.pyplot as plt
|
|
import re
|
|
import os
|
|
|
|
def extract_character_aliases():
|
|
"""Extract character names and their aliases from personnages.org"""
|
|
characters = {}
|
|
current_character = None
|
|
|
|
try:
|
|
with open('personnages.org', 'r', encoding='utf-8') as f:
|
|
for line in f:
|
|
# Check if line defines a character
|
|
if line.startswith('**'):
|
|
current_character = line.strip('* \n').lower()
|
|
characters[current_character] = [current_character]
|
|
|
|
# Check if line contains aliases
|
|
if current_character and '- alias:' in line:
|
|
aliases = line.split('- alias:')[1].strip()
|
|
if aliases:
|
|
for alias in re.split(r'[,;]', aliases):
|
|
alias = alias.strip().lower()
|
|
if alias:
|
|
characters[current_character].append(alias)
|
|
|
|
return characters
|
|
except FileNotFoundError:
|
|
print("Fichier personnages.org non trouvé.")
|
|
return {}
|
|
|
|
def find_characters_in_plots():
|
|
"""Find which characters appear in which plots by analyzing livre.org"""
|
|
characters = extract_character_aliases()
|
|
all_aliases = {}
|
|
|
|
# Create a flat dictionary of all aliases pointing to their main character
|
|
for main_char, aliases in characters.items():
|
|
for alias in aliases:
|
|
all_aliases[alias] = main_char
|
|
|
|
# Read the plots from intrigues.csv
|
|
plots_df = pd.read_csv('intrigues.csv')
|
|
|
|
# Read the book content
|
|
try:
|
|
with open('livre.org', 'r', encoding='utf-8') as f:
|
|
book_content = f.read().lower()
|
|
except FileNotFoundError:
|
|
print("Fichier livre.org non trouvé.")
|
|
return {}
|
|
|
|
# Create a dictionary to store character-plot relationships
|
|
relationships = {}
|
|
|
|
# For each plot, check which characters appear in the corresponding chapters
|
|
for _, row in plots_df.iterrows():
|
|
plot_name = row['Intrigue'].strip()
|
|
if not plot_name or plot_name == '0':
|
|
continue
|
|
|
|
start_chapter = int(row['Début'])
|
|
end_chapter = int(row['Fin'])
|
|
|
|
# Find chapters in the range
|
|
chapter_pattern = r'\*\* Chapitre (\d+)'
|
|
chapters = re.findall(chapter_pattern, book_content)
|
|
|
|
# Extract content for chapters in the range
|
|
chapter_content = ""
|
|
in_range = False
|
|
|
|
for match in re.finditer(r'\*\* Chapitre (\d+)', book_content):
|
|
chapter_num = int(match.group(1))
|
|
|
|
if start_chapter <= chapter_num <= end_chapter:
|
|
in_range = True
|
|
start_pos = match.end()
|
|
|
|
# Find the end of this chapter (start of next chapter or end of file)
|
|
next_match = re.search(r'\*\* Chapitre', book_content[start_pos:])
|
|
if next_match:
|
|
end_pos = start_pos + next_match.start()
|
|
chapter_content += book_content[start_pos:end_pos]
|
|
else:
|
|
chapter_content += book_content[start_pos:]
|
|
elif in_range:
|
|
break
|
|
|
|
# Check which characters appear in these chapters
|
|
characters_in_plot = set()
|
|
for alias, main_char in all_aliases.items():
|
|
if alias in chapter_content:
|
|
characters_in_plot.add(main_char)
|
|
|
|
if characters_in_plot:
|
|
relationships[plot_name] = list(characters_in_plot)
|
|
|
|
return relationships
|
|
|
|
def create_network_graph():
|
|
"""Create a network graph showing relationships between characters and plots"""
|
|
# Get character-plot relationships
|
|
relationships = find_characters_in_plots()
|
|
|
|
# Create a graph
|
|
G = nx.Graph()
|
|
|
|
# Add nodes for plots (as squares)
|
|
for plot in relationships.keys():
|
|
G.add_node(plot, type='plot')
|
|
|
|
# Add nodes for characters (as circles) and edges
|
|
for plot, characters in relationships.items():
|
|
for character in characters:
|
|
G.add_node(character, type='character')
|
|
G.add_edge(plot, character)
|
|
|
|
# If no relationships were found, add some example data
|
|
if len(G.nodes()) == 0:
|
|
# Add plots from intrigues.csv
|
|
plots_df = pd.read_csv('intrigues.csv')
|
|
for _, row in plots_df.iterrows():
|
|
plot_name = row['Intrigue'].strip()
|
|
if plot_name and plot_name != '0':
|
|
G.add_node(plot_name, type='plot')
|
|
|
|
# Add characters from personnages.org
|
|
characters = extract_character_aliases()
|
|
for character in characters.keys():
|
|
G.add_node(character, type='character')
|
|
|
|
# Add some random connections
|
|
for plot in [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'plot']:
|
|
for character in [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'character']:
|
|
if np.random.random() > 0.7: # 30% chance of connection
|
|
G.add_edge(plot, character)
|
|
|
|
# Create figure
|
|
plt.figure(figsize=(12, 10))
|
|
|
|
# Define node positions using spring layout
|
|
pos = nx.spring_layout(G, k=0.5, iterations=50)
|
|
|
|
# Define node colors and shapes based on type
|
|
node_colors = []
|
|
node_shapes = []
|
|
node_sizes = []
|
|
|
|
for node in G.nodes():
|
|
if G.nodes[node]['type'] == 'plot':
|
|
node_colors.append('#3498db') # Blue for plots
|
|
node_shapes.append('s') # Square for plots
|
|
node_sizes.append(1000) # Larger size for plots
|
|
else:
|
|
node_colors.append('#e74c3c') # Red for characters
|
|
node_shapes.append('o') # Circle for characters
|
|
node_sizes.append(700) # Smaller size for characters
|
|
|
|
# Draw the network
|
|
character_nodes = [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'character']
|
|
plot_nodes = [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'plot']
|
|
|
|
# Draw plot nodes (squares)
|
|
nx.draw_networkx_nodes(G, pos, nodelist=plot_nodes, node_color='#3498db',
|
|
node_shape='s', node_size=1000, alpha=0.8)
|
|
|
|
# Draw character nodes (circles)
|
|
nx.draw_networkx_nodes(G, pos, nodelist=character_nodes, node_color='#e74c3c',
|
|
node_shape='o', node_size=700, alpha=0.8)
|
|
|
|
# Draw edges
|
|
nx.draw_networkx_edges(G, pos, width=2, alpha=0.5, edge_color='gray')
|
|
|
|
# Draw labels with custom font sizes
|
|
nx.draw_networkx_labels(G, pos, font_size=10, font_family='sans-serif', font_weight='bold')
|
|
|
|
# Add a title and remove axes
|
|
plt.title('Réseau des Personnages et Intrigues', fontsize=16)
|
|
plt.axis('off')
|
|
|
|
# Add a legend
|
|
plt.plot([0], [0], 's', color='#3498db', label='Intrigues', markersize=10)
|
|
plt.plot([0], [0], 'o', color='#e74c3c', label='Personnages', markersize=10)
|
|
plt.legend(loc='upper right')
|
|
|
|
# Save the figure
|
|
plt.tight_layout()
|
|
plt.savefig('reseau_personnages_intrigues.png', dpi=150, bbox_inches='tight')
|
|
plt.savefig('reseau_personnages_intrigues.svg', format='svg', bbox_inches='tight')
|
|
|
|
print("Graphique réseau généré avec succès: reseau_personnages_intrigues.png et reseau_personnages_intrigues.svg")
|
|
|
|
if __name__ == "__main__":
|
|
create_network_graph() |