5  コロケーション

Code
dat_txt <-
  tibble::tibble(
    doc_id = seq_along(audubon::polano) |> as.character(),
    text = audubon::polano
  ) |>
  dplyr::mutate(text = audubon::strj_normalize(text))
dat <- gibasa::tokenize(dat_txt, text, doc_id)
dat_count <- dat |>
  gibasa::prettify(col_select = c("POS1", "Original")) |>
  dplyr::filter(POS1 %in% c("名詞", "動詞", "形容詞")) |>
  dplyr::mutate(
    doc_id = forcats::fct_drop(doc_id),
    token = dplyr::if_else(is.na(Original), token, Original)
  ) |>
  dplyr::count(doc_id, token)

5.1 文書内での共起

共起関係を数える機能はgibasaには実装されていません。文書内での共起を簡単に数えるには、たとえば次のようにします。

dat_fcm <- dat_count |>
  tidytext::cast_dfm(doc_id, token, n) |>
  quanteda::fcm()
#> Warning in .recacheSubclasses(def@className, def, env): undefined subclass
#> "ndiMatrix" of class "replValueSp"; definition not updated

dat_fcm
#> Feature co-occurrence matrix of: 2,167 by 2,167 features.
#>                       features
#> features               ポラーノ 広場 宮沢 賢治 レオーノ・キュースト 七 十 官 等
#>   ポラーノ                    5   52    0    0                    0  0  2  0  0
#>   広場                        0    5    0    0                    0  0  2  0  0
#>   宮沢                        0    0    0    2                    0  0  0  0  0
#>   賢治                        0    0    0    0                    0  0  0  0  0
#>   レオーノ・キュースト        0    0    0    0                    0  1  3  3  3
#>   七                          0    0    0    0                    0  0  9  1  1
#>   十                          0    0    0    0                    0  0  8  6  6
#>   官                          0    0    0    0                    0  0  0  0  5
#>   等                          0    0    0    0                    0  0  0  0  0
#>   誌                          0    0    0    0                    0  0  0  0  0
#>                       features
#> features               誌
#>   ポラーノ              0
#>   広場                  0
#>   宮沢                  0
#>   賢治                  0
#>   レオーノ・キュースト  1
#>   七                    1
#>   十                    1
#>   官                    1
#>   等                    1
#>   誌                    0
#> [ reached max_feat ... 2,157 more features, reached max_nfeat ... 2,157 more features ]

5.2 任意のウィンドウ内での共起

5.2.1 共起の集計

RMeCab::collocateのような任意のウィンドウの中での共起を集計するには、次のようにする必要があります。ここではwindowは前後5個のトークンを見るようにします。

dat_corpus <- dat |>
  gibasa::pack()

dat_fcm <- dat_corpus |>
  quanteda::corpus() |>
  quanteda::tokens(what = "fastestword") |>
  quanteda::fcm(context = "window", window = 5)

こうすると、nodeについて共起しているtermとその頻度を確認できます。以下では、「わたくし」というnodeと共起しているtermで頻度が上位20までであるものを表示しています。

dat_fcm <- dat_fcm |>
  tidytext::tidy() |>
  dplyr::rename(node = document, term = term) |>
  dplyr::filter(node == "わたくし") |>
  dplyr::slice_max(count, n = 20)

dat_fcm
#> # A tibble: 20 × 3
#>    node     term  count
#>    <chr>    <chr> <dbl>
#>  1 わたくし は      205
#>  2 わたくし 。      122
#>  3 わたくし た      110
#>  4 わたくし て       99
#>  5 わたくし 、       91
#>  6 わたくし まし     90
#>  7 わたくし を       62
#>  8 わたくし に       61
#>  9 わたくし が       51
#> 10 わたくし し       37
#> 11 わたくし も       35
#> 12 わたくし で       33
#> 13 わたくし ども     28
#> 14 わたくし と       23
#> 15 わたくし 」       23
#> 16 わたくし です     22
#> 17 わたくし へ       17
#> 18 わたくし い       15
#> 19 わたくし 「       15
#> 20 わたくし から     14

5.2.2 T値やMI値の算出

T値やMI値は、たとえば次のようにして計算できます。

T値については「1.65」を越える場合、その共起が偶然ではないと考える大まかな目安となるそうです。また、MI値については「1.58」を越える場合に共起関係の大まかな目安となります(いずれの値についても「2」などを目安とする場合もあります)。

ntok <- dat_corpus |>
  quanteda::corpus() |>
  quanteda::tokens(what = "fastestword") |>
  quanteda::ntoken() |>
  sum()

total <- dat_corpus |>
  quanteda::corpus() |>
  quanteda::tokens(what = "fastestword") |>
  quanteda::tokens_select(c("わたくし", dat_fcm$term)) |>
  quanteda::dfm() |>
  quanteda::colSums()

dat_fcm |>
  dplyr::select(-node) |>
  dplyr::mutate(
    expect = total[term] / ntok * total["わたくし"] * 5 * 2, ## 5はwindowのサイズ
    t = (count - expect) / sqrt(count),
    mi = log2(count / expect)
  )
#> # A tibble: 20 × 5
#>    term  count expect       t      mi
#>    <chr> <dbl>  <dbl>   <dbl>   <dbl>
#>  1 は      205  88.1    8.17   1.22  
#>  2 。      122 161.    -3.53  -0.400 
#>  3 た      110 108.     0.190  0.0264
#>  4 て       99 106.    -0.694 -0.0972
#>  5 、       91 102.    -1.13  -0.162 
#>  6 まし     90  64.1    2.73   0.489 
#>  7 を       62  67.4   -0.689 -0.121 
#>  8 に       61  68.8   -1.00  -0.174 
#>  9 が       51  51.9   -0.126 -0.0252
#> 10 し       37  24.1    2.11   0.616 
#> 11 も       35  31.4    0.615  0.158 
#> 12 で       33  34.7   -0.290 -0.0710
#> 13 ども     28   3.11   4.70   3.17  
#> 14 と       23  28.7   -1.18  -0.317 
#> 15 」       23  54.8   -6.63  -1.25  
#> 16 です     22  15.4    1.40   0.512 
#> 17 へ       17  16.5    0.114  0.0403
#> 18 い       15  17.4   -0.628 -0.217 
#> 19 「       15  56.2  -10.6   -1.91  
#> 20 から     14  20.4   -1.72  -0.546

注意点として、quantedaは全角スペースなどをトークンとして数えないようなので、ここでの総語数(ntok)は、RMeCabの計算で使われる総語数よりも少なくなることがあります。RMeCabでの計算結果と概ね一致させたい場合は、総語数としてgibasa::tokenizeの戻り値の行数を使ってください。