4  抽出語メニュー3

Code
suppressPackageStartupMessages({
  library(ggplot2)
  library(duckdb)
})
drv <- duckdb::duckdb()
con <- duckdb::dbConnect(drv, dbdir = "tutorial_jp/kokoro.duckdb", read_only = TRUE)

tbl <-
  readxl::read_xls("tutorial_jp/kokoro.xls",
    col_names = c("text", "section", "chapter", "label"),
    skip = 1
  ) |>
  dplyr::mutate(
    doc_id = factor(dplyr::row_number()),
    dplyr::across(where(is.character), ~ audubon::strj_normalize(.))
  ) |>
  dplyr::filter(!gibasa::is_blank(text)) |>
  dplyr::relocate(doc_id, text, section, label, chapter)

4.1 階層的クラスター分析(A.5.9)

4.1.1 非類似度のヒートマップ🍳

Jaccard係数を指定して非類似度のヒートマップを描くと、そもそもパターンがほとんど見えなかった。

Code
dfm <-
  dplyr::tbl(con, "tokens") |>
  dplyr::filter(
    pos %in% c(
      "名詞", # "名詞B", "名詞C",
      "地名", "人名", "組織名", "固有名詞",
      "動詞", "未知語", "タグ"
    )
  ) |>
  dplyr::mutate(
    token = dplyr::if_else(is.na(original), token, original),
    token = paste(token, pos, sep = "/")
  ) |>
  dplyr::count(doc_id, token) |>
  dplyr::collect() |>
  tidytext::cast_dfm(doc_id, token, n)

dat <- dfm |>
  quanteda::dfm_trim(min_termfreq = 30, termfreq_type = "rank") |>
  quanteda::dfm_weight(scheme = "boolean") |>
  proxyC::simil(margin = 2, method = "dice") |>
  rlang::as_function(~ 1 - .)()

factoextra::fviz_dist(as.dist(dat))
#> Warning in vp$just: 'just' の 'justification' への部分的マッチ

4.1.2 階層的クラスタリング

Code
clusters <-
  as.dist(dat) |>
  hclust(method = "ward.D2")

4.1.3 シルエット分析🍳

Code
factoextra::fviz_nbclust(
  as.matrix(dat),
  FUNcluster = factoextra::hcut,
  k.max = ceiling(sqrt(nrow(dat)))
)

Code
cluster::silhouette(cutree(clusters, k = 5), dist = dat) |>
  factoextra::fviz_silhouette(print.summary = FALSE) +
  theme_classic()
#> Warning in vp$just: 'just' の 'justification' への部分的マッチ

4.1.4 デンドログラム

デンドログラムについては、似たような表現を手軽に実現できる方法が見つけられない。ラベルの位置が左右反転しているが、factoextra::fviz_dend(horiz = TRUE)とするのが簡単かもしれない。

Code
factoextra::fviz_dend(clusters, k = 5, horiz = TRUE, labels_track_height = 0.3)
#> Warning in seq.default(15, 375, length = k + 1): 'length' の 'length.out'
#> への部分的な引数のマッチング
#> Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
#> of ggplot2 3.3.4.
#> ℹ The deprecated feature was likely used in the factoextra package.
#>   Please report the issue at <https://github.com/kassambara/factoextra/issues>.

4.1.5 デンドログラムと棒グラフ

KH Coderのソースコードを見た感じ、デンドログラムと一緒に語の出現回数を描いている表現は、やや独特なことをしている。むしろ語の出現回数のほうが主な情報になってよいなら、ふつうの棒グラフの横にggh4x::scale_y_dendrogram()でデンドログラムを描くことができる。

Code
dfm |>
  quanteda::dfm_trim(min_termfreq = 30, termfreq_type = "rank") |>
  quanteda::colSums() |>
  tibble::enframe() |>
  dplyr::mutate(
    clust = (clusters |> cutree(k = 5))[name]
  ) |>
  ggplot(aes(x = value, y = name, fill = factor(clust))) +
  geom_bar(stat = "identity", show.legend = FALSE) +
  scale_x_sqrt() +
  ggh4x::scale_y_dendrogram(hclust = clusters) +
  labs(x = "出現回数", y = element_blank()) +
  theme_bw()
#> Warning: The S3 guide system was deprecated in ggplot2 3.5.0.
#> ℹ It has been replaced by a ggproto system that can be extended.

4.2 共起ネットワーク(A.5.10)

4.2.1 グラフの作成

描画するグラフをtbl_graphとして作成する。

Code
dfm <-
  dplyr::tbl(con, "tokens") |>
  dplyr::filter(
    pos %in% c(
      "名詞", # "名詞B", "名詞C",
      "地名", "人名", "組織名", "固有名詞",
      "動詞", "未知語", "タグ"
    )
  ) |>
  dplyr::mutate(
    token = dplyr::if_else(is.na(original), token, original),
    token = paste(token, pos, sep = "/")
  ) |>
  dplyr::count(doc_id, token) |>
  dplyr::collect() |>
  tidytext::cast_dfm(doc_id, token, n)

dat <- dfm |>
  quanteda::dfm_trim(min_termfreq = 45, termfreq_type = "count") |>
  quanteda::dfm_weight(scheme = "boolean") |>
  proxyC::simil(margin = 2, method = "jaccard", rank = 3) |>
  as.matrix() |>
  tidygraph::as_tbl_graph(directed = FALSE) |>
  dplyr::distinct() |> # 重複を削除
  tidygraph::activate(edges) |>
  dplyr::filter(from != to)

dat
#> # A tbl_graph: 47 nodes and 82 edges
#> #
#> # An undirected simple graph with 2 components
#> #
#> # Edge Data: 82 × 3 (active)
#>     from    to weight
#>    <int> <int>  <dbl>
#>  1     1    12 0.132 
#>  2     1    38 0.123 
#>  3     2    30 0.0947
#>  4     2    43 0.0738
#>  5     3    19 0.131 
#>  6     3    46 0.131 
#>  7     4    12 0.163 
#>  8     4    19 0.171 
#>  9     4    34 0.0905
#> 10     4    40 0.141 
#> # ℹ 72 more rows
#> #
#> # Node Data: 47 × 1
#>   name     
#>   <chr>    
#> 1 東京/地名
#> 2 分る/動詞
#> 3 知る/動詞
#> # ℹ 44 more rows

4.2.2 相関係数の計算

ggraph::geom_edge_link2()alphaに渡す相関係数を計算する。このあたりのコードは書くのがなかなか難しかったので、あまりスマートなやり方ではないかもしれない。

KH Coderには、それぞれの共起が文書集合内のどのあたりの位置に出現したかを概観できるようにするために、共起ネットワーク中のエッジについて、共起の出現位置との相関係数によって塗り分ける機能がある。これを実現するには、まずそれぞれの文書について文書集合内での通し番号を振ったうえで、それぞれの文書についてエッジとして描きたい共起の有無を1, 0で表してから、通し番号とのあいだの相関係数を計算するということをやる。

まず、共起ネットワーク中に描きこむ共起と、それらを含む文書番号をリストアップした縦長のデータフレームをつくる。

Code
nodes <- tidygraph::activate(dat, nodes) |> dplyr::pull("name")
from <- nodes[tidygraph::activate(dat, edges) |> dplyr::pull("from")]
to <- nodes[tidygraph::activate(dat, edges) |> dplyr::pull("to")]

has_coocurrences <-
  dplyr::tbl(con, "tokens") |>
  dplyr::filter(
    pos %in% c(
      "名詞", # "名詞B", "名詞C",
      "地名", "人名", "組織名", "固有名詞",
      "動詞", "未知語", "タグ"
    )
  ) |>
  dplyr::mutate(
    token = dplyr::if_else(is.na(original), token, original),
    token = paste(token, pos, sep = "/")
  ) |>
  dplyr::filter(token %in% nodes) |>
  dplyr::collect() |>
  dplyr::reframe(
    from = from,
    to = to,
    has_from = purrr::map_lgl(from, ~ . %in% token),
    has_to = purrr::map_lgl(to, ~ . %in% token),
    .by = doc_id
  ) |>
  dplyr::filter(has_from & has_to) |>
  dplyr::group_by(from, to) |>
  dplyr::reframe(doc_id = doc_id)

has_coocurrences
#> # A tibble: 2,164 × 3
#>    from          to       doc_id
#>    <chr>         <chr>     <int>
#>  1 お嬢さん/名詞 K/未知語   1034
#>  2 お嬢さん/名詞 K/未知語   1035
#>  3 お嬢さん/名詞 K/未知語   1041
#>  4 お嬢さん/名詞 K/未知語   1042
#>  5 お嬢さん/名詞 K/未知語   1045
#>  6 お嬢さん/名詞 K/未知語   1046
#>  7 お嬢さん/名詞 K/未知語   1048
#>  8 お嬢さん/名詞 K/未知語   1049
#>  9 お嬢さん/名詞 K/未知語   1052
#> 10 お嬢さん/名詞 K/未知語   1054
#> # ℹ 2,154 more rows

次に、このデータフレームを共起ごとにグルーピングして、共起の有無と通し番号とのあいだの相関係数を含むデータフレームをつくる。

Code
correlations <- has_coocurrences |>
  dplyr::group_by(from, to) |>
  dplyr::group_map(\(.x, .y) {
    tibble::tibble(
      doc_number = seq_len(nrow(tbl)),
      from = which(nodes == .y$from),
      to = which(nodes == .y$to)
    ) |>
      dplyr::group_by(from, to) |>
      dplyr::summarise(
        cor = cor(doc_number, as.numeric(doc_number %in% .x[["doc_id"]])),
        .groups = "drop"
      )
  }) |>
  purrr::list_rbind()

correlations
#> # A tibble: 82 × 3
#>     from    to     cor
#>    <int> <int>   <dbl>
#>  1    43    46  0.292 
#>  2    43    44  0.141 
#>  3    10    15  0.104 
#>  4    10    34  0.0467
#>  5    20    46  0.246 
#>  6    20    43  0.199 
#>  7    32    43  0.158 
#>  8    32    41  0.104 
#>  9     8    17 -0.176 
#> 10     8    19 -0.127 
#> # ℹ 72 more rows

最後に、相関係数をtbl_graphのエッジと結合する。

Code
dat <- dat |>
  tidygraph::activate(edges) |>
  dplyr::left_join(correlations, by = dplyr::join_by(from == from, to == to))

4.2.3 共起ネットワーク

上の処理が間違っていなければ、文書集合の後のほうによく出てくる共起であるほど、エッジの色が濃くなっているはず。

Code
dat |>
  tidygraph::activate(nodes) |>
  dplyr::mutate(
    community = factor(tidygraph::group_leading_eigen())
  ) |>
  ggraph::ggraph(layout = "fr") +
  ggraph::geom_edge_link2(
    aes(
      alpha = dplyr::percent_rank(cor) + .01, # パーセンタイルが0だと透明になってしまうので、適当に下駄をはかせる
      width = dplyr::percent_rank(weight) + 1
    ),
    colour = "red"
  ) +
  ggraph::geom_node_point(aes(colour = community), show.legend = FALSE) +
  ggraph::geom_node_label(aes(colour = community, label = name), repel = TRUE, show.legend = FALSE) +
  ggraph::theme_graph()

4.3 自己組織化マップ(A.5.11)

4.3.1 自己組織化マップ(SOM)

SOMの実装としては、KH Coderはsomを使っているようだが、kohonenを使ったほうがよい。

行列が非常に大きい場合にはkohonen::som(mode = "online")としてもよいのかもしれないが、一般にバッチ型のほうが収束が早く、数十ステップ程度回せば十分とされる。

与える単語文書行列は、ここではtidytext::bind_tf_idf()を使ってTF-IDFで重みづけし、上位100語ほど抽出する。

Code
dat <-
  dplyr::tbl(con, "tokens") |>
  dplyr::filter(
    pos %in% c(
      "名詞", # "名詞B", "名詞C",
      "地名", "人名", "組織名", "固有名詞",
      "動詞", "未知語", "タグ"
    )
  ) |>
  dplyr::mutate(
    token = dplyr::if_else(is.na(original), token, original),
    token = paste(token, pos, sep = "/")
  ) |>
  dplyr::count(doc_id, token) |>
  dplyr::collect() |>
  tidytext::bind_tf_idf(token, doc_id, n) |>
  tidytext::cast_dfm(doc_id, token, tf_idf) |>
  quanteda::dfm_trim(
    min_termfreq = 100,
    termfreq_type = "rank"
  ) |>
  as.matrix() |>
  scale() |>
  t()

som_fit <-
  kohonen::som(
    dat,
    grid = kohonen::somgrid(20, 16, "hexagonal"),
    rlen = 50, # 学習回数
    alpha = c(0.05, 0.01),
    radius = 8,
    dist.fcts = "sumofsquares",
    mode = "batch",
    init = aweSOM::somInit(dat, 20, 16)
  )
#> Warning in seq.default(along = data): 'along' の 'along.with'
#> への部分的な引数のマッチング
#> Warning in seq.default(along = whatmap): 'along' の 'along.with'
#> への部分的な引数のマッチング
#> Warning in seq.default(along = data): 'along' の 'along.with'
#> への部分的な引数のマッチング
Code
aweSOM::somQuality(som_fit, dat)
#> 
#> ## Quality measures:
#>  * Quantization error     :  83.9693 
#>  * (% explained variance) :  92.52 
#>  * Topographic error      :  0.36 
#>  * Kaski-Lagus error      :  21.49617 
#>  
#> ## Number of obs. per map cell:
#>   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
#>   0   0   1   0   1   0   0   0   0   1   0   0   1   0   0   1   0   0   1   1 
#>  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40 
#>   1   0   0   1   0   0   0   0   1   0   0   0   0   0   0   1   0   0   1   0 
#>  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60 
#>   0   0   1   0   0   0   1   0   0   0   0   0   1   0   0   1   0   0   0   1 
#>  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80 
#>   1   0   0   0   1   0   0   1   0   0   2   0   0   1   1   0   0   0   2   0 
#>  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 
#>   0   0   1   0   1   0   0   0   1   1   0   1   0   0   0   0   0   1   0   1 
#> 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 
#>   1   1   0   0   0   0   0   0   0   0   1   0   0   0   0   0   1   0   0   0 
#> 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 
#>   0   0   0   0   0   0   1   0   0   0   0   0   0   1   0   0   0   0   0   1 
#> 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 
#>   1   0   0   1   0   2   0   1   0   1   1   0   1   1   1   0   0   0   2   0 
#> 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 
#>   0   0   0   1   0   0   1   0   1   0   0   0   0   0   1   0   0   0   1   0 
#> 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 
#>   0   0   1   0   0   0   0   0   0   0   0   0   0   0   0   1   0   1   1   0 
#> 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 
#>   0   1   1   0   0   1   0   0   0   0   1   0   1   0   0   0   0   0   0   1 
#> 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 
#>   1   0   1   0   0   0   1   0   0   1   0   0   0   1   0   0   0   1   0   0 
#> 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 
#>   0   0   0   1   0   0   0   0   1   1   0   0   1   0   0   0   1   0   0   2 
#> 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 
#>   1   0   0   0   1   2   0   0   0   2   0   0   0   0   1   0   0   1   0   0 
#> 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 
#>   0   1   0   0   1   0   0   1   0   0   0   0   0   1   0   0   1   0   0   0 
#> 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 
#>   2   0   1   0   0   0   0   0   1   0   0   1   0   0   0   1   0   0   0   1

4.3.2 U-Matrix

U-matrixは「各ノードの参照ベクトルが近傍ノードと異なる度合いで色づけする方法」(自己組織化マップ入門)。暖色の箇所はデータ密度が低い「山間部」で、寒色の箇所はデータ密度が高い「平野部」みたいなイメージ、写像の勾配が急峻になっている箇所を境にしてクラスタが分かれていると判断するみたいな見方をする。

Code
aweSOM::aweSOMsmoothdist(som_fit)
#> Warning in vp$just: 'just' の 'justification' への部分的マッチ

Code
aweSOM::aweSOMplot(
  som_fit,
  data = dat,
  type = "UMatrix"
)

4.3.3 ヒットマップ🍳

色を付けるためのクラスタリングをしておく。一部の「山間部」や「盆地」がクラスタになって、後はその他の部分みたいな感じに分かれるようだが、解釈するのに便利な感じで分かれてはくれなかったりする。

Code
clusters <- som_fit |>
  purrr::pluck("codes", 1) |> # 参照ベクトル(codebook vectors)は`codes`にリストとして格納されている
  dist() |>
  hclust(method = "ward.D2") |>
  cutree(k = 10)

ヒットマップ(hitmap, proportion map)は以下のような可視化の方法。ノードの中の六角形は各ノードが保持する参照ベクトルの数(比率)を表している。ノードの背景色が上のコードで得たクラスタに対応する。

Code
aweSOM::aweSOMplot(
  som_fit,
  data = dat,
  type = "Hitmap",
  superclass = clusters
)


Code
duckdb::dbDisconnect(con)
duckdb::duckdb_shutdown(drv)

sessioninfo::session_info(info = "packages")
#> ═ Session info ═══════════════════════════════════════════════════════════════
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package      * version    date (UTC) lib source
#>  abind          1.4-5      2016-07-21 [1] RSPM (R 4.3.0)
#>  audubon        0.5.1.9001 2023-09-03 [1] https://paithiov909.r-universe.dev (R 4.3.1)
#>  aweSOM         1.3        2022-08-30 [1] RSPM (R 4.3.0)
#>  backports      1.4.1      2021-12-13 [1] CRAN (R 4.3.0)
#>  blob           1.2.4      2023-03-17 [1] RSPM (R 4.3.0)
#>  broom          1.0.5      2023-06-09 [1] RSPM (R 4.3.0)
#>  cachem         1.0.8      2023-05-01 [1] CRAN (R 4.3.0)
#>  car            3.1-2      2023-03-30 [1] RSPM (R 4.3.0)
#>  carData        3.0-5      2022-01-06 [1] RSPM (R 4.3.0)
#>  cellranger     1.1.0      2016-07-27 [1] RSPM (R 4.3.0)
#>  class          7.3-22     2023-05-03 [4] CRAN (R 4.3.1)
#>  cli            3.6.2      2023-12-11 [1] RSPM (R 4.3.0)
#>  cluster        2.1.6      2023-12-01 [4] CRAN (R 4.3.2)
#>  colorspace     2.1-0      2023-01-23 [1] RSPM (R 4.3.0)
#>  curl           5.2.1      2024-03-01 [1] CRAN (R 4.3.3)
#>  DBI          * 1.2.2      2024-02-16 [1] CRAN (R 4.3.2)
#>  dbplyr         2.4.0      2023-10-26 [1] RSPM (R 4.3.0)
#>  dendextend     1.17.1     2023-03-25 [1] RSPM (R 4.3.0)
#>  digest         0.6.34     2024-01-11 [1] CRAN (R 4.3.2)
#>  dotCall64      1.1-1      2023-11-28 [1] RSPM (R 4.3.0)
#>  dplyr          1.1.4      2023-11-17 [1] CRAN (R 4.3.2)
#>  duckdb       * 0.9.2-1    2023-11-28 [1] RSPM (R 4.3.2)
#>  e1071          1.7-14     2023-12-06 [1] RSPM (R 4.3.0)
#>  ellipsis       0.3.2      2021-04-29 [1] CRAN (R 4.3.0)
#>  evaluate       0.23       2023-11-01 [1] CRAN (R 4.3.1)
#>  factoextra     1.0.7      2020-04-01 [1] RSPM (R 4.3.2)
#>  fansi          1.0.6      2023-12-08 [1] CRAN (R 4.3.2)
#>  farver         2.1.1      2022-07-06 [1] RSPM (R 4.3.0)
#>  fastmap        1.1.1      2023-02-24 [1] CRAN (R 4.3.0)
#>  fastmatch      1.1-4      2023-08-18 [1] RSPM (R 4.3.0)
#>  fields         15.2       2023-08-17 [1] RSPM (R 4.3.0)
#>  generics       0.1.3      2022-07-05 [1] CRAN (R 4.3.0)
#>  ggdendro       0.2.0      2024-02-23 [1] CRAN (R 4.3.2)
#>  ggh4x          0.2.8      2024-01-23 [1] RSPM (R 4.3.0)
#>  ggplot2      * 3.5.0      2024-02-23 [1] CRAN (R 4.3.2)
#>  ggpubr         0.6.0      2023-02-10 [1] RSPM (R 4.3.0)
#>  ggrepel        0.9.5      2024-01-10 [1] CRAN (R 4.3.2)
#>  ggsignif       0.6.4      2022-10-13 [1] RSPM (R 4.3.0)
#>  gibasa         1.1.0      2024-02-17 [1] https://paithiov909.r-universe.dev (R 4.3.2)
#>  glue           1.7.0      2024-01-09 [1] CRAN (R 4.3.2)
#>  gridExtra      2.3        2017-09-09 [1] RSPM (R 4.3.0)
#>  gtable         0.3.4      2023-08-21 [1] CRAN (R 4.3.1)
#>  htmltools      0.5.7      2023-11-03 [1] RSPM (R 4.3.0)
#>  htmlwidgets    1.6.4      2023-12-06 [1] RSPM (R 4.3.0)
#>  httpgd         1.3.1      2023-01-30 [1] CRAN (R 4.3.0)
#>  httpuv         1.6.14     2024-01-26 [1] RSPM (R 4.3.0)
#>  igraph         2.0.2      2024-02-17 [1] RSPM (R 4.3.0)
#>  isoband        0.2.7      2022-12-20 [1] RSPM (R 4.3.0)
#>  janeaustenr    1.0.0      2022-08-26 [1] RSPM (R 4.3.0)
#>  jsonlite       1.8.8      2023-12-04 [1] CRAN (R 4.3.2)
#>  knitr          1.45       2023-10-30 [1] CRAN (R 4.3.1)
#>  kohonen        3.0.12     2023-06-09 [1] RSPM (R 4.3.0)
#>  labeling       0.4.3      2023-08-29 [1] RSPM (R 4.3.0)
#>  later          1.3.2      2023-12-06 [1] RSPM (R 4.3.0)
#>  lattice        0.22-5     2023-10-24 [4] CRAN (R 4.3.1)
#>  lifecycle      1.0.4      2023-11-07 [1] RSPM (R 4.3.0)
#>  magrittr       2.0.3      2022-03-30 [1] CRAN (R 4.3.0)
#>  maps           3.4.2      2023-12-15 [1] RSPM (R 4.3.0)
#>  MASS           7.3-60     2023-05-04 [4] CRAN (R 4.3.1)
#>  Matrix         1.6-3      2023-11-14 [4] CRAN (R 4.3.2)
#>  memoise        2.0.1      2021-11-26 [1] CRAN (R 4.3.0)
#>  mime           0.12       2021-09-28 [1] CRAN (R 4.3.0)
#>  munsell        0.5.0      2018-06-12 [1] RSPM (R 4.3.0)
#>  pillar         1.9.0      2023-03-22 [1] CRAN (R 4.3.0)
#>  pkgconfig      2.0.3      2019-09-22 [1] CRAN (R 4.3.0)
#>  plyr           1.8.9      2023-10-02 [1] RSPM (R 4.3.0)
#>  promises       1.2.1      2023-08-10 [1] RSPM (R 4.3.0)
#>  proxy          0.4-27     2022-06-09 [1] RSPM (R 4.3.0)
#>  proxyC         0.3.4      2023-10-25 [1] RSPM (R 4.3.0)
#>  purrr          1.0.2      2023-08-10 [1] RSPM (R 4.3.0)
#>  quanteda       3.3.1      2023-05-18 [1] RSPM (R 4.3.0)
#>  R.cache        0.16.0     2022-07-21 [1] CRAN (R 4.3.0)
#>  R.methodsS3    1.8.2      2022-06-13 [1] CRAN (R 4.3.0)
#>  R.oo           1.26.0     2024-01-24 [1] RSPM (R 4.3.0)
#>  R.utils        2.12.3     2023-11-18 [1] CRAN (R 4.3.2)
#>  R6             2.5.1      2021-08-19 [1] CRAN (R 4.3.0)
#>  RColorBrewer   1.1-3      2022-04-03 [1] RSPM (R 4.3.0)
#>  Rcpp           1.0.12     2024-01-09 [1] RSPM (R 4.3.0)
#>  RcppParallel   5.1.7      2023-02-27 [1] CRAN (R 4.3.0)
#>  readxl         1.4.3      2023-07-06 [1] RSPM (R 4.3.0)
#>  reshape2       1.4.4      2020-04-09 [1] RSPM (R 4.3.0)
#>  rlang          1.1.3      2024-01-10 [1] CRAN (R 4.3.2)
#>  rmarkdown      2.26       2024-03-05 [1] RSPM (R 4.3.0)
#>  rstatix        0.7.2      2023-02-01 [1] RSPM (R 4.3.0)
#>  scales         1.3.0      2023-11-28 [1] RSPM (R 4.3.0)
#>  sessioninfo    1.2.2      2021-12-06 [1] RSPM (R 4.3.0)
#>  shiny          1.8.0      2023-11-17 [1] CRAN (R 4.3.2)
#>  SnowballC      0.7.1      2023-04-25 [1] RSPM (R 4.3.0)
#>  spam           2.10-0     2023-10-23 [1] RSPM (R 4.3.0)
#>  stopwords      2.3        2021-10-28 [1] RSPM (R 4.3.0)
#>  stringi        1.8.3      2024-01-28 [1] https://gagolews.r-universe.dev (R 4.3.2)
#>  stringr        1.5.1      2023-11-14 [1] RSPM (R 4.3.0)
#>  styler         1.10.2     2023-08-29 [1] RSPM (R 4.3.0)
#>  systemfonts    1.0.6      2024-03-07 [1] RSPM (R 4.3.0)
#>  tibble         3.2.1      2023-03-20 [1] CRAN (R 4.3.0)
#>  tidygraph      1.3.1      2024-01-30 [1] RSPM (R 4.3.0)
#>  tidyr          1.3.1      2024-01-24 [1] RSPM (R 4.3.0)
#>  tidyselect     1.2.0      2022-10-10 [1] CRAN (R 4.3.0)
#>  tidytext       0.4.1      2023-01-07 [1] RSPM (R 4.3.0)
#>  tokenizers     0.3.0      2022-12-22 [1] RSPM (R 4.3.0)
#>  utf8           1.2.4      2023-10-22 [1] RSPM (R 4.3.0)
#>  V8             4.4.2      2024-02-15 [1] CRAN (R 4.3.2)
#>  vctrs          0.6.5      2023-12-01 [1] CRAN (R 4.3.2)
#>  viridis        0.6.5      2024-01-29 [1] RSPM (R 4.3.0)
#>  viridisLite    0.4.2      2023-05-02 [1] RSPM (R 4.3.0)
#>  withr          3.0.0      2024-01-16 [1] RSPM (R 4.3.0)
#>  xfun           0.42       2024-02-08 [1] CRAN (R 4.3.2)
#>  xtable         1.8-4      2019-04-21 [1] RSPM (R 4.3.0)
#>  yaml           2.3.8      2023-12-11 [1] RSPM (R 4.3.0)
#> 
#>  [1] /home/paithiov909/R/x86_64-pc-linux-gnu-library/4.3
#>  [2] /usr/local/lib/R/site-library
#>  [3] /usr/lib/R/site-library
#>  [4] /usr/lib/R/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────