Skip to content

Instantly share code, notes, and snippets.

@onidzelskyi
Created December 25, 2018 11:13
Show Gist options
  • Save onidzelskyi/53fe2cbf3ec8f1d7afd70e70032e677a to your computer and use it in GitHub Desktop.
Save onidzelskyi/53fe2cbf3ec8f1d7afd70e70032e677a to your computer and use it in GitHub Desktop.
Language detect
""" -*- coding: utf-8 -*-
#
# Sample program for language detection
# Link to original paper https://www.kleemans.ch/letter-frequency
#
# How to run
# 1. add environment PYTHONIOENCODING="UTF-8"
# 2. python language_detect.py < letter_frequency.csv
#
"""
# Sample text section
ITALIAN_BOCCACCIO_TEXT = """Mossi adunque piú cosí egregi come antichi popoli da questa laudevole
sentenzia e apertissimamente vera, alcuna volta di deitá, altra di
marmorea statua, e sovente di celebre sepultura, e tal fiata di
triunfale arco, e quando di laurea corona secondo i meriti precedenti
onoravano i valorosi: le pene, per opposito, a' colpevoli date non
curo di raccontare. Per li quali onori e purgazioni la assiria, la
macedonica, la greca e ultimamente la romana republica aumentate, con
l'opere le fini della terra, e con la fama toccaron le stelle. Le
vestigie de' quali in cosí alti esempli, non solamente da' successori
presenti, e massimamente da' miei fiorentini, sono male seguite, ma in
tanto s'è disviato da esse, che ogni premio di virtú possiede
l'ambizione; per che, sí come e io e ciascun altro che a ciò con
occhio ragionevole vuole guardare, non senza grandissima afflizione
d'animo possiamo vedere li malvagi e perversi uomini a' luoghi eccelsi
e a' sommi ofici e guiderdoni elevare, e li buoni scacciare, deprimere
e abbassare. Alle quali cose qual fine serbi il giudicio di Dio,
coloro il veggiano che il timone governano di questa nave: percioché
noi, piú bassa turba, siamo trasportati dal fiotto, della fortuna, ma
non della colpa partecipi. E, comeché con infinite ingratitudini e
dissolute perdonanze apparenti si potessero le predette cose
verificare, per meno scoprire li nostri difetti e per pervenire al mio
principale intento, una sola mi fia assai avere raccontata (né questa
fia poco o picciola), ricordando l'esilio del chiarissimo uomo Dante
Alighieri. Il quale, antico cittadino né d'oscuri parenti nato, quanto
per vertú e per scienzia e per buone operazioni meritasse, assai il
mostrano e mostreranno le cose che da lui fatte appaiono: le quali, se
in una republica giusta fossero state operate, niuno dubbio ci è che
esse non gli avessero altissimi meriti apparecchiati."""
DUTCH_ETHERLANDS_TEXT = """Zijn vader had vier kinderen, drie zoons en één dochter. Hij moet een
verdienstelijk en verstandig man zijn geweest, want hij schijnt aan al
zijne kinderen het onderwijs te hebben doen geven, dat de gewone school
aanbood. Christophorus had goed leeren lezen, schrijven en rekenen. Ook
had hij eenige vorderingen gemaakt in het Latijn en het teekenen. Zelfs
bezocht hij de hoogeschool te Pavia, waar hij zich vlijtig oefende
in meetkunde, aardrijkskunde, sterrekunde en zeevaartkunde."""
FINNISH_KIVI_TEXT = """Jukolan talo, eteläisessä Hämeessä, seisoo erään mäen pohjaisella
rinteellä, liki Toukolan kylää. Sen läheisin ympäristö on kivinen
tanner, mutta alempana alkaa pellot, joissa, ennenkuin talo oli häviöön
mennyt, aaltoili teräinen vilja. Peltojen alla on niittu, apilaäyräinen,
halkileikkaama monipolvisen ojan; ja runsaasti antoi se heiniä,
ennenkuin joutui laitumeksi kylän karjalle. Muutoin on talolla avaria
metsiä, soita ja erämaita, jotka, tämän tilustan ensimmäisen perustajan
oivallisen toiminnan kautta, olivat langenneet sille osaksi jo ison-jaon
käydessä entisinä aikoina. Silloinpa Jukolan isäntä, pitäen enemmän
huolta jälkeentulevainsa edusta kuin omasta parhaastansa, otti vastaan
osaksensa kulon polttaman metsän ja sai sillä keinolla seitsemän vertaa
enemmän kuin toiset naapurinsa. Mutta kaikki kulovalkean jäljet olivat
jo kadonneet hänen piiristänsä ja tuuhea metsä kasvanut sijaan.--Ja tämä
on niiden seitsemän veljen koto, joiden elämänvaiheita tässä nyt käyn
kertoilemaan."""
# Define variables
# csv separator
CSV_SEP = ';'
def load_csv(sep=','):
"""load language frequencies from standard input.
@:param sep - separator for csv file, string.
@:return tuple (frequencies as dictionary, letters as list)"""
# dictionary of supported languages
lang_freq = {}
# list of supported languages
language_list = []
# list of supported letters
letters = []
# TODO: Add an implementation
return lang_freq, letters
def calc_mse(lang_freq, let_freq):
"""Calculate Mean Square Error.
@:param language_freq - pre-calculated frequencies for supported languages.
@:param letters_freq - calculated frequencies for detected text.
@:return dictionary of MSE for for supported languages."""
# MSE dictionary for each language
mse = {}
# TODO: Add an implementation
return mse
def calc_letter_frequency(text, all_letters):
"""Calculate frequencies for letters in detected text.
@:param text - text to be analyzed, string.
@:param all_letters - list of letters taken in accounts.
@:return dictionary for calculated frequencies for detected text."""
# total count of letters in text
letters_count = 0
# dictionary for letter frequency in text
letters_freq = {}
# TODO: Add an implementation
return letters_freq
# get text frequencies
def detect_language(text, lang_freq, all_letters):
"""Calculate probabilities for supported languages of given text.
@:param text - text to be analyzed, string.
@:param lang_freq - pre-calculated frequencies for supported languages.
@:param all_letters - list of letters taken in accounts.
@:return detected language for given text as string."""
# language for text
predicted_language = '<undetected>'
# TODO: Add an implementation
# MSE
# TODO: Add an implementation
# Find out language itself as minimal value of MSE
# TODO: Add an implementation
return predicted_language
# Demo app
if __name__ == "__main__":
# Load pre-calculated values
language_freq, allowed_letters = load_csv(CSV_SEP)
# For each sample text detect its language
for expected_lang, sample_text in zip(['Finnish', 'Italian', 'Dutch'],
[FINNISH_KIVI_TEXT, ITALIAN_BOCCACCIO_TEXT, DUTCH_ETHERLANDS_TEXT]):
detected_language = detect_language(sample_text, language_freq, allowed_letters)
print('Expected / detected language: {} / {}'.format(expected_lang, detected_language))
We can make this file beautiful and searchable if this error is corrected: No commas found in this CSV file in line 0.
Letter;French;German;Spanish;Portuguese;Esperanto;Italian;Turkish;Swedish;Polish;Dutch;Danish;Icelandic;Finnish;Czech
a;7.636%;6.516%;11.525%;14.634%;12.117%;11.745%;12.920%;9.383%;10.503%;7.486%;6.025%;10.110%;12.217%;8.421%
b;0.901%;1.886%;2.215%;1.043%;0.980%;0.927%;2.844%;1.535%;1.740%;1.584%;2.000%;1.043%;0.281%;0.822%
c;3.260%;2.732%;4.019%;3.882%;0.776%;4.501%;1.463%;1.486%;3.895%;1.242%;0.565%;0;0.281%;0.740%
d;3.669%;5.076%;5.010%;4.992%;3.044%;3.736%;5.206%;4.702%;3.725%;5.933%;5.858%;1.575%;1.043%;3.475%
e;14.715%;16.396%;12.181%;12.570%;8.995%;11.792%;9.912%;10.149%;7.352%;17.324%;15.453%;6.418%;7.968%;7.562%
f;1.066%;1.656%;0.692%;1.023%;1.037%;1.153%;0.461%;2.027%;0.143%;0.805%;2.406%;3.013%;0.194%;0.084%
g;0.866%;3.009%;1.768%;1.303%;1.171%;1.644%;1.253%;2.862%;1.731%;3.403%;4.077%;4.241%;0.392%;0.092%
h;0.737%;4.577%;0.703%;0.781%;0.384%;0.636%;1.212%;2.090%;1.015%;2.380%;1.621%;1.871%;1.851%;1.356%
i;7.529%;6.550%;6.247%;6.186%;10.012%;10.143%;9.600%;5.817%;8.328%;6.499%;6.000%;7.578%;10.817%;6.073%
j;0.613%;0.268%;0.493%;0.397%;3.501%;0.011%;0.034%;0.614%;1.836%;1.461%;0.730%;1.144%;2.042%;1.433%
k;0.049%;1.417%;0.011%;0.015%;4.163%;0.009%;5.683%;3.140%;2.753%;2.248%;3.395%;3.314%;4.973%;2.894%
l;5.456%;3.437%;4.967%;2.779%;6.104%;6.510%;5.922%;5.275%;2.564%;3.568%;5.229%;4.532%;5.761%;3.802%
m;2.968%;2.534%;3.157%;4.738%;2.994%;2.512%;3.752%;3.471%;2.515%;2.213%;3.237%;4.041%;3.202%;2.446%
n;7.095%;9.776%;6.712%;4.446%;7.955%;6.883%;7.987%;8.542%;6.237%;10.032%;7.240%;7.711%;8.826%;6.468%
o;5.796%;2.594%;8.683%;9.735%;8.779%;9.832%;2.976%;4.482%;6.667%;6.063%;4.636%;2.166%;5.614%;6.695%
p;2.521%;0.670%;2.510%;2.523%;2.755%;3.056%;0.886%;1.839%;2.445%;1.370%;1.756%;0.789%;1.842%;1.906%
q;1.362%;0.018%;0.877%;1.204%;0;0.505%;0;0.020%;0;0.009%;0.007%;0;0.013%;0.001%
r;6.693%;7.003%;6.871%;6.530%;5.914%;6.367%;7.722%;8.431%;5.243%;6.411%;8.956%;8.581%;2.872%;4.799%
s;7.948%;7.270%;7.977%;6.805%;6.092%;4.981%;3.014%;6.590%;5.224%;5.733%;5.805%;5.630%;7.862%;5.212%
t;7.244%;6.154%;4.632%;4.336%;5.276%;5.623%;3.314%;7.691%;2.475%;6.923%;6.862%;4.953%;8.750%;5.727%
u;6.311%;4.166%;2.927%;3.639%;3.183%;3.011%;3.235%;1.919%;2.062%;2.192%;1.979%;4.562%;5.008%;2.160%
v;1.838%;0.846%;1.138%;1.575%;1.904%;2.097%;0.959%;2.415%;0.012%;1.854%;2.332%;2.437%;2.250%;5.344%
w;0.074%;1.921%;0.017%;0.037%;0;0.033%;0;0.142%;5.813%;1.821%;0.069%;0;0.094%;0.016%
x;0.427%;0.034%;0.215%;0.253%;0;0.003%;0;0.159%;0.004%;0.036%;0.028%;0.046%;0.031%;0.027%
y;0.128%;0.039%;1.008%;0.006%;0;0.020%;3.336%;0.708%;3.206%;0.035%;0.698%;0.900%;1.745%;1.043%
z;0.326%;1.134%;0.467%;0.470%;0.494%;1.181%;1.500%;0.070%;4.852%;1.374%;0.034%;0;0.051%;1.503%
ß;0;0.307%;0;0;0;0;0;0;0;0;0;0;0;0
à;0.486%;0;0;0.072%;0;0.635%;0;0;0;0;0;0;0;0
á;0;0;0.502%;0.118%;0;0;0;0;0;0;0;1.799%;0;0.867%
â;0.051%;0;0;0.562%;0;0;0;0;0;0;0;0;0;0
ã;0;0;0;0.733%;0;0;0;0;0;0;0;0;0;0
ä;0;0.578%;0;0;0;0;0;1.797%;0;0;0;0;3.577%;0
å;0;0;0;0;0;0;0;1.338%;0;0;1.190%;0;0.003%;0
æ;0;0;0;0;0;0;0;0;0;0;0.872%;0.867%;0;0
ç;0.085%;0;0;0.530%;0;0;1.156%;0;0;0;0;0;0;0
è;0.271%;0;0;0;0;0.263%;0;0;0;0;0;0;0;0
é;1.504%;0;0.433%;0.337%;0;0;0;0;0;0;0;0.647%;0;0.633%
ê;0.218%;0;0;0.450%;0;0;0;0;0;0;0;0;0;0
ë;0.008%;0;0;0;0;0;0;0;0;0;0;0;0;0
ì;0;0;0;0;0;0.030%;0;0;0;0;0;0;0;0
í;0;0;0.725%;0.132%;0;0;0;0;0;0;0;1.570%;0;1.643%
î;0.045%;0;0;0;0;0;0;0;0;0;0;0;0;0
ï;0.005%;0;0;0;0;0;0;0;0;0;0;0;0;0
ð;0;0;0;0;0;0;0;0;0;0;0;4.393%;0;0
ñ;0;0;0.311%;0;0;0;0;0;0;0;0;0;0;0
ò;0;0;0;0;0;0.002%;0;0;0;0;0;0;0;0
ó;0;0;0.827%;0.296%;0;0;0;0;1.141%;0;0;0.994%;0;0.024%
ô;0.023%;0;0;0.635%;0;0;0;0;0;0;0;0;0;0
ö;0;0.443%;0;0;0;0;0.777%;1.305%;0;0;0;0.777%;0.444%;0
ø;0;0;0;0;0;0;0;0;0;0;0.939%;0;0;0
ù;0.058%;0;0;0;0;0.166%;0;0;0;0;0;0;0;0
ú;0;0;0.168%;0.207%;0;0;0;0;0;0;0;0.613%;0;0.045%
ü;0;0.995%;0.012%;0.026%;0;0;1.854%;0;0;0;0;0;0;0
ý;0;0;0;0;0;0;0;0;0;0;0;0.228%;0;0.995%
þ;0;0;0;0;0;0;0;0;0;0;0;1.455%;0;0
ą;0;0;0;0;0;0;0;0;0.699%;0;0;0;0;0
ć;0;0;0;0;0;0;0;0;0.743%;0;0;0;0;0
ĉ;0;0;0;0;0.657%;0;0;0;0;0;0;0;0;0
č;0;0;0;0;0;0;0;0;0;0;0;0;0;0.462%
ď;0;0;0;0;0;0;0;0;0;0;0;0;0;0.015%
ę;0;0;0;0;0;0;0;0;1.035%;0;0;0;0;0
ě;0;0;0;0;0;0;0;0;0;0;0;0;0;1.222%
ĝ;0;0;0;0;0.691%;0;0;0;0;0;0;0;0;0
ğ;0;0;0;0;0;0;1.125%;0;0;0;0;0;0;0
ĥ;0;0;0;0;0.022%;0;0;0;0;0;0;0;0;0
ı;0;0;0;0;0;0;5.114%;0;0;0;0;0;0;0
ĵ;0;0;0;0;0.055%;0;0;0;0;0;0;0;0;0
ł;0;0;0;0;0;0;0;0;2.109%;0;0;0;0;0
ń;0;0;0;0;0;0;0;0;0.362%;0;0;0;0;0
ň;0;0;0;0;0;0;0;0;0;0;0;0;0;0.007%
œ;0.018%;0;0;0;0;0;0;0;0;0;0;0;0;0
ř;0;0;0;0;0;0;0;0;0;0;0;0;0;0.380%
ś;0;0;0;0;0;0;0;0;0.814%;0;0;0;0;0
ŝ;0;0;0;0;0.385%;0;0;0;0;0;0;0;0;0
ş;0;0;0;0;0;0;1.780%;0;0;0;0;0;0;0
š;0;0;0;0;0;0;0;0;0;0;0;0;0;0.688%
ť;0;0;0;0;0;0;0;0;0;0;0;0;0;0.006%
ŭ;0;0;0;0;0.520%;0;0;0;0;0;0;0;0;0
ů;0;0;0;0;0;0;0;0;0;0;0;0;0;0.204%
ź;0;0;0;0;0;0;0;0;0.078%;0;0;0;0;0
ż;0;0;0;0;0;0;0;0;0.706%;0;0;0;0;0
ž;0;0;0;0;0;0;0;0;0;0;0;0;0;0.721%
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment