This commit is contained in:
Filip Gralinski 2022-07-06 09:20:06 +02:00
parent 5de86ecb81
commit 515f0edb56
2 changed files with 15 additions and 1621 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
* Języki i ich prawa
* Języki i ich prawa statystyczne
Jakim rozkładom statystycznym podlegają języki?
@ -9,7 +9,7 @@ Używać będziemy generatorów.
*Pytanie* Dlaczego generatory zamiast list?
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
import requests
url = 'https://wolnelektury.pl/media/book/txt/pan-tadeusz.txt'
@ -31,7 +31,7 @@ Powrót pani
*** Znaki
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from itertools import islice
def get_characters(t):
@ -45,7 +45,7 @@ Powrót pani
['K', 's', 'i', 'ę', 'g', 'a', ' ', 'p', 'i', 'e', 'r', 'w', 's', 'z', 'a', '\r', '\n', '\r', '\n', '\r', '\n', '\r', '\n', 'G', 'o', 's', 'p', 'o', 'd', 'a', 'r', 's', 't', 'w', 'o', '\r', '\n', '\r', '\n', 'P', 'o', 'w', 'r', 'ó', 't', ' ', 'p', 'a', 'n', 'i']
:end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from collections import Counter
c = Counter(get_characters(pan_tadeusz))
@ -65,7 +65,7 @@ Napiszmy pomocniczą funkcję, która zwraca *listę frekwencyjną*.
Counter({' ': 63444, 'a': 30979, 'i': 29353, 'e': 25343, 'o': 23050, 'z': 22741, 'n': 15505, 'r': 15328, 's': 15255, 'w': 14625, 'c': 14153, 'y': 13732, 'k': 12362, 'd': 11465, '\r': 10851, '\n': 10851, 't': 10757, 'm': 10269, 'ł': 10059, ',': 9130, 'p': 8031, 'u': 7699, 'l': 6677, 'j': 6586, 'b': 5753, 'ę': 5534, 'ą': 4794, 'g': 4775, 'h': 3915, 'ż': 3334, 'ó': 3097, 'ś': 2524, '.': 2380, 'ć': 1956, ';': 1445, 'P': 1265, 'W': 1258, ':': 1152, '!': 1083, 'S': 1045, 'T': 971, 'I': 795, 'N': 793, 'Z': 785, 'J': 729, '—': 720, 'A': 698, 'K': 683, 'ń': 651, 'M': 585, 'B': 567, 'O': 567, 'C': 556, 'D': 552, '«': 540, '»': 538, 'R': 489, '?': 441, 'ź': 414, 'f': 386, 'G': 358, 'L': 316, 'H': 309, 'Ż': 219, 'U': 184, '…': 157, '*': 150, '(': 76, ')': 76, 'Ś': 71, 'F': 47, 'é': 43, '-': 33, 'Ł': 24, 'E': 23, '/': 19, 'Ó': 13, '8': 10, '9': 8, '2': 6, 'v': 5, 'Ź': 4, '1': 4, '3': 3, 'x': 3, 'V': 3, '7': 2, '4': 2, '5': 2, 'q': 2, 'æ': 2, 'à': 1, 'Ć': 1, '6': 1, '0': 1})
:end:
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from collections import Counter
from collections import OrderedDict
@ -88,7 +88,7 @@ OrderedDict([(' ', 63444), ('a', 30979), ('i', 29353), ('e', 25343), ('o', 23050
:end:
#+BEGIN_SRC python :session mysession :results file
#+BEGIN_SRC ipython :session mysession :results file
import matplotlib.pyplot as plt
from collections import OrderedDict
@ -119,7 +119,7 @@ Co rozumiemy pod pojęciem słowa czy wyrazu, nie jest oczywiste. W praktyce zal
Załóżmy, że przez wyraz rozumieć będziemy nieprzerwany ciąg liter bądź cyfr (oraz gwiazdek
— to za chwilę ułatwi nam analizę pewnego tekstu…).
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
#+BEGIN_SRC ipython :session mysession :exports both :results raw drawer
from itertools import islice
import regex as re
@ -138,7 +138,7 @@ Załóżmy, że przez wyraz rozumieć będziemy nieprzerwany ciąg liter bądź
Zobaczmy 20 najczęstszych wyrazów.
#+BEGIN_SRC python :session mysession :results file
#+BEGIN_SRC ipython :session mysession :results file
rang_freq_with_labels('pt-words-20', get_words(pan_tadeusz), top=20)
#+END_SRC
@ -147,7 +147,7 @@ Zobaczmy 20 najczęstszych wyrazów.
Zobaczmy pełny obraz, już bez etykiet.
#+BEGIN_SRC python :session mysession :results file
#+BEGIN_SRC ipython :session mysession :results file
import matplotlib.pyplot as plt
from math import log
@ -172,7 +172,7 @@ Zobaczmy pełny obraz, już bez etykiet.
Widać, jak różne skale obejmuje ten wykres. Zastosujemy logarytm,
najpierw tylko do współrzędnej $y$.
#+BEGIN_SRC python :session mysession :results file
#+BEGIN_SRC ipython :session mysession :results file
import matplotlib.pyplot as plt
from math import log
@ -222,7 +222,7 @@ logarytmicznej dla **obu** osi, otrzymamy kształt zbliżony do linii prostej.
Tę własność tekstów nazywamy **prawem Zipfa**.
#+BEGIN_SRC python :session mysession :results file
#+BEGIN_SRC ipython :session mysession :results file
import matplotlib.pyplot as plt
from math import log
@ -249,7 +249,7 @@ Tę własność tekstów nazywamy **prawem Zipfa**.
Powiązane z prawem Zipfa prawo językowe opisuje zależność między
częstością użycia słowa a jego długością. Generalnie im krótsze słowo, tym częstsze.
#+BEGIN_SRC python :session mysession :results file
#+BEGIN_SRC ipython :session mysession :results file
def freq_vs_length(name, g, top=None):
freq = freq_list(g)
@ -268,229 +268,3 @@ częstością użycia słowa a jego długością. Generalnie im krótsze słowo,
#+RESULTS:
[[file:02_Jezyki/pt-lengths.png]]
** N-gramy
W modelowaniu języka często rozpatruje się n-gramy, czyli podciągi o
rozmiarze $n$.
Na przykład /digramy/ (/bigramy/) to zbitki dwóch jednostek, np. liter albo wyrazów.
|$n$| $n$-gram| nazwa |
|---+---------+---------------|
| 1 | 1-gram | unigram |
| 2 | 2-gram | digram/bigram |
| 3 | 3-gram | trigram |
| 4 | 4-gram | tetragram |
| 5 | 5-gram | pentagram |
*Pytanie:* Jak nazywa się 6-gram?
Jak widać, dla symetrii mówimy czasami o unigramach, jeśli operujemy
po prostu na jednostkach, nie na ich podciągach.
*** N-gramy z Pana Tadeusza
Statystyki, które policzyliśmy dla pojedynczych liter czy wyrazów, możemy powtórzyć dla n-gramów.
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
def ngrams(iter, size):
ngram = []
for item in iter:
ngram.append(item)
if len(ngram) == size:
yield tuple(ngram)
ngram = ngram[1:]
list(ngrams("kotek", 3))
#+END_SRC
#+RESULTS:
:results:
[('k', 'o', 't'), ('o', 't', 'e'), ('t', 'e', 'k')]
:end:
Zauważmy, że policzyliśmy wszystkie n-gramy, również częściowo pokrywające się.
Zawsze powinniśmy się upewnić, czy jest jasne, czy chodzi o n-gramy znakowe czy wyrazowe
*** 3-gramy znakowe
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('pt-3-char-ngrams-log-log', ngrams(get_characters(pan_tadeusz), 3))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/pt-3-char-ngrams-log-log.png]]
*** 2-gramy wyrazowe
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('pt-2-word-ngrams-log-log', ngrams(get_words(pan_tadeusz), 2))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/pt-2-word-ngrams-log-log.png]]
** Tajemniczy język Manuskryptu Wojnicza
[[https://pl.wikipedia.org/wiki/Manuskrypt_Wojnicza][Manuskrypt Wojnicza]] to powstały w XV w. manuskrypt spisany w
tajemniczym alfabecie, do dzisiaj nieodszyfrowanym. Rękopis stanowi
jedną z największych zagadek historii (i lingwistyki).
[[./02_Jezyki/voynich135.jpg][Źródło: https://commons.wikimedia.org/wiki/File:Voynich_Manuscript_(135).jpg]]
Sami zbadajmy statystyczne własności tekstu manuskryptu. Użyjmy
transkrypcji Vnow, gdzie poszczególne znaki tajemniczego alfabetu
zamienione na litery alfabetu łacińskiego, cyfry i gwiazdkę. Jak
transkrybować manuskrypt, pozostaje sprawą dyskusyjną, natomiast wybór
takiego czy innego systemu transkrypcji nie powinien wpływać
dramatycznie na analizę statystyczną.
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
import requests
voynich_url = 'http://www.voynich.net/reeds/gillogly/voynich.now'
voynich = requests.get(voynich_url).content.decode('utf-8')
voynich = re.sub(r'\{[^\}]+\}|^<[^>]+>|[-# ]+', '', voynich, flags=re.MULTILINE)
voynich = voynich.replace('\n\n', '#')
voynich = voynich.replace('\n', ' ')
voynich = voynich.replace('#', '\n')
voynich = voynich.replace('.', ' ')
voynich[100:150]
#+END_SRC
#+RESULTS:
:results:
9 OR 9FAM ZO8 QOAR9 Q*R 8ARAM 29 [O82*]OM OPCC9 OP
:end:
#+BEGIN_SRC python :session mysession :results file
rang_freq_with_labels('voy-chars', get_characters(voynich))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/voy-chars.png]]
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('voy-log-log', get_words(voynich))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/voy-log-log.png]]
#+BEGIN_SRC python :session mysession :results file
rang_freq_with_labels('voy-words-20', get_words(voynich), top=20)
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/voy-words-20.png]]
#+BEGIN_SRC python :session mysession :results file
log_rang_log_freq('voy-words-log-log', get_words(voynich))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/voy-words-log-log.png]]
** Język DNA
Kod genetyczny przejawia własności zaskakująco podobne do języków naturalnych.
Przede wszystkim ma charakter dyskretny, genotyp to ciąg symboli ze skończonego alfabetu.
Podstawowe litery są tylko cztery, reprezentują one nukleotydy, z których zbudowana jest nić DNA:
a, g, c, t.
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
import requests
dna_url = 'https://raw.githubusercontent.com/egreen18/NanO_GEM/master/rawGenome.txt'
dna = requests.get(dna_url).content.decode('utf-8')
dna = ''.join(dna.split('\n')[1:])
dna = dna.replace('N', 'A')
dna[0:100]
#+END_SRC
#+RESULTS:
:results:
TATAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTAACCCTA
:end:
#+BEGIN_SRC python :session mysession :results file
rang_freq_with_labels('dna-chars', get_characters(dna))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/dna-chars.png]]
*** Tryplety — znaczące cząstki genotypu
Nukleotydy rzeczywiście są jak litery, same w sobie nie niosą
znaczenia. Dopiero ciągi trzech nukleotydów, /tryplety/, kodują jeden
z dwudziestu aminokwasów.
#+BEGIN_SRC python :session mysession :results file
genetic_code = {
'ATA':'I', 'ATC':'I', 'ATT':'I', 'ATG':'M',
'ACA':'T', 'ACC':'T', 'ACG':'T', 'ACT':'T',
'AAC':'N', 'AAT':'N', 'AAA':'K', 'AAG':'K',
'AGC':'S', 'AGT':'S', 'AGA':'R', 'AGG':'R',
'CTA':'L', 'CTC':'L', 'CTG':'L', 'CTT':'L',
'CCA':'P', 'CCC':'P', 'CCG':'P', 'CCT':'P',
'CAC':'H', 'CAT':'H', 'CAA':'Q', 'CAG':'Q',
'CGA':'R', 'CGC':'R', 'CGG':'R', 'CGT':'R',
'GTA':'V', 'GTC':'V', 'GTG':'V', 'GTT':'V',
'GCA':'A', 'GCC':'A', 'GCG':'A', 'GCT':'A',
'GAC':'D', 'GAT':'D', 'GAA':'E', 'GAG':'E',
'GGA':'G', 'GGC':'G', 'GGG':'G', 'GGT':'G',
'TCA':'S', 'TCC':'S', 'TCG':'S', 'TCT':'S',
'TTC':'F', 'TTT':'F', 'TTA':'L', 'TTG':'L',
'TAC':'Y', 'TAT':'Y', 'TAA':'_', 'TAG':'_',
'TGC':'C', 'TGT':'C', 'TGA':'_', 'TGG':'W',
}
def get_triplets(t):
for triplet in re.finditer(r'.{3}', t):
yield genetic_code[triplet.group(0)]
rang_freq_with_labels('dna-aminos', get_triplets(dna))
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/dna-aminos.png]]
*** „Zdania” w języku DNA
Z aminokwasów zakodowanych przez tryplet budowane są białka.
Maszyneria budująca białka czyta sekwencję aż do napotkania
trypletu STOP (_ powyżej). Taka sekwencja to /gen/.
#+BEGIN_SRC python :session mysession :results file
def get_genes(triplets):
gene = []
for ammino in triplets:
if ammino == '_':
yield gene
gene = []
else:
gene.append(ammino)
plt.figure().clear()
plt.hist([len(g) for g in get_genes(get_triplets(dna))], bins=100)
fname = '02_Jezyki/dna_length.png'
plt.savefig(fname)
fname
#+END_SRC
#+RESULTS:
[[file:02_Jezyki/dna_length.png]]