Lecture 2 - started
This commit is contained in:
parent
f80793e88e
commit
cb86308013
308
wyk/02_Jezyki.org
Normal file
308
wyk/02_Jezyki.org
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
* Języki i ich prawa
|
||||||
|
|
||||||
|
Jakim rozkładom statystycznym podlegają języki?
|
||||||
|
|
||||||
|
** Język naturalny albo „Pan Tedeusz” w liczbach
|
||||||
|
|
||||||
|
Przygotujmy najpierw „infrastrukturę” do /segmentacji/ tekstu na różnego rodzaju jednostki.
|
||||||
|
Używać będziemy generatorów.
|
||||||
|
|
||||||
|
*Pytanie* Dlaczego generatory zamiast list?
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = 'https://wolnelektury.pl/media/book/txt/pan-tadeusz.txt'
|
||||||
|
pan_tadeusz = requests.get(url).content.decode('utf-8')
|
||||||
|
|
||||||
|
pan_tadeusz[100:150]
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
Księga pierwsza
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Gospodarstwo
|
||||||
|
|
||||||
|
Powrót pani
|
||||||
|
:end:
|
||||||
|
|
||||||
|
*** Znaki
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :exports both :results raw drawer
|
||||||
|
from itertools import islice
|
||||||
|
|
||||||
|
def get_characters(t):
|
||||||
|
yield from t
|
||||||
|
|
||||||
|
list(islice(get_characters(pan_tadeusz), 100, 150))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
['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
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
c = Counter(get_characters(pan_tadeusz))
|
||||||
|
|
||||||
|
c
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
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:
|
||||||
|
|
||||||
|
Napiszmy pomocniczą funkcję, która zwraca /listę frekwencyjną/.
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
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
|
||||||
|
from collections import Counter
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
def freq_list(g, top=None):
|
||||||
|
c = Counter(g)
|
||||||
|
|
||||||
|
if top is None:
|
||||||
|
items = c.items()
|
||||||
|
else:
|
||||||
|
items = c.most_common(top)
|
||||||
|
|
||||||
|
return OrderedDict(sorted(items, key=lambda t: -t[1]))
|
||||||
|
|
||||||
|
freq_list(get_characters(pan_tadeusz), top=8)
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
OrderedDict([(' ', 63444), ('a', 30979), ('i', 29353), ('e', 25343), ('o', 23050), ('z', 22741), ('n', 15505), ('r', 15328)])
|
||||||
|
:end:
|
||||||
|
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :results file
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
def rang_freq_with_labels(name, g, top=None):
|
||||||
|
freq = freq_list(g, top)
|
||||||
|
|
||||||
|
plt.figure(figsize=(12, 3))
|
||||||
|
plt.bar(freq.keys(), freq.values())
|
||||||
|
|
||||||
|
fname = f'{name}.png'
|
||||||
|
|
||||||
|
plt.savefig(fname)
|
||||||
|
|
||||||
|
return fname
|
||||||
|
|
||||||
|
rang_freq_with_labels('pt-chars', get_characters(pan_tadeusz))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file:pt-chars.png]]
|
||||||
|
|
||||||
|
*** Słowa
|
||||||
|
|
||||||
|
Co rozumiemy pod pojęciem słowa czy wyrazu, nie jest oczywiste. W praktyce zależy to od wyboru /tokenizatora/.
|
||||||
|
|
||||||
|
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
|
||||||
|
from itertools import islice
|
||||||
|
import regex as re
|
||||||
|
|
||||||
|
def get_words(t):
|
||||||
|
for m in re.finditer(r'[\p{L}0-9\*]+', t):
|
||||||
|
yield m.group(0)
|
||||||
|
|
||||||
|
list(islice(get_words(pan_tadeusz), 100, 130))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
:results:
|
||||||
|
['Ty', 'co', 'gród', 'zamkowy', 'Nowogródzki', 'ochraniasz', 'z', 'jego', 'wiernym', 'ludem', 'Jak', 'mnie', 'dziecko', 'do', 'zdrowia', 'powróciłaś', 'cudem', 'Gdy', 'od', 'płaczącej', 'matki', 'pod', 'Twoją', 'opiekę', 'Ofiarowany', 'martwą', 'podniosłem', 'powiekę', 'I', 'zaraz']
|
||||||
|
:end:
|
||||||
|
|
||||||
|
Zobaczmy 20 najczęstszych wyrazów.
|
||||||
|
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :results file
|
||||||
|
rang_freq_with_labels('pt-words-20', get_words(pan_tadeusz), top=20)
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file:pt-words-20.png]]
|
||||||
|
|
||||||
|
Zobaczmy pełny obraz, już bez etykiet.
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :results file
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from math import log
|
||||||
|
|
||||||
|
def rang_freq(name, g):
|
||||||
|
freq = freq_list(g)
|
||||||
|
|
||||||
|
plt.figure().clear()
|
||||||
|
plt.plot(range(1, len(freq.values())+1), freq.values())
|
||||||
|
|
||||||
|
fname = f'{name}.png'
|
||||||
|
|
||||||
|
plt.savefig(fname)
|
||||||
|
|
||||||
|
return fname
|
||||||
|
|
||||||
|
rang_freq('pt-words', get_words(pan_tadeusz))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file:pt-words.png]]
|
||||||
|
|
||||||
|
Widać, jak różne skale obejmuje ten wykres. Zastosujemy logartm,
|
||||||
|
najpierw tylko do współrzędnej y.
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :results file
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from math import log
|
||||||
|
|
||||||
|
def rang_log_freq(name, g):
|
||||||
|
freq = freq_list(g)
|
||||||
|
|
||||||
|
plt.figure().clear()
|
||||||
|
plt.plot(range(1, len(freq.values())+1), [log(y) for y in freq.values()])
|
||||||
|
|
||||||
|
fname = f'{name}.png'
|
||||||
|
|
||||||
|
plt.savefig(fname)
|
||||||
|
|
||||||
|
return fname
|
||||||
|
|
||||||
|
rang_log_freq('pt-words-log', get_words(pan_tadeusz))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file:pt-words-log.png]]
|
||||||
|
|
||||||
|
**Pytanie** Dlaczego widzimy coraz dłuższe „schodki”?
|
||||||
|
|
||||||
|
*** Hapax legomena
|
||||||
|
|
||||||
|
Z poprzedniego wykresu możemy odczytać, że ok. 2/3 wyrazów wystąpiło
|
||||||
|
dokładnie 1 raz. Słowa występujące jeden raz w danym korpusie noszą
|
||||||
|
nazwę /hapax legomena/ (w liczbie pojedycznej /hapax legomenon/, ἅπαξ
|
||||||
|
λεγόμενον, „raz powiedziane”, żargonowo: „hapaks”).
|
||||||
|
|
||||||
|
„Prawdziwe” hapax legomena, słowa, które wystąpiły tylko raz w /całym/
|
||||||
|
korpusie tekstów danego języka (np. starożytnego) rzecz jasna
|
||||||
|
sprawiają olbrzymie trudności w tłumaczeniu. Przykładem jest greckie
|
||||||
|
słowo ἐπιούσιος, przydawka odnosząca się do chleba w modlitwie „Ojcze
|
||||||
|
nasz”. Jest to jedyne poświadczenie tego słowa w całym znanym korpusie
|
||||||
|
greki (nie tylko z Pisma Świętego). W języku polskim tłumaczymy je na
|
||||||
|
„powszedni”, ale na przykład w rosyjskim przyjął się odpowiednik
|
||||||
|
„насущный” — o przeciwstawnym do polskiego znaczeniu!
|
||||||
|
|
||||||
|
W sumie podobne problemy hapaksy mogą sprawiać metodom statystycznym
|
||||||
|
przy przetwarzaniu jakiekolwiek korpusu.
|
||||||
|
|
||||||
|
*** Wykres log-log
|
||||||
|
|
||||||
|
Jeśli wspomniany wcześniej wykres narysujemy używając skali
|
||||||
|
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
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from math import log
|
||||||
|
|
||||||
|
def log_rang_log_freq(name, g):
|
||||||
|
freq = freq_list(g)
|
||||||
|
|
||||||
|
plt.figure().clear()
|
||||||
|
plt.plot([log(x) for x in range(1, len(freq.values())+1)], [log(y) for y in freq.values()])
|
||||||
|
|
||||||
|
fname = f'{name}.png'
|
||||||
|
|
||||||
|
plt.savefig(fname)
|
||||||
|
|
||||||
|
return fname
|
||||||
|
|
||||||
|
log_rang_log_freq('pt-words-log-log', get_words(pan_tadeusz))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file:pt-words-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:voy-chars.png]]
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :session mysession :results file
|
||||||
|
log_rang_log_freq('voy-log-log', get_words(voynich))
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
[[file: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: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:voy-words-log-log.png]]
|
BIN
wyk/02_Jezyki/voynich135.jpg
Normal file
BIN
wyk/02_Jezyki/voynich135.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 MiB |
Loading…
Reference in New Issue
Block a user