【R】言語処理100本ノック

2020年版（[https://nlp100.github.io/ja/](https://nlp100.github.io/ja/)）をRで解いています。
R言語初心者です。もっと簡単な方法ありましたら是非教えてください。よろしくお願いします。
用語が間違っているかもしれません。すみません。編集リクエストをお願いします。

R言語（に関わらずPythonなどでも）の仕様を知って、慣れるのにもとても良い題材だと思います。作成者の方に感謝します。

関数の説明では、後ろの方の引数を省略してるものがあります。

第１章〜第4章（途中）です。（27、28は今後追加）
続きもやります。

``` R
R.version.string
# -> [1] "R version 3.6.3 (2020-02-29)"
```

## ライブラリ
```R
library(dplyr)
library(purrr)  # 第1章
library(stringr)
library(jsonlite)  # 第３章 問29
library(data.table)  # 第4章
```

## 第１章　準備運動

### 00. 文字列の逆順
```R
"stressed" %>% strsplit("") %>% unlist() %>% rev() %>% paste(collapse = "")
# -> [1] "desserts"

stri_reverse("stressed")  # stringiライプラリを使用
# -> [1] "desserts"
```
#### strsplit(x, split)
文字列（のベクトル）xを文字列splitで区切り、リストを返す。
最初は、「なんでリスト返すん？　unlistするのめんどいだけじゃん」って思ってたけど、Rでは、"stressed"も**長さ1のベクトル**なんだってことか。

#### paste(..., sep = " ", collapse = NULL)
ベクトルを与えて文字列に結合する場合は、`collapse = "挟む文字列"`を指定する。

### 01. 「パタトクカシーー」
```R
"パタトクカシーー" %>% strsplit("") %>% unlist() %>% .[c(T,F)] %>% paste(collapse = "")
# -> [1] "パトカー"
```
#### 奇数番目・偶数番目
インデックスを`[c(TRUE, FALSE)]`とするとベクトルから奇数番目を取り出せる。偶数番目は`[c(FALSE, TRUE)]`。

### 02. 「パトカー」＋「タクシー」＝「パタトクカシーー」
```R
c("パトカー", "タクシー") %>% strsplit("") %>% unlist() %>% matrix(nrow = 2, byrow = T) %>% paste(collapse = "")
# -> [1] "パタトクカシーー"

c("パトカー", "タクシー") %>% strsplit("") %>% {rbind(.[[1]], .[[2]])} %>% paste(collapse = "")
# -> [1] "パタトクカシーー"
```

ベクトル → 行列 → 文字列
#### matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE)
ベクトルから行列を作成する。行はrow、列はcolumn（カラム）。
デフォルトでは左のように入る。`byrow = TRUE`にすると右のように入る。

```R
c(1,2,3,4,5,6) %>% matrix(nrow = 2)    c(1,2,3,4,5,6) %>% matrix(nrow = 2, byrow = T)
#      [,1] [,2] [,3]                  #      [,1] [,2] [,3]
# [1,]    1    3    5                  # [1,]    1    2    3
# [2,]    2    4    6                  # [2,]    4    5    6
```
nrowとncolのどちらかを指定すると、それを満たす行列が作成される。ベクトルの長さが7なのに、nrowを3とかにすると、余ったところに最初の値から入り出す。
nrowとncolの両方を指定すれば、入り切る分だけで作成される。足りなければ、これも最初から入る。
#### rbind(...)
与えられたベクトルを行ベクトルとして結合する。列ベクトルとして結合する場合はcbind関数を使う。

```R
rbind(c(1,2,3,4), c(5,6,7,8))
# ->      [,1] [,2] [,3] [,4]
#    [1,]    1    2    3    4
#    [2,]    5    6    7    8
```
#### パイプ演算子`%>% {...}`
中括弧は、パイプが関数内の最初の引数を使用しないようにする。

### 03. 円周率
```R
str <- "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
str %>% str_replace_all(",|\\.", "") %>% strsplit(" ") %>% unlist() %>% nchar()
# -> [1] 3 1 4 1 5 9 2 6 5 3 5 8 9 7 9
```
#### str_replace_all(string, pattern, replacement)
stringrパッケージ

### 04. 元素記号
マッピング関数を使うと自然に書ける。

```R
str <- "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
index <- c(1,5,6,7,8,9,15,16,19)
str %>% strsplit(" ") %>% unlist() -> strs
strs <- 1:length(strs) %>% map(function(i) {
    setNames(i, substring(strs[i], 1, ifelse(i %in% index, 1, 2)))
}) %>% unlist()
strs
# ->  H He Li Be  B  C  N  O  F Ne Na Mi Al Si  P  S Cl Ar  K Ca 
#     1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 
```
マッピング関数を使わないと、要素を１つ１つ上書きして、最後にそれを名前として数字につけるようなやり方でできる。

```R
for (i in 1:length(strs)) {
    strs[i] %>% substring(1, ifelse(i %in% index, 1, 2)) -> strs[i]
}
1:length(strs) %>% setNames(strs) -> strs
```
`for (i in 1:length(strs)) { ... }`の3行は

```R
strs[index] %>% substring(1, 1) -> strs[index]
strs[-index] %>% substring(1, 2) -> strs[-index]
```
と同じ。これはこれでシンプル。

#### ifelse(test, yes, no)
三項演算子`test ? yes : no`は無い。

#### map(.x, .f, ...)
purrrパッケージ（標準でMap関数があるが、引数の順番が扱いにくい）

マッピング（写像）。ベクトルの各要素に処理f（関数）を施す。リストを返す。これは１つの要素からベクトルを返すようなことができるため。

簡単な例：ベクトル1,2,3,4,5を与え、各要素を２倍にする。（`1:5 * 2`をmapを使って表すと）

```R
1:5 %>% map(function(i) i * 2) %>% unlist()
# -> [1]  2  4  6  8 10

# 省略記法
1:5 %>% map(~ . * 2) %>% unlist()
```
上のように`function(i) {i * 2}`とせずに１行なら`{}`を省略できる。

### 05. n-gram
```R
# ベクトルにしてから渡す
n_gram <- function(input_seq, n = 2) {
    if (n == 1) return(input_seq)
    embed(input_seq, n)[, n:1] %>% asplit(1)
}

n_gram("I am an NLPer" %>% strsplit(" ") %>% unlist())
# -> [[1]] [1] "I"  "am"
#    [[2]] [1] "am" "an"
#    [[3]] [1] "an"    "NLPer"
n_gram("I am an NLPer" %>% strsplit("") %>% unlist())
# -> [[1]]  [1] "I" " "
#    [[2]]  [1] " " "a"
#    [[3]]  [1] "a" "m"
#    [[4]]  [1] "m" " "
#    [[5]]  [1] " " "a"
#    [[6]]  [1] "a" "n"
#    [[7]]  [1] "n" " "
#    [[8]]  [1] " " "N"
#    [[9]]  [1] "N" "L"
#    [[10]] [1] "L" "P"
#    [[11]] [1] "P" "e"
#    [[12]] [1] "e" "r"
```
n=1のときasplit関数でエラーになるので、そのまま返している。
#### embed(x, dimension = 1)
まさにn-gram。行列で返す。列は欲しい順番と逆順になっているので、`[, n:1]`で交換。

#### asplit(x, MARGIN)
行列をリストにする。MARGINが1で行ごと、2で列ごと。

### 06. 集合
```R
bi_gram_x <- n_gram("paraparaparadise" %>% strsplit("") %>% unlist()) %>%
    unlist() %>% matrix(nrow = 2) %>% apply(2, paste, collapse = "")
bi_gram_y <- n_gram("paragraph" %>% strsplit("") %>% unlist()) %>%
    unlist() %>% matrix(nrow = 2) %>% apply(2, paste, collapse = "")

# 和集合
union(bi_gram_x, bi_gram_y)
# -> [1] "pa" "ar" "ra" "ap" "ad" "di" "is" "se" "ag" "gr" "ph"

# 積集合
intersect(bi_gram_x, bi_gram_y)
# -> [1] "pa" "ar" "ra" "ap"

# 差集合
setdiff(bi_gram_x, bi_gram_y)
# -> [1] "ad" "di" "is" "se"

"se" %in% bi_gram_x
# -> [1] TRUE
"se" %in% bi_gram_y
# -> [1] FALSE
```
`%>% unlist() %>% matrix(nrow = 2) %>% apply(2, paste, collapse = "")`はn_gram関数から受け取ったリストを文字列のベクトルに変換している。（この場合`%>% unlist() %>% matrix(nrow = 2) %>% apply(2, paste, collapse = "")`の代わりにlapply関数が使えた。lapply関数は問16を参照。）

n_gramのnを変えた場合は、matrix関数に渡している引数nrowをそのnに変える。
#### apply(X, MARGIN, FUN, ...)
行列Xを列ごとか行ごと（MARGINで指定）にFUNで変換し、ベクトルで返す。`...`はFUNの引数。

#### 例題
文字列"xxyyxyxy"を2文字ずつ"xx" "yy" "xy" "xy"に分けたい。

```R
"xxyyxyxy" %>% strsplit("") %>% unlist() %>% matrix(nrow = 2) %>% apply(2, paste, collapse = "")
# -> [1] "xx" "yy" "xy" "xy"
```
文字列 → 行列 → ベクトル。分かりやすい方法でいいと思います。
別の例はstack overflowで。

### 07. テンプレートによる文生成
```R
make_time_message <- function(x, y, z) {
    sprintf("%s時の%sは%s", x, y, z)
}

make_time_message(12, "気温", 22.4)
# -> [1] "12時の気温は22.4"
```
`paste(x, "時の", y, "は", z, sep = "")`でもOK。

### 08. 暗号文
```R
cipher <- function(target_str) {
    target_str %>% strsplit("") %>% unlist() %>% map(function(s) {
        raw_s <- as.integer(charToRaw(s))
        ifelse(as.integer(charToRaw("a")) <= raw_s && raw_s <= as.integer(charToRaw("z")), rawToChar(as.raw(219 - raw_s)), s)
    }) %>% unlist() %>% paste(collapse = "")
}
```
mapを使わないバージョン。

```R
cipher <- function(target_str) {
    target_str %>% strsplit("") %>% unlist() -> target_str_s
    for (i in 1:length(target_str_s)) {
        raw_i <- as.integer(charToRaw(target_str_s[i]))
        if (as.integer(charToRaw("a")) <= raw_i && raw_i <= as.integer(charToRaw("z"))) {
            rawToChar(as.raw(219 - raw_i)) -> target_str_s[i]
        }
    }
    return(paste(target_str_s, collapse = ""))
}

cipher("Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.")
# -> [1] "Nld I mvvw z wirmp, zoxlslorx lu xlfihv, zugvi gsv svzeb ovxgfivh rmeloermt jfzmgfn nvxszmrxh."

cipher("Nld I mvvw z wirmp, zoxlslorx lu xlfihv, zugvi gsv svzeb ovxgfivh rmeloermt jfzmgfn nvxszmrxh.")
# -> [1] "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

cipher("abcdefghijklmnopqrstuvwxyz")
# -> [1] "zyxwvutsrqponmlkjihgfedcba"
```
1文字ずつ小文字かどうかを調べて、小文字だったら文字コードを`219 - 文字コード`に変更する。

### 09. Typoglycemia
map関数を使うバージョン。

```R
typoglycemia <- function(target_str) {
    target_str %>% strsplit(" ") %>% unlist() %>% map(function(s) {
        ifelse(nchar(s) > 4, {
            s %>% strsplit("") %>% unlist() -> ss
            len_ss <- length(ss)
            c(ss[1], sample(ss[2:(len_ss-1)], size = len_ss-2), ss[len_ss]) %>% paste(collapse = "")
        }, s)
    }) %>% unlist() %>% paste(collapse = " ")
}
```
使わないバージョン。

```R
typoglycemia <- function(target_str) {
    target_str %>% strsplit(" ") %>% unlist() -> target_str_s
    for (i in 1:length(target_str_s)) {
        if (nchar(target_str_s[i]) > 4) {
            target_str_s[i] %>% strsplit("") %>% unlist() -> ss
            len_ss <- length(ss)
            c(ss[1], sample(ss[2:(len_ss-1)], size = len_ss-2), ss[len_ss]) %>%
                paste(collapse = "") -> target_str_s[i]
        }
    }
    return(paste(target_str_s, collapse = " "))
}

typoglycemia("I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")
# -> [1] "I cud'nolt bleeive that I could altclauy unanretdsd what I was riendag : the panenoemhl pweor of the human mind ."
```
`ss[2:(len_ss-1)]`のところ括弧なくて一瞬ハマった。
`1:5 - 1`は`c(0,1,2,3,4)`か、確かにマイナスの前後開けたら`:`の方が順位高く見える。

1単語ずつ5文字以上か調べ、5文字以上だったら中の文字をランダムに並び替え。
#### sample(x, size, replace = FALSE)
ベクトルxからsize個ランダムに抽出する。`replace = TRUE`で重複を許す。
最適すぎる関数。

## 第２章　UNIXコマンド
とにかく遅い（それでも、コマンドが40msなら、Rscriptは400〜600ms程度）けど問題じゃない。

各問、一番最後の`$`から始まるのがUNIXコマンドです。

### ファイルを読み込む
1行ずつベクトルに入れる。

```R
lines <- readLines("popular-names.txt")
```
テーブルに入れる。

```R
data <- read.table("popular-names.txt")
names(data) <- c("name", "sex", "num", "year")
head(data, 3)
# ->   name sex  num year
#    1 Mary   F 7065 1880
#    2 Anna   F 2604 1880
#    3 Emma   F 2003 1880
```

以下では、このlinesかdataが使われています。

### 10. 行数のカウント
```R
length(lines)
# -> [1] 2780
```
```shell:コマンド
$ wc popular-names.txt
    2780   11120   55026 popular-names.txt
#   行数    単語数   文字数
```

### 11. タブをスペースに置換
```R
linesr <- lines %>% str_replace_all("\t", " ")
head(linesr, 3)
# -> [1] "Mary F 7065 1880" "Anna F 2604 1880" "Emma F 2003 1880"
```
```shell:コマンド
$ cat popular-names.txt | tr "\t" " " | head -3
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
```

### 12. 1列目をcol1.txtに，2列目をcol2.txtに保存
```R
data$name %>% as.vector() %>% paste(collapse = "\n") %>% write("col1.txt")
data$sex %>% as.vector() %>% paste(collapse = "\n") %>% write("col2.txt")
```
`data$name`のようにして得られるfactor型はlevels（水準）を含んでいるので全て取り除くために`as.vector()`を通している。（正しい方法か分かりません。droplevels関数は使用してないlevelsを取り除く。）
levelsは問17のような場面で有効です。

```shell:コマンド
$ cut -f 1 popular-names.txt > col1.txt
$ cut -f 2 popular-names.txt > col2.txt
```
diffコマンドで差分がないことが確認できます。

### 13. col1.txtとcol2.txtをマージ
```R
col1 <- readLines("col1.txt")
col2 <- readLines("col2.txt")

paste(col1, col2, sep = "\t") %>% head(3)
# -> [1] "Mary\tF" "Anna\tF" "Emma\tF"

paste(col1, col2, sep = "\t") %>% head(3) %>% cat(sep = "\n")
# -> Mary	F
#    Anna	F
#    Emma	F
```
#### paste(..., sep = " ", collapse = NULL)
```R
paste(c(1,2,3,4), c(5,6,7,8), sep = " ")
# -> [1] "1 5" "2 6" "3 7" "4 8"
```
ベクトルを2つ以上渡すとこんな挙動。
#### cat(..., file = "", sep = " ")
ベクトルを文字列として出力する。sepで与えた文字列を挟む。

```shell:コマンド
$ paste -d "\t" col1.txt col2.txt | head -3
Mary	F
Anna	F
Emma	F
```

### 14. 先頭からN行を出力
```R
head(lines, 3)
# -> [1] "Mary\tF\t7065\t1880" "Anna\tF\t2604\t1880" "Emma\tF\t2003\t1880"
```
```shell:コマンド
$ head -3 popular-names.txt
Mary	F	7065	1880
Anna	F	2604	1880
Emma	F	2003	1880
```

### 15. 末尾のN行を出力
```R
tail(lines, 3)
# -> [1] "Lucas\tM\t12585\t2018" "Mason\tM\t12435\t2018" "Logan\tM\t12352\t2018"
```
```shell:コマンド
$ tail -3 popular-names.txt
Lucas	M	12585	2018
Mason	M	12435	2018
Logan	M	12352	2018
```

### 16. ファイルをN分割する
```R
split_lines <- function(lines, n) {
    split(lines, rep_len(1:n, length(lines)) %>% sort()) %>% setNames(NULL)
}

split_lines(lines, 3) %>% lapply(head, 3)
# -> [[1]] [1] "Mary\tF\t7065\t1880" "Anna\tF\t2604\t1880" "Emma\tF\t2003\t1880"
#    [[2]] [1] "Virginia\tF\t16162\t1926" "Mildred\tF\t13551\t1926"  "Frances\tF\t13355\t1926"
#    [[3]] [1] "John\tM\t43181\t1972"   "Robert\tM\t43037\t1972" "Jason\tM\t37446\t1972"
```
#### rep_len(x, length.out)
ベクトルxを長さlength.outになるまで繰り返したベクトルを返す。`rep(x, length = length.out)`と同じ。

#### sort(x, decreasing = FALSE)
ベクトルxをソートして返す。デフォルトでは昇順、decreasingをTRUEにすると降順。

```R
rep_len(1:3, 10)
# -> [1] 1 2 3 1 2 3 1 2 3 1
rep_len(1:3, 10) %>% sort()
# -> [1] 1 1 1 1 2 2 2 3 3 3
```
#### split(x, f, drop = FALSE)
ベクトルxをベクトルfに対応してリストに分類する。
fにlevelsを持つfactorを渡したときに、未使用のlevelsを除外するかしないかがdrop。
（`split(c(1,2,3,4,5,6), names %>% head(2), drop = T)`のdropあるなし、試してみて！）

```R
split(c(1,2,3,4,5), c(1,1,1,2,2))
# -> $`1` [1] 1 2 3
#    $`2` [1] 4 5
split(c(1,2,3,4,5,6), c("a", "b"))
# -> $a   [1] 1 3 5
#    $b   [1] 2 4 6
```
#### setNames(object = nm, nm)
splitのfに数字のベクトルを渡しても、リストの名前はその数字の文字列になってしまいます。（これは、与えられた数字のベクトルが1からの連番になってるとは限らないからだと思います。）namesにNULLを設定すると、リストのインデックスは1からの数字の連番になります。（必要なければやらなくていい。）

#### lapply(X, FUN, ...)
リストXに対してインデックスごとにFUNに渡す。FUNの引数はそれ以降に書く。

```shell:コマンド
$ split -l 1000 popular-names.txt popular-names-
```
1000行ずつ、popular-names-aa、popular-names-ab、popular-names-acの３つのファイルができる。
macのsplitコマンドはn分割できない。

### 17. １列目の文字列の異なり
```R
data$name %>% levels() %>% head(6)
# -> [1] "Abigail"   "Aiden"     "Alexander" "Alexis"    "Alice"     "Amanda"
```
貯金箱にいっぱい硬貨が入っているけど、あるのは、1円、5円、10円、50円、100円、500円だけ。
#### levels(x)
factor xのlevelsをベクトルで取得する。

```shell:コマンド
$ cut -f 1 popular-names.txt | sort | uniq | head -6
Abigail
Aiden
Alexander
Alexis
Alice
Amanda
```
uniqコマンドは連続する2行が同じなら1行消去する。

### 18. 各行を3コラム目の数値の降順にソート
```R
data[order(data$num, decreasing = T),]
# ->             name sex   num year
#    1341       Linda   F 99689 1947
#    1361       Linda   F 96211 1948
#    1351       James   M 94757 1947
#    1551     Michael   M 92704 1957
#     ...
```
#### order(..., na.last = TRUE, decreasing = FALSE)
ソートしたときの順序をベクトルで返す。デフォルトでは昇順、decreasingをTRUEにすると降順。

```R
x <- c(4,6,8,2,7,1,5,9,3)
sort(x)
# -> [1] 1 2 3 4 5 6 7 8 9
order(x)
# -> [1] 6 4 9 1 7 2 5 3 8
# 6番、4番、9番、1番、 ··· の順に並べたらソートされる
x[order(x)]
# -> [1] 1 2 3 4 5 6 7 8 9
```

```shell:コマンド
$ sort -n -r -k 3 popular-names.txt | head -4
Linda	F	99689	1947
Linda	F	96211	1948
James	M	94757	1947
Michael	M	92704	1957
```
sortコマンドのオプション

* `-n`　数値としてソート
* `-r`　降順でソート
* `-k 3`　ソートに使う列のキー

### 19. 各行の1コラム目の文字列の出現頻度を求め，出現頻度の高い順に並べる
```R
data$name %>% table() %>% sort(decreasing = T) %>% names() %>% head(6)
# -> [1] "James"   "William" "John"    "Robert"  "Mary"    "Charles"
```
#### table(...)
table関数にベクトルやfactorを渡すとそれぞれの要素と要素数が結びついたテーブルが得られる。

```shell:コマンド
$ cut -f 1 popular-names.txt | sort | uniq -c | sort -n -r -k 1 | head -6
 118 James
 111 William
 108 Robert
 108 John
  92 Mary
  75 Charles
```
uniqコマンドのオプション

* `-c`　連続する同じ行の行数を数え、表にする。列1に要素数、列2に要素。

## 第３章　正規表現
### 20. JSONデータの読み込み
１行１記事だから`{"title": "イギリス"`から始まる行を取れば良い。

```R
data <- readLines("jawiki-country.json")
article_UK <- grep("^\\{\"title\": \"イギリス\"", data, value = T)
lines_UK <- article_UK %>% strsplit("\\n", fixed = T) %>% unlist()
```
#### grep(pattern, x, value = FALSE)
ベクトルxのうち、正規表現patternのマッチするもののインデックスを得る。`value = TRUE`にすると要素のベクトルを得る。

#### strsplit(x, split, fixed = FALSE)
`fixed = TRUE`としないと、エスケープされた改行に一致してくれない。

ちなみに、jawiki-country.jsonは次のような感じになっています。

```R
length(data)
# -> [1] 248
data %>% head(5) %>% substring(1, 20)
# -> [1] "{\"title\": \"エジプト\", \"t"  "{\"title\": \"オーストリア\", " "{\"title\": \"インドネシア\", "
#    [4] "{\"title\": \"イラク\", \"te"   "{\"title\": \"イラン\", \"te"
lines_UK %>% head(10) %>% cat(sep = "\n")
# -> {"title": "イギリス", "text": "{{redirect|UK}}
#    {{redirect|英国|春秋時代の諸侯国|英 (春秋)}}
#    {{Otheruses|ヨーロッパの国|長崎県・熊本県の郷土料理|いぎりす}}
#    {{基礎情報 国
#    |略名  =イギリス
#    |日本語国名 = グレートブリテン及び北アイルランド連合王国
#    |公式国名 = {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />
#    *{{lang|gd|An Rìoghachd Aonaichte na Breatainn Mhòr agus Eirinn mu Thuath}}（[[スコットランド・ゲール語]]）
#    *{{lang|cy|Teyrnas Gyfunol Prydain Fawr a Gogledd Iwerddon}}（[[ウェールズ語]]）
#    *{{lang|ga|Ríocht Aontaithe na Breataine Móire agus Tuaisceart na hÉireann}}（[[アイルランド語]]）
```

### 21. カテゴリ名を含む行を抽出
lines_UKから`[[Category:ほげほげ]]`を含む行を取り出す。（行で取り出すと最後のにおまけがついてくるから、ここでは行で取り出していない。含む行を取り出すためには`grep("\\[\\[Category:.*\\]\\]", lines_UK, value = T)`とする必要がある。）

```R
lines_UK %>% str_extract_all("\\[\\[Category:.*\\]\\]") %>% unlist() -> categories_UK
cat(categories_UK, sep = "\n")
# -> [[Category:イギリス|*]]
#    [[Category:イギリス連邦加盟国]]
#    [[Category:英連邦王国|*]]
#    [[Category:G8加盟国]]
#    [[Category:欧州連合加盟国|元]]
#    [[Category:海洋国家]]
#    [[Category:現存する君主国]]
#    [[Category:島国]]
#    [[Category:1801年に成立した国家・領域]]
```

#### str_extract_all(string, pattern)
stringrパッケージ
正規表現patternの部分を満たして、文字列のリストを返す。マッチした行（要素）が必要じゃなければいきなりstr_extract_all関数を使うべき。

### 22. カテゴリ名の抽出
```R
categories_UK %>% str_replace_all("^\\[\\[Category:|\\]\\]$", "") %>% cat(sep = "\n")
# -> イギリス|*
#    イギリス連邦加盟国
#    英連邦王国|*
#    G8加盟国
#    欧州連合加盟国|元
#    海洋国家
#    現存する君主国
#    島国
#    1801年に成立した国家・領域
```

### 23. セクション構造
```R
sections_UK <- str_extract_all(lines_UK, "==.*==") %>% unlist()
sections_UK <- matrix(c(
    sections_UK %>% str_extract_all("^=*") %>% unlist() %>% nchar() - 1,
    sections_UK %>% str_replace_all("\\s*={2,4}\\s*", "")
), ncol = 2)

sections_UK %>% apply(1, paste, collapse = " ") %>% head(10) %>% cat(sep = "\n")
# -> 1 国名
#    1 歴史
#    1 地理
#    2 主要都市
#    2 気候
#    1 政治
#    2 元首
#    2 法
#    2 内政
#    2 地方行政区分
```

### 24. ファイル参照の抽出
```R
str_extract_all(lines_UK, "\\[\\[ファイル:.*?(\\||\\]\\])") %>% unlist() %>%
    str_replace_all("^\\[\\[ファイル:|\\|$|\\]\\]$", "") %>% head(10) %>% cat(sep = "\n")
# -> Royal Coat of Arms of the United Kingdom.svg
#    United States Navy Band - God Save the Queen.ogg
#    Descriptio Prime Tabulae Europae.jpg
#    Lenepveu, Jeanne d'Arc au siège d'Orléans.jpg
#    London.bankofengland.arp.jpg
#    Battle of Waterloo 1815.PNG
#    Uk topo en.jpg
#    BenNevis2005.jpg
#    Population density UK 2011 census.png
#    2019 Greenwich Peninsula & Canary Wharf.jpg
```

### 25. テンプレートの抽出
```R
article_UK %>% str_extract_all("\\{\\{基礎情報.*?(\\{\\{.*?\\}\\}.*?)*?\\}\\}") %>% unlist() %>%
    strsplit("\\n", fixed = T) %>% unlist() %>% grep("^\\|", ., value = T) -> std_info_lines

std_info <- std_info_lines %>% str_extract_all("=\\s*.*?$") %>% unlist() %>% str_replace_all("^=\\s*", "")
names(std_info) <- std_info_lines %>% str_extract_all("^\\|.*?\\s*=") %>% unlist() %>% str_replace_all("^\\||\\s*=$", "")

matrix(c(names(std_info), std_info), ncol = 2) %>% apply(1, paste, collapse = ": ") %>% head(10) %>% cat(sep = "\n")
# -> 略名: イギリス
#    日本語国名: グレートブリテン及び北アイルランド連合王国
#    公式国名: {{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />
#    国旗画像: Flag of the United Kingdom.svg
#    国章画像: [[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]
#    国章リンク: （[[イギリスの国章|国章]]）
#    標語: {{lang|fr|[[Dieu et mon droit]]}}<br />（[[フランス語]]:[[Dieu et mon droit|神と我が権利]]）
#    国歌: [[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />''神よ女王を護り賜え''<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}
#    地図画像: Europe-UK.svg
#    位置画像: United Kingdom (+overseas territories) in the World (+Antarctica claims).svg
```

### 26. 強調マークアップの除去
`''`と`'''`を取り除く。（でいいの？）

```R
std_info_lines <- std_info_lines %>% str_replace_all("''|'''", "")

std_info <- std_info_lines %>% str_extract_all("=\\s*.*?$") %>% unlist() %>% str_replace_all("^=\\s*", "")
names(std_info) <- std_info_lines %>% str_extract_all("^\\|.*?\\s*=") %>% unlist() %>% str_replace_all("^\\||\\s*=$", "")
```

### 27. 内部リンクの除去
お待ちください。

### 28. MediaWikiマークアップの除去
今後追加します。

### 29. 国旗画像のURLを取得する
```R
image_info <- read_json("https://en.wikipedia.org/w/api.php?action=query&format=json&prop=imageinfo&iiprop=url&titles=File:Flag%20of%20the%20United%20Kingdom.svg")
image_info$query$pages$`23473560`$imageinfo[[1]]$url
# -> [1] "https://upload.wikimedia.org/wikipedia/en/a/ae/Flag_of_the_United_Kingdom.svg"
```
jsonliteパッケージを使用。

## 第４章　形態素解析

### 形態素解析
neko.txtは1文1行で記載されていて、mecabは1行ずつ解析する。

```shell:コマンド
$ cat neko.txt | mecab -o neko.txt.mecab_ \
    # --bos-format='surface\tbase\tpos\tpos1\n' \
    --node-format='%m\t%f[6]\t%f[0]\t%f[1]\n' \
    --unk-format='%m\t%m\t\t\n' \
    --eon-format='EOS\n'
```
mecabコマンドのオプション

* `-o`　出力ファイル名
* その他　出力フォーマットオプション

このままだと、後でRでテーブルとして読み込むときに`'`が邪魔をするのでエスケープします。（上のようにフォーマットを設定するとエスケープしなくても読み込めますが、文中の`'`が無かったことになる。）詳細はあまり関係ないけど今度追記。

```shell:コマンド
$ sed -e "s/'/\\\'/g" neko.txt.mecab_ > neko.txt.mecab
```
neko.txt.mecabは

```text
surface	base	pos	pos1     <- ヘッダー（上でコメントアウトしているからない）
 ......
を	を	助詞	格助詞
見る	見る	動詞	自立
 ......
EOS                          <- End Of Sentence
surface	base	pos	pos1     <- ヘッダー（上でコメントアウトしているからない）
 ......
EOS                          <- End Of Sentence
 ......
```
のように1文ずつ形態素解析されて分かれている。文と文はEOSで区切られている。（ヘッダーはコメントアウトした。）

### 30. 形態素解析結果の読み込み
```R
neko.dt <- fread("neko.txt.mecab", fill = T, col.names = c("surface", "base", "pos", "pos1"))
```

1文ずつテーブルを分けるのは処理に時間がかかるので、全ての文を同じテーブルに入れた。同じテーブルに入れても、文と文の間に読点かEOSがあれば問題ない気がする。（書き直すかも。）

テーブルフレームなど、調べたら書き直す。現段階では次で結果が得られる。

### 31. 動詞
```R
neko.dt[pos=="動詞"]$surface %>% unique() %>% sort()
```

### 32. 動詞の原形
```R
neko.dt[pos=="動詞"]$base %>% unique() %>% sort()
```

### 33. 「AのB」
```R
neko.pos3 <- neko.dt$pos %>% embed(3) %>% .[, 3:1]
neko.surface3 <- neko.dt$surface %>% embed(3) %>% .[, 3:1]

AnoB.index <- grep("の", neko.surface3[,2]) %>%
    intersect(grep("名詞", neko.pos3[,1])) %>%
    intersect(grep("助詞", neko.pos3[,2])) %>%
    intersect(grep("名詞", neko.pos3[,3]))

neko.surface3[AnoB.index,] %>% apply(1, paste, collapse = "")  # 出現順
```