1 file changed, 66 insertions(+), 40 deletions(-)
final.py | 106 +++++++++++++++++++++++++++++++++++++++------------------------

modified   final.py
@@ -49,8 +49,18 @@ from nltk.corpus import CategorizedPlaintextCorpusReader, CategorizedCorpusReade

# In[4]:

huvel = nltk.corpus.CategorizedPlaintextCorpusReader("/home/ivankartac/edu/python/huvel", r".*\.txt", encoding="utf-8", cat_pattern=r".*-(.*?)\,")
# Čistě formátovací záležitost: velmi dlouhé řádky můžeme uvnitř závorek rozdělit na více řádků,
# přičemž obsah závorek většinou odsazujeme na úroveň otevírací závorky...

huvel = nltk.corpus.CategorizedPlaintextCorpusReader("/home/ivankartac/edu/python/huvel",
                                                     r".*\.txt", encoding="utf-8",
                                                     cat_pattern=r".*-(.*?)\,")

# ... nebo (zvláště v případě, že by takto vznikly velmi krátké řádky) odřádkujeme hned za otevírací
# závorkou a odsadíme o standardní čtyři mezery:

huvel = nltk.corpus.CategorizedPlaintextCorpusReader(
    "/home/ivankartac/edu/python/huvel", r".*\.txt", encoding="utf-8", cat_pattern=r".*-(.*?)\,")

# In[5]:

@@ -103,10 +113,14 @@ import csv
def load_fdist(path):
    fdist = nltk.FreqDist()
    with open(path, newline='') as file:
        reader = csv.reader(file, delimiter = ';', quotechar = '"')
        # V Pythonu je zvykem, že kolem `=` ve volání funkce se nedělají mezery, kdežto kolem `=`
        # při přiřazování proměnné ano (z obou stran).
        reader = csv.reader(file, delimiter=';', quotechar='"')
        for row in reader:
            fdist[row[1]] = int(row[2])
    return(fdist)
    # `return` není funkce, je to speciální jazykový konstrukt, takže nepotřebuje závorky (ne že by
    # něčemu vadily, ale tím, že tam nemusí být, se zde obvykle nepíšou).
    return fdist


# **ZADÁNÍ:** Následně tuto funkci využijte k nahrání následujících dvou referenčních frekvenčních distribucí do Pythonu, např. do proměnných `syn2015_fdist` a `totalita_fdist` (jedná se o frekvenční distribuce slovních tvarů, case insensitive):
@@ -244,9 +258,16 @@ fd.N()
# In[9]:

def is_keyword_candidate(word, text_fd, ref_fd):
    # Pozor, je-li frekvence v textu *menší* než v ref. korpusu, měli bychom rovnou rozhodnout, že
    # slovo nebude klíčové (jinak nám jako kandidáti na klíčová slova vyvstanou i slova, jejichž
    # frekvence je statisticky významně nižší v textu než v ref. korpusu). Musíme porovnávat
    # relativní frekvence, protože text a korpus budou skoro jistě jinak dlouhé:
    if text_fd.freq(word) < ref_fd.freq(word):
        return False
    p = chi2_contingency([[text_fd[word], text_fd.N() - text_fd[word]], [ref_fd[word], ref_fd.N() - ref_fd[word]]], lambda_="log-likelihood")[1]
    is_candidate = (p < 0.001)
    return(is_candidate)
    is_candidate = p < 0.001
    return is_candidate
    # Případně lze taky rovnou `return p < 0.001`.


# **OVĚŘENÍ:** Otestujte si, zda vaše funkce dobře funguje. V následující buňce jsou proporce v textu i v referenčním korpusu stejné, výsledek by tedy měl být `False`.
@@ -273,8 +294,13 @@ is_keyword_candidate("kočka", text_fd_test, ref_fd_test)
text_fd_test = nltk.FreqDist()
ref_fd_test = nltk.FreqDist()

text_fd_test["kočka"] = 90
text_fd_test["zbytek"] = 1910
# Zkuste zde schválně upravit hodnoty tak, aby relativní frekvence v textu byla *nižší* než v
# referenčním korpusu; uvidíte, že původní verze funkce `is_keyword_candidate()` vrátí `True` i v
# tomto případě (bude-li rozdíl ve frekvencích dostatečně velký na to, aby byl statisticky
# významný).

text_fd_test["kočka"] = 10
text_fd_test["zbytek"] = 1990
ref_fd_test["kočka"] = 3000
ref_fd_test["zbytek"] = 197000

@@ -300,8 +326,11 @@ is_keyword_candidate("kočka", text_fd_test, ref_fd_test)
def din(word, text_fd, ref_fd):
    relf_text = text_fd[word] / text_fd.N()
    relf_ref = ref_fd[word] / ref_fd.N()
    index = 100 * ((relf_text - relf_ref) / (relf_text + relf_ref))
    return(index)
    # V případě nejistot stran vyhodnocení složitějšího matematického výrazu s vícero operátory
    # pochopitelně ničemu neuškodí seskupení zaznamenat explicitně pomocí závorek (dokonce bych
    # řekl, better safe than sorry), jen pro úplnost doplním, že ty vnější nutné nejsou :)
    index = 100 * (relf_text - relf_ref) / (relf_text + relf_ref)
    return index


# **OVĚŘENÍ:** Následující buňka by měla vrátit -100 -- cílové slovo se v textu vůbec nevyskytuje.
@@ -390,26 +419,27 @@ husak75 = list(tagger.tag(test))
# In[18]:

def keywords(text, ref_fd):
    frekvence = nltk.FreqDist()
    kandidati = []
    for token in text:
        word = token.word
        if word in frekvence:
            frekvence[word] += 1
        else:
            frekvence[word] = 1

    # Původní kód je naprosto v pořádku a funguje, ale inicializaci frekvenční distribuce můžeme v
    # tomto případě provést jednodušeji tak, že konstruktoru objektu (= funkci `nltk.FreqDist()`)
    # předáme kolekci nebo generátor objektů, z nichž chceme f.d. vytvořit. Zápis je kratší a
    # myslím, že by to takhle mělo být i o něco rychlejší (lze ověřit, kdyby vás to zajímalo) :)
    frekvence = nltk.FreqDist(token.word for token in text)
    kandidati = set()

    # Velmi pěkné, čisté a úsporné řešení! Jen drobnost: asi bych do proměnné `kandidati` uložil
    # rovnou množinu a přidával prvky do ní, čímž si ušetříme dodatečnou deduplikaci v samostatném
    # kroku (konverzi seznamu v množinu).
    for token in text:
        word = token.word
        if re.match(r"^[^RJTZ]", token.tag) and frekvence[word] > 2:
            if is_keyword_candidate(word, frekvence, ref_fd):
                index = din(word, frekvence, ref_fd)
                kandidati.append((word, index, frekvence[word]))
                kandidati.add((word, index, frekvence[word]))

    kandidati = set(kandidati)

    return sorted(kandidati, key=lambda kandidati:
                      (kandidati[1], kandidati[2]), reverse=True)
    # Po dvojtečce v rámci `lambda` odřádkování není nutné. Jinak nevím, jestli jste jméno
    # `kandidati` pro parametr anonymní funkce (`lambda`) použil jen tak nebo cíleně, každopádně to
    # není potřeba, parametr se může jmenovat jakkoli, typicky se volí nějaké krátké jméno.
    return sorted(kandidati, key=lambda x: (x[1], x[2]), reverse=True)


# In[19]:
@@ -493,8 +523,9 @@ def fdist(text):

    return(frekvence)

husak_fdist = fdist(husak_komplet)
havel_fdist = fdist(havel_komplet)
# Viz výše, zde by bylo asi nejjednodušší rovnou použít konstruktor `nltk.FreqDist()`.
husak_fdist = nltk.FreqDist(t.word for t in husak_komplet)
havel_fdist = nltk.FreqDist(t.word for t in havel_komplet)


# In[124]:
@@ -529,37 +560,29 @@ keywords(husak_komplet, syn2015_fdist)

# 'minfreq' určuje minimální frekvenci slova

def keywords(text, ref_fd, pos = r"^[^RJTZ]", minfreq = 3, alpha = 0.001):
    frekvence = nltk.FreqDist()
    kandidati = []
    for token in text:
        word = token.word
        if word in frekvence:
            frekvence[word] += 1
        else:
            frekvence[word] = 1
def keywords(text, ref_fd, pos=r"^[^RJTZ]", minfreq=3, alpha=0.001):
    frekvence = nltk.FreqDist(token.word for token in text)
    kandidati = set()

    for token in text:
        word = token.word
        if re.match(pos, token.tag) and frekvence[word] >= minfreq:
            if is_keyword_candidate(word, frekvence, ref_fd, alpha):
                index = din(word, frekvence, ref_fd)
                kandidati.append((word, index, frekvence[word]))

    kandidati = set(kandidati)
                kandidati.add((word, index, frekvence[word]))

    return sorted(kandidati, key=lambda kandidati:
                (kandidati[1], kandidati[2]), reverse=True)
                  (kandidati[1], kandidati[2]), reverse=True)


# In[28]:

# fakultativním argumentem 'alpha' je případně možné zvolit práh statistické významnosti

def is_keyword_candidate(word, text_fd, ref_fd, alpha = 0.001):
def is_keyword_candidate(word, text_fd, ref_fd, alpha=0.001):
    p = chi2_contingency([[text_fd[word], text_fd.N() - text_fd[word]], [ref_fd[word], ref_fd.N() - ref_fd[word]]], lambda_="log-likelihood")[1]
    is_candidate = (p < alpha)
    return(is_candidate)
    return is_candidate


# In[39]:
@@ -568,3 +591,6 @@ def is_keyword_candidate(word, text_fd, ref_fd, alpha = 0.001):
# s defaultním nastavením tu nejsou zarhnuty roky

keywords(husak75, syn2015_fdist, pos = r"^[^RJTZC]")

# Pěkné postřehy k datům a velmi dobré nápady na vylepšení funkce `keywords()`! A obzvlášť oceňuju,
# že ty nápady jsou převážně implementovány velmi idiomaticky, čistě a přehledně :)

Generated by David Lukeš using scpaste at Mon Feb 20 17:18:52 2017. CET. (original)