« 2023年8月 | トップページ | 2023年10月 »

2023年9月30日 (土)

[R]tibbleの行の並び順を任意の列で替える

tidyverse(dplyr)パッケージに含まれるarrange関数を使う。以下のような中身のCSVファイルをmeibo.csvと保存しておく。

no,    sei,  mei, age, weight
1, あいう, えお, 11, 101.1
2, かきく, けこ, 22, 202.2
3, さしす, せそ, 22, 303.3
4, あいう, けこ, 33, 404.4

列を指定すると、その列で並び順を変える。降順にする場合はdesc関数を使う。以下の例のとおり、複数指定した場合は、指定した列の順に並び替える。%>%演算子を使わない場合は、第一引数に変換元のtibbleを指定する。簡潔な表示にするため、tibbleの表示はデータフレームに変換して行っている。

> library(tidyverse)
> tbl <- read_csv("meibo.csv", show_col_types = FALSE, progress = FALSE)
> as.data.frame(tbl)
no sei mei age weight
1 1 あいう えお 11 101.1
2 2 かきく けこ 22 202.2
3 3 さしす せそ 22 303.3
4 4 あいう けこ 33 404.4
> as.data.frame(tbl %>% arrange(sei))
no sei mei age weight
1 1 あいう えお 11 101.1
2 4 あいう けこ 33 404.4
3 2 かきく けこ 22 202.2
4 3 さしす せそ 22 303.3
> as.data.frame(tbl %>% arrange(sei, desc(age)))
no sei mei age weight
1 4 あいう けこ 33 404.4
2 1 あいう えお 11 101.1
3 2 かきく けこ 22 202.2
4 3 さしす せそ 22 303.3

2023年9月29日 (金)

[R]tibbleで様々な文字コードのCSVファイルを読み込む

read_csv関数を使う。localeオプションに文字コードを指定する。

最初にPowerShellを使ってカレントディレクトリに、順番にシフトJIS、UTF-8(BOM無し)、UTF-8(BOM付き)、UTF-16(ビッグエンディアン、BOM付き)、UTF-16(リトルエンディアン、BOM付き)のCSVファイルを出力する。PowerShellのコマンドレットはUnicode系は原則BOM付きとなるため、BOM無しのUTF-8の出力にFileクラスのWriteAllLinesメソッドを使用している。

PS > $lines = "1,ABC", "2,abc", "3,あいう"
PS > $odir = (gl).Path
PS > $lines | Out-File -Encoding default ($odir + "\sjis.csv")
PS > [IO.File]::WriteAllLines(($odir + "\utf8nb.csv"), $lines)
PS > $lines | Out-File -Encoding utf8 ($odir + "\utf8wb.csv")
PS > $lines | Out-File -Encoding bigendianunicode ($odir + "\utf16bewb.csv")
PS > $lines | Out-File -Encoding unicode ($odir + "\utf16lewb.csv")
PS > Get-Content .\sjis.csv
1,ABC
2,abc
3,あいう
PS > Get-Content .\utf8nb.csv -Encoding utf8
1,ABC
2,abc
3,あいう

Rを起動してそれぞれread_csv関数で読み込む。localeオプションにそれぞれ文字コードを指定する。UTF-16の場合、「UTF-16」を指定すればバイトオーダーマークからエンディアンを推定して読み込むが、エンディアンを指定することもできる。最後の例のとおりに、エンディアンの指定を間違えると、指定のとおりに読み込もうとして読み込みに失敗する。

簡潔な表示にするため、最初にoptions関数で画面表示に関するオプションを指定し、読み込んだtibbleはデータフレームに変換している。

> options(readr.show_progress = FALSE, readr.show_col_types = FALSE)
> as.data.frame(read_csv("sjis.csv", locale = locale(encoding = "SJIS")))
no name
1 1 ABC
2 2 abc
3 3 あいう
> as.data.frame(read_csv("utf8nb.csv", locale = locale(encoding = "UTF-8")))
no name
1 1 ABC
2 2 abc
3 3 あいう
> as.data.frame(read_csv("utf8wb.csv", locale = locale(encoding = "UTF-8")))
no name
1 1 ABC
2 2 abc
3 3 あいう
> as.data.frame(read_csv("utf16bewb.csv", locale = locale(encoding = "UTF-16")))
no name
1 1 ABC
2 2 abc
3 3 あいう
> as.data.frame(read_csv("utf16lewb.csv", locale = locale(encoding = "UTF-16")))
no name
1 1 ABC
2 2 abc
3 3 あいう
> as.data.frame(read_csv("utf16bewb.csv", locale = locale(encoding = "UTF-16BE")))
no name
1 1 ABC
2 2 abc
3 3 あいう
> as.data.frame(read_csv("utf16lewb.csv", locale = locale(encoding = "UTF-16LE")))
no name
1 1 ABC
2 2 abc
3 3 あいう
> as.data.frame(read_csv("utf16lewb.csv", locale = locale(encoding = "UTF-16BE")))
[1] \ufffe渀漀Ⰰ渀愀洀攀ഀ\u0a00\u3100Ⰰ䄀䈀䌀ഀ\u0a00㈀Ⰰ愀戀挀ഀ\u0a00㌀Ⰰ䈰䐰䘰ഀ\u0a00
<0 行> (または長さ 0 の row.names)

2023年9月28日 (木)

[R]tibbleを任意の列のみのtibbleに変換する

tidyverse(dplyr)パッケージに含まれるselect関数を使う。以下のような中身のCSVファイルをmeibo.csvと保存しておく。

no,    sei,  mei, age, weight
1, あいう, えお, 11, 101.1
2, かきく, けこ, 22, 202.2
3, さしす, せそ, 22, 303.3
4, あいう, けこ, 33, 404.4

%>%演算子を使わない場合は、第一引数に変換元のtibbleを指定する。簡潔な表示にするため、tibbleの表示はデータフレームに変換して行っている。

> library(tidyverse)
> tbl <- read_csv("meibo.csv", show_col_types = FALSE, progress = FALSE)
> as.data.frame(select(tbl, no, sei, mei))
no sei mei
1 1 あいう えお
2 2 かきく けこ
3 3 さしす せそ
4 4 あいう けこ
> as.data.frame(tbl %>% select(no, sei, mei))
no sei mei
1 1 あいう えお
2 2 かきく けこ
3 3 さしす せそ
4 4 あいう けこ

2023年9月27日 (水)

[R]tibbleに別のtibbleで特定の列が一致する列を結合する

tidyverse(dplyr)パッケージのinner_join関数を使う。Aを基準としてBを結合する場合、inner_join関数ではBに指定の列の値がAにない場合は、Aのその行は出力から落とされる。落とされずにとりあえずAの行すべてを出力したい場合は、left_join関数を使う。指定の列を基準としてAにもBにも含まれる行を出力する場合はfull_join関数を使う。なお、簡潔な表示にするため、表示する際にはデータフレームに変換している。

> library(tidyverse)
> tbl <- read_csv("meibo.csv", show_col_types = FALSE, progress = FALSE)
> tbl2 <- tibble(no = c(1, 2, 4, 5), size = c("S", "M", "L", "LL"))
> as.data.frame(tbl)
no sei mei age weight
1 1 あいう えお 11 101.1
2 2 かきく けこ 22 202.2
3 3 さしす せそ 22 303.3
4 4 あいう けこ 33 404.4
> as.data.frame(tbl2)
no size
1 1 S
2 2 M
3 4 L
4 5 LL
> as.data.frame(tbl %>% inner_join(tbl2, by = "no"))
no sei mei age weight size
1 1 あいう えお 11 101.1 S
2 2 かきく けこ 22 202.2 M
3 4 あいう けこ 33 404.4 L
> as.data.frame(tbl %>% left_join(tbl2, by = "no"))
no sei mei age weight size
1 1 あいう えお 11 101.1 S
2 2 かきく けこ 22 202.2 M
3 3 さしす せそ 22 303.3 <NA> 4 4 あいう けこ 33 404.4 L
> as.data.frame(tbl %>% full_join(tbl2, by = "no"))
no sei mei age weight size
1 1 あいう えお 11 101.1 S
2 2 かきく けこ 22 202.2 M
3 3 さしす せそ 22 303.3 <NA> 4 4 あいう けこ 33 404.4 L
5 5 <NA> <NA> NA NA LL

2023年9月26日 (火)

[R]tibbleで特定の列の集計を行う

group_by関数とsummarize関数を組み合わせて使う。以下の例ではgroup_by関数にclass列を指定し、class列ごとのweightの平均を求めている。簡潔な表示にするため、表示する際にはデータフレームに変換している。group_by関数を使った後の出力を見ると「Groups: class [3]」と表示されており、内部的にはグループ化された異なるオブジェクトであることがわかる。

> library(tidyverse)
> no <- 1:6
> name <- c("AB", "CD", "EF", "GH", "IJ", "KL")
> weight <- c(11, 22, 33, 44, 55, 66)
> class <- c("a", "b", "c", "a", "a", "b")
> tbl <- tibble(no, name, weight, class)
> as.data.frame(tbl)
no name weight class
1 1 AB 11 a
2 2 CD 22 b
3 3 EF 33 c
4 4 GH 44 a
5 5 IJ 55 a
6 6 KL 66 b
> as.data.frame(tbl %>% group_by(class) %>% summarize(weight = mean(weight)))
class weight
1 a 36.66667
2 b 44.00000
3 c 33.00000
> tbl %>% group_by(class)
# A tibble: 6 × 4
# Groups: class [3]
no name weight class
(以下、表示省略)

2023年9月25日 (月)

[R]文字からCIDを得る

ここからファイルaj17-kanji.txtをダウンロードしてカレントディレクトリに置き、以下のコマンドを実行する。

> library(tidyverse)
> tbl <- read_delim("aj17-kanji.txt", delim = "\t", comment = "#", col_names = FALSE, progress = FALSE, show_col_types = FALSE)
> cid0 <- tbl[[1]]
> ucd0 <- tbl[[19]]
> idx <- which(nchar(ucd0) == 6)
> cid <- cid0[idx]
> ucd <- ucd0[idx]
> h1 <- as.integer(as.hexmode(substr(ucd, 3, 4)))
> h2 <- as.integer(as.hexmode(substr(ucd, 5, 6)))
> mx <- matrix(c(h1, h2), ncol = 2)
> s <- character(nrow(mx))
> for (i in 1:nrow(mx)) {
+ s[i] <- iconv(rawToChar(as.raw(mx[i, ])), from = "unicodeFFFE")
+ }

上記は処理を簡単にするため、Unicodeが16進数表記で「U+」を除く4桁の文字のみ取り出している。文字を指定して、CIDを取り出してみる。最後の例のとおり、該当する文字がない場合は数値のNULLが返る。

> idx <- which(s == "江")  # CIDの2000が示す文字は「江」
> print(cid[idx])
[1] 2000
> idx <- which(s == "輪") # CIDの4000が示す文字は「輪」
> print(cid[idx])
[1] 4000
> idx <- which(s == "𩸽") # サロゲートペアの文字は本整理では対象外
> print(cid[idx])
numeric(0)

2023年9月24日 (日)

[R]CIDから文字を得る

ここからファイルaj17-kanji.txtをダウンロードしてカレントディレクトリに置き、以下のコマンドを実行する。

> library(tidyverse)
> tbl <- read_delim("aj17-kanji.txt", delim = "\t", comment = "#", col_names = FALSE, progress = FALSE, show_col_types = FALSE)
> cid0 <- tbl[[1]]
> ucd0 <- tbl[[19]]
> idx <- which(nchar(ucd0) == 6)
> cid <- cid0[idx]
> ucd <- ucd0[idx]
> h1 <- as.integer(as.hexmode(substr(ucd, 3, 4)))
> h2 <- as.integer(as.hexmode(substr(ucd, 5, 6)))
> mx <- matrix(c(h1, h2), ncol = 2)

上記は処理を簡単にするため、Unicodeが16進数表記で「U+」を除く4桁の文字のみ取り出している。CIDを指定して、文字を取り出してみる。最後の例のとおり、文字が割り当てられていないCIDを指定すると、文字列の空欄が返る。

> idx <- which(cid == 2000)  # CIDの2000が示す文字は「江」
> print(iconv(rawToChar(as.raw(mx[idx, ])), from = "unicodeFFFE"))
[1] "江"
> idx <- which(cid == 4000) # CIDの4000が示す文字は「輪」
> print(iconv(rawToChar(as.raw(mx[idx, ])), from = "unicodeFFFE"))
[1] "輪"
> idx <- which(cid == 99999) # CIDの99999が示す文字は存在しない
> print(iconv(rawToChar(as.raw(mx[idx, ])), from = "unicodeFFFE"))
[1] "

2023年9月23日 (土)

[R]二つのベクトルの要素の共通部分と和集合を得る

二つのベクトルのどちらにも属している要素(共通部分、A∩B)のベクトルを得るには、union関数を使う。

二つのベクトルの少なくとも一方に属している要素(和集合、A∪B)のベクトルを得るには、intersect関数を使う。

一方にしか属していない要素のベクトルを得るには、setdiff関数を使う。第一引数のベクトルの要素から、第二引数のベクトルに含まれる要素を取り除いたベクトルを返す。

> a <- 1:4
> b <- 3:7
> a
[1] 1 2 3 4
> b
[1] 3 4 5 6 7
> union(a, b)
[1] 1 2 3 4 5 6 7
> intersect(a, b)
[1] 3 4
> setdiff(a, b)
[1] 1 2
> setdiff(b, a)
[1] 5 6 7

2023年9月22日 (金)

[R]ベクトルの要素同士を比較する

ベクトル同士を比較演算して比較すると、要素数が同じか、一方の要素数が一方の整数倍の場合のみ、要素同士で比較することが可能。

> i <- c(1, 2, 3, 4)
> j <- c(1, 2, 3)
> k <- c(1, 2, 4)
> l <- c(1, 2)
> m <- 4
> j == k
[1] TRUE TRUE FALSE
> i == l
[1] TRUE TRUE FALSE FALSE
> i == m
[1] FALSE FALSE FALSE TRUE
> i == j
[1] TRUE TRUE TRUE FALSE
警告メッセージ:
i == j で:
長いオブジェクトの長さが短いオブジェクトの長さの倍数になっていません

上記の等号の比較演算子による比較は、互いの要素すべてが同じ場合、戻り値のベクトルはすべてがTRUEになる。すべての要素が同じ(戻り値がすべてTRUE)か否かの判定には、all関数を使う。all関数は、要素がすべてTRUEならばTRUEを返す。すべての要素が異なる場合の判定は論理否定の演算子を追加する。すべての要素が同じか否かの判定は、setequal関数も使える。

> i <- c(1, 2, 3)
> j <- c(1, 2, 4)
> k <- c(1, 2, 3)
> all(i == j)
[1] FALSE
> all(i == k)
[1] TRUE
> !all(i == k)
[1] FALSE
> setequal(i, j)
[1] FALSE
> setequal(i, k)
[1] TRUE

互いの要素が一つでも同じ場合の判定には、any関数を使う。any関数は要素が一つでもTRUEの場合にTRUEを返す。

> i <- 1:3
> j <- c(1, 2, 4)
> k <- 4:6
> any(i == j)
[1] TRUE
> any(i == k)
[1] FALSE

2023年9月21日 (木)

[R]任意の文字コードから文字列を作成する

iconv関数、rawToChar関数、as.raw関数を組み合わせて使用する。以下は文字コードがUTF-8の環境で行った例。toオプションを省略すると自動的に現在の環境のUTF-8て出力されるため、以下の例ではtoオプションは省略可能。それぞれ順番にシフトJIS、UTF-8、UTF-16(ビッグエンディアン)、UTF-16(ビッグエンディアン)、UTF-16(リトルエンディアン)、Unicodeの文字コード(バイト列)からにそれぞれ文字列「阿位」を作成している。

> Sys.getlocale()
[1] "LC_COLLATE=Japanese_Japan.utf8;LC_CTYPE=Japanese_Japan.utf8;LC_MONETARY=Japanese_Japan.utf8;LC_NUMERIC=C;LC_TIME=Japanese_Japan.utf8"
> iconv(rawToChar(as.raw(c(0x88, 0xa2, 0x88, 0xca))), from = "SJIS", to = "UTF-8")
[1] "阿位"
> iconv(rawToChar(as.raw(c(0xe9, 0x98, 0xbf, 0xe4, 0xbd, 0x8d))), from = "UTF-8", to = "UTF-8")
[1] "阿位"
> iconv(rawToChar(as.raw(c(0x96, 0x3f, 0x4f, 0x4d))), from = "UTF-16", to = "UTF-8")
[1] "阿位"
> iconv(rawToChar(as.raw(c(0x96, 0x3f, 0x4f, 0x4d))), from = "UTF-16BE", to = "UTF-8")
[1] "阿位"
> iconv(rawToChar(as.raw(c(0x3f, 0x96, 0x4d, 0x4f))), from = "UTF-16LE", to = "UTF-8")
[1] "阿位"
> iconv(rawToChar(as.raw(c(0x96, 0x3f, 0x4f, 0x4d))), from = "unicodeFFFE", to = "UTF-8")
[1] "阿位"

2023年9月20日 (水)

[R]任意の進数表記の文字列を任意の進数に変換する

strtoi関数を使う。baseオプションに変換したい進数の値(2進数なら2、10進数なら10、・・・)を与える。

> strtoi("01111", base = 10)
[1] 1111
> strtoi("01111", base = 2)
[1] 15
> strtoi("f", base = 16)
[1] 15
> strtoi("ff", base = 16)
[1] 255

2023年9月19日 (火)

[R]文字列の16進数を10進数に変換する

as.integer関数とas.hexmode関数を組み合わせる。as.hexmode関数は引数に与えた文字列をhexmodeという種類の値に変換し、as.integer関数で整数に変換している。

> as.hexmode("f")
[1] "f"
> class(as.hexmode("f"))
[1] "hexmode"
> as.integer(as.hexmode("f"))
[1] 15
> as.integer(as.hexmode("ff"))
[1] 255
> as.integer(as.hexmode("ff")) + 1
[1] 256

2023年9月18日 (月)

[R]文字列から各文字の文字コードを得る

iconv関数で指定の文字列を変換してそれをcharToRaw関数でロウ型に変換すると文字コードがバイト列で表示される。

> Sys.getlocale()
[1] "LC_COLLATE=Japanese_Japan.utf8;LC_CTYPE=Japanese_Japan.utf8;LC_MONETARY=Japanese_Japan.utf8;LC_NUMERIC=C;LC_TIME=Japanese_Japan.utf8"
> charToRaw(iconv("阿", to = "ISO-2022-JP")) # JIS
[1] 1b 24 42 30 24
> charToRaw(iconv("阿", to = "SJIS")) # シフトJIS
[1] 88 a2
> charToRaw(iconv("阿", to = "UTF-8")) # UTF-8
[1] e9 98 bf
> charToRaw(iconv("阿", to = "UTF-16")) # UTF-16(ビッグエンディアン)
[1] fe ff 96 3f
> charToRaw(iconv("阿", to = "UTF-16BE")) # UTF-16(ビッグエンディアン)
[1] 96 3f
> charToRaw(iconv("阿", to = "UTF-16LE")) # UTF-16(リトルエンディアン)
[1] 3f 96
> charToRaw(iconv("阿", to = "unicodeFFFE")) # Unicode
[1] 96 3f

iconv関数に与える文字は文字列でもかまわない。ただし、要素が1つだけ(長さが1)の文字ベクトルでなければならない。

> charToRaw(iconv("阿位鵜", to = "SJIS"))
[1] 88 a2 88 ca 89 4c
> charToRaw(iconv(c("阿位", "鵜江"), to = "SJIS"))
[1] 88 a2 88 ca
警告メッセージ:
charToRaw(iconv(c("阿位", "鵜江"), to = "SJIS")) で:
引数は長さ 1 の文字ベクトルである必要があります
最初の要素以外は全て無視されます

2023年9月17日 (日)

[R]tibbleから指定の列だけをベクトルで抜き出す

tidyverse(dplyr)パッケージに含まれるpull関数を使う。以下のような中身のCSVファイルをmeibo.csvと保存しておく。

no,    sei,  mei, age, weight
1, あいう, えお, 11, 101.1
2, かきく, けこ, 22, 202.2
3, さしす, せそ, 22, 303.3
4, あいう, けこ, 33, 404.4

meibo.csvを読み込んで、条件を指定して行を抜き出してみる。%>%演算子も使える。

> library(tidyverse)
> tbl <- read_csv("meibo.csv", show_col_types = FALSE, progress = FALSE)
> pull(tbl, 2)
[1] "あいう" "かきく" "さしす" "あいう"
> pull(tbl, mei)
[1] "えお" "けこ" "せそ" "けこ"
> tbl %>% pull(4)
[1] 11 22 22 33
> tbl %>% pull(weight)
[1] 101.1 202.2 303.3 404.4

[[ ]]記号や$記号を使う場合と全く同じである。

> tbl[[4]]
[1] 11 22 22 33
> tbl$weight
[1] 101.1 202.2 303.3 404.4

2023年9月15日 (金)

[R]read_delim関数使用時の画面表示を抑制する

readrパッケージのread_delim関数は、デフォルトでは、読み込み時にいろいろ情報を表示する。これを一切表示させないようにするには、show_col_typesにFALSEを指定する。読み込みの経過を表示させないようにするため、あらかじめprogressにもFALSEを指定している。

> library(readr)
> tbl <- read_delim("c:/windows/win.ini", delim = "\n", col_names = FALSE, progress = FALSE)
Rows: 7 Columns: 1
── Column specification ─────────────────────────────────
Delimiter: "\001"
chr (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
> tbl <- read_delim("c:/windows/win.ini", delim = "\n", col_names = FALSE, progress = FALSE, show_col_types = FALSE)
>

以下を「C:\Users\○○○\Documents\.RProfile」(○○○はアカウント名)に記述しておけば、R起動時に自動的に読み込み、デフォルトでそのような設定になる。

options(
readr.show_progress = FALSE,
readr.show_col_types = FALSE
)

R起動後でも、以下のコマンドを実行すれば同じようにデフォルトでそのような設定になる。

> options(readr.show_progress = FALSE, readr.show_col_types = FALSE)

試してみる。

> library(readr)
> tbl <- read_delim("c:/windows/win.ini", delim = "\n", col_names = FALSE)
>

2023年9月13日 (水)

[R]read_delim関数を使うと画面表示が乱れる(カーソルが飛ぶ)

readrパッケージのread_delim関数は、デフォルトでは、読み込み時に実際は表示されないものの経過を画面表示しようとするため、その影響で、使用後は画面表示が乱れ、カーソルがおかしなところに飛んでしまう。この画面表示の乱れが起きないようにするには、progressオプションにFALSEを指定する。

> library(readr)
> tbl <- read_delim("c:/windows/win.ini", delim = "\n", col_names = FALSE)

←カーソルがコマンドラインのプロンプトから離れた右側に表示され、表示が乱れる。

一度Rを終了し、再起動して次のコマンドを実行する。

> library(readr)
> tbl <- read_delim("c:/windows/win.ini", delim = "\n", col_names = FALSE, progress = FALSE)

←カーソルがコマンドラインのプロンプトのすぐ右側に表示され、表示が乱れない。

以下を.RProfileに記述しておけば、R起動時に自動的に読み込み、デフォルトでそのような設定になる。

options(
readr.show_progress = FALSE
)

R起動後でも、以下のコマンドを実行すれば同じようにデフォルトでそのような設定になる。

> options(readr.show_progress = FALSE)

2023年9月12日 (火)

[R]文字コードを指定してスクリプトを実行する

source関数は、指定したスクリプトファイルを読み込んで実行するが、そのファイルの文字コードが実行環境と同じでない場合は実行することができない。以下は、UTF-8の環境下で、文字コードがシフトJISの以下のようなスクリプトファイル(exec_sjis.Rと保存)を読み込んでコマンドを実行した例。

d <- sum(1:100)
cat(sprintf("1から100まで足した合計 %d\n", d))

実行してみる。

> R.version.string
[1] "R version 4.3.0 (2023-04-21 ucrt)"
> Sys.getlocale()
[1] "LC_COLLATE=Japanese_Japan.utf8;LC_CTYPE=Japanese_Japan.utf8;LC_MONETARY=Japanese_Japan.utf8;LC_NUMERIC=C;LC_TIME=Japanese_Japan.utf8"
> source("exec_sjis.R")
エラー: invalid multibyte character in parser (exec_sjis.R:2:15)
> source("exec_sjis.R", encoding = "SJIS")
1から100まで足した合計 5050

2023年9月11日 (月)

[R]Mickey Mantleの打撃成績推移(「Rによるセイバーメトリクス入門」(技術評論社)pp.184-188)

> library(tidyverse)
> library(Lahman)
> get_stats <- function(player.id) {
+ batting %>%
+ filter(playerID == player.id) %>%
+ inner_join(People, by = "playerID") %>%
+ mutate(birthyear = ifelse(birthMonth >= 7, birthYear + 1,
+ birthYear),
+ Age = yearID - birthyear,
+ SLG = (H - X2B - X3B - HR + 2 * X2B + 3 * X3B + 4 * HR) / AB,
+ OBP = (H + BB + HBP) / (AB + BB + HBP + SF),
+ OPS = SLG + OBP) %>%
+ select(Age, SLG, OBP, OPS)
+ }
> fit_model <- function(d) {
+ fit <- lm(OPS ~ I(Age - 30) + I((Age - 30) ^ 2), data = d)
+ b <- coef(fit)
+ Age.max <- 30 - b[2] / b[3] / 2
+ Max <- b[1] - b[2] ^ 2 / b[3] / 4
+ list(fit = fit, Age.max = Age.max, Max = Max)
+ }
> People %>%
+ filter(nameFirst == "Mickey", nameLast == "Mantle") %>%
+ pull(playerID) -> mantle_id
> Batting %>% replace_na(list(SF = 0, HBP = 0)) -> batting
> Mantle <- get_stats(mantle_id)
> g8_1 <- ggplot(Mantle, aes(Age, OPS)) + geom_point()
> ggsave("fig8_1.png", plot = g8_1)
Saving 7 x 7 in image
> F2 <- fit_model(Mantle)
> print(coef(F2$fit))
(Intercept) I(Age - 30) I((Age - 30)^2)
1.043134189 -0.022883024 -0.003868915
> print(c(F2$Age.max, F2$Max))
I(Age - 30) (Intercept)
27.04271 1.07697
> g8_2 <- ggplot(Mantle, aes(Age, OPS)) + geom_point() +
+ geom_smooth(method = "lm", se = FALSE, size = 1.5,
+ formula = y ~ poly(x, 2, raw= TRUE)) +
+ geom_vline(xintercept = F2$Age.max, linetype = "dashed",
+ color = "darkgrey") +
+ geom_hline(yintercept = F2$Max, linetype = "dashed", color = "darkgrey") +
+ annotate(geom = "text", x = c(29, 20), y = c(0.72, 1.1),
+ label = c("Peak age", "Max"), size = 5)
> ggsave("fig8_2.png", plot = g8_2)
Saving 7 x 7 in image
> print(F2 %>% pluck("fit") %>% summary())
Call:
lm(formula = OPS ~ I(Age - 30) + I((Age - 30)^2), data = d)
Residuals:
Min 1Q Median 3Q Max
-0.17282 -0.04010 0.02203 0.04507 0.12819
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.0431342 0.0279009 37.387 3.19e-16 ***
I(Age - 30) -0.0228830 0.0056381 -4.059 0.001029 **
I((Age - 30)^2) -0.0038689 0.0008283 -4.671 0.000302 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.08421 on 15 degrees of freedom
Multiple R-squared: 0.6018, Adjusted R-squared: 0.5488
F-statistic: 11.34 on 2 and 15 DF, p-value: 0.001001

出力したfig8_1.png(図8.1)とfig8_2.png(図8.2)は以下のとおり。
Fig8_1 Fig8_2

2023年9月10日 (日)

[R]様々な文字コードのCSVファイルを読み込む

read.table関数を使う。fileEncodingオプションに文字コードを指定する。

動作確認のため、最初にPowerShellを使ってカレントディレクトリに、順番にシフトJIS、UTF-8(BOM無し)、UTF-8(BOM付き)、UTF-16(ビッグエンディアン、BOM付き)、UTF-16(リトルエンディアン、BOM付き)のCSVファイルを出力する。PowerShellのコマンドレットでは、Unicode系は原則BOM付きとなる。そのため、BOM無しのUTF-8の出力には、FileクラスのWriteAllLinesメソッドを使用している。

PS > $lines = "1,ABC", "2,abc", "3,あいう"
PS > $odir = (gl).Path
PS > $lines | Out-File -Encoding default ($odir + "\sjis.csv")
PS > [IO.File]::WriteAllLines(($odir + "\utf8nb.csv"), $lines)
PS > $lines | Out-File -Encoding utf8 ($odir + "\utf8wb.csv")
PS > $lines | Out-File -Encoding bigendianunicode ($odir + "\utf16bewb.csv")
PS > $lines | Out-File -Encoding unicode ($odir + "\utf16lewb.csv")
PS > Get-Content .\sjis.csv
1,ABC
2,abc
3,あいう
PS > Get-Content .\utf8nb.csv -Encoding utf8
1,ABC
2,abc
3,あいう

Rを起動して、read.table関数で読み込んでみる。現在の環境下における文字コードは特にオプションを指定しなくても読み込むことができる。

> Sys.getlocale()
[1] "LC_COLLATE=Japanese_Japan.utf8;LC_CTYPE=Japanese_Japan.utf8;LC_MONETARY=Japanese_Japan.utf8;LC_NUMERIC=C;LC_TIME=Japanese_Japan.utf8"
> read.table("sjis.csv", sep = ",")
V1 V2
1 no name
2 1 ABC
3 2 abc
4 3 \x82\xa0\x82\xa2\x82\xa4
> read.table("utf8nb.csv", sep = ",")
V1 V2
1 no name
2 1 ABC
3 2 abc
4 3 あいう

fileEncodingオプションにそれぞれ文字コードを指定する。UTF-16の場合、「UTF-16」を指定すればバイトオーダーマークからエンディアンを推定して読み込む。エンディアンを指定することもできる。最後の例のとおりに、エンディアンの指定を間違えると、指定のとおりに読み込もうとして読み込みに失敗する。

> read.table("sjis.csv", sep = ",", fileEncoding = "SJIS")
V1 V2
1 1 ABC
2 2 abc
3 3 あいう
> read.table("utf8nb.csv", sep = ",", fileEncoding = "UTF-8")
V1 V2
1 1 ABC
2 2 abc
3 3 あいう
> read.table("utf8wb.csv", sep = ",", fileEncoding = "UTF-8")
V1 V2
1 1 ABC
2 2 abc
3 3 あいう
> read.table("utf16bewb.csv", sep = ",", fileEncoding = "UTF16")
V1 V2
1 1 ABC
2 2 abc
3 3 あいう
> read.table("utf16lewb.csv", sep = ",", fileEncoding = "UTF16")
V1 V2
1 1 ABC
2 2 abc
3 3 あいう
> read.table("utf16bewb.csv", sep = ",", fileEncoding = "UTF-16BE")
V1 V2
1 1 ABC
2 2 abc
3 3 あいう
> read.table("utf16lewb.csv", sep = ",", fileEncoding = "UTF-16LE")
V1 V2
1 1 ABC
2 2 abc
3 3 あいう
> read.table("utf16lewb.csv", sep = ",", fileEncoding = "UTF-16BE")
V1
1 \ufffe\u3100Ⰰ䄀䈀䌀ഀ\u0a00㈀Ⰰ愀戀挀ഀ\u0a00㌀Ⰰ䈰䐰䘰ഀ\u0a00
警告メッセージ:
read.table("utf16lewb.csv", sep = ",", fileEncoding = "UTF-16BE") で:
incomplete final line found by readTableHeader on 'utf16lewb.csv'

2023年9月 9日 (土)

[R]巨大なCSVファイルを高速で読み込む

tribbleパッケージのread_csv関数を使用する。まずは巨大なCSVファイルを作成する。PowerShellのコマンドラインで以下を実行する。

PS > $s = "1,abc,2.3`r`n2,あい,4.5`r`n3,えおyz,NA`r`n"
PS > [Text.Encoding]::GetEncoding("utf-8").GetByteCount($s)
40
PS > $n = 1024 * 1024 * 4
PS > $file = (gl).Path + "\temp_utf8nb.csv"
PS > [IO.File]::WriteAllText($file, $s * $n)
PS > Get-ChildItem .\temp_utf8nb.csv
ディレクトリ: ○○○
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2023/08/20 22:27 167772160 temp_utf8nb.csv
PS > Get-Content .\temp_utf8nb.csv -TotalCount 5 -Encoding utf8
1,abc,2.3
2,あい,4.5
3,えおyz,NA
1,abc,2.3
2,あい,4.5

変数$sには大きさが40バイトの文字列(改行コード含む)が格納され、それを1024倍して40KiB(キビバイト)、さらに1024倍して40MiB(メビバイト)、最後に4倍して160MiBにしてCSVファイル(temp_utf8nb.csv)に出力している。これの160MiBのCSVファイルを使用する。なお、FileクラスのWriteAllTextメソッドを使用して文字コードをUTF-8(BOM無し)で出力していることに注意。改行コードは$sの中身のとおりにCR+LF。

Rで標準で使うことができるread.table関数を使ってみる。

> system.time(dtf <- read.table("temp_utf8nb.csv", sep = ",", fileEncoding = "UTF-8"))
ユーザ システム 経過
7.50 0.16 7.84
> system.time(dtf <- read.table("temp_utf8nb.csv", sep = ",", fileEncoding = "UTF-8"))
ユーザ システム 経過
7.10 0.14 7.88
> system.time(dtf <- read.table("temp_utf8nb.csv", sep = ",", fileEncoding = "UTF-8"))
ユーザ システム 経過
7.33 0.15 7.83
> nrow(dtf)
[1] 12582912
> head(dtf, 5)
V1 V2 V3
1 1 abc 2.3
2 2 あい 4.5
3 3 えおyz NA
4 1 abc 2.3
5 2 あい 4.5

キャッシュの影響を考慮して3回試している。この環境では読み込みに7.8秒程度要する。次にreadrパッケージのread_csv関数を使ってみる。

> library(readr)
> system.time(tbl <- read_csv("temp_utf8nb.csv", col_names = FALSE, progress = FALSE, show_col_types = FALSE))
ユーザ システム 経過
7.70 0.12 1.34
> system.time(tbl <- read_csv("temp_utf8nb.csv", col_names = FALSE, progress = FALSE, show_col_types = FALSE))
ユーザ システム 経過
7.42 0.19 1.31
> system.time(tbl <- read_csv("temp_utf8nb.csv", col_names = FALSE, progress = FALSE, show_col_types = FALSE))
ユーザ システム 経過
7.56 0.22 1.36
> nrow(tbl)
[1] 12582912
> head(as.data.frame(tbl), 5)
X1 X2 X3
1 1 abc 2.3
2 2 あい 4.5
3 3 えおyz NA
4 1 abc 2.3
5 2 あい 4.5

キャッシュの影響を考慮して3回試している。この環境では読み込みに1.4秒程度要する。read_csvはread.tableよりもファイルの読み込みが5倍以上早いことがわかる。なお、read_delim関数でも同じくらい早ことがわかる。

> system.time(tbl <- read_delim("temp_utf8nb.csv", col_names = FALSE, delim = ",", progress = FALSE, show_col_types = FALSE))
ユーザ システム 経過
7.14 0.08 1.25

原因は不明だが、このテスト環境(Windows版4.2.2)では、read.csv関数はfileEncodingオプションが指定どおり動作しない。そのため、上の例では代わりにread.table関数を使用している。

2023年9月 8日 (金)

[R]tibbleから条件に一致する行を抜き出す

tidyverse(dplyr)パッケージに含まれるfilter関数を使う。以下のような中身のCSVファイルをmeibo.csvと保存しておく。

no,    sei,  mei, age, weight
1, あいう, えお, 11, 101.1
2, かきく, けこ, 22, 202.2
3, さしす, せそ, 22, 303.3
4, あいう, けこ, 33, 404.4

meibo.csvを読み込んで、条件を指定して行を抜き出してみる。

> library(tidyverse)
> tbl <- read_csv("meibo.csv", show_col_types = FALSE, progress = FALSE)
> print(tbl)
# A tibble: 4 × 5
no sei mei age weight
<dbl> <chr> <chr> <dbl> <dbl>
1 1 あいう えお 11 101.
2 2 かきく けこ 22 202.
3 3 さしす せそ 22 303.
4 4 あいう けこ 33 404.
> filter(tbl, sei == "あいう")
# A tibble: 2 × 5
no sei mei age weight
<dbl> <chr> <chr> <dbl> <dbl>
1 1 あいう えお 11 101.
2 4 あいう けこ 33 404.
> filter(tbl, sei == "あいう", mei == "けこ")
# A tibble: 1 × 5
no sei mei age weight
  <dbl> <chr> <chr> <dbl> <dbl>
1 4 あいう けこ 33 404.
> filter(tbl, sei == "あいう" & mei == "けこ")
# A tibble: 1 × 5
no sei mei age weight
  <dbl> <chr> <chr> <dbl> <dbl>
1 4 あいう けこ 33 404.
> tbl %>% filter(age == 22)
# A tibble: 2 × 5
no sei mei age weight
  <dbl> <chr> <chr> <dbl> <dbl>
1 2 かきく けこ 22 202.
2 3 さしす せそ 22 303.

2023年9月 7日 (木)

[R]プロキシを設定する(Windows版)

学校や職場でネットワークにプロキシが設定されている場合、パッケージのインストールがうまくいかないことがある。例えば、magickパッケージをインストールしてみる。

> install.packages("magick")
パッケージを ‘C:/Users/○○○’ 中にインストールします
(‘lib’ が指定されていないため)
--- このセッションで使うために、CRAN のミラーサイトを選んでください ---
警告: failed to download mirrors file ( URL 'https://cran.r-project.org/CRAN_mirrors.csv' を開けません ); using local file '○○○/CRAN_mirrors.csv'
警告: リポジトリー https://○○○/pub/cran/src/contrib に対する索引にアクセスできません :
URL 'https://○○○/pub/cran/src/contrib/PACKAGES' を開けません

ミラーサイト選択のダイアログ表示まで10秒以上待たされ、選択後に30秒以上経過して「~に対する索引にアクセスできません」と表示された場合は、プロキシに接続しようとして失敗している可能性が高い。

このような状態では、curlGetHeaders関数でhttp://www.google.co.jpのヘッダーを読み取ると、以下のようなエラーが生じるはず。

> curlGetHeaders("http://www.google.co.jp")
curlGetHeaders("http://www.google.co.jp") でエラー:
libcurl error code ○:
Failed to connect to www.google.co.jp port ○ after ○ ms: Couldn't connect to server

プロキシの設定を正しく行うことで解決できる。

「コントロールパネル」→「ユーザーアカウント」→左側メニュー「環境変数の変更」。

上のユーザー環境変数に、以下の2つの環境変数を設定する。ここでプロキシのアカウント名はnamae、そのパスワードはpasu。プロキシサーバーのアドレスはproxy.company.co.jp、そのポートは1234としている。2つ目のHTTPS_PROXYは「http:~」であることに注意。

HTTP_PROXY

http://namae:pasu@proxy.company.co.jp:1234
HTTPS_PROXY

http://namae:pasu@proxy.company.co.jp:1234

設定後に、Rを再起動してcurlGetHeaders関数でhttp://www.google.co.jpのヘッダーを取得してみる。

> curlGetHeaders("http://www.google.co.jp")
[1] "HTTP/1.0 200 OK\r\n"
(以下、表示省略)

上記のように表示されればプロキシの設定は成功。install.packages関数でパッケージのインストールが出来るようになるはず。

以下のように表示された場合は、アカウント名やパスワードの設定を間違えている可能性が高い。

> curlGetHeaders("http://www.google.co.jp")
[1] "HTTP/1.0 407 Proxy Authentication Required\r\n"
(表示省略)

2023年9月 6日 (水)

[R]文字列を特定の文字または文字列で分割する

stringrパッケージのstr_split関数を使う。分割する文字列はベクトルで与えることができる。戻り値はリスト。分割させる文字または文字列は正規表現で指定することができる。

> library(stringr)
> s <- c("A B C", " D E F ")
> str_split(s, " ")
[[1]]
[1] "A" "B" "C"
[[2]]
[1] "" "D" "E" "" "F" ""

上の例では、1つの空白(0x20)で分割しようとしたが、ベクトルの2番目の要素は与えた文字列の先頭や最後に空白が含まれており、文字間の空白も1つ以上のため、分割がうまくいっていない。このような場合はあらかじめ前後の空白をtrimws関数で取り除き、正規表現のパターンを「[ ]+」(1つ以上の連続した空白(0x20))とすればよい。

> str_split(trimws(s), "[ ]+")
[[1]]
[1] "A" "B" "C"
[[2]]
[1] "D" "E" "F"

strsplit関数の戻り値はリストであり、これをベクトルにする場合はunlist関数を使う。

> unlist(str_split(trimws(s), "[ ]+"))
[1] "A" "B" "C" "D" "E" "F"

規則性なく空白が含まれるCSVの文字列の場合は、以下のような分割パターンを正規表現で与えれば、うまく分割することができる。

> unlist(str_split(trimws(s), "[ ,]+"))
[1] "A" "B" "C"

2023年9月 5日 (火)

[R]日付時刻型ベクトルの特定の要素(年、月、日、時、分、秒)だけを修正する

lubridateパッケージのupdate関数を使う。

> library(lubridate)
> dttm <- ymd_hms("2012-12-31 12:34:56", "2034-01-02 23:45:12", tz = "Japan")
> print(dttm)
[1] "2012-12-31 12:34:56 JST" "2034-01-02 23:45:12 JST"
> update(dttm, month = 3)
[1] "2012-03-31 12:34:56 JST" "2034-03-02 23:45:12 JST"
> update(dttm, hour = 7)
[1] "2012-12-31 07:34:56 JST" "2034-01-02 07:45:12 JST"

2023年9月 4日 (月)

[R]日や時間などをしてして日付時刻型ベクトルを作成する

lubridateパッケージのymd_hms関数を使う。年、月、日、時、分、秒をすべて指定する必要がある。tzオプションにタイムゾーンの情報も適切に指定する必要がある。指定しないとUTCの日付時刻を作成する。

> library(lubridate)
> dttm <- ymd_hms("2012-12-31")
警告メッセージ:
All formats failed to parse. No formats found.
> dttm <- ymd_hms("2012-12-31 12:34:56")
> print(dttm)
[1] "2012-12-31 12:34:56 UTC"
> dttm <- ymd_hms("2012-12-31 12:34:56", "2034-01-02 23:45:12")
> print(dttm)
[1] "2012-12-31 12:34:56 UTC" "2034-01-02 23:45:12 UTC"
> dttm <- ymd_hms("2012-12-31 12:34:56", "2034-01-02 23:45:12", tz = "Japan")
> print(dttm)
[1] "2012-12-31 12:34:56 JST" "2034-01-02 23:45:12 JST"
> dttm <- ymd_hms("2012-12-31 12:34:56", "2034-01-02 23:45", tz = "Japan")
警告メッセージ:
1 failed to parse.
> typeof(dttm)
[1] "double"
> class(dttm)
[1] "POSIXct" "POSIXt"
> mode(dttm)
[1] "numeric"

« 2023年8月 | トップページ | 2023年10月 »

無料ブログはココログ

■■

■■■