Skip to content

Instantly share code, notes, and snippets.

@LinguList
Last active August 3, 2023 13:47
Show Gist options
  • Save LinguList/159392d371a7015a307846f76f1b6c07 to your computer and use it in GitHub Desktop.
Save LinguList/159392d371a7015a307846f76f1b6c07 to your computer and use it in GitHub Desktop.
Constructing rhyme networks

Constructing and analyzing rhyme networks (accompanying data and code)

This little repository offers data and code accompanying the 5th and 6th blog post in the series From rhymes to networks.

The data is taken from the AntRhyme database. For copy-right reasons, all non-rhyming words have been replaced by dummy symbols.

Since the dataset is still being curated for the time being, I would ask you to contact me before using the data, since I have annotated them myself. In any case, you should cite both the blog post which will appear in August, and also the AntRhyme database as well as the RhyAnt annotation tool:

List, Johann-Mattis (2020): AntRhyme: Annothated Rhyme Database. Version 0.1. Jena: Max Planck Institute for the Science of Human History. https://digling.org/rhyant/

List, Johann-Mattis (2020): RhyAnT: A tool for interactive rhyme annotation. Version 0.1. Jena: Max Planck Institue for the Science of Human History. URL: https://digling.org/calc/rhyant/

Dependencies for Python code

  • networkx
  • tabulate
  • lingpy
  • python-igraph

Running the code

To run the code for the fifth blogpost, just type:

$ python code-1.py

To inspect the networks, just take the file edges.tsv and load it into Cytoscape.

Output (visible) is:

|:--------------|------:|
| components    |   996 |
| authors       |    72 |
| poems         |   336 |
| stanzas       |  1544 |
| lines         |  8340 |
| rhyme words   |  3950 |
| words rhyming |  5431 |
| words total   | 49797 |

To run the code for the sixth blgopost, just type:

$ python code-2.py

The results are stored in diredges.tsv and dirnodes.tsv and can accordingly be loaded into Cytoscape.

Output is (this can differ, due to the igraph algorithms, since no seed was used):

# 1 Basic statistics
|:--------------|------:|
| components    |   840 |
| authors       |    72 |
| poems         |   336 |
| stanzas       |  1544 |
| lines         |  8340 |
| rhyme words   |  3104 |
| words rhyming |  7707 |
| words total   | 49797 |
| communities   |   931 |

# 2 Promiscuous Rhymes
| rhyme word   |   links |   occurrences |
|:-------------|--------:|--------------:|
| singen       |       6 |             9 |
| wind         |       6 |            12 |
| rein         |       6 |            17 |
| frei         |       6 |            18 |
| haus         |       7 |            45 |
| nacht        |       7 |            40 |
| an           |       7 |            29 |
| auch         |       7 |            16 |
| zieht        |       7 |            11 |
| schon        |       7 |            12 |
| welt         |       7 |            32 |
| sagen        |       8 |            19 |
| leben        |       8 |            39 |
| schein       |       8 |            26 |
| mehr         |       8 |            25 |
| nicht        |       8 |            32 |
| zeit         |       8 |            36 |
| ein          |       9 |            34 |
| bei          |       9 |            36 |
| sein         |      14 |            87 |

# 3 Frequent Edges
| rhyme word A   | rhyme word B   |   occurrences |
|:---------------|:---------------|--------------:|
| sein           | lein           |            10 |
| aus            | haus           |            10 |
| haus           | aus            |             9 |
| triebe         | liebe          |             9 |
| leben          | geben          |             9 |
| geben          | leben          |             9 |
| zeit           | keit           |             9 |
| nein           | sein           |             8 |
| wieder         | lieder         |             7 |
| nur            | tur            |             7 |
| nommen         | kommen         |             7 |
| nacht          | wacht          |             6 |
| glück          | rück           |             6 |
| licht          | nicht          |             6 |
| lein           | sein           |             6 |
| lauf           | auf            |             6 |
| klein          | sein           |             6 |
| schmerz        | herz           |             6 |
| raus           | haus           |             5 |
| nacht          | macht          |             5 |
import sqlite3
import networkx as nx
from collections import defaultdict
from itertools import combinations
from tabulate import tabulate
def strip_chars(word):
return ''.join([w for w in word.lower() if w not in
'’;,«».——-./!?():*–“'+"'"])
def parse_line(line, rhymed, poem, stanza):
for word in line.split():
if '[' in word:
rhymes = []
in_rhyme = False
rhyme_words = ['']
for char in word:
if char == '[':
in_rhyme = True
rhymes += ['']
elif char == ']':
in_rhyme = False
elif char == '_':
rhyme_words += ['']
else:
if in_rhyme:
rhymes[-1] += char
else:
rhyme_words[-1] += char
for rhyme, rword in zip(rhymes, rhyme_words):
if rhyme != '+':
rhymed[rhyme] += [
(strip_chars(rword), word, poem, stanza)]
G = nx.Graph()
numpo, numsta, numli, wota = 0, 0, 0, 0
authors = defaultdict(int)
with open('rhyme-data.txt') as f:
rdata = f.read().split('\n\n\n')
for idx, poem in enumerate(rdata):
numpo += 1
stanzas = poem.split('\n\n')
authors[stanzas[0].split('\n')[1][9:]] += 1
for sid, stanza in enumerate(stanzas[1:]):
numsta += 1
rhymes = defaultdict(list)
for line in stanza.split('\n'):
if line.strip() and not line.startswith('#'):
parse_line(line, rhymes, idx, sid)
wota += len(line.split())
numli += 1
for key, vals in rhymes.items():
for (rA, wA, pA, sA), (rB, wB, pB, sB) in combinations(vals, r=2):
if rA != rB:
try:
G[rA][rB]['weight'] += 1
except:
try:
G.node[rA]['occ'] += 1
G.node[rA]['poem'] += [(pA, sA)]
G.node[rA]['words'] += [wA]
except:
G.add_node(rA, occ=1, poem=[(pA, pB)], words=[wA])
try:
G.node[rB]['occ'] += 1
G.node[rB]['occ'] += [(pB, sB)]
G.node[rB]['words'] += [wB]
except:
G.add_node(rB, occ=1, poem=[(pB, sB)], words=[wB])
G.add_edge(rA, rB, weight=1)
with open('edges.tsv', 'w') as f:
f.write('nodeA\tnodeB\tweight\n')
for nA, nB, data in G.edges(data=True):
f.write(nA+'\t'+nB+'\t'+str(data['weight'])+'\n')
with open('nodes.tsv', 'w') as f:
f.write('node\tfrequency\toccurrences\twords\n')
for node, data in G.nodes(data=True):
f.write(node+'\t'+str(data['occ'])+'\t'+'/'.join(
['{0}:{1}'.format(a, b) for a, b in data['poem']])+\
'\t'+'//'.join(data['words'])+'\n')
comps = list(nx.connected_components(G))
print(len(comps), len(G), numpo, numsta)
numw = sum([data['occ'] for _, data in G.nodes(data=True)])
table = [
['components', len(comps)],
['authors', len(authors)],
['poems', numpo],
['stanzas', numsta],
['lines', numli],
['rhyme words', len(G)],
['words rhyming', numw],
['words total', wota]
]
print(tabulate(table, tablefmt='pipe'))
import networkx as nx
from collections import defaultdict
from itertools import combinations
from tabulate import tabulate
from lingpy.convert.graph import networkx2igraph
def strip_chars(word):
return ''.join([w for w in word.lower() if w not in
'_’;,«».——-./!?():*–“"'+"'"])
def parse_line(line, poem, stanza):
rhymed = []
for word in line.split():
if '[' in word:
rhymes = []
in_rhyme = False
rhyme_words = []
for char in word:
if char == '[':
in_rhyme = True
rhymes += ['']
rhyme_words += ['']
elif char == ']':
in_rhyme = False
else:
if in_rhyme:
rhymes[-1] += char
else:
if not rhyme_words:
pass
else:
rhyme_words[-1] += char
nrhymes, nrhyme_words = [], []
for rhyme, rword in zip(rhymes, rhyme_words):
if rhyme != '+':
nrhymes += [rhyme]
nrhyme_words += [rword]
rhymes = '-'.join(nrhymes)
rhyme_words = '_'.join([strip_chars(r) for r in nrhyme_words])
rhymed += [
(rhymes, rhyme_words, word, poem, stanza)]
return rhymed
G = nx.DiGraph()
numpo, numsta, numli, wota = 0, 0, 0, 0
authors = defaultdict(int)
with open('rhyme-data.txt') as f:
rdata = f.read().split('\n\n\n')
for idx, poem in enumerate(rdata):
numpo += 1
stanzas = poem.split('\n\n')
authors[stanzas[0].split('\n')[1][9:]] += 1
for sid, stanza in enumerate(stanzas[1:]):
numsta += 1
# iterate line by line and check before
queue = []
for line in stanza.split('\n'):
if line.strip() and not line.startswith('#'):
queue += parse_line(line, idx, sid)
wota += len(line.split())
numli += 1
while queue:
rA, wA, phA, pA, sA = queue.pop(0)
try:
G.node[wA]['occ'] += 1
G.node[wA]['poem'] += [pA]
G.node[wA]['words'] += [phA]
except:
G.add_node(wA, occ=1, poem=[pA], words=[phA])
for i, (rB, wB, phB, pB, sB) in enumerate(queue):
if rA == rB and wA != wB:
try:
G[wA][wB]['weight'] += 1
G[wA][wB]['poems'] += [pA]
except:
G.add_edge(wA, wB, weight=1, poems=[pA])
break
GI = networkx2igraph(G)
comms = GI.community_infomap(edge_weights='weight')
for i, comm in enumerate(comms):
for node in comm:
G.node[GI.vs[node]['Name']]['community'] = i+1
with open('diredges.tsv', 'w') as f:
f.write('nodeA\tnodeB\tweight\tpoems\n')
for nA, nB, data in G.edges(data=True):
f.write(nA+'\t'+nB+'\t'+str(data['weight'])+'\t'+str(len(set(data['poems'])))+'\n')
with open('dirnodes.tsv', 'w') as f:
f.write('node\tfrequency\toccurrences\twords\tcommunity\n')
for node, data in G.nodes(data=True):
f.write(node+'\t'+str(data['occ'])+'\t'+'/'.join(
['{0}'.format(a) for a in data['poem']])+\
'\t'+'//'.join(data['words'])+'\t'+str(data['community'])+'\n')
comps = list(nx.connected_components(G.to_undirected()))
numw = sum([data['occ'] for _, data in G.nodes(data=True)])
table = [
['components', len(comps)],
['authors', len(authors)],
['poems', numpo],
['stanzas', numsta],
['lines', numli],
['rhyme words', len(G)],
['words rhyming', numw],
['words total', wota],
['communities', len(comms)]
]
print('# 1 Basic statistics')
print(tabulate(table, tablefmt='pipe'))
print('')
print('# 2 Promiscuous Rhymes')
table = [[node, len(G[node]), data['occ']] for node, data in
sorted(G.nodes(data=True), key=lambda x: len(G[x[0]]))[-20:]]
print(tabulate(table, headers=['rhyme word', 'links', 'occurrences'],
tablefmt='pipe'))
print('')
print('# 3 Frequent Edges')
table = [[nA, nB, len(set(data['poems']))] for nA, nB, data in
sorted(G.edges(data=True), key=lambda x: len(set(x[2]['poems'])),
reverse=True)[:20]]
print(tabulate(table, headers=['rhyme word A', 'rhyme word B', 'occurrences'],
tablefmt='pipe'))pprint(tabulate(table, headers=['rhyme word', 'links', 'occurrences'],
tablefmt='pipe'))
rint(tabulate(table, headers=['rhyme word', 'links', 'occurrences'],
tablefmt='pipe'))
nodeA nodeB weight poems
traut laut 1 1
traut schaut 1 1
laut braut 2 2
laut auf 1 1
laut haupt 1 1
laut haut 1 1
laut raus 2 1
schlagen tagen 1 1
schlagen zagen 1 1
schlagen tragen 1 1
tagen sagen 3 3
tagen plagen 1 1
tagen tragen 1 1
tagen klagen 1 1
sagen tragen 2 2
sagen fragen 1 1
sagen plagen 1 1
sagen klagen 2 2
sagen jagen 1 1
sagen wagen 1 1
sagen raden 1 1
sagen tagen 1 1
wo so 2 2
wo froh 1 1
so klo 1 1
so froh 1 1
treiben scheiben 1 1
treiben bleiben 3 3
scheiben bleiben 1 1
bleiben scheiben 1 1
leiten zeiten 2 2
zeiten schreiten 2 2
zeiten keiten 1 1
droß schloß 1 1
schloß schoß 1 1
schloß los 1 1
schloß groß 1 1
schreiten weiten 1 1
schreiten streiten 1 1
wissen kissen 2 2
wissen missen 1 1
wissen schissen 1 1
kissen rissen 1 1
kissen missen 1 1
haus raus 8 3
haus aus 9 9
haus schaust 1 1
haus naus 4 4
haus maus 1 1
haus strauß 1 1
haus graus 1 1
raus haus 9 5
rissen wissen 1 1
sinnen winnen 1 1
sinnen drinnen 1 1
sinnen rinnen 2 2
sinnt kind 2 2
sinnt ginnt 1 1
kind schwind 1 1
kind sind 2 2
kind lind 1 1
kind wind 2 2
spinnen minnen 1 1
sind lind 1 1
sind find 1 1
sind kind 2 2
sind schwind 1 1
sind wind 1 1
sind drin 1 1
triebe liebe 9 9
liebe bliebe 2 2
liebe triebe 1 1
eben schweben 1 1
schweben leben 4 4
schweben reden 1 1
schweben weben 1 1
erwacht nacht 1 1
nacht macht 5 5
nacht sacht 2 2
nacht wacht 6 6
nacht bracht 4 4
nacht pracht 1 1
nacht lacht 2 2
nacht dacht 2 2
leben heben 2 2
leben weben 2 2
leben streben 1 1
leben geben 9 9
leben beben 1 1
leben reben 1 1
leben neben 1 1
leben schweben 2 2
alles talles 1 1
ringsum stumm 1 1
stumm drum 1 1
stumm rum 1 1
stumm um 1 1
sterne gerne 1 1
sterne ferne 3 3
gerne sterne 1 1
gerne kerne 1 1
groß los 4 4
lieben schrieben 1 1
schrieben trieben 1 1
gann an 2 2
an tan 2 2
an kommen 1 1
an ran 3 1
an kann 4 4
an bahn 2 2
an dann 1 1
an mann 1 1
diese wiese 1 1
wiese diese 1 1
legt wegt 1 1
bange gange 1 1
bange lange 1 1
gange wange 1 1
nimmt stimmt 1 1
stimmt sind 1 1
laube glaube 1 1
naht pfad 1 1
stuhle spule 1 1
stuhle schule 1 1
schooß los 1 1
kühlen stühlen 1 1
vasen lasen 1 1
pelle schwelle 1 1
schwelle welle 2 2
schwelle nelle 1 1
schwelle stelle 1 1
deutet läutet 1 1
deutet reitet 1 1
tag schlag 4 4
tag mag 4 4
tag lag 2 2
tag grad 1 1
falten halten 1 1
halten stalten 1 1
halten walten 1 1
halten spalten 1 1
schein sein 5 4
schein lein 4 4
schein rein 1 1
schein ein 2 2
schein seyn 1 1
schein pein 2 2
schein stein 1 1
schein nein 2 2
sein streun 1 1
sein nein 3 3
sein hain 1 1
sein lein 16 10
sein wein 1 1
sein ein 4 4
sein frein 1 1
sein scheint 3 1
sein stein 1 1
sein schein 3 3
sein dein 1 1
sein klein 1 1
sein allein 1 1
sein reihn 1 1
scherben sterben 1 1
scherben werden 1 1
sterben erben 1 1
sterben scherben 1 1
weit keit 1 1
weit leid 1 1
weit trogen 1 1
weit schreit 1 1
keit freit 1 1
keit neid 1 1
keit zeit 4 4
keit heit 1 1
keit weiht 1 1
müßtest wüßtest 1 1
wüßtest küßtest 1 1
gaßen straßen 1 1
straßen saßen 1 1
straßen rasen 1 1
schwiegen stiegen 1 1
stiegen schmiegen 1 1
schmiegen wiegen 1 1
mehr her 1 1
mehr leer 3 3
mehr schwer 1 1
mehr sehr 1 1
mehr wehr 1 1
mehr wär 6 1
mehr hör 1 1
mehr er 1 1
her wär 1 1
her meer 4 4
her mehr 5 5
her er 2 2
her schwer 1 1
ginge dinge 1 1
gegen legen 1 1
legen wegen 2 2
legen beten 1 1
kleid keit 1 1
kleid streut 1 1
munde stunde 1 1
stunde munde 1 1
stunde runde 3 3
stunde grunde 1 1
stunde sunde 1 1
hand land 5 5
hand fand 2 2
hand wand 1 1
hand mant 1 1
land hand 2 2
land wand 3 3
land strand 1 1
land fand 1 1
weise leise 5 5
weise reise 1 1
weise speise 1 1
leise weise 3 3
leise kreise 2 2
bahn an 3 3
bahn kahn 1 1
litten schritten 1 1
brach nach 1 1
nach brach 1 1
nach schwach 1 1
nach bach 3 2
rücken brücken 1 1
rücken schmücken 1 1
zeihn sein 1 1
geben beben 1 1
geben leben 10 9
geben schweben 1 1
geben weben 1 1
beben leben 2 2
beben heben 2 2
glück rück 6 6
glück blick 1 1
glück zurück 1 1
rück glück 5 5
rück stück 1 1
rück schritt 2 1
schmalen dralen 1 1
dralen malen 1 1
mühn blühn 1 1
mühn hin 1 1
blühn grün 1 1
wegen regen 3 3
wegen legen 1 1
wegen gegen 1 1
mund grund 1 1
mund wund 1 1
mund sund 1 1
mund kund 1 1
mund bunt 1 1
grund rum 1 1
grund kund 1 1
regen gegen 3 3
regen segen 3 3
kuß muß 1 1
muß nuß 1 1
lier dir 1 1
dir hier 1 1
dir mir 3 3
dir bier 1 1
dir ihr 1 1
ohne krone 1 1
und mund 1 1
und grund 2 2
lider glieder 1 1
lider wieder 1 1
glieder lieder 1 1
glieder wieder 1 1
schließ nis 1 1
lasse masse 1 1
glänzt grenzt 1 1
licht spricht 3 3
licht nicht 6 6
licht wärmte 1 1
licht sicht 1 1
licht bricht 1 1
licht zicht 1 1
spricht sicht 1 1
zwar tar 1 1
sicht dicht 1 1
sicht sich 1 1
sicht pflicht 1 1
sicht nicht 4 4
dicht licht 1 1
haar wahr 2 2
haar war 2 2
haar ja 1 1
wahr haar 1 1
wahr da 1 1
wahr klar 1 1
nicht sicht 3 3
nicht licht 2 2
nicht nichts 1 1
nicht stich 1 1
nicht ich 1 1
nicht spricht 1 1
nicht pflicht 1 1
nicht dicht 1 1
funden stunden 1 1
funden bunden 3 3
funden wunden 1 1
stunden schwunden 2 2
stunden pfunden 1 1
liebt trübt 1 1
liebt schiebt 1 1
schieden frieden 4 3
frieden schieden 2 2
frieden süden 1 1
hin ziehn 2 2
hin bin 3 3
hin sinn 5 5
hin ihn 1 1
hin queen 1 1
blieben drüben 1 1
blieben trieben 2 2
drüben lieben 1 1
weint reint 1 1
weint scheint 1 1
gästen festen 1 1
festen besten 2 2
festen ästen 1 1
gefahr haar 1 1
pufft duft 1 1
tragen wagen 1 1
tragen schlagen 1 1
tragen sagen 1 1
tragen fragen 1 1
saß_ich gaß_ich 1 1
strahl tal 1 1
strahl qual 3 2
strahl mal 3 2
tal strahl 3 3
tal qual 2 2
tal leuten 1 1
wälder felder 3 3
felder wälder 3 3
glanz kranz 3 3
glanz ganz 1 1
glanz tanz 2 2
kranz tanz 1 1
kranz schanzt 1 1
kranz glanz 1 1
laubte haupte 1 1
haupte raubte 1 1
au blau 1 1
au tau 2 2
au grau 1 1
blau frau 1 1
blau grau 1 1
blau nau 1 1
winkte blinkte 1 1
süß dies 2 2
dies stieß 1 1
dies hieß 1 1
hände fände 1 1
rauscht lauscht 1 1
rauscht tauscht 1 1
rauscht aus 1 1
lauscht rauscht 1 1
wieder glieder 1 1
wieder lieder 7 7
wieder nieder 2 2
wieder flieder 1 1
hier dir 1 1
hier bier 1 1
hier tür 1 1
hier mir 1 1
hier ihr 1 1