11
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-04-27

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

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

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

第1章〜第4章(途中)です。(27、28は今後追加)
続きもやります。

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

ライブラリ

library(dplyr)
library(purrr)  # 第1章
library(stringr)
library(jsonlite)  # 第3章 問29
library(data.table)  # 第4章

第1章 準備運動

00. 文字列の逆順

"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. 「パタトクカシーー」

"パタトクカシーー" %>% strsplit("") %>% unlist() %>% .[c(T,F)] %>% paste(collapse = "")
# -> [1] "パトカー"

奇数番目・偶数番目

インデックスを[c(TRUE, FALSE)]とするとベクトルから奇数番目を取り出せる。偶数番目は[c(FALSE, TRUE)]

02. 「パトカー」+「タクシー」=「パタトクカシーー」

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にすると右のように入る。

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関数を使う。

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. 円周率

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. 元素記号

マッピング関数を使うと自然に書ける。

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 

マッピング関数を使わないと、要素を1つ1つ上書きして、最後にそれを名前として数字につけるようなやり方でできる。

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行は

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つの要素からベクトルを返すようなことができるため。

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

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

# 省略記法
1:5 %>% map(~ . * 2) %>% unlist()

上のようにfunction(i) {i * 2}とせずに1行なら{}を省略できる。

05. n-gram

# ベクトルにしてから渡す
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. 集合

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"に分けたい。

"xxyyxyxy" %>% strsplit("") %>% unlist() %>% matrix(nrow = 2) %>% apply(2, paste, collapse = "")
# -> [1] "xx" "yy" "xy" "xy"

文字列 → 行列 → ベクトル。分かりやすい方法でいいと思います。
別の例はstack overflowで。

07. テンプレートによる文生成

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. 暗号文

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を使わないバージョン。

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関数を使うバージョン。

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 = " ")
}

使わないバージョン。

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 - 1c(0,1,2,3,4)か、確かにマイナスの前後開けたら:の方が順位高く見える。

1単語ずつ5文字以上か調べ、5文字以上だったら中の文字をランダムに並び替え。

sample(x, size, replace = FALSE)

ベクトルxからsize個ランダムに抽出する。replace = TRUEで重複を許す。
最適すぎる関数。

第2章 UNIXコマンド

とにかく遅い(それでも、コマンドが40msなら、Rscriptは400〜600ms程度)けど問題じゃない。

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

ファイルを読み込む

1行ずつベクトルに入れる。

lines <- readLines("popular-names.txt")

テーブルに入れる。

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. 行数のカウント

length(lines)
# -> [1] 2780
コマンド
$ wc popular-names.txt
    2780   11120   55026 popular-names.txt
#   行数    単語数   文字数

11. タブをスペースに置換

linesr <- lines %>% str_replace_all("\t", " ")
head(linesr, 3)
# -> [1] "Mary F 7065 1880" "Anna F 2604 1880" "Emma F 2003 1880"
コマンド
$ 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に保存

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のような場面で有効です。

コマンド
$ cut -f 1 popular-names.txt > col1.txt
$ cut -f 2 popular-names.txt > col2.txt

diffコマンドで差分がないことが確認できます。

13. col1.txtとcol2.txtをマージ

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)

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で与えた文字列を挟む。

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

14. 先頭からN行を出力

head(lines, 3)
# -> [1] "Mary\tF\t7065\t1880" "Anna\tF\t2604\t1880" "Emma\tF\t2003\t1880"
コマンド
$ head -3 popular-names.txt
Mary	F	7065	1880
Anna	F	2604	1880
Emma	F	2003	1880

15. 末尾のN行を出力

tail(lines, 3)
# -> [1] "Lucas\tM\t12585\t2018" "Mason\tM\t12435\t2018" "Logan\tM\t12352\t2018"
コマンド
$ tail -3 popular-names.txt
Lucas	M	12585	2018
Mason	M	12435	2018
Logan	M	12352	2018

16. ファイルをN分割する

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にすると降順。

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あるなし、試してみて!)

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の引数はそれ以降に書く。

コマンド
$ split -l 1000 popular-names.txt popular-names-

1000行ずつ、popular-names-aa、popular-names-ab、popular-names-acの3つのファイルができる。
macのsplitコマンドはn分割できない。

17. 1列目の文字列の異なり

data$name %>% levels() %>% head(6)
# -> [1] "Abigail"   "Aiden"     "Alexander" "Alexis"    "Alice"     "Amanda"

貯金箱にいっぱい硬貨が入っているけど、あるのは、1円、5円、10円、50円、100円、500円だけ。

levels(x)

factor xのlevelsをベクトルで取得する。

コマンド
$ cut -f 1 popular-names.txt | sort | uniq | head -6
Abigail
Aiden
Alexander
Alexis
Alice
Amanda

uniqコマンドは連続する2行が同じなら1行消去する。

18. 各行を3コラム目の数値の降順にソート

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にすると降順。

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
コマンド
$ 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コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

data$name %>% table() %>% sort(decreasing = T) %>% names() %>% head(6)
# -> [1] "James"   "William" "John"    "Robert"  "Mary"    "Charles"

table(...)

table関数にベクトルやfactorを渡すとそれぞれの要素と要素数が結びついたテーブルが得られる。

コマンド
$ 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に要素。

第3章 正規表現

20. JSONデータの読み込み

1行1記事だから{"title": "イギリス"から始まる行を取れば良い。

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は次のような感じになっています。

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)とする必要がある。)

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. カテゴリ名の抽出

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

23. セクション構造

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. ファイル参照の抽出

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. テンプレートの抽出

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. 強調マークアップの除去

'''''を取り除く。(でいいの?)

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を取得する

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パッケージを使用。

第4章 形態素解析

形態素解析

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

コマンド
$ 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でテーブルとして読み込むときに'が邪魔をするのでエスケープします。(上のようにフォーマットを設定するとエスケープしなくても読み込めますが、文中の'が無かったことになる。)詳細はあまり関係ないけど今度追記。

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

neko.txt.mecabは

surface	base	pos	pos1     <- ヘッダー(上でコメントアウトしているからない)
 ......
を	を	助詞	格助詞
見る	見る	動詞	自立
 ......
EOS                          <- End Of Sentence
surface	base	pos	pos1     <- ヘッダー(上でコメントアウトしているからない)
 ......
EOS                          <- End Of Sentence
 ......

のように1文ずつ形態素解析されて分かれている。文と文はEOSで区切られている。(ヘッダーはコメントアウトした。)

30. 形態素解析結果の読み込み

neko.dt <- fread("neko.txt.mecab", fill = T, col.names = c("surface", "base", "pos", "pos1"))

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

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

31. 動詞

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

32. 動詞の原形

neko.dt[pos=="動詞"]$base %>% unique() %>% sort()

33. 「AのB」

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 = "")  # 出現順
11
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?