In [1]:
# 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.
In [2]:
# Setup CLTK tools

word_tokenizer = WordTokenizer('latin')
replacer = JVReplacer()
In [3]:
# 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.
In [4]:
# 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))
In [5]:
# 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
In [6]:
# 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)
In [7]:
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

Following [Alajmi 2012]

In [8]:
# 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)
In [9]:
M = len(vocab)
N= len(raw_files)
In [10]:
# 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
In [11]:
# Calculate mean probability
# i.e. Sum of probabilities for each word / number of documents

probsum = np.ravel(probs.sum(axis=0))
MP = probsum/N
In [12]:
# Make array of bar probability

length = sum(raw_lengths)
barprobs = dtm/length
bP=barprobs
In [13]:
variance = (P-bP) ** 2
varsum = np.ravel(variance.sum(axis=0))
VP = varsum/N
In [14]:
cutoff = 100
In [15]:
# 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']
In [16]:
# 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']
In [17]:
# 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']
In [18]:
with np.errstate(divide='ignore', invalid='ignore'):
    logprobs = np.where(probs != 0, np.log10(1/probs), 0)
ent = probs * logprobs
In [19]:
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']
In [20]:
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)
In [21]:
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']

Other Latin stopword lists

In [22]:
tesserae = ['qui', 'quis', 'et', 'sum', 'in', 'is', 'non', 'hic', 'ego', 'ut']
In [23]:
# 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]
In [24]:
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']
In [25]:
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']

References

  • Alajmi, A., Saad, E.M., and R.R. Darwish. 2012. "Toward an Arabic Stop-Words List Generation," International Journal of Computer Applications 48(8): 8-13.
  • Zou, F., F. L. Wang, X. Deng, S. Han, and L. S. Wang. 2006. “Automatic Construction of Chinese Stop Word List.” In Proceedings of the 5th WSEAS International Conference on Applied Computer Science, 1010–1015.