Visualizzatore dello Spazio Narrativo Proppiano
Description
Questo software, denominato “Visualizzatore dello Spazio Narrativo Proppiano (VSNP)“, rappresenta un’implementazione computazionale (Versione 1.0.0) per l’analisi e la visualizzazione di narrazioni, specificamente fiabe, codificate secondo le 31 funzioni morfologiche di Vladimir Propp. Sviluppato nell’ambito della più ampia ricerca del Dr. Luigi Usai sulla Teoria del Tensore Narrativo, lo script utilizza Python e librerie scientifiche standard (NumPy, Matplotlib, Scikit-learn) per:
- Rappresentare le fiabe come vettori in uno spazio funzionale proppiano.
- Applicare tecniche di riduzione dimensionale (PCA e t-SNE) per proiettare questo spazio multidimensionale in rappresentazioni 2D, facilitando l’identificazione visiva di cluster e relazioni strutturali tra le narrazioni.
- Calcolare metriche di similarità (es. similarità del coseno) per quantificare la vicinanza strutturale tra le fiabe basata sulla loro composizione funzionale. Il VSNP è concepito come uno strumento esplorativo e analitico per ricercatori in narratologia computazionale, studi folklorici, e discipline umanistiche digitali, offrendo un passo concreto verso l’operazionalizzazione del concetto di Tensore Narrativo per l’analisi comparativa delle strutture narrative. Il codice include un dataset esemplificativo basato su fiabe europee note, ma è strutturato per essere estensibile a corpora più ampi e all’intero set di funzioni proppiane.
# Visualizzatore dello Spazio Narrativo Proppiano
# Autore: Dr. Luigi Usai (concettualizzazione) e Gemini (implementazione)
# Data: 25 Maggio 2025
# Descrizione: Questo script analizza un insieme di narrazioni (fiabe) codificate
# secondo le funzioni morfologiche di Vladimir Propp.
# Utilizza tecniche di riduzione dimensionale per visualizzare
# le fiabe in uno spazio 2D e calcola la loro similarità strutturale.
# Questo strumento è un passo verso l’operazionalizzazione del
# concetto di Tensore Narrativo.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.metrics.pairwise import cosine_similarity
import itertools # Importato per coerenza con il generatore, anche se non usato direttamente qui
# Definiamo le 31 funzioni di Propp (dal Suo generatore propp_varianti_generator_py_v1)
# Manteniamo solo i codici per la definizione della matrice, le descrizioni complete
# possono essere usate per etichette più ricche nei plot, se necessario.
FUNZIONI_PROPP_CODICI = [f”F{i+1}” for i in range(31)]
FUNZIONI_PROPP_DESCRIZIONI = {
“F1”: “Allontanamento”, “F2”: “Divieto”, “F3”: “Infrazione”,
“F4”: “Investigazione”, “F5”: “Delazione”, “F6”: “Tranello”,
“F7”: “Connivenza”, “F8”: “Danneggiamento/Mancanza”, “F9”: “Mediazione”,
“F10”: “Consenso dell’Eroe”, “F11”: “Partenza dell’Eroe”, “F12”: “Messa alla Prova”,
“F13”: “Reazione dell’Eroe”, “F14”: “Conseguimento Mezzo Magico”, “F15”: “Trasferimento”,
“F16”: “Lotta”, “F17”: “Marchiatura”, “F18”: “Vittoria”,
“F19”: “Rimozione Danno/Mancanza”, “F20”: “Ritorno dell’Eroe”, “F21”: “Persecuzione”,
“F22”: “Salvataggio”, “F23”: “Arrivo in Incognito”, “F24”: “Pretese Falso Eroe”,
“F25”: “Compito Difficile”, “F26”: “Adempimento Compito”, “F27”: “Riconoscimento”,
“F28”: “Smascheramento”, “F29”: “Trasfigurazione”, “F30”: “Punizione”,
“F31”: “Nozze/Ricompensa”
}
# Utilizziamo le funzioni selezionate nel Suo esempio tabellare per la dimostrazione.
# Questo è un subset per rendere l’esempio più gestibile.
# Idealmente, la matrice dovrebbe avere 31 colonne.
FUNZIONI_SUBSET_CODICI = [“F1”, “F2”, “F3”, “F8”, “F11”, “F14”, “F16”, “F18”, “F31”]
FUNZIONI_SUBSET_NOMI_SINTETICI = [
“Allont.”, “Divieto”, “Infr.”, “Mancanza”,
“Partenza”, “DonoMag.”, “Lotta”, “Vittoria”, “Ricomp.”
]
# Matrice esemplificativa di fiabe e funzioni di Propp (basata sulla Sua tabella)
# Ogni riga è una fiaba, ogni colonna una funzione del FUNZIONI_SUBSET_CODICI.
# 1 se la funzione è presente, 0 altrimenti.
NOMI_FIABE = [
“Cappuccetto Rosso”,
“Biancaneve”,
“Cenerentola”,
“Aladino”,
“Il Gatto con gli Stivali”,
“Hansel e Gretel (sempl.)”, # Fiaba aggiunta per arricchire l’esempio
“La Bella Addormentata (sempl.)” # Fiaba aggiunta
]
# Dati corrispondenti al subset di 9 funzioni:
# Allont., Divieto, Infr., Mancanza, Partenza, DonoMag., Lotta, Vittoria, Ricomp.
MATRICE_FIABE_FUNZIONI = np.array([
[1, 1, 1, 1, 1, 0, 1, 0, 0], # Cappuccetto Rosso
[1, 0, 0, 1, 1, 1, 1, 1, 1], # Biancaneve
[1, 0, 0, 1, 1, 1, 0, 1, 1], # Cenerentola
[0, 1, 1, 1, 1, 1, 1, 1, 1], # Aladino
[1, 0, 0, 1, 1, 1, 1, 1, 1], # Il Gatto con gli Stivali
[1, 0, 0, 1, 0, 1, 0, 1, 0], # Hansel e Gretel (ipotetico, semplificato)
[0, 1, 1, 1, 0, 0, 0, 1, 1] # La Bella Addormentata (ipotetico, semplificato)
])
# Verifica dimensioni
assert MATRICE_FIABE_FUNZIONI.shape[1] == len(FUNZIONI_SUBSET_CODICI), \
“Numero di colonne non corrisponde al numero di funzioni nel subset”
assert MATRICE_FIABE_FUNZIONI.shape[0] == len(NOMI_FIABE), \
“Numero di righe non corrisponde al numero di fiabe”
def visualizza_spazio_narrativo(matrice_dati, etichette_punti, etichette_funzioni, titolo=”Spazio Narrativo Proppiano (PCA)”):
“””
Applica PCA per ridurre la dimensionalità e visualizza i punti in 2D.
Args:
matrice_dati (np.array): Matrice (fiabe x funzioni).
etichette_punti (list): Lista dei nomi delle fiabe.
etichette_funzioni (list): Lista dei nomi delle funzioni (per i componenti PCA).
titolo (str): Titolo del grafico.
“””
if matrice_dati.shape[0] < 2 or matrice_dati.shape[1] < 2:
print(“Dati insufficienti per la riduzione dimensionale significativa.”)
return
# Riduzione dimensionale a 2 componenti principali con PCA
# PCA è utile per massimizzare la varianza spiegata e identificare le direzioni principali di variazione.
pca = PCA(n_components=2, random_state=42) # random_state per riproducibilità
dati_ridotti_pca = pca.fit_transform(matrice_dati)
# Visualizzazione con Matplotlib
plt.figure(figsize=(12, 8))
scatter = plt.scatter(dati_ridotti_pca[:, 0], dati_ridotti_pca[:, 1], s=100, alpha=0.7, cmap=’viridis’)
# Aggiunta etichette per ogni punto (fiaba)
for i, etichetta in enumerate(etichette_punti):
plt.annotate(etichetta, (dati_ridotti_pca[i, 0], dati_ridotti_pca[i, 1]),
textcoords=”offset points”, xytext=(0,10), ha=’center’, fontsize=9)
plt.title(titolo, fontsize=16)
plt.xlabel(f”Componente Principale 1 (Varianza Spiegata: {pca.explained_variance_ratio_[0]:.2f})”, fontsize=12)
plt.ylabel(f”Componente Principale 2 (Varianza Spiegata: {pca.explained_variance_ratio_[1]:.2f})”, fontsize=12)
plt.grid(True, linestyle=’–‘, alpha=0.7)
# Interpretazione dei componenti principali (loadings)
# I loadings indicano quanto ciascuna funzione originale contribuisce a ciascun componente principale.
# Questo aiuta a interpretare il significato degli assi del grafico.
print(“\n— Interpretazione Componenti Principali (Loadings PCA) —“)
for i in range(pca.components_.shape[0]): # Per ogni componente principale
print(f”\nComponente Principale {i+1}:”)
# Associa i loadings alle funzioni originali
loadings_componente = pca.components_[i]
funzioni_e_loadings = sorted(zip(etichette_funzioni, loadings_componente), key=lambda x: abs(x[1]), reverse=True)
for funzione, loading in funzioni_e_loadings:
print(f” – {funzione}: {loading:.3f}”)
print(f”Varianza spiegata da questo componente: {pca.explained_variance_ratio_[i]:.3f}”)
plt.tight_layout()
plt.show()
# Alternativa con t-SNE per visualizzazione non lineare (utile per scoprire cluster)
# t-SNE è ottimo per visualizzare la struttura locale e i cluster,
# ma le distanze nello spazio t-SNE non sono direttamente interpretabili come in PCA.
# n_samples deve essere >= n_components per PCA, e per t-SNE è meglio avere più campioni.
if matrice_dati.shape[0] >= 3 : # t-SNE può essere instabile con pochi campioni
tsne = TSNE(n_components=2, random_state=42, perplexity=min(30, matrice_dati.shape[0]-1), n_iter=1000)
dati_ridotti_tsne = tsne.fit_transform(matrice_dati)
plt.figure(figsize=(12, 8))
plt.scatter(dati_ridotti_tsne[:, 0], dati_ridotti_tsne[:, 1], s=100, alpha=0.7, cmap=’viridis’)
for i, etichetta in enumerate(etichette_punti):
plt.annotate(etichetta, (dati_ridotti_tsne[i, 0], dati_ridotti_tsne[i, 1]),
textcoords=”offset points”, xytext=(0,10), ha=’center’, fontsize=9)
plt.title(“Spazio Narrativo Proppiano (t-SNE)”, fontsize=16)
plt.xlabel(“Dimensione t-SNE 1”, fontsize=12)
plt.ylabel(“Dimensione t-SNE 2”, fontsize=12)
plt.grid(True, linestyle=’–‘, alpha=0.7)
plt.tight_layout()
plt.show()
else:
print(“\nSkipping t-SNE visualization due to insufficient samples.”)
def calcola_similarita_coseno(matrice_dati, etichette_punti):
“””
Calcola e stampa la matrice di similarità del coseno tra i punti.
La similarità del coseno misura il coseno dell’angolo tra due vettori,
indicando la loro somiglianza direzionale (indipendentemente dalla magnitudine).
Valori vicini a 1 indicano alta similarità, vicini a 0 bassa similarità,
vicini a -1 (improbabile con vettori binari non negativi) dissimilarità.
“””
if matrice_dati.shape[0] < 2:
print(“Similarità del coseno richiede almeno due punti dati.”)
return
mat_similarita = cosine_similarity(matrice_dati)
print(“\n— Matrice di Similarità del Coseno tra le Fiabe —“)
# Stampa intestazione
riga_intestazione = “{:<25}”.format(“”)
for etichetta in etichette_punti:
riga_intestazione += “{:<25}”.format(etichetta)
print(riga_intestazione)
print(“-” * len(riga_intestazione))
# Stampa righe della matrice
for i, etichetta_riga in enumerate(etichette_punti):
riga_dati = “{:<25}”.format(etichetta_riga)
for j in range(mat_similarita.shape[1]):
riga_dati += “{:<25.3f}”.format(mat_similarita[i, j])
print(riga_dati)
return mat_similarita
# — Esecuzione Principale —
if __name__ == “__main__”:
print(“— Visualizzatore dello Spazio Narrativo Proppiano —“)
print(f”Numero di fiabe analizzate: {MATRICE_FIABE_FUNZIONI.shape[0]}”)
print(f”Numero di funzioni di Propp considerate (subset): {MATRICE_FIABE_FUNZIONI.shape[1]}”)
# 1. Visualizza lo spazio narrativo
visualizza_spazio_narrativo(MATRICE_FIABE_FUNZIONI, NOMI_FIABE, FUNZIONI_SUBSET_NOMI_SINTETICI)
# 2. Calcola e stampa la similarità del coseno
mat_similarita = calcola_similarita_coseno(MATRICE_FIABE_FUNZIONI, NOMI_FIABE)
# Esempio di interpretazione della similarità:
if mat_similarita is not None and mat_similarita.shape == (len(NOMI_FIABE), len(NOMI_FIABE)):
print(“\n— Esempio di Interpretazione Similarità —“)
idx_biancaneve = NOMI_FIABE.index(“Biancaneve”)
idx_cenerentola = NOMI_FIABE.index(“Cenerentola”)
similarita_bc = mat_similarita[idx_biancaneve, idx_cenerentola]
print(f”Similarità tra ‘Biancaneve’ e ‘Cenerentola’: {similarita_bc:.3f}”)
idx_cappuccetto = NOMI_FIABE.index(“Cappuccetto Rosso”)
similarita_cb = mat_similarita[idx_cappuccetto, idx_biancaneve]
print(f”Similarità tra ‘Cappuccetto Rosso’ e ‘Biancaneve’: {similarita_cb:.3f}”)
print(“\n— Fine Analisi —“)