-
Notifications
You must be signed in to change notification settings - Fork 0
/
words-menu-1.qmd
222 lines (182 loc) · 6.24 KB
/
words-menu-1.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# 抽出語メニュー1
```{r}
#| label: setup
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)
```
---
## 抽出語リスト(A.5.1)
活用語をクリックするとそれぞれの活用の出現頻度も見れるUIについては、Rだとどうすれば実現できるかわからない。
```{r}
#| label: freq-list
dat <- dplyr::tbl(con, "tokens") |>
dplyr::filter(
!pos %in% c("その他", "名詞B", "動詞B", "形容詞B", "副詞B", "否定助動詞", "形容詞(非自立)")
) |>
dplyr::count(token, pos) |>
dplyr::filter(n >= 100) |>
dplyr::collect()
reactable::reactable(
dat,
filterable = TRUE,
defaultColDef = reactable::colDef(
cell = reactablefmtr::data_bars(dat, text_position = "outside-base")
)
)
```
## 出現回数の分布(A.5.2)
### 度数分布表
```{r}
#| label: calc-tf
dat <-
dplyr::tbl(con, "tokens") |>
dplyr::filter(
!pos %in% c("その他", "名詞B", "動詞B", "形容詞B", "副詞B", "否定助動詞", "形容詞(非自立)")
) |>
dplyr::count(token, pos) |>
dplyr::summarise(
degree = sum(n, na.rm = TRUE),
.by = n
) |>
dplyr::mutate(
prop = degree / sum(degree, na.rm = TRUE)
) |>
dplyr::arrange(n) |>
dplyr::compute() |>
dplyr::mutate(
cum_degree = cumsum(degree),
cum_prop = cumsum(prop)
) |>
dplyr::collect()
dat
```
### 折れ線グラフ
```{r}
#| label: plot-tf-dist
dat |>
dplyr::filter(cum_prop < .8) |>
ggplot(aes(x = n, y = degree)) +
geom_line() +
theme_bw() +
labs(x = "出現回数", y = "度数")
```
## 文書数の分布(A.5.3)
### ヒストグラム🍳
段落(`doc_id`)ではなく、章ラベル(`label`)でグルーピングして集計している。`docfreq`について上でやったのと同様に処理すれば度数分布表にできる。
```{r}
#| label: calc-df
dat <-
dplyr::tbl(con, "tokens") |>
dplyr::mutate(token = paste(token, pos, sep = "/")) |>
dplyr::count(label, token) |>
dplyr::collect() |>
tidytext::cast_dfm(label, token, n) |>
quanteda.textstats::textstat_frequency() |>
dplyr::as_tibble()
dat
```
度数分布をグラフで確認したいだけなら、このかたちからヒストグラムを描いたほうが楽。
```{r}
#| label: plot-df-hist
dat |>
ggplot(aes(x = docfreq)) +
geom_histogram(binwidth = 3) +
scale_y_sqrt() +
theme_bw() +
labs(x = "文書数", y = "度数")
```
### Zipf's law🍳
よく見かけるグラフ。[言語処理100本ノック 2020](https://nlp100.github.io/ja/ch04.html#39-zipf%E3%81%AE%E6%B3%95%E5%89%87)の39はこれにあたる。
```{r}
#| label: plot-zipf
dat |>
ggplot(aes(x = rank, y = frequency)) +
geom_line() +
geom_smooth(method = lm, formula = y ~ x, se = FALSE) +
scale_x_log10() +
scale_y_log10() +
theme_bw()
```
## 出現回数・文書数のプロット(A.5.4)
図 A.25のようなグラフの例。ggplot2で`graphics::identify()`のようなことをするやり方がわからないので、適当に条件を指定してgghighlightでハイライトしている。
```{r}
#| label: plot-tf-df
#| cache: true
dat |>
ggplot(aes(x = frequency, y = docfreq)) +
geom_jitter() +
gghighlight::gghighlight(
frequency > 100 & docfreq < 60
) +
ggrepel::geom_text_repel(aes(label = feature)) +
scale_x_log10() +
theme_bw() +
labs(x = "出現回数", y = "文書数")
```
## KWIC(A.5.5)
### コンコーダンス
コンコーダンスは`quanteda::kwic()`で確認できる。もっとも、KH Coderの提供するコンコーダンス検索やコロケーション統計のほうが明らかにリッチなのと、Rのコンソールは日本語の長めの文字列を表示するのにあまり向いていないというのがあるので、このあたりの機能が必要ならKH Coderを利用したほうがよい。
```{r}
#| label: kwic
dat <-
dplyr::tbl(con, "tokens") |>
dplyr::filter(section == "[1]上_先生と私") |>
dplyr::select(label, token) |>
dplyr::collect() |>
dplyr::reframe(token = list(token), .by = label) |>
tibble::deframe() |>
quanteda::as.tokens() |>
quanteda::tokens_select("[[:punct:]]", selection = "remove", valuetype = "regex", padding = FALSE) |>
quanteda::tokens_select("^[\\p{Hiragana}]{1,2}$", selection = "remove", valuetype = "regex", padding = TRUE)
quanteda::kwic(dat, pattern = "^向[いくけこ]$", window = 5, valuetype = "regex")
```
なお、上の例ではひらがな1~2文字の語をpaddingしつつ除外したので、一部の助詞などは表示されていない(それぞれの窓のなかでトークンとして数えられてはいる)。
### コロケーション
たとえば、前後5個のwindow内のコロケーション(nodeを含めて11語の窓ということ)の合計については次のように確認できる。
```{r}
#| label: collocation-1
dat |>
quanteda::fcm(context = "window", window = 5) |>
tidytext::tidy() |>
dplyr::rename(node = document, term = term) |>
dplyr::filter(node == "向い") |>
dplyr::slice_max(count, n = 10)
```
「左合計」や「右合計」については、たとえば次のようにして確認できる。paddingしなければ`tidyr::separate_wider_delim()`で展開して位置ごとに集計することもできそう。
```{r}
#| label: collocation-2
dat |>
quanteda::kwic(pattern = "^向[いくけこ]$", window = 5, valuetype = "regex") |>
dplyr::as_tibble() |>
dplyr::select(docname, keyword, pre, post) |>
tidyr::pivot_longer(
c(pre, post),
names_to = "window",
values_to = "term",
values_transform = ~ strsplit(., " ", fixed = TRUE)
) |>
tidyr::unnest(term) |>
dplyr::count(window, term, sort = TRUE)
```
---
```{r}
#| label: cleanup
duckdb::dbDisconnect(con)
duckdb::duckdb_shutdown(drv)
sessioninfo::session_info(info = "packages")
```