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