Dune: una rete nascosta.  In questo articolo, con Patrik Szigeti… |  di Milan Janosov |  Marzo 2024

 | Intelligenza-Artificiale

9 minuti di lettura

3 ore fa

Dopo il successo di Dune sia al botteghino che di critica nel 2021, Dune: Parte seconda è stato uno dei film più attesi del 2024 e non ha deluso. Sulla buona strada per guadagnare di più e con valutazioni più alte sia su Rotten Tomatoes che su iMDB rispetto al suo prequel al momento della stesura di questo articolo, con il suo panorama politico in continua evoluzione, Dune è il franchise perfetto in cui immergersi attraverso la scienza delle reti. In questo breve pezzo, abbiamo mirato a esplorare le connessioni tra le diverse Case e le persone dell'Impremium sulla base dei primi tre libri di Frank Herbert: Dune (1965), Dune Messiah (1969) e Children of Dune (1976).

Nella prima parte di questo articolo, presentiamo un approccio basato su Python per raccogliere i dati del profilo dei personaggi dal file Duna Wiki e trasforma quei profili in un accattivante grafico di rete. Poi, nella seconda sezione, piuttosto ricca di spoiler, ci immergiamo nelle profondità della rete ed estraiamo tutte le storie che ha da dire sulla prima trilogia di Dune.

Tutte le immagini sono state create dagli autori.

Innanzitutto, utilizziamo Python per raccogliere l'elenco completo dei personaggi di Dune. Quindi, scarichiamo i loro profili biografici dal sito wiki dei fan di ciascun personaggio e contiamo il numero di volte in cui la storia di ciascun personaggio menziona la storia di qualsiasi altro personaggio, presupponendo che queste menzioni codifichino varie interazioni tra due coppie di personaggi qualsiasi. Quindi utilizzeremo la scienza delle reti per trasformare queste relazioni in un grafico complesso.

1.1 Raccolta dell'elenco dei personaggi

Prima di tutto, abbiamo raccolto l'elenco di tutti i personaggi rilevanti dal sito wiki dei fan di Dune. Vale a dire, abbiamo utilizzato urllib e bs4 per estrarre i nomi e gli ID wiki dei fan di ciascun personaggio menzionato e abbiamo una propria pagina wiki codificata dal loro ID. Abbiamo fatto questo per i primi tre libri: Dune, Dune Messiah e Childen of Dune. Questi tre libri coprono l'ascesa della casata degli Atreides.

Fonti:

Innanzitutto, scarica l'HTML del sito con l'elenco dei caratteri:

dune_meta = {
'Dune': {'url': 'https://dune.fandom.com/wiki/Dune_(novel)'},
'Dune Messiah': {'url': 'https://dune.fandom.com/wiki/Dune_Messiah'},
'Children of Dune': {'url': 'https://dune.fandom.com/wiki/Children_of_Dune_(novel)'}
}

for book, url in dune_meta.items():
sauce = urlopen(url('url')).read()
soup = bs.BeautifulSoup(sauce,'lxml')
dune_meta(book)('chars') = soup.find_all('li')

Un piccolo aiuto manuale per mettere a punto il nome e l'ID del personaggio:

dune_meta('Dune')('char_start') = 'Abulurd'
dune_meta('Dune')('char_end') = 'Arrakis'
dune_meta('Dune Messiah')('char_start') = 'Abumojandis'
dune_meta('Dune Messiah')('char_end') = 'Arrakis'
dune_meta('Children of Dune')('char_start') = '2018 Edition'
dune_meta('Children of Dune')('char_end') = 'Categories'

Quindi, abbiamo estratto tutti i nomi potenzialmente rilevanti e gli URL del profilo corrispondente. Qui abbiamo controllato manualmente da quale tag iniziano i nomi (ad esempio in contrapposizione al contorno del sito con l'elenco dei caratteri). Inoltre, abbiamo deciso di eliminare i caratteri contrassegnati da “XD” e “DE” corrispondenti alla serie estesa, così come i personaggi che erano “Solo menzionati” in un determinato libro:

for k, v in dune_meta.items():
names_urls = {}
keep_row = False
print(f'----- {k} -----')
for char in v('chars'):
if v('char_start') in char.text.strip():
keep_row = True
if v('char_end') in char.text.strip():
keep_row = False
if keep_row and 'Video' not in char.text:
try:
url = 'https://dune.fandom.com' + str(char).split('href="')(1).split('" title')(0)
name = char.text.strip()
if 'wiki' in url and 'XD' not in name and 'DE' not in name and '(Mentioned only)' not in name:
names_urls(name) = url
print(name)
except:
pass
dune_meta(k)('names_urls') = names_urls

Questo blocco di codice restituisce quindi l'elenco di caratteri, come ad esempio:

Esempio sui nomi estratti.

Infine, controlliamo il numero di caratteri che abbiamo raccolto e salviamo gli URL e gli identificatori del loro profilo per il sottocapitolo successivo.

dune_names_urls = {}
for k, v in dune_meta.items():
dune_names_urls.update(dune_meta(k)('names_urls'))

names_ids = {n : u.split('/')(-1) for n, u in dune_names_urls.items()}

print(len(dune_names_urls))

Gli output di questa cella, che mostrano 119 caratteri con URL del profilo:

1.2 Scaricare i profili dei personaggi

Il nostro obiettivo è mappare la rete sociale dei personaggi di Dune, il che significa che dobbiamo capire chi ha interagito con chi. Nel sottocapitolo precedente abbiamo ottenuto l'elenco di tutti i “chi” e ora avremo le informazioni sulle loro storie personali. Recupereremo quelle storie utilizzando semplici tecniche di web scraping e quindi salveremo localmente la fonte del sito personale di ciascun personaggio in un file separato:

# output folder for the profile htmls
folderout = 'fandom_profiles'
if not os.path.exists(folderout):
os.makedirs(folderout)

# crawl and save the profile htmls
for ind, (name, url) in enumerate(dune_names_urls.items()):
if not os.path.exists(folderout + '/' + name + '.html'):
try:
fout = open(folderout + '/' + name + '.html', "w")
fout.write(str(urlopen(url).read()))
except:
pass

Il risultato dell'esecuzione di questo codice sarà una cartella nella nostra directory locale con tutti i profili del sito wiki dei fan appartenenti a ogni singolo personaggio selezionato.

1.3 Costruire la rete

Per costruire la rete tra i personaggi, contiamo il numero di volte in cui la fonte del sito wiki di ciascun personaggio fa riferimento all'identificatore wiki di qualsiasi altro personaggio utilizzando la seguente logica. Qui, costruiamo l'elenco dei bordi: l'elenco delle connessioni che contiene sia il nodo di origine che quello di destinazione (carattere) delle connessioni, nonché il peso (frequenza di co-riferimento) tra le pagine dei due personaggi.

# extract the name mentions from the html sources
# and build the list of edges in a dictionary
edges = {}

for fn in (fn for fn in os.listdir(folderout) if '.html' in fn):

name = fn.split('.html')(0)

with open(folderout + '/' + fn) as myfile:
text = myfile.read()
soup = bs.BeautifulSoup(text,'lxml')
text = ' '.join((str(a) for a in soup.find_all('p')(2:)))
soup = bs.BeautifulSoup(text,'lxml')

for n, i in names_ids.items():

w = text.split('Image Gallery')(0).count('/' + i)
if w>0:
edge = '\t'.join(sorted((name, n)))
if edge not in edges:
edges(edge) = w
else:
edges(edge) += w

len(edges)

Una volta eseguito questo blocco di codice, otterremo il risultato di 307 come numero di spigoli che collegano i 119 caratteri di Dune.

Successivamente, utilizziamo la libreria di analisi dei grafici NetworkX per trasformare l'elenco dei bordi in un oggetto grafico e restituire il numero di nodi e bordi del grafico:

#  create the networkx graph from the dict of edges
import networkx as nx
G = nx.Graph()
for e, w in edges.items():
if w>0:
e1, e2 = e.split('\t')
G.add_edge(e1, e2, weight=w)

G.remove_edges_from(nx.selfloop_edges(G))

print('Number of nodes: ', G.number_of_nodes())
print('Number of edges: ', G.number_of_edges())

Il risultato di questo blocco di codice:

Il numero di nodi è solo 72, il che significa che 47 caratteri non sono stati collegati a nessun membro centrale nei loro profili wiki – probabilmente piuttosto brevi. Inoltre, vediamo una diminuzione di quattro nel numero di bordi perché sono stati rimossi anche alcuni self-loop.

Diamo una breve panoramica della rete utilizzando il plotter Matplotlib integrato:

# take a very brief look at the network
import matplotlib.pyplot as plt
f, ax = plt.subplots(1,1,figsize=(15,15))
nx.draw(G, ax=ax, with_labels=True)

L'output di questa cella:

Visualizzazione iniziale in rete dei personaggi di Dune.

Sebbene questo oggetto visivo mostri già una struttura di rete, abbiamo esportato il grafico in un file Gephi utilizzando la seguente riga di codice e progettato la rete allegata nella figura seguente (la procedura per tali oggetti visivi di rete sarà l'argomento di un prossimo tutorial articolo):

nx.write_gexf(G, 'dune_network.gexf')

La rete completa di Dune:

Fonte: towardsdatascience.com

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *