3  N-gram

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)

3.1 dplyrを使ってNgramを数える方法

dplyrを使って簡単にやる場合、次のようにすると2-gramを集計できます。

bigram <- gibasa::ngram_tokenizer(2)

dat_ngram <- dat |>
  gibasa::prettify(col_select = "Original") |>
  dplyr::mutate(
    token = dplyr::if_else(is.na(Original), token, Original)
  ) |>
  dplyr::reframe(token = bigram(token, sep = "-"), .by = doc_id) |>
  dplyr::count(doc_id, token)

str(dat_ngram)
#> tibble [24,398 × 3] (S3: tbl_df/tbl/data.frame)
#>  $ doc_id: Factor w/ 899 levels "1","2","3","4",..: 1 1 2 3 3 3 3 3 3 4 ...
#>  $ token : chr [1:24398] "の-広場" "ポラーノ-の" "宮沢-賢治" "レオーノ・キュースト-誌" ...
#>  $ n     : int [1:24398] 1 1 1 1 1 1 1 1 1 1 ...

なお、RMeCabでできるような「名詞-名詞」の2-gramだけを抽出したいといったケースでは、2-gramをつくる前に品詞でフィルタしてしまうと元の文書内におけるトークンの隣接関係を破壊してしまい、正しい2-gramを抽出することができません。そのようなことをしたい場合には、あらかじめ品詞のNgramもつくったうえで、後から品詞のNgramでフィルタします。

dat_ngram <- dat |>
  gibasa::prettify(col_select = c("POS1", "Original")) |>
  dplyr::mutate(
    token = dplyr::if_else(is.na(Original), token, Original)
  ) |>
  dplyr::reframe(
    token = bigram(token, sep = "-"),
    pos = bigram(POS1, sep = "-"), # 品詞のNgramをつくる
    .by = doc_id
  ) |>
  dplyr::filter(pos %in% c("名詞-名詞")) |> # 品詞のNgramでフィルタする
  dplyr::count(doc_id, token)

str(dat_ngram)
#> tibble [866 × 3] (S3: tbl_df/tbl/data.frame)
#>  $ doc_id: Factor w/ 899 levels "1","2","3","4",..: 2 3 3 3 3 3 4 4 5 5 ...
#>  $ token : chr [1:866] "宮沢-賢治" "レオーノ・キュースト-誌" "七-等" "十-七" ...
#>  $ n     : int [1:866] 1 1 1 1 1 1 1 1 1 1 ...

3.2 quantedaにNgramを持ちこむ方法

gibasa::packを使ってNgramの分かち書きをつくることもできます。この場合、次のようにquantedaの枠組みの中でNgramをトークンとして数えることで集計することができます。

dat_ngram <- dat |>
  gibasa::prettify(col_select = "Original") |>
  dplyr::mutate(token = dplyr::if_else(is.na(Original), token, Original)) |>
  gibasa::pack(n = 2)

str(dat_ngram)
#> tibble [899 × 2] (S3: tbl_df/tbl/data.frame)
#>  $ doc_id: Factor w/ 899 levels "1","2","3","4",..: 1 2 3 4 5 6 7 8 9 10 ...
#>  $ text  : chr [1:899] "ポラーノ-の の-広場" "宮沢-賢治" "前-十 十-七 七-等 等-官 官-レオーノ・キュースト レオーノ・キュースト-誌" "宮沢-賢治 賢治-訳述" ...

dat_ngram |>
  quanteda::corpus() |>
  quanteda::tokens(what = "fastestword") |>
  quanteda::dfm()
#> Warning in .recacheSubclasses(def@className, def, env): undefined subclass
#> "ndiMatrix" of class "replValueSp"; definition not updated
#> Document-feature matrix of: 899 documents, 10,890 features (99.75% sparse) and 0 docvars.
#>     features
#> docs ポラーノ-の の-広場 宮沢-賢治 前-十 十-七 七-等 等-官
#>    1           1       1         0     0     0     0     0
#>    2           0       0         1     0     0     0     0
#>    3           0       0         0     1     1     1     1
#>    4           0       0         1     0     0     0     0
#>    5           0       0         0     0     0     0     0
#>    6           0       0         0     0     0     0     1
#>     features
#> docs 官-レオーノ・キュースト レオーノ・キュースト-誌 賢治-訳述
#>    1                       0                       0         0
#>    2                       0                       0         0
#>    3                       1                       1         0
#>    4                       0                       0         1
#>    5                       0                       0         0
#>    6                       0                       0         0
#> [ reached max_ndoc ... 893 more documents, reached max_nfeat ... 10,880 more features ]

3.3 quantedaでNgramを数える方法

また、quantedaの枠組みの中でNgramをつくりながら数えて集計することもできます。

dat_ngram <- dat |>
  gibasa::prettify(col_select = "Original") |>
  dplyr::mutate(token = dplyr::if_else(is.na(Original), token, Original)) |>
  gibasa::pack()

str(dat_ngram)
#> tibble [899 × 2] (S3: tbl_df/tbl/data.frame)
#>  $ doc_id: Factor w/ 899 levels "1","2","3","4",..: 1 2 3 4 5 6 7 8 9 10 ...
#>  $ text  : chr [1:899] "ポラーノ の 広場" "宮沢 賢治" "前 十 七 等 官 レオーノ・キュースト 誌" "宮沢 賢治 訳述" ...

dat_ngram |>
  quanteda::corpus() |>
  quanteda::tokens(what = "fastestword") |>
  quanteda::tokens_ngrams(n = 2) |>
  quanteda::dfm()
#> Document-feature matrix of: 899 documents, 10,890 features (99.75% sparse) and 0 docvars.
#>     features
#> docs ポラーノ_の の_広場 宮沢_賢治 前_十 十_七 七_等 等_官
#>    1           1       1         0     0     0     0     0
#>    2           0       0         1     0     0     0     0
#>    3           0       0         0     1     1     1     1
#>    4           0       0         1     0     0     0     0
#>    5           0       0         0     0     0     0     0
#>    6           0       0         0     0     0     0     1
#>     features
#> docs 官_レオーノ・キュースト レオーノ・キュースト_誌 賢治_訳述
#>    1                       0                       0         0
#>    2                       0                       0         0
#>    3                       1                       1         0
#>    4                       0                       0         1
#>    5                       0                       0         0
#>    6                       0                       0         0
#> [ reached max_ndoc ... 893 more documents, reached max_nfeat ... 10,880 more features ]