# Imports
import os
import string
import math
import re
from collections import Counter
from pprint import pprint
import html
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from cltk.corpus.latin import latinlibrary
from cltk.tokenize.word import WordTokenizer
from cltk.stem.latin.j_v import JVReplacer
Arabic not supported. Install `pyarabic` library to tokenize Arabic.
# Setup CLTK tools
word_tokenizer = WordTokenizer('latin')
replacer = JVReplacer()
# Setup files
files = latinlibrary.fileids()
print("There are %d files in the Latin Library corpus." % len(files))
There are 2164 files in the Latin Library corpus.
# Typical setup
files = [file for file in files]
# Filter for classical texts
#classical = []
#remove = ["The Bible","Ius Romanum","Papal Bulls","Medieval Latin","Christian Latin","Christina Latin","Neo-Latin","The Miscellany","Contemporary Latin"]
#for file in files:
# raw = latinlibrary.raw(file)
# if not any(x in raw for x in remove):
# classical.append(file)
#files = classical
#print("There are %d files in the Latin Library Classical subcorpus." % len(files))
# Preprocess texts
def preprocess(text):
text = html.unescape(text) # Handle html entities
text = re.sub(r' ?', ' ',text) # stripped incorrectly in corpus?
text = re.sub('\x00',' ',text) #Another space problem?
text = text.lower()
text = replacer.replace(text) #Normalize u/v & i/j
punctuation ="\"#$%&\'()*+,-/:;<=>@[\]^_`{|}~.?!«»"
translator = str.maketrans({key: " " for key in punctuation})
text = text.translate(translator)
translator = str.maketrans({key: " " for key in '0123456789'})
text = text.translate(translator)
remove_list = [r'\bthe latin library\b',
r'\bthe classics page\b',
r'\bneo-latin\b',
r'\bmedieval latin\b',
r'\bchristian latin\b',
r'\bchristina latin\b',
r'\bpapal bulls\b',
r'\bthe miscellany\b',
]
for pattern in remove_list:
text = re.sub(pattern, '', text)
text = re.sub('[ ]+',' ', text) # Remove double spaces
text = re.sub('\s+\n+\s+','\n', text) # Remove double lines and trim spaces around new lines
return text
# Make list of texts
raw_files = []
for file in files:
raw = latinlibrary.raw(file)
raw = preprocess(raw)
if len(raw) < 1000:
pass
else:
raw_tokens = raw.split()
raw = " ".join(raw_tokens[50:-50])
raw_files.append(raw)
tokens = [file.split() for file in raw_files]
tokens = [val for sublist in tokens for val in sublist]
print(len(tokens))
print(len(set(tokens)))
rank = Counter(tokens)
print(rank.most_common(25))
hapax = len([x for x in tokens if rank[x] == 1])
print(hapax)
13128342 425701 [('et', 440141), ('in', 269084), ('est', 165313), ('non', 164309), ('ad', 131369), ('ut', 117286), ('quod', 102967), ('cum', 99426), ('si', 92820), ('qui', 92050), ('de', 78600), ('sed', 73519), ('a', 73441), ('quae', 63286), ('ex', 59054), ('quam', 55412), ('per', 50078), ('esse', 48947), ('nec', 44791), ('sunt', 43609), ('hoc', 43142), ('enim', 42204), ('uel', 41287), ('se', 41250), ('aut', 40337)] 194347
# See also Zou et al. 2006
# Make document-term matrix and vocabulary
vectorizer = CountVectorizer(input='content', min_df=2)
dtm = vectorizer.fit_transform(raw_files)
dtm = dtm.toarray()
vocab = vectorizer.get_feature_names()
vocab = np.array(vocab)
M = len(vocab)
N= len(raw_files)
# Make array of probabilities per book
raw_lengths = [len(tokens.split()) for tokens in raw_files]
l = np.array(raw_lengths)
ll = l.reshape(len(l),1)
probs = dtm/ll
P=probs
# Calculate mean probability
# i.e. Sum of probabilities for each word / number of documents
probsum = np.ravel(probs.sum(axis=0))
MP = probsum/N
# Make array of bar probability
length = sum(raw_lengths)
barprobs = dtm/length
bP=barprobs
variance = (P-bP) ** 2
varsum = np.ravel(variance.sum(axis=0))
VP = varsum/N
cutoff = 100
# Return top counts
freq = np.ravel(dtm.sum(axis=0))
wordfreq = list(zip(vocab,freq))
wordfreq.sort(key=lambda x: x[1], reverse=True)
wf = [item[0] for item in wordfreq]
wf = wf[:cutoff]
print(wf)
['et', 'in', 'est', 'non', 'ad', 'ut', 'quod', 'cum', 'si', 'qui', 'de', 'sed', 'quae', 'ex', 'quam', 'per', 'esse', 'nec', 'sunt', 'hoc', 'enim', 'uel', 'se', 'aut', 'autem', 'ab', 'etiam', 'eius', 'quid', 'sit', 'atque', 'quo', 'quia', 'me', 'te', 'ac', 'ne', 'tamen', 'id', 'ita', 'dig', 'haec', 'nam', 'iam', 'eo', 'eum', 'pro', 'uero', 'mihi', 'neque', 'tibi', 'quidem', 'ea', 'quibus', 'sic', 'nisi', 'erat', 'quoque', 'inter', 'sibi', 'ipse', 'quem', 'nihil', 'post', 'qua', 'ego', 'nunc', 'his', 'ergo', 'quis', 'sine', 'ille', 'omnia', 'esset', 'potest', 'ei', 'modo', 'ubi', 'omnes', 'eorum', 'fuit', 'nos', 'ante', 'illa', 'tam', 'hic', 'causa', 'an', 'sicut', 'tu', 'eos', 'apud', 'res', 'igitur', 'contra', 'quos', 'nobis', 'omnibus', 'super', 'dum']
# Return top mean prob
test = list(zip(vocab,MP))
test.sort(key=lambda x: x[1], reverse=True)
mp = [item[0] for item in test]
mp = mp[:cutoff]
print(mp)
['et', 'in', 'est', 'non', 'ad', 'ut', 'cum', 'quod', 'qui', 'sed', 'si', 'de', 'quae', 'quam', 'per', 'ex', 'nec', 'sunt', 'esse', 'se', 'hoc', 'enim', 'ab', 'aut', 'autem', 'etiam', 'quid', 'te', 'atque', 'uel', 'eius', 'me', 'quo', 'sit', 'iam', 'quia', 'ne', 'haec', 'mihi', 'tamen', 'ac', 'tibi', 'nam', 'sic', 'ita', 'id', 'pro', 'eo', 'nunc', 'uero', 'neque', 'inter', 'quem', 'erat', 'ille', 'ergo', 'ipse', 'eum', 'quibus', 'quoque', 'sibi', 'ego', 'quidem', 'nisi', 'qua', 'omnia', 'hic', 'post', 'fuit', 'tu', 'nihil', 'ea', 'illa', 'his', 'omnes', 'nos', 'esset', 'modo', 'dum', 'sine', 'quis', 'ubi', 'sicut', 'ante', 'sub', 'tam', 'secundum', 'deus', 'potest', 'dei', 'nobis', 'quos', 'igitur', 'ei', 'omnibus', 'res', 'cui', 'sua', 'apud', 'eorum']
# Return top variance prob
test = list(zip(vocab,VP))
test.sort(key=lambda x: x[1], reverse=True)
vp = [item[0] for item in test]
vp = vp[:cutoff]
print(vp)
['et', 'in', 'est', 'non', 'quod', 'ad', 'ut', 'cum', 'qui', 'de', 'si', 'sed', 'quae', 'per', 'ex', 'quam', 'esse', 'nec', 'te', 'sunt', 'autem', 'me', 'enim', 'se', 'dig', 'hoc', 'aut', 'ab', 'bibit', 'quid', 'uel', 'atque', 'mihi', 'eius', 'quaestio', 'pro', 'etiam', 'tibi', 'quia', 'sit', 'iam', 'secundum', 'quo', 'ac', 'ne', 'ergo', 'od', 'nihil', 'tu', 'haec', 'sic', 'id', 'nam', 'ego', 'neque', 'tamen', 'eum', 'deus', 'nunc', 'dei', 'ita', 'eo', 'uero', 'sicut', 'uos', 'hic', 'erat', 'nouus', 'fuit', 'nos', 'ille', 'inter', 'dum', 'quem', 'quoque', 'quidem', 'esset', 'bellum', 'ipse', 'sibi', 'nummus', 'anno', 'quibus', 'post', 'his', 'omnia', 'ea', 'super', 'qua', 'sub', 'illa', 'dominus', 'deo', 'rex', 'nisi', 'totus', 'dixit', 'dicitur', 'ed', 'ante']
with np.errstate(divide='ignore', invalid='ignore'):
logprobs = np.where(probs != 0, np.log10(1/probs), 0)
ent = probs * logprobs
ents = np.ravel(ent.sum(axis=0))
entrank = list(zip(vocab,ents))
entrank.sort(key=lambda x: x[1], reverse=True)
e = [item[0] for item in entrank]
e = e[:cutoff]
print(e)
['et', 'in', 'est', 'non', 'ad', 'ut', 'cum', 'quod', 'qui', 'sed', 'si', 'de', 'quae', 'quam', 'per', 'ex', 'nec', 'sunt', 'esse', 'se', 'hoc', 'ab', 'enim', 'aut', 'autem', 'etiam', 'quid', 'quo', 'atque', 'eius', 'te', 'uel', 'sit', 'me', 'iam', 'ne', 'haec', 'quia', 'tamen', 'nam', 'ac', 'mihi', 'ita', 'sic', 'tibi', 'id', 'pro', 'eo', 'inter', 'nunc', 'quem', 'ipse', 'uero', 'neque', 'quibus', 'ille', 'erat', 'eum', 'sibi', 'qua', 'nisi', 'quoque', 'ergo', 'quidem', 'omnia', 'post', 'hic', 'fuit', 'ego', 'ea', 'nihil', 'omnes', 'his', 'illa', 'modo', 'tu', 'esset', 'sine', 'nos', 'dum', 'ubi', 'ante', 'quis', 'tam', 'sub', 'sicut', 'quos', 'omnibus', 'potest', 'nobis', 'sua', 'cui', 'igitur', 'res', 'ei', 'tantum', 'cuius', 'apud', 'contra', 'magis']
def borda_sort(lists):
### From http://stackoverflow.com/a/30259368/1816347 ###
scores = {}
for l in lists:
for idx, elem in enumerate(reversed(l)):
if not elem in scores:
scores[elem] = 0
scores[elem] += idx
return sorted(scores.keys(), key=lambda elem: scores[elem], reverse=True)
lists = [wf, mp, vp, e]
borda = borda_sort(lists)
print(borda[:100])
['et', 'in', 'est', 'non', 'ad', 'ut', 'quod', 'cum', 'qui', 'si', 'sed', 'de', 'quae', 'quam', 'per', 'ex', 'nec', 'esse', 'sunt', 'se', 'hoc', 'enim', 'autem', 'ab', 'aut', 'te', 'quid', 'uel', 'etiam', 'atque', 'me', 'eius', 'quo', 'sit', 'quia', 'iam', 'ne', 'ac', 'mihi', 'haec', 'tamen', 'tibi', 'pro', 'nam', 'id', 'ita', 'sic', 'eo', 'neque', 'uero', 'eum', 'nunc', 'inter', 'ergo', 'erat', 'quem', 'ipse', 'ego', 'quibus', 'nihil', 'ille', 'quoque', 'quidem', 'sibi', 'dig', 'nisi', 'qua', 'post', 'ea', 'tu', 'hic', 'fuit', 'omnia', 'his', 'esset', 'nos', 'sicut', 'illa', 'omnes', 'sine', 'secundum', 'bibit', 'modo', 'dum', 'quis', 'quaestio', 'ubi', 'deus', 'od', 'ante', 'dei', 'potest', 'tam', 'sub', 'ei', 'uos', 'nouus', 'quos', 'nobis', 'bellum']
tesserae = ['qui', 'quis', 'et', 'sum', 'in', 'is', 'non', 'hic', 'ego', 'ut']
# Cf. http://www.perseus.tufts.edu/hopper/stopwords
# Same as the list w. the following:
# from cltk.stop.latin.stops import STOPS_LIST
perseus = ['ab', 'ac', 'ad', 'adhic', 'aliqui', 'aliquis', 'an', 'ante', 'apud', 'at', 'atque', 'aut', 'autem', 'cum', 'cur', 'de', 'deinde', 'dum', 'ego', 'enim', 'ergo', 'es', 'est', 'et', 'etiam', 'etsi', 'ex', 'fio', 'haud', 'hic', 'iam', 'idem', 'igitur', 'ille', 'in', 'infra', 'inter', 'interim', 'ipse', 'is', 'ita', 'magis', 'modo', 'mox', 'nam', 'ne', 'nec', 'necque', 'neque', 'nisi', 'non', 'nos', 'o', 'ob', 'per', 'possum', 'post', 'pro', 'quae', 'quam', 'quare', 'qui', 'quia', 'quicumque', 'quidem', 'quilibet', 'quis', 'quisnam', 'quisquam', 'quisque', 'quisquis', 'quo', 'quoniam', 'sed', 'si', 'sic', 'sive', 'sub', 'sui', 'sum', 'super', 'suus', 'tam', 'tamen', 'trans', 'tu', 'tum', 'ubi', 'uel', 'uero', 'unus', 'ut']
perseus = [replacer.replace(word) for word in perseus]
pprint(list(set(perseus) - set(borda)))
pprint(list(set(borda) - set(perseus)))
['aliquis', 'mox', 'es', 'possum', 'o', 'siue', 'is', 'ob', 'adhic', 'tum', 'quilibet', 'unus', 'idem', 'sum', 'quicumque', 'trans', 'fio', 'necque', 'quoniam', 'etsi', 'quisnam', 'quisquis', 'sui', 'quisquam', 'at', 'suus', 'cur', 'aliqui', 'deinde', 'haud', 'infra', 'interim', 'quare', 'quisque'] ['his', 'esset', 'qua', 'omnes', 'dixit', 'me', 'fuit', 'te', 'sicut', 'omnibus', 'eo', 'hoc', 'ei', 'se', 'dig', 'dei', 'esse', 'quid', 'illa', 'dominus', 'res', 'eius', 'quod', 'bibit', 'potest', 'nummus', 'quos', 'quibus', 'secundum', 'bellum', 'cui', 'nihil', 'quaestio', 'nobis', 'eos', 'causa', 'tibi', 'sit', 'quem', 'dicitur', 'sine', 'ea', 'uos', 'haec', 'deo', 'deus', 'tantum', 'id', 'nouus', 'od', 'erat', 'anno', 'eum', 'omnia', 'quoque', 'contra', 'eorum', 'mihi', 'totus', 'sibi', 'cuius', 'rex', 'ed', 'sua', 'sunt', 'nunc']
pprint(list(set(tesserae) - set(borda)))
pprint(list(set(borda) - set(tesserae)))
['is', 'sum'] ['his', 'quae', 'qua', 'omnes', 'fuit', 'hoc', 'quid', 'sed', 'res', 'bibit', 'nummus', 'ex', 'sub', 'inter', 'autem', 'aut', 'magis', 'tam', 'nos', 'quidem', 'an', 'dum', 'totus', 'sibi', 'ed', 'pro', 'esset', 'me', 'sicut', 'iam', 'esse', 'dei', 'eius', 'ille', 'illa', 'dominus', 'quos', 'post', 'ergo', 'nam', 'quaestio', 'quam', 'sua', 'causa', 'dicitur', 'ea', 'uos', 'deo', 'ad', 'ita', 'eum', 'nisi', 'omnia', 'nunc', 'rex', 'dixit', 'ei', 'quo', 'se', 'ipse', 'dig', 'per', 'quod', 'si', 'potest', 'quibus', 'uel', 'secundum', 'bellum', 'super', 'enim', 'eos', 'tibi', 'sit', 'quem', 'nouus', 'od', 'est', 'anno', 'ac', 'quoque', 'contra', 'ab', 'te', 'omnibus', 'eo', 'de', 'ante', 'neque', 'tamen', 'uero', 'ne', 'cui', 'ubi', 'etiam', 'nihil', 'nobis', 'apud', 'nec', 'cum', 'sine', 'sic', 'haec', 'deus', 'tantum', 'igitur', 'erat', 'tu', 'eorum', 'mihi', 'atque', 'modo', 'quia', 'cuius', 'id', 'sunt']