1 Beolvasás: A .txt fájlok betöltése

library(readtext)

# A fájlstruktúra betöltése

fajlnevek <- list.files(
    path      = "C:/corpushun",
    pattern   = "\\.txt$",
    recursive = TRUE,
    full.names = TRUE
)

# A fájlok tatalmának betöltése

texts <- readtext(fajlnevek, encoding = "UTF-8")

2 A vizsgálat előkészítése #1: korpusz létrehozása 16 regény teljes szövegéből

#Szerző és cím kinyerése az EREDETI fajlnevek vektorból
texts$szerzo <- basename(dirname(fajlnevek))
texts$cim    <- sub("\\.txt$", "", basename(fajlnevek))

konyv_korpusz <- quanteda::corpus(texts)
# Ellenőrzés
summary(konyv_korpusz)

Tokens (Szóelőfordulások/Textus-szavak) = A szövegben található összes szó száma, függetlenül attól, hogy hányszor ismétlődnek.
Types (Szóalakok/Szótári szavak) = A szövegben előforduló különböző, egyedi szóalakok száma. Minden egyedi forma csak egyszer számít.
A Type/Token arány (TTR) a szöveggazdagságot, választékosságot mutatja.

3 Tisztítás: Tokenizálás, írásjelek és a magyar stoplistás szavak kiszűrése

szavak <- tokens(konyv_korpusz, 
                 remove_punct = TRUE, 
                 remove_numbers = TRUE, 
                 remove_symbols = TRUE)
szavak <- tokens_tolower(szavak)

# Magyar szavak és az egyedi szavak összevonása egy vektorba
sajat_kizárandó <- c(stopwords("hu"), "page", "oldal", "txt", "pdf")

# Alkalmazás a korpuszon
szavak <- tokens_remove(szavak, sajat_kizárandó)
#

Figyelem! Ez egy kiemelt információs doboz.

A szövegbányászatban (text mining) és a természetes nyelvfeldolgozásban (NLP) a stopword-ök (stoplisták vagy tiltószavak) olyan rendkívül gyakori szavak, amelyek kevés önálló jelentéssel bírnak, ezért a szövegek előfeldolgozása során általában eltávolítjuk őket. Példák stopword-ökre: Angol nyelvben: the, is, at, which, on, and, a, an Magyar nyelvben: a, az, egy, és, hogy, vagy, volt, mint, sem Ha van egy mondatunk: „A macska ül az asztal mellett.” – a stopword-ök (a, az, mellett) kiszűrése után a lényegi kulcsszavak maradnak meg: „macska, ül, asztal”. A magyar nyelv szaktudósok által kiválasztott tiltószavai (összesen 199 db):

   print(stopwords("hu"))
##   [1] "a"          "ahogy"      "ahol"       "aki"        "akik"      
##   [6] "akkor"      "alatt"      "által"      "általában"  "amely"     
##  [11] "amelyek"    "amelyekben" "amelyeket"  "amelyet"    "amelynek"  
##  [16] "ami"        "amit"       "amolyan"    "amíg"       "amikor"    
##  [21] "át"         "abban"      "ahhoz"      "annak"      "arra"      
##  [26] "arról"      "az"         "azok"       "azon"       "azt"       
##  [31] "azzal"      "azért"      "aztán"      "azután"     "azonban"   
##  [36] "bár"        "be"         "belül"      "benne"      "cikk"      
##  [41] "cikkek"     "cikkeket"   "csak"       "de"         "e"         
##  [46] "eddig"      "egész"      "egy"        "egyes"      "egyetlen"  
##  [51] "egyéb"      "egyik"      "egyre"      "ekkor"      "el"        
##  [56] "elég"       "ellen"      "elő"        "először"    "előtt"     
##  [61] "első"       "én"         "éppen"      "ebben"      "ehhez"     
##  [66] "emilyen"    "ennek"      "erre"       "ez"         "ezt"       
##  [71] "ezek"       "ezen"       "ezzel"      "ezért"      "és"        
##  [76] "fel"        "felé"       "hanem"      "hiszen"     "hogy"      
##  [81] "hogyan"     "igen"       "így"        "illetve"    "ill."      
##  [86] "ill"        "ilyen"      "ilyenkor"   "ison"       "ismét"     
##  [91] "itt"        "jó"         "jól"        "jobban"     "kell"      
##  [96] "kellett"    "keresztül"  "keressünk"  "ki"         "kívül"     
## [101] "között"     "közül"      "legalább"   "lehet"      "lehetett"  
## [106] "legyen"     "lenne"      "lenni"      "lesz"       "lett"      
## [111] "maga"       "magát"      "majd"       "majd"       "már"       
## [116] "más"        "másik"      "meg"        "még"        "mellett"   
## [121] "mert"       "mely"       "melyek"     "mi"         "mit"       
## [126] "míg"        "miért"      "milyen"     "mikor"      "minden"    
## [131] "mindent"    "mindenki"   "mindig"     "mint"       "mintha"    
## [136] "mivel"      "most"       "nagy"       "nagyobb"    "nagyon"    
## [141] "ne"         "néha"       "nekem"      "neki"       "nem"       
## [146] "néhány"     "nélkül"     "nincs"      "olyan"      "ott"       
## [151] "össze"      "ő"          "ők"         "őket"       "pedig"     
## [156] "persze"     "rá"         "s"          "saját"      "sem"       
## [161] "semmi"      "sok"        "sokat"      "sokkal"     "számára"   
## [166] "szemben"    "szerint"    "szinte"     "talán"      "tehát"     
## [171] "teljes"     "tovább"     "továbbá"    "több"       "úgy"       
## [176] "ugyanis"    "új"         "újabb"      "újra"       "után"      
## [181] "utána"      "utolsó"     "vagy"       "vagyis"     "valaki"    
## [186] "valami"     "valamint"   "való"       "vagyok"     "van"       
## [191] "vannak"     "volt"       "voltam"     "voltak"     "voltunk"   
## [196] "vissza"     "vele"       "viszont"    "volna"

4 A vizsgálat előkészítése #2: DFM (=Document Feature Matrix) létrehozása

# DFM létrehozása
konyv_dfm <- dfm(szavak)

# Ritka szavak kiszűrése (opcionális, pl. ami csak 1-2 dokumentumban van)
konyv_dfm <- dfm_trim(konyv_dfm, min_docfreq = 2)

5 A teljes korpusz alapján szó-együttelőfordulási hálózat rajzolása

# 1. Szükséges csomagok telepítése és betöltése
# install.packages(c("quanteda", "quanteda.textmodels", "quanteda.textplots"))
library(quanteda)
library(quanteda.textplots)

# 2. A konyv korpusz betöltése és tokenizálása
# Kisbetűssé alakítunk és eltávolítjuk az írásjeleket
tokens_konyv <- tokens(konyv_korpusz, 
                       remove_punct = TRUE, 
                       remove_symbols = TRUE) %>%
  tokens_tolower()

# 3. Stop szavak (töltelékszavak) eltávolítása
tokens_clean <- tokens_select(tokens_konyv, 
                              pattern = stopwords("hu"), 
                              selection = "remove")

# 4. Dokumentum-kifejezés mátrix (DFM) létrehozása
# Csak a leggyakoribb szavakat tartjuk meg a hálózat átláthatóságáért
dfm_konyv <- dfm(tokens_clean)
dfm_top <- dfm_select(dfm_konyv, pattern = names(topfeatures(dfm_konyv, 50)))

# 5. Szó-együttelőfordulási mátrix (FCM) létrehozása
fcm_konyv <- fcm(dfm_top)

# 6. A szóhálózat kirajzolása
textplot_network(fcm_konyv, 
                 min_freq = 0.8,         # Kapcsolatok erősségének szűrése
                 edge_color = "steelblue", # Élek színe
                 edge_alpha = 0.6,       # Élek átlátszósága
                 vertex_labelsize = 4)   # Címkék betűmérete

6 A teljes korpusz 25 leggyakoribb szavának kigyűjtése

gyakori_szavak <- textstat_frequency(konyv_dfm, n = 25)
print(gyakori_szavak)
##    feature frequency rank docfreq group
## 1       is     15359    1      16   all
## 2       ha      5390    2      16   all
## 3      hát      3096    3      16   all
## 4       se      2888    4      15   all
## 5    ember      2464    5      16   all
## 6      két      2436    6      16   all
## 7      kis      2209    7      16   all
## 8       te      2104    8      16   all
## 9   mondta      1843    9      16   all
## 10      úr      1701   10      16   all
## 11      le      1363   11      16   all
## 12    mind      1214   12      16   all
## 13    szép      1114   13      16   all
## 14   szólt      1063   14      15   all
## 15     hol      1034   15      16   all
## 16   török      1031   16       7   all
## 17 egyszer       985   17      16   all
## 18    öreg       942   18      16   all
## 19 asszony       939   19      15   all
## 20      ön       939   19       9   all
## 21   hozzá       847   21      16   all
## 22      no       847   21      15   all
## 23    uram       837   23      13   all
## 24     nap       833   24      16   all
## 25  nézett       819   25      15   all

7 A szógyakoriság vizualizációja ggplot2-vel

ggplot(gyakori_szavak, aes(x =
reorder(feature, frequency), y = frequency)) +
geom_col() +
coord_flip() +
labs(title = "A 25 leggyakoribb szó a teljes korpuszban", x = "Szavak", y = "Gyakoriság")

8 A szógyakoriság vizualizációja wordcloud-dal

# Ha még nincs meg a gyakori_szavak, így generálható:
gyakori_szavak <- textstat_frequency(konyv_dfm, n = 25)
# wordcloud2 adatframe-et vár "word" és "freq" oszlopokkal

if(!require(ggwordcloud)) install.packages("ggwordcloud")
## A szükséges csomag betöltődik: ggwordcloud
library(ggwordcloud)

# 1. Adatok előkészítése
wc_data <- data.frame(
  word = gyakori_szavak$feature,
  freq = gyakori_szavak$frequency
)

# 2. Kirajzolás ggplot alapon
ggplot(wc_data, aes(label = word, size = freq, color = freq)) +
  geom_text_wordcloud(shape = "triangle-forward") + # A beépített formák közül ez hasonlít legjobban a toll hegyére
  scale_size_area(max_size = 25) +
  theme_minimal() +
  scale_color_gradient(low = "darkblue", high = "darkred")

A szavakat írótoll alakban rendeztettem el az algoritmussal (ld. forráskód), a ‘stylus’ ui. az ókori Rómában ezt jelentette, s innen került át a mai nyelvekbe.

9 Szógyakoriság-táblázat szerzőnként

#A stilometriai elemzések alapja a dokumentum/szó gyakoriságtáblázat. Az
#alábbi függvény ezt normált relatív értékekkel adja vissza.

# Csoportonkénti TOP 5 szó kigyűjtése
#freq_top5 <- textstat_frequency(konyv_dfm, n = 5, groups = docvars(konyv_dfm,"szerzo"))
#print(freq_top5)

tofu <- topfeatures(
  konyv_dfm,
  n = 20,
  decreasing = TRUE,
  groups = szerzo
)
print(tofu)
## $`Fekete István`
##      is      ha   gyula  mondta  bütyök     kis     hát   miska    kele   bácsi 
##    2206     820     758     492     466     436     412     384     355     332 
##      se  nézett    öreg   ember   kutya      le  érezte     két csikasz   gólya 
##     311     299     254     226     216     211     203     198     197     186 
## 
## $`Gárdonyi Géza`
##       is    török       ha      hát  gergely      két       se    ember 
##     2870      937      877      858      732      646      466      444 
##       te  felelte     uram mondotta      kis   atilla    szólt      pap 
##      440      408      359      334      326      277      274      264 
##  asszony     mind      hol       le 
##      255      254      250      250 
## 
## $`Jókai Mór`
##      is      ha     hát      ön      úr     két   timár   ember      te    mind 
##    2439    1168     733     694     667     646     618     607     595     485 
##      őt    szép      no  mihály     kis      le egyszer   hozzá   szólt   amint 
##     361     352     342     340     338     301     291     288     285     274 
## 
## $`Kosztolányi Dezső`
##       is       se     nero   mondta  császár    szólt     anna   seneca 
##     1104      451      400      305      249      238      210      190 
##   melyet      föl       te      két       ha      kis       őt kérdezte 
##      176      170      164      161      159      158      149      143 
##   jancsi       le     ment  valamit 
##      113      109      103      103 
## 
## $`Márai Sándor`
##         is      ember         ha          j     emberi         te       élet 
##        763        336        317        262        222        186        160 
##    emberek     mondja   tábornok      világ  krisztina       soha     mondta 
##        147        145        132        128        111        108        102 
##     melyet    valamit        két valamilyen      igazi      napon 
##         86         81         78         73         66         61 
## 
## $`Mikszáth Kálmán`
##     is     ha  jókai     se    hát    kis  ember    két     úr magyar   közt 
##   2134    699    616    496    339    332    285    261    217    196    189 
##   szép  gyuri    hol     le   öreg  végre  mégis  hozzá    pál 
##    188    171    157    155    142    133    130    130    127 
## 
## $`Móra Ferenc`
##      is      se      ha  ferenc  mátyás     hát császár     kis      te    etel 
##    3262     951     863     615     497     496     470     458     450     429 
##   ember     két  mondta   isten      úr    lány     föl   ugyan     fiú      le 
##     326     321     297     287     284     251     230     221     218     215 
## 
## $`Rejtő Jenő`
##     is     ha    sir mondta   gróf     ön     úr  ember  kérem  leány    hát 
##    581    487    280    275    245    178    175    169    162    148    146 
##    kis     mr   lord    két   miss  kissé  tanár hiszem   állt 
##    142    139    135    125    123    113    112     98     97

10 Relatív szógyakoriság táblázat

A stilometriai elemzések alapja a dokumentum × szó frekvenciatáblázat. A make.table.of.frequencies() ezt normalizált relatív értékként adja vissza.

# make.table.of.frequencies() a szólistát (features) és a korpuszt várja.
texts_raw <- as.list(as.character(konyv_korpusz))
# `load.corpus()` és `make.frequency.list()` — Korpusz betöltése és szógyakoriságok
# Tokenizálás a stylo txt.to.words() segítségével
word_lists <- lapply(texts_raw, txt.to.words)

# make.frequency.list() alapértelmezetten (value=FALSE) csak a szavak neveit adja vissza.
# value=TRUE esetén a frekvenciaértékeket adja, a neveket names()-szel érjük el.
freq_list_vals  <- make.frequency.list(word_lists, value = TRUE)   # named numeric vektor
freq_list_words <- names(freq_list_vals)                            # szavak sorrendben

# A features argumentum: karaktervektor a szavakról (freq_list_words)
freq_table <- make.table.of.frequencies(word_lists, freq_list_words, relative = TRUE)
## processing  16  text samples
## .
## combining frequencies into a table...
freq_table_100 <- freq_table[, 1:100]

cat("Táblázat dimenziói:", nrow(freq_table_100), "dokumentum ×",
    ncol(freq_table_100), "szó\n")
## Táblázat dimenziói: 16 dokumentum × 100 szó
# Első 6 sor és 8 oszlop megtekintése
knitr::kable(round(freq_table_100[1:16, 1:10], 4),
             caption = "Relatív szófrekvenciák (16 dokumentum, top 10 szó)")
Relatív szófrekvenciák (16 dokumentum, top 10 szó)
a az és nem hogy is egy s volt de
Kele.txt 9.1833 2.6079 2.7720 2.1610 1.0637 1.3626 0.7091 0.3619 0.8175 1.2453
Tüskevár.txt 8.4372 2.2713 2.7583 2.0899 1.2520 1.5242 0.8832 0.5502 0.8952 1.3809
A láthatatlan ember.txt 9.8224 2.9230 1.5485 1.4379 1.7319 1.5633 0.9132 1.1209 0.7351 0.8322
Egri csillagok.txt 10.7482 3.0666 1.6043 1.2616 1.4344 1.2580 1.0396 0.9720 0.6044 0.5985
A kőszívű ember fiai.txt 7.1920 3.0327 1.0001 1.5487 1.5346 0.7958 1.2137 1.2453 0.7670 0.4340
Az arany ember.txt 7.8196 2.9742 0.9611 1.5077 1.7157 0.8991 1.0726 1.6509 0.8110 0.4172
Édes Anna.txt 7.8504 2.9549 1.3251 1.5396 1.7140 1.1868 1.3131 0.7197 0.8039 0.6375
Néró, a véres költő.txt 7.0404 2.2368 2.9921 1.7664 1.2707 0.9229 0.8688 0.3767 0.6849 0.6327
A gyertyak csonkig egnek.txt 9.0030 3.0919 2.8191 1.9263 1.0499 1.0306 1.0058 1.6286 0.9425 0.5071
Füveskonyv.txt 7.3494 3.4508 3.3421 1.8549 1.1705 1.2441 0.8443 1.3496 0.1599 0.6940
Jókai Mór élete és kora.txt 8.0824 2.8969 1.6645 1.3507 1.3437 1.2990 1.1772 0.8651 0.9677 0.8090
Szent Péter esernyője.txt 8.4838 3.1855 1.0861 1.5160 1.5505 1.2512 1.2723 0.8348 0.8271 0.8635
Aranykoporsó.txt 9.0161 3.3532 1.6402 1.7903 1.7830 1.3846 0.6568 0.8206 0.7214 0.7005
Ének a búzamezőkről.txt 8.7138 3.6177 1.3460 2.1291 2.0209 2.2182 0.6851 0.2598 0.6571 1.1244
A láthatatlan légió.txt 7.8436 2.5479 2.8067 1.7315 1.7268 0.7011 1.4586 0.0188 0.6799 0.9481
A szőke ciklon.txt 8.1940 2.5689 2.5079 1.3722 1.8328 0.6897 1.4282 0.0244 0.6386 0.6727

11 dist.delta() — Burrows-féle delta távolság a művek között

A Burrows-Delta a stilometria legismertebb távolságmértéke: a z-score-ra normalizált szófrekvenciák Manhattan-távolságaként számítja ki két szöveg stilisztikai különbségét.
A Z-score (magyarul: sztenderdizált érték vagy Z-érték) azt mutatja meg, hogy egy adott adatpont hány szórásnyi távolságra van a csoport átlagától. A kifejezést a tudomány, az orvostudomány és a pénzügy különböző területein használják, de a magva mindegyiknél ugyanaz a matematikai elv.
A képlet: \(Z = \frac{X - \mu}{\sigma}\), ahol x a megfigyelt érték, μ a populáció átlaga, és σ a populáció szórása.
A Manhattan-távolság a két pont közötti távolságot méri úgy, hogy csak vízszintesen és függőlegesen lehet haladni – mint egy sakktáblán vagy városban, ahol csak az utcákon lehet közlekedni.
Képlete (2D-ben): \[d_{\text{Manhattan}}(A, B) = |x_1 - x_2| + |y_1 - y_2|\] szemben az euklideszi távolsággal, amely az „átlós” légvonalbeli távolságot méri: \[d_{\text{Euler}}(A, B) = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}\]

# Z-score normalizálás
zscores <- scale(freq_table_100)

# Delta-távolság kiszámítása
delta_dist <- dist.delta(freq_table_100)
delta_mat  <- as.matrix(delta_dist)

# Leghasonlóbb szövegpárok (legkisebb Delta-érték)
delta_df <- as.data.frame(as.table(delta_mat)) %>%
  rename(Doc1 = Var1, Doc2 = Var2, Delta = Freq) %>%
  filter(as.character(Doc1) < as.character(Doc2)) %>%
  arrange(Delta) %>%
  head(15)

knitr::kable(delta_df, digits = 3,
             caption = "Leghasonlóbb szövegpárok (legkisebb Burrows-Delta)") %>%
  kable_styling(bootstrap_options = c("striped", "condensed"))
Leghasonlóbb szövegpárok (legkisebb Burrows-Delta)
Doc1 Doc2 Delta
A kőszívű ember fiai.txt Az arany ember.txt 0.390
A láthatatlan légió.txt A szőke ciklon.txt 0.569
Kele.txt Tüskevár.txt 0.595
Jókai Mór élete és kora.txt Szent Péter esernyője.txt 0.680
A láthatatlan ember.txt Egri csillagok.txt 0.711
A kőszívű ember fiai.txt Szent Péter esernyője.txt 0.757
Édes Anna.txt Néró, a véres költő.txt 0.772
Az arany ember.txt Szent Péter esernyője.txt 0.780
Édes Anna.txt Szent Péter esernyője.txt 0.818
A szőke ciklon.txt Édes Anna.txt 0.834
Aranykoporsó.txt Édes Anna.txt 0.845
Aranykoporsó.txt Ének a búzamezőkről.txt 0.863
Kele.txt Szent Péter esernyője.txt 0.865
Édes Anna.txt Jókai Mór élete és kora.txt 0.873
A szőke ciklon.txt Szent Péter esernyője.txt 0.877

12 A Delta-távolság hőtérképe

delta_long <- as.data.frame(delta_mat) %>%
  tibble::rownames_to_column("Doc1") %>%
  pivot_longer(-Doc1, names_to = "Doc2", values_to = "Delta")

ggplot(delta_long, aes(x = Doc1, y = Doc2, fill = Delta)) +
  geom_tile() +
  scale_fill_distiller(palette = "RdYlBu", direction = -1) +
  labs(title    = "Burrows-Delta hőtérkép",
       subtitle = "A 16 vizsgált regény páronkénti távolsága",
       x = NULL, y = NULL) +
  theme_minimal(base_size = 10) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

13 dist.cosine() — Koszinusz-távolság

A koszinusz-hasonlóság a vektorok szögét méri; dist.cosine() az ebből számított távolságot adja vissza (1 − koszinusz-hasonlóság).

cos_dist <- dist.cosine(freq_table_100)
cos_mat  <- as.matrix(cos_dist)

# Leghasonlóbb párok koszinusz szerint
cos_df <- as.data.frame(as.table(cos_mat)) %>%
  rename(Doc1 = Var1, Doc2 = Var2, CosDist = Freq) %>%
  filter(as.character(Doc1) < as.character(Doc2)) %>%
  arrange(CosDist) %>%
  head(15)

knitr::kable(cos_df, digits = 4,
             caption = "Leghasonlóbb párok — koszinusz-távolság alapján") %>%
  kable_styling(bootstrap_options = c("striped", "condensed"))
Leghasonlóbb párok — koszinusz-távolság alapján
Doc1 Doc2 CosDist
A kőszívű ember fiai.txt Az arany ember.txt 0.0025
Kele.txt Tüskevár.txt 0.0040
A láthatatlan légió.txt A szőke ciklon.txt 0.0045
Jókai Mór élete és kora.txt Szent Péter esernyője.txt 0.0052
Édes Anna.txt Szent Péter esernyője.txt 0.0053
A láthatatlan ember.txt Egri csillagok.txt 0.0058
Édes Anna.txt Jókai Mór élete és kora.txt 0.0062
Aranykoporsó.txt Édes Anna.txt 0.0073
A láthatatlan ember.txt Aranykoporsó.txt 0.0074
Aranykoporsó.txt Jókai Mór élete és kora.txt 0.0076
Aranykoporsó.txt Szent Péter esernyője.txt 0.0078
A kőszívű ember fiai.txt Szent Péter esernyője.txt 0.0086
A láthatatlan ember.txt Szent Péter esernyője.txt 0.0089
A láthatatlan ember.txt Jókai Mór élete és kora.txt 0.0091
Az arany ember.txt Szent Péter esernyője.txt 0.0109

14 Hierarchikus klaszterezés és dendrogram

A stylo belső munkafolyamatát követve a Delta-távolságmátrixból hierarchikus klaszter és dendrogram készíthető.

library(ggdendro)
## Warning: a(z) 'ggdendro' csomag az R 4.5.2 verziójával lett fordítva
# Hierarchikus klaszterezés Ward-módszerrel
hc <- hclust(delta_dist, method = "ward.D2")

# Dendrogram ggplot-tal
dend_data <- dendro_data(hc, type = "rectangle")

ggplot() +
  geom_segment(data = segment(dend_data),
               aes(x = x, y = y, xend = xend, yend = yend),
               colour = "#555555") +
  geom_text(data   = label(dend_data),
            aes(x = x, y = y, label = label),
            hjust  = 1, size = 2.8, nudge_y = -0.02) +
  coord_flip() +
  scale_y_reverse(expand = expansion(mult = c(.3, .05))) +
  labs(title    = "Hierarchikus klaszterfa — Burrows-Delta (Ward)",
       subtitle = "16 magyar regény",
       x = NULL, y = "Delta-távolság") +
  theme_minimal(base_size = 11) +
  theme(axis.text.y = element_blank(),
        panel.grid  = element_blank())

15 n-gramok — Karakter és szó n-gramok

A stylo a szavakon kívül n-gramokkal is tud dolgozni, amelyek robusztusabbak zajosabb szövegekre. A make.ngrams() függvény ezt biztosítja. Felmerülhet, hogy ekkora háttér adatbázis mellett (16 teljes regény) ezek a számok valóben megfelelnek-e a szövegeknek? Nos, az egyik regény szívemhez különösen közel álló két alakjának példájával, a nagyszerű ‘kwic’ - ‘keyword in context’ - függvény segítségével a regény szavainak sorszámáig menő pontossággal támasztom alá a statisztikát.

# Karakter-trigramok (egy minta-könyven illusztrálva)
sample_words <- word_lists[["Kele.txt"]]
char_trigrams <- make.ngrams(sample_words, ngram.size = 3)

# Top 20 karakter-trigram
char_freq <- sort(table(char_trigrams), decreasing = TRUE)
top_char <- data.frame(
  trigram = names(char_freq)[1:25],
  count   = as.numeric(char_freq)[1:25]
)

knitr::kable(top_char,
             caption = "Top 25 szó-trigram — Fekete István: Kele") %>%
  kable_styling(bootstrap_options = c("striped", "condensed")) 
Top 25 szó-trigram — Fekete István: Kele
trigram count
a nagy fény 26
tás tás tás 19
most már nem 18
a kis fény 16
volt mint a 16
már nem is 15
azt mondta volna 12
és most már 12
a gólya nem 10
a kutya nem 10
a magányos gólya 10
mintha azt mondta 10
a nagy fehérség 9
de ez a 9
de most már 9
meg se moccant 9
nem is volt 9
a levegőbe és 8
a levegőben a 8
abban a pillanatban 8
az ember nem 8
az öreg gólya 8
az öreg ribizke 8
azt mondta hogy 8
billegette fülét miska 8
kave <- kwic(tokens(konyv_korpusz), pattern = phrase("az öreg gólya"))
kave
kave <- kwic(tokens(konyv_korpusz), pattern = phrase("billegette fülét Miska"))
kave

16 PCA — Főkomponens-elemzés

# A gyakorisági lista elkészítése (Melyek a leggyakoribb szavak?)
global_frequent_words <- make.frequency.list(szavak)

# 5. A keresett numerikus MÁTRIX legenerálása
# Itt az első 100 leggyakoribb szót (MFW) vesszük alapul, relatív gyakorisággal
fkv_matrix <- make.table.of.frequencies(szavak, 
                                        features = global_frequent_words[1:100], 
                                        relative = TRUE)
## processing  16  text samples
## .
## combining frequencies into a table...
# --- ELLENŐRZÉS ---
# Nézzük meg a mátrix szerkezetét (dimenzióit)
print(dim(fkv_matrix))  # Sorok száma (szövegek) x Oszlopok száma (100 szó)
## [1]  16 100
# Nézzünk bele az első 10 oszlopba
print(fkv_matrix[, 1:10])
##                                    is        ha        hát          se
## Kele.txt                     2.278853 0.8282284 0.40186229 0.622396471
## Tüskevár.txt                 2.569783 0.9707174 0.49945624 0.114794377
## A láthatatlan ember.txt      2.493546 0.7723752 0.58950086 0.342082616
## Egri csillagok.txt           1.978446 0.5989686 0.67528503 0.354987165
## A kőszívű ember fiai.txt     1.278868 0.6467707 0.43230919 0.098200781
## Az arany ember.txt           1.483636 0.6759290 0.39760528 0.123825644
## Édes Anna.txt                1.867154 0.2523182 0.21447045 0.586639753
## Néró, a véres költő.txt      1.411285 0.2177568 0.06615397 0.730450123
## A gyertyak csonkig egnek.txt 1.719777 0.5104152 0.05977836 0.009196671
## Füveskonyv.txt               2.018682 1.0690192 0.03632590 0.025947068
## Jókai Mór élete és kora.txt  2.060050 0.6283014 0.20155685 0.453155407
## Szent Péter esernyője.txt    2.022458 0.7661766 0.60177430 0.527327998
## Aranykoporsó.txt             2.262760 0.6898295 0.19178449 0.680909267
## Ének a búzamezőkről.txt      3.752912 0.8605815 0.79156242 1.063324994
## A láthatatlan légió.txt      1.104972 0.9900256 0.40787571 0.037079610
## A szőke ciklon.txt           1.077233 0.8374253 0.13703323 0.000000000
##                                   ember        két        kis         te
## Kele.txt                     0.36755697 0.19112963 0.52683166 0.25483950
## Tüskevár.txt                 0.15305917 0.24167237 0.44507995 0.15104523
## A láthatatlan ember.txt      0.33347676 0.35929432 0.20008606 0.32917384
## Egri csillagok.txt           0.33417359 0.55387248 0.26942023 0.33186097
## A kőszívű ember fiai.txt     0.27089871 0.40521932 0.14899429 0.28782988
## Az arany ember.txt           0.41691754 0.32603633 0.23401911 0.38624513
## Édes Anna.txt                0.07884943 0.31855169 0.26493408 0.19239261
## Néró, a véres költő.txt      0.12679512 0.16538493 0.20397475 0.28391080
## A gyertyak csonkig egnek.txt 0.69894698 0.29889180 0.06437670 0.52880857
## Füveskonyv.txt               0.95485210 0.06746238 0.02594707 0.36844837
## Jókai Mór élete és kora.txt  0.24464832 0.22657770 0.29886016 0.04309147
## Szent Péter esernyője.txt    0.33811030 0.30398908 0.36292574 0.10856753
## Aranykoporsó.txt             0.19327119 0.28693338 0.41627641 0.53818593
## Ének a búzamezőkről.txt      0.42274178 0.27607627 0.38391856 0.18980243
## A láthatatlan légió.txt      0.35967222 0.19281397 0.25584931 0.05561941
## A szőke ciklon.txt           0.27406646 0.27787294 0.27787294 0.03425831
##                                   mondta          úr
## Kele.txt                     0.394511149 0.029404558
## Tüskevár.txt                 0.666612962 0.074515648
## A láthatatlan ember.txt      0.131239243 0.144148021
## Egri csillagok.txt           0.123725168 0.197729007
## A kőszívű ember fiai.txt     0.097072036 0.413120527
## Az arany ember.txt           0.087473162 0.341940541
## Édes Anna.txt                0.305935785 0.189238630
## Néró, a véres költő.txt      0.573334436 0.005512831
## A gyertyak csonkig egnek.txt 0.464431876 0.018393342
## Füveskonyv.txt               0.005189414 0.025947068
## Jókai Mór élete és kora.txt  0.029190992 0.063942174
## Szent Péter esernyője.txt    0.062038588 0.530429927
## Aranykoporsó.txt             0.221518517 0.055007954
## Ének a búzamezőkről.txt      0.319213183 0.532740920
## A láthatatlan légió.txt      0.630353369 0.470911046
## A szőke ciklon.txt           0.399680256 0.182710974
#A `stylo` a PCA-t a frekvenciatáblán végzi. Az `prcomp()` + vizualizáció
#megmutatja, milyen stilisztikai tengelyek mentén különülnek el a szövegek.
# PCA a z-score-olt top 100 MFW-en
zscores <- scale(freq_table_100)
pca_res <- prcomp(zscores, scale. = FALSE)
#
#1. Összefoglaló táblázat (megmutatja a magyarázott variancia %-os arányát)
#2. Az első 5 szöveg koordinátáinak megtekintése az első 2 főkomponensen
#head(pca_res$x[, 1:2])
#3. Megnézni, melyik szavak kapták a legnagyobb súlyt az első főkomponensben
head(sort(pca_res$rotation[, 1], decreasing = TRUE))
##      vagy    minden        és     ember      kell     ilyen 
## 0.1617785 0.1578463 0.1552553 0.1456440 0.1427342 0.1398548
#
# 1. A szórásokat négyzetre emeljük, hogy megkapjuk a varianciákat (sajátértékeket)
pca_variance <- pca_res$sdev^2

# 2. Kiszámoljuk az egyes komponensek százalékos arányát a teljes varianciához képest
pca_percentage <- (pca_variance / sum(pca_variance)) * 100

# 3. Kerekítjük az eredményt tizedesjegyre a szebb megjelenésért
pca_percentage <- round(pca_percentage, 1)

# Ellenőrzés: írassuk ki az első két komponens százalékát
print(paste0("PC1: ", pca_percentage[1], "%"))
## [1] "PC1: 24.8%"
print(paste0("PC2: ", pca_percentage[2], "%"))
## [1] "PC2: 15%"
# And now visualize PCA results

# Grafikon rajzolása a százalékos értékek automatikus megjelenítésével
plot(pca_res$x[,1], pca_res$x[,2], 
     xlab = paste0("PC1 (", pca_percentage[1], "%)"), 
     ylab = paste0("PC2 (", pca_percentage[2], "%)"), 
     main = "Szövegek stilisztikai térképe (PCA)",
     type = "n")

# Szövegek neveinek kiírása
text(pca_res$x[,1], pca_res$x[,2], 
     labels = rownames(fkv_matrix), 
     col = "darkred", cex = 0.8)

#
# 1. Kiszámoljuk a százalékokat (ha az előző lépésből nem maradt meg a memóriában)
pca_variance <- pca_res$sdev^2
pca_percentage <- (pca_variance / sum(pca_variance)) * 100

# 2. Megrajzoljuk a kőgörbét az első 10 főkomponensre
plot(pca_percentage[1:10], 
     type = "b",                    # "b" mint both: pontok és vonalak egyszerre
     pch = 19,                      # Teli kör alakú pontok
     col = "blue",                  # Kék színű vonal
     lwd = 2,                       # Vonalvastagság
     xlab = "Főkomponensek", 
     ylab = "Magyarázott variancia (%)", 
     main = "Scree Plot (Százalékos bontás)",
     xaxt = "n")                    # Kikapcsoljuk az alapértelmezett X tengelyt a szebb számozásért

# 3. Egyedi X tengely feliratok (PC1, PC2, PC3...)
axis(1, at = 1:10, labels = paste0("PC", 1:10))

# 4. Rács hozzáadása a könnyebb leolvashatóságért
grid(nx = NULL, ny = TRUE, col = "gray", lty = "dotted")

#
# Biplot
# 1. Kiszámoljuk a szavak távolságát a középponttól a PC1 és PC2 síkon
word_importance <- rowSums(pca_res$rotation[, 1:2]^2)

# 2. Kiválasztjuk a 10 legnagyobb hatású szó indexét
top_words <- order(word_importance, decreasing = TRUE)[1:10]

# 3. Készítünk egy másolatot a PCA eredményből, ahol csak a top szavak maradnak
pca_clean <- pca_res
pca_clean$rotation <- pca_res$rotation[top_words, ]

# 4. Erre hívjuk meg a biplotot – a kép letisztul, a nyilak értelmezhetőek lesznek
# 1. Üres grafikon létrehozása a tengelyekkel
plot(pca_res$x[,1], pca_res$x[,2], 
     xlab = "PC1", ylab = "PC2", 
     main = "Tiszta PCA Grafikon",
     type = "n") # a "n" jelentése: ne rajzoljon pontokat

# 2. Csak a regények címeinek kiírása
text(pca_res$x[,1], pca_res$x[,2], 
     labels = rownames(pca_res$x), 
     col = "darkgray", cex = 0.8, font = 2) # font = 2: félkövér szöveg

#…………………………………………………….