« 2023年12月 | トップページ | 2024年2月 »

2024年1月31日 (水)

[R]tibbleの列名を変える

rename関数を使う。引数には、変えたい列を「(新列名)=(旧列名)」と指定する。複数同時に指定できる。

以下の例では簡潔な表示にするため、表示の際にtibbleはデータフレームに変換している。

> library(tidyverse)
> no <- 1:3
> name <- c("ABC", "abc", "123")
> age <- 23:21
> tib <- tibble(no, name, age)
> tib %>% as.data.frame()
no name age
1 1 ABC 23
2 2 abc 22
3 3 123 21
> tib %>% rename(seimei = name, toshi = age) %>% as.data.frame()
no seimei toshi
1 1 ABC 23
2 2 abc 22
3 3 123 21

2024年1月30日 (火)

[R]特定の列の値でtibbleの行を並び替える

arrange関数を使う。引数に、並び替えの基準にする列を指定する。複数同時に指定できる。デフォルトでは昇順に並び替えられるが、これを降順にする場合は列の指定にdesc関数を指定して指定すればよい。

以下の例では簡潔な表示にするため、表示の際にtibbleはデータフレームに変換している。

> library(tidyverse)
> no <- 1:5
> name <- c("ABC", "ABC", "abc", "123", "あいう")
> age <- 25:21
> tib <- tibble(no, name, age)
> as.data.frame(tib)
no name age
1 1 ABC 25
2 2 ABC 24
3 3 abc 23
4 4 123 22
5 5 あいう 21
> tib %>% arrange(age) %>% as.data.frame()
no name age
1 5 あいう 21
2 4 123 22
3 3 abc 23
4 2 ABC 24
5 1 ABC 25
> tib %>% arrange(name, age) %>% as.data.frame()
no name age
1 4 123 22
2 2 ABC 24
3 1 ABC 25
4 3 abc 23
5 5 あいう 21
> tib %>% arrange(name, desc(age)) %>% as.data.frame()
no name age
1 4 123 22
2 1 ABC 25
3 2 ABC 24
4 3 abc 23
5 5 あいう 21

2024年1月29日 (月)

[R]JISコードから区点番号を得る

以下の自作関数kutenToJisは、JISコードを与えると区点番号を返す。

jisToKuten <- function(jis) {
s <- sprintf("%04x", jis)
n1 <- as.integer(sprintf("0x%s", substr(s, 1, 2))) - 0x20
n2 <- as.integer(sprintf("0x%s", substr(s, 3, 4))) - 0x20
return(c(n1, n2))
}

以下の2つの漢字で動作確認をする。

美 - JISコード:0x487e(10進数 18558), 区点番号:4094
雲 - JISコード:0x3140(10進数 12608), 区点番号:1732

動作例。

> jisToKuten <- function(jis) {
+ s <- sprintf("%04x", jis)
+ n1 <- as.integer(sprintf("0x%s", substr(s, 1, 2))) - 0x20
+ n2 <- as.integer(sprintf("0x%s", substr(s, 3, 4))) - 0x20
+ return(c(n1, n2))
+ }
> kuten <- jisToKuten(0x487e)
> print(kuten)
[1] 40 94
> length(kuten)
[1] 2
> kuten <- jisToKuten(12608)
> print(kuten)
[1] 17 32
> length(kuten)
[1] 2

2024年1月28日 (日)

[R]区点番号からJISコードを得る

以下の自作関数kutenToJisは、区点番号を与えるとそのJISコードを返す。

kutenToJis <- function(ku, ten) {
s12 <- as.hexmode(ku + 0x20)
s34 <- as.hexmode(ten + 0x20)
s <- sprintf("0x%s%s", s12, s34)
return(as.integer(s))
}

以下の2つの漢字で動作確認をする。

美 - 区点番号:4094, JISコード:0x487e(10進数 18558)
雲 - 区点番号:1732, JISコード:0x3140(10進数 12608)

動作例。

> kutenToJis <- function(ku, ten) {
+ s12 <- as.hexmode(ku + 0x20)
+ s34 <- as.hexmode(ten + 0x20)
+ s <- sprintf("0x%s%s", s12, s34)
+ return(as.integer(s))
+ }
> kutenToJis(40, 94)
[1] 18558
> jis <- kutenToJis(40, 94)
> print(jis)
[1] 18558
> print(as.hexmode(jis))
[1] "487e"
> jis <- kutenToJis(17, 32)
> print(jis)
[1] 12608
> print(as.hexmode(jis))
[1] "3140"

2024年1月27日 (土)

[R]ヌル(NULL)が含まれるテキストファイルをread.table関数で読み込む

read.table関数は、読み込むファイルにヌル(NULL、0x00)が含まれていると、列内のヌル以降は読み込まなくなる。これをヌルは無視してとにかく読み込むようにするには、skipNulオプションにTRUEを指定する。以下は、3行からなるテキストファイルtemp.txtを作成し、そのファイルを読み込んだ例。3行目の「c」の次にはヌルを含んでおり、デフォルトでは警告が発生しているが、skipNulオプションにTRUEを指定すると、ヌルを無視してすべて読み込んでいることがわかる。

> 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"
> ch1 <- c(0x30:0x39, 0x0d, 0x0a, 0x41:0x5a, 0x0d, 0x0a)
> ch2 <- c(0x61:0x63, 0x00, 0x65:0x7a, 0x0d, 0x0a)
> ra <- as.raw(c(ch1, ch2))
> writeBin(ra, "temp.txt")
> read.table("temp.txt")
V1
1 0123456789
2 ABCDEFGHIJKLMNOPQRSTUVWXYZ
3 abc
警告メッセージ:
read.table("temp.txt") で: line 3 appears to contain embedded nulls
> read.table("temp.txt", skipNul = TRUE)
V1
1 0123456789
2 ABCDEFGHIJKLMNOPQRSTUVWXYZ
3 abcefghijklmnopqrstuvwxyz

2024年1月26日 (金)

[R]ベクトルやデータフレームをファイルに保存し、後で読み込んで再利用する

saveRDS関数を使うことで、オブジェクト(ベクトルやデータフレームなど)をファイルに保存することができる。このファイルに保存した内容は、後(Rの再起動後も可)でreadRDS関数を使ってその環境に読み込み再利用することができる。save関数と異なり、一つのオブジェクトしか保存することができず、readRDS関数で読み込む際は、その読み込み先のオブジェクトを指定する必要がある。

> rm(list = ls(all.names = TRUE))
> ls()
character(0)
> no <- c(30, 58, 64)
> name <- c("ライスシャワー", "メイショウドトウ", "メジロパーマー")
> seiyu <- c("石見舞菜香", "和多田美咲", "のぐちゆり")
> meibo <- data.frame(no, name, seiyu)
> print(meibo)
no name seiyu
1 30 ライスシャワー 石見舞菜香
2 58 メイショウドトウ 和多田美咲
3 64 メジロパーマー のぐちゆり
> saveRDS(meibo, file = "meibo.rds")
> rm(list = ls(all.names = TRUE))
> ls()
character(0)
> readRDS(file = "meibo.rds")
no name seiyu
1 30 ライスシャワー 石見舞菜香
2 58 メイショウドトウ 和多田美咲
3 64 メジロパーマー のぐちゆり
> ls()
character(0)
> dtf <- readRDS(file = "meibo.rds")
> ls()
[1] "dtf"
> print(dtf)
no name seiyu
1 30 ライスシャワー 石見舞菜香
2 58 メイショウドトウ 和多田美咲
3 64 メジロパーマー のぐちゆり

saveRDS関数はデフォルトではファイルをgzip圧縮をする。ファイルサイズは小さくなるが、その分、保存時も読み込み時も時間がかかることに注意。

2024年1月25日 (木)

[R]ベクトルやデータフレームをファイルに保存し、後で読み込んで再利用する

save関数を使うことで、複数のオブジェクト(ベクトルやデータフレームなど)をファイルにまとめて保存することができる。このファイルに保存した内容は、後(Rの再起動後も可)でload関数を使ってその環境に読み込んで再利用することができる。読み込み時は保存時のベクトル名などがそのまま使用される。保存するファイルの拡張子は以下の例のとおり「RData」とするのが無難。

> rm(list = ls(all.names = TRUE))
> no <- c(30, 58, 64)
> name <- c("ライスシャワー", "メイショウドトウ", "メジロパーマー")
> seiyu <- c("石見舞菜香", "和多田美咲", "のぐちゆり")
> dtf <- data.frame(no, name, seiyu)
> print(dtf)
no name seiyu
1 30 ライスシャワー 石見舞菜香
2 58 メイショウドトウ 和多田美咲
3 64 メジロパーマー のぐちゆり
> lis <- list(no, seiyu)
> print(lis)
[[1]]
[1] 30 58 64
[[2]]
[1] "石見舞菜香" "和多田美咲" "のぐちゆり"
> ls()
[1] "dtf" "lis" "name" "no" "seiyu"
> save(dtf, lis, file = "test.RData")
> rm(list = ls(all.names = TRUE))
> ls()
character(0)
> load("test.RData")
> ls()
[1] "dtf" "lis"
> print(dtf)
no name seiyu
1 30 ライスシャワー 石見舞菜香
2 58 メイショウドトウ 和多田美咲
3 64 メジロパーマー のぐちゆり
> print(lis)
[[1]]
[1] 30 58 64
[[2]]
[1] "石見舞菜香" "和多田美咲" "のぐちゆり"

load関数で読み込まれたベクトルなどと同じ名前のベクトルなどが既にその環境下にある場合は、上書きされてしまうことに注意。

> rm(list = ls(all.names = TRUE))
> ls()
character(0)
> dtf <- data.frame(no = 1:3)
> print(dtf)
no
1 1
2 2
3 3
> load("test.RData")
> ls()
[1] "dtf" "lis"
> print(dtf)
no name seiyu
1 30 ライスシャワー 石見舞菜香
2 58 メイショウドトウ 和多田美咲
3 64 メジロパーマー のぐちゆり

save関数はデフォルトでファイルをgzip圧縮をするため、ファイルサイズは小さくなるが、その分、保存時も読み込み時も時間がかかることに注意。

2024年1月24日 (水)

[R]SQL Serverのデータベースを作成する

RODBCパッケージを使う。以下はSQL Server(インスタンス名:SQLEXPRESS)が稼働しているサーバーで、自身のサーバのSQL Serverに接続してprincessというデータベースを作成した例。接続はWindows認証を前提としている。作成に成功すると何も表示されないが、以下のように失敗すると(既に作成済みのデータベースを作成しようとしている)、エラーメッセージが返される。

> library(RODBC)
> drv <- "driver={SQL Server}"
> srv <- "server=localhost\\sqlexpress"
> s <- paste(drv, srv, sep = ";")
> dc <- odbcDriverConnect(s)
> r <- sqlQuery(dc, "create database princess")
> print(r)
character(0)
> r <- sqlQuery(dc, "create database princess")
> print(r)
[1] "42000 1801 [Microsoft][ODBC SQL Server Driver][SQL Server]データベース 'princess' は既に存在します。別のデータベース名を選択してください。"
[2] "[RODBC] ERROR: Could not SQLExecDirect 'create database princess'"

select文を使い、作成したデータベースを確認してみる。

> r <- sqlQuery(dc, "select name, database_id, create_date from sys.databases")
> print(r)
name database_id create_date
1 master 1 2003-04-08 09:13:36
2 tempdb 2 ○○-○-○ ○:○:○
3 model 3 2003-04-08 09:13:36
4 msdb 4 2019-09-24 14:21:42
5 princess 5 ○○-○-○ ○:○:○

master、tempdb、model、msdbはSQL Serverインストール時に自動作成されるシステムのデータベース。princessデータベースが作成されていることがわかる。

2024年1月23日 (火)

[R]正規表現を使用してファイル名を表す文字列から拡張子を消す

gsub関数を使う。1つ目の例は拡張子をすべて削除、2つ目の例は拡張子を一つだけ削除した例。

> s <- c("カナメ.バッカニア.txt", "フレイア.ヴィオン.txt")
> print(s)
[1] "カナメ.バッカニア.txt" "フレイア.ヴィオン.txt"
> gsub("\\..+$", "", s)
[1] "カナメ" "フレイア"
> gsub("\\.[^\\.]+$", "", s)
[1] "カナメ.バッカニア" "フレイア.ヴィオン"

2024年1月22日 (月)

[R]文字列から正規表現にマッチする文字列を抜き出す

str_extract関数を使う。str_match関数はその抜き出した文字列を分解して配列にして返す。マッチする文字列が無い場合はNAを返す。

以下は、郵便番号を表していると思われる文字列から、郵便番号の箇所のみ抜き出した例。正規表現では、「3桁の数字」+「「-」か何も無し」+「4桁の数字」からなる文字列を郵便番号と仮定して、抜き出している。str_match関数はマッチした内容を分解して表形式にして返す。

> s <- c("123-4567", "〒123-4567", "1234567", "123@4567", " 123-4567 ")
> str_extract(s, "([0-9]{3})(-*)([0-9]{4})")
[1] "123-4567" "123-4567" "1234567" NA "123-4567"
> str_match(s, "([0-9]{3})(-*)([0-9]{4})")
[,1] [,2] [,3] [,4]
[1,] "123-4567" "123" "-" "4567"
[2,] "123-4567" "123" "-" "4567"
[3,] "1234567" "123" "" "4567"
[4,] NA NA NA NA
[5,] "123-4567" "123" "-" "4567"

2024年1月21日 (日)

[R]fwriteによる日付時刻型の出力

data.tableパッケージのfwrite関数では、日付時刻型のオブジェクトを出力するとUTCに変換して出力されてしまう(ちょうど9時間前の日付時刻が出力される)。以下の例のとおり、出力元のオブジェクトのタイムゾーンをJSTに設定しても、出力はUTCの日付時刻になり、それを表す記号(Z)が付けられて出力される。オブジェクトの値をそのまま(JSTのまま)出力したい場合は、dateTimeAsオプション(デフォルトは「ISO」)に「write.csv」を指定すると、よく見る書式でかつJSTで出力される。

「write.csv」を指定したときのファイルはExcelで開くと、その列はそのまま日付時刻型の値になる便利な書式である。

> library(data.table)
> library(lubridate)
> n <- 3
> no <- 1:n
> dtm <- make_datetime(2000, 1:n, 1, 2, 3, 4, "Asia/Tokyo")
> dtf <- data.frame(no, name, dtm)
> print(dtf)
no name dtm
1 1 January 2000-01-01 02:03:04
2 2 February 2000-02-01 02:03:04
3 3 March 2000-03-01 02:03:04
> fwrite(dtf, "temp.csv", sep = ",")
> shell("type temp.csv")
no,name,dtm
1,January,1999-12-31T17:03:04Z
2,February,2000-01-31T17:03:04Z
3,March,2000-02-29T17:03:04Z
> fwrite(dtf, "temp.csv", sep = ",", dateTimeAs = "ISO")
> shell("type temp.csv")
no,name,dtm
1,January,1999-12-31T17:03:04Z
2,February,2000-01-31T17:03:04Z
3,March,2000-02-29T17:03:04Z
> fwrite(dtf, "temp.csv", sep = ",", dateTimeAs = "squash")
> shell("type temp.csv")
no,name,dtm
1,January,19991231170304000
2,February,20000131170304000
3,March,20000229170304000
> fwrite(dtf, "temp.csv", sep = ",", dateTimeAs = "write.csv")
> shell("type temp.csv")
no,name,dtm
1,January,2000-01-01 02:03:04
2,February,2000-02-01 02:03:04
3,March,2000-03-01 02:03:04

2024年1月20日 (土)

[R]ベクトルの要素について集計する

table関数を使うと、ベクトルの要素に含まれる値を集計することができる。以下は英大文字を復元抽出で32個選び、それを集計した例。戻り値はテーブル型になる。

> LETTERS
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P"
[17] "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
> s <- sample(LETTERS, 32, replace = TRUE)
> print(sort(s))
[1] "A" "C" "C" "D" "D" "G" "H" "H" "J" "K" "L" "L" "M" "M" "O" "O"
[17] "Q" "Q" "Q" "R" "U" "V" "V" "V" "W" "W" "W" "W" "X" "Y" "Y" "Z"
> tab <- table(s)
> print(tab)
s
A C D G H J K L M O Q R U V W X Y Z
1 2 2 1 2 1 1 2 2 2 3 1 1 3 4 1 2 1
> tab['A']
A
1
> tab[c('A', 'Q')]
s
A Q
1 3
> class(tab)
[1] "table"
> as.data.frame(tab)
s Freq
1 A 1
2 C 2
3 D 2
4 G 1
5 H 2
6 J 1
7 K 1
8 L 2
9 M 2
10 O 2
11 Q 3
12 R 1
13 U 1
14 V 3
15 W 4
16 X 1
17 Y 2
18 Z 1

2024年1月19日 (金)

[R]正規表現を使用してtibbleから条件に一致する行を抜き出す

tidyverse(dplyr、stringr)パッケージに含まれるfilter関数とstr_detect関数を組み合わせて使う。以下の例では、簡潔な表示にするために表示の際にtibbleをデータフレームに変換している。

> library(tidyverse)
> no <- 1:5
> mei <- c("カナメ", "フレイア", "美雲", "マキナ", "レイナ")
> sei <- c("バッカニア", "ヴィオン", "ギンヌメール", "中島", "プラウラー")
> fname <- c("Kaname", "Freyja", "Mikumo", "Makina", "Reina")
> lname <- c("Buccaneer", "Wion", "Guynemer", "Nakajima", "Prowler")
> seiyu <- c("安野希世乃", "鈴木みのり", "小清水亜美", "西田望見", "東山奈央")
> tib <- tibble(no, mei, sei, fname, lname, seiyu)
> print(as.data.frame(tib))
no mei sei fname lname seiyu
1 1 カナメ バッカニア Kaname Buccaneer 安野希世乃
2 2 フレイア ヴィオン Freyja Wion 鈴木みのり
3 3 美雲 ギンヌメール Mikumo Guynemer 小清水亜美
4 4 マキナ 中島 Makina Nakajima 西田望見
5 5 レイナ プラウラー Reina Prowler 東山奈央
> tib %>% filter(str_detect(mei, "ナ")) %>% as.data.frame()
no mei sei fname lname seiyu
1 1 カナメ バッカニア Kaname Buccaneer 安野希世乃
2 4 マキナ 中島 Makina Nakajima 西田望見
3 5 レイナ プラウラー Reina Prowler 東山奈央
> tib %>% filter(str_detect(mei, "ナ$")) %>% as.data.frame()
no mei sei fname lname seiyu
1 4 マキナ 中島 Makina Nakajima 西田望見
2 5 レイナ プラウラー Reina Prowler 東山奈央
> tib %>% filter(str_detect(mei, "ナ") & str_detect(lname, "er$")) %>% as.data.frame()
no mei sei fname lname seiyu
1 1 カナメ バッカニア Kaname Buccaneer 安野希世乃
2 5 レイナ プラウラー Reina Prowler 東山奈央

2024年1月17日 (水)

[R]ファイル名を示す文字列から拡張子を削除する

ファイル名を文字列型ベクトルに格納して、gsub関数で正規表現を使えば簡単に削除できる。以下、例。

> s <- c("abc", "abc.def", "abc.def.ghi")
> gsub("\\.[^\\.]+$", "", s)
[1] "abc" "abc" "abc.def"

「¥¥.」は「.」(ピリオド)一文字を表現している。「.」はパターンを表す書式に使用されるため、それを避けるために前に「¥」を付けている。この付けた「¥」自体もパターンを表す書式に使用する文字のため、「¥」の前にもさらに「¥」を付けている。

「[^¥¥.」+」とは、ピリオド以外の文字を一つ以上ということ。このように指定しないと、含まれるピリオドより右側すべての文字が削除されてしまう。

2024年1月16日 (火)

[R]バイナリファイルを作成する

数値型ベクトルでバイト列を作成し、それをロウ型に変換してwriteBin関数で出力すればよい。以下は、ch1とch2の2つの数値型ベクトルでバイト列を作成し、それを純粋にバイト単位でファイル出力した例。

> ch1 <- c(0x30:0x39, 0x0d, 0x0a, 0x41:0x5a, 0x0d, 0x0a, 0x61:0x7a, 0x0d, 0x0a)
> ch2 <- c(0xe3, 0x81, 0x82, 0xe3, 0x81, 0x84, 0xe3, 0x81, 0x86, 0x0d, 0x0a)
> ra <- as.raw(c(ch1, ch2))
> writeBin(ra, "temp.txt")

temp.txtを画面にダンプした結果は以下のとおり。

PS > Format-Hex .\temp.txt
パス: ○○○
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 30 31 32 33 34 35 36 37 38 39 0D 0A 41 42 43 44 0123456789..ABCD
00000010 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 EFGHIJKLMNOPQRST
00000020 55 56 57 58 59 5A 0D 0A 61 62 63 64 65 66 67 68 UVWXYZ..abcdefgh
00000030 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 ijklmnopqrstuvwx
00000040 79 7A 0D 0A E3 81 82 E3 81 84 E3 81 86 0D 0A yz..ããã..

temp.txtをテキストエディタで文字コードをUTF-8に指定して開くと、以下のようになるはず。

0123456789
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
あいう

本例では、結果をわかりやすくするためUTF-8の文字コードに相当する範囲で出力したのであって、そうではないバイト列(ヌル等)でも問題なく出力することができる。

2024年1月14日 (日)

[R]ランダムに作成された文字列を得る

runif関数とpaste関数を組み合わせて使う。chベクトルには文字列にランダムで割り振る文字を指定し、lenベクトルには得たい文字列の長さを1つだけ指定する。ただし、この方法では一度に一つの文字列しか得ることはできない。

定数LETTERSとlettersにはそれぞれ英大文字と英小文字を含むベクトルのため、これらをそのまま利用すれば英大小文字だけからなるランダムな文字列を作成することができる。当然、記号を適当に含めれば、それらの文字列からなる任意の文字列を作成することができる。

> len <- 10
> ch <- LETTERS
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "WTHNZZCBIJ"
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "STATFLICHV"
> len <- 20
> ch <- c(LETTERS, letters, "#", "$", "%", "&")
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "bBYEODy&DlSZuKcaaykO"
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "hCYaqYkpWkDbP$HcAZPB"

文字列はRの乱数の規則に則って作成される。そのため、乱数の種をset.seed関数で指定すると、作成される文字列を再現することができる。

> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "XG#C#CwhHgBWKgyVdblP"
> set.seed(20)
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "xrPd$%FDSUoqApKZSGQt"
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "bBYEODy&DlSZuKcaaykO"
> set.seed(20)
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "xrPd$%FDSUoqApKZSGQt"
> paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
[1] "bBYEODy&DlSZuKcaaykO"

2024年1月13日 (土)

[R]ランダムに作成された文字列を得る

stringiパッケージのstri_rand_strings関数を使う。一度に複数個をベクトルで得ることができる。

> library(stringi)
> stri_rand_strings(1, 14)
[1] "cJBFBNlLVvbqLQ"
> s <- stri_rand_strings(3, 14)
> library(stringi)
> stri_rand_strings(1, 14)
[1] "NJNxejqJl3dgYS"
> s <- stri_rand_strings(3, 10)
> print(s)
[1] "5Y4del2QiL" "9JK3rYewTL" "4IXEI2ADaN"

ヘルプには、文字列の作成に使用される文字についての説明がある。

> ?stri_rand_strings

以下の説明がある。

Usage
stri_rand_strings(n, length, pattern = "[A-Za-z0-9]")

正規表現の書式を模した書式により、使用される文字列の指定ができる。デフォルトでは英大文字、英小文字、英数字だけだが、例えば、#、$、%、&の記号を4つ追加する場合は、以下のように指定をすればよい。$と&は書式を示す文字のため、前に¥を付けなければならないが、¥はエスケープシーケンスを表す記号でもあるため、前に「¥¥」と2つ続けて入力している。

> stri_rand_strings(3, 14, pattern = "[a-z#\\$%\\&]")
[1] "yapn%u$iaguxyj" "#fnkunslor&blp" "v&nro&mvxvpjke"

文字列はRの乱数の規則に則って作成される。そのため、乱数の種をset.seed関数で指定すると、作成される文字列を再現することができる。

> stri_rand_strings(4, 6)
[1] "O45lLP" "L0uBiZ" "XMpBOU" "UNdLx2"
> set.seed(6)
> stri_rand_strings(4, 6)
[1] "bwGNoy" "xlV3du" "5IlFWg" "9hxp7E"
> stri_rand_strings(4, 6)
[1] "kJMuHQ" "W7xl8v" "N3zlhi" "4wyp7Q"
> set.seed(6)
> stri_rand_strings(4, 6)
[1] "bwGNoy" "xlV3du" "5IlFWg" "9hxp7E"
> stri_rand_strings(4, 6)
[1] "kJMuHQ" "W7xl8v" "N3zlhi" "4wyp7Q"

2024年1月11日 (木)

[R]ランダムに作成された整数からなる数値型ベクトルを作成する

runif関数とtrunc関数を組み合わせて使う。runif関数はrunif(n, a, b)とすると、a < x < bなるxがn個からなる数値型ベクトルを返す。trunc関数は引数に与えた数値の小数点以下を切り捨てて返す関数(正数も負数も0に寄せる方向に切り捨てる)。

runif関数のヘルプには以下のような記述があり、指定した範囲は原則開区間(両端の値を含まない区間)になることに注意。

runif will not generate either of the extreme values unless max = min or max-min is small compared to min, and in particular not for the default arguments.

以下は、無作為抽出により0~10の11個の整数からなる、要素数が5の数値型ベクトルを作成した例。runif関数の第1引数に指定した数だけ乱数が作成される。上記のとおりrunif関数に指定の範囲は開区間のため、runif関数の第3引数には1加えた11を指定していることに注意。最後はtable関数で100万個の整数を作成した際の度数分布表を表示しており、0~10から均等に抽出されていることがわかる。

> floor(runif(5, 0, 11))
[1] 7 8 10 4 9
> floor(runif(5, 0, 11))
[1] 4 3 4 0 6
> floor(runif(5, 0, 11))
[1] 3 4 5 5 9
> floor(runif(5, 0, 11))
[1] 3 0 10 7 3
> table(floor(runif(1000000, 0, 11)))
0 1 2 3 4 5 6 7 8 9 10
91093 90902 90937 90639 91052 90885 90969 90530 91507 90554 90932

2024年1月10日 (水)

[R]ランダムに作成された文字列を得る

sample関数とpaste関数を組み合わせて使う。sample関数の第2引数に、得たい文字列の長さを指定する。

定数LETTERSとlettersにはそれぞれ英大文字と英小文字を含むベクトルのため、これらをそのまま利用すれば英大小文字だけからなるランダムな文字列を作成することができる。当然、記号を適当に含めれば、それらの文字列からなる任意の文字列を作成することができる。

> LETTERS
[1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
> letters
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
> s <- LETTERS
> paste(sample(s, 14, replace = TRUE), collapse = "")
[1] "UJTMRBBANJHZHR"
> paste(sample(s, 14, replace = TRUE), collapse = "")
[1] "PWYFPVJQHJGSNI"
> s <- c(LETTERS, letters)
> paste(sample(s, 14, replace = TRUE), collapse = "")
[1] "QjrFTEySFGwOEG"
> paste(sample(s, 14, replace = TRUE), collapse = "")
[1] "SNRBuCpZdkqpIs"
> s <- c(LETTERS, letters, "#", "$", "%", "&")
> paste(sample(s, 24, replace = TRUE), collapse = "")
[1] "bEimcMQlAHCdRkc%m#GbfifS"
> paste(sample(s, 24, replace = TRUE), collapse = "")
[1] "CVrUHSYWWRPwcrsXF&StkSrK"

2024年1月 8日 (月)

[R]ベクトルやデータフレームをファイルに保存して、後で読み込んで再利用する

readrパッケージのwrite_rds関数を使うことで、オブジェクト(ベクトルやデータフレームなど)をファイルに保存することができる。このファイルに保存した内容は、後(Rの再起動後も可)でread_rds関数を使ってその環境に読み込み再利用することができる。save関数と異なり、一つのオブジェクトしか保存することができず、readRDS関数で読み込む際は、その読み込み先のオブジェクトを指定する必要がある。

> rm(list = ls(all.names = TRUE))
> ls()
character(0)
> no <- 1:3
> name <- c("安野希世乃", "石見舞菜香", "和多田美咲")
> meibo <- data.frame(no, name)
> print(meibo)
no name
1 1 安野希世乃
2 2 石見舞菜香
3 3 和多田美咲
> readr::write_rds(meibo, file = "meibo.rds")
> rm(list = ls(all.names = TRUE))
> ls()
character(0)
> readr::read_rds(file = "meibo.rds")
no name
1 1 安野希世乃
2 2 石見舞菜香
3 3 和多田美咲
> ls()
character(0)
> dtf <- readr::read_rds(file = "meibo.rds")
> ls()
[1] "dtf"
> print(dtf)
no name
1 1 安野希世乃
2 2 石見舞菜香
3 3 和多田美咲

write_rds関数はsave関数やsaveRDS関数と異なり、デフォルトではファイルを圧縮処理しない。そのため、ファイルサイズは大きくなるが、その分、保存時も読み込み時も動作が早い。なお、ファイルを圧縮処理するように指定することもできる。

以下ではwrite_rds関数とread_rds関数について、適当な巨大なデータフレームを作成して、ファイルの圧縮処理するか否かで、保存・読み込み時の実行時間を比較した例。見てのとおり、圧縮処理するとファイルサイズは約半分になるが、保存時は10倍以上時間がかかることがわかる。読み込み時も、保存時ほどではないが処理が早く済むことがわかる。

> rm(list = ls(all.names = TRUE))
> len <- 20
> ch <- c(LETTERS, letters)
> n <- 10 ^ 6
> sei <- mei <- character(n)
> for (i in 1:n) sei[i] <- paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
> for (i in 1:n) mei[i] <- paste(ch[floor(runif(len, 1, length(ch) + 1))], collapse = "")
> no <- 1:(n * 10)
> age <- floor(runif(n * 10, 20, 66))
> meibo <- data.frame(no, sei, mei, age)
> nrow(meibo)
[1] 10000000
> tail(meibo)
no sei mei age
9999995 9999995 hidOwZgfbrLerBCHprrj rEXTnzTjfnRHoaAYFQLk 53
9999996 9999996 KJPkdfMRQmoAsxvNXzYx DBwannCmxuEdYyfnaYTC 58
9999997 9999997 ODqATMYtqIvEzcvzfZmd QpBDgfnOUeWAkmWfzjcI 25
9999998 9999998 JsnBlbiwMdyRzGzYlOPA nJBFwQMStWHPdwEFgKMe 32
9999999 9999999 fvQMCLOULLReHDuJSrbs pSwrJDxXPYvJMeECpeoJ 33
10000000 10000000 kVmjgBweXtfoFzROlivD RaHBkUfyvlkMBeEgbTYa 36
> system.time(readr::write_rds(meibo, file = "meibo_none.rds"))
ユーザ システム 経過
1.15 0.47 2.49
> system.time(readr::write_rds(meibo, file = "meibo_none.rds"))
ユーザ システム 経過
1.06 0.27 3.32
> system.time(readr::write_rds(meibo, file = "meibo_gz.rds", compress = "gz"))
ユーザ システム 経過
18.08 0.16 21.34
> system.time(readr::write_rds(meibo, file = "meibo_gz.rds", compress = "gz"))
ユーザ システム 経過
14.11 0.23 21.75
> rm(list = ls(all.names = TRUE))
> ls()
character(0)
> file.info(dir(pattern = "*.rds"))["size"]
size
meibo_gz.rds 354125588
meibo_none.rds 680000218
> system.time(dtf <- readr::read_rds(file = "meibo_none.rds"))
ユーザ システム 経過
2.97 0.14 3.58
> system.time(dtf <- readr::read_rds(file = "meibo_none.rds"))
ユーザ システム 経過
3.48 0.32 5.09
> system.time(dtf <- readr::read_rds(file = "meibo_gz.rds"))
ユーザ システム 経過
5.98 0.03 6.72
> system.time(dtf <- readr::read_rds(file = "meibo_gz.rds"))
ユーザ システム 経過
5.92 0.07 6.74

2024年1月 7日 (日)

[R]ヌル(0x00)を含むCSVファイルやTSVファイルを読み込む

始めにヌルを含むCSVファイルを作成する。

> ch1 <- c(0x41:0x43, 0x2c, 0x00, 0x2c, 0x47:0x49, 0x0d, 0x0a)
> ch1 <- c(0x41:0x43, 0x2c, 0x00, 0x2c, 0x47:0x49, 0x0d, 0x0a)
> ch2 <- c(0x61, 0x00, 0x63, 0x2c, 0x64:0x66, 0x2c, 0x67:0x69, 0x0d, 0x0a)
> ra <- as.raw(c(ch1, ch2))
> writeBin(ra, "temp.csv")

ファイルをメモ帳で開くと以下のようになる。1行目の2列目は値自体がヌルで、2行目の1列は「a」と「c」の間は空白(0x20)ではなくヌル(0x00)である。

ABC, ,GHI
a c,def,ghi

標準で搭載されているread.tableは、skipNulオプションにTRUEを指定しないと、ヌルだけの列は列とは認識されず、行によってフィールド数が異なることになるため、エラーが発生して読み込みに失敗する。skipNulオプションにTRUEを指定と、ヌルは完全に無視して他はすべて読み込まれる。ヌルの次の「c」もきちんと読み込まれている。

> dtf <- read.table("temp.csv", header = FALSE, sep = ",")
scan(file = file, what = what, sep = sep, quote = quote, dec = dec, でエラー:
line 2 did not have 2 elements
追加情報: 警告メッセージ:
1: read.table("temp.csv", header = FALSE, sep = ",") で:
line 1 appears to contain embedded nulls
2: read.table("temp.csv", header = FALSE, sep = ",") で:
line 2 appears to contain embedded nulls
> print(dtf)
エラー: オブジェクト 'dtf' がありません
> dtf <- read.table("temp.csv", header = FALSE, sep = ",", skipNul = TRUE)
> print(dtf)
V1 V2 V3
1 ABC GHI
2 ac def ghi

readrパッケージのread_delim関数を試してみる。ヌルだけの列はきちんと処理されているようだが、ヌルを含む列は、ヌル以降は読み込まれていない(「c」が表示されない)。

> library(readr)
> tib <- read_delim("temp.csv", delim = ",", col_names = FALSE, progress = FALSE, show_col_types = FALSE)
警告メッセージ:
One or more parsing issues, call `problems()` on your data frame for details, e.g.:
dat <- vroom(...)
problems(dat)
> print(data.frame(tib))
X1 X2 X3
1 ABC GHI
2 a def ghi

data.tableパッケージのfread関数を使う。これはヌルを完全に無視して読み込むし、ヌルだけの列は空欄(NA)ということで処理できているし、ヌル以降の文字もきちんと読み込まれている。

> library(data.table)
> dtt <- fread("temp.csv", header = FALSE, sep = ",", showProgress = FALSE)
> print(dtt)
V1 V2 V3
1: ABC GHI
2: ac def ghi

2024年1月 6日 (土)

[R]文字列の文字コードを調べる

iconv関数を使う。iconv関数は第1引数に指定した文字列を、toオプションに指定した文字コードに変換する。toオプションだけを指定した場合は変換後の文字列をそのまま返すが、実行環境と文字コードが合わない場合は表示が乱れるので注意。TRUEを指定したtoRawオプションを付けると、戻り値はロウ型になりバイト列で表示されるため、このオプションを使用した利用が推奨される。

以下は、「石見舞菜香」という5つの漢字からなる文字列について、文字コードをバイト列で表示した例。なお、各漢字のそれぞれの文字コードは以下のとおり。実行した環境の文字コードはUTF-8で、JISコードとシフトJISにそれぞれ変換している。toRawオプションにTRUEを指定した場合の戻り値はロウ型かつリストであることに注意。ロウ型はバイト列で表示される。

漢字 JISコード シフトJIS UTF-8
石 0x4050 0x90ce 0xe79fb3
見 0x382b 0x8ca9 0xe8a68b
舞 0x4971 0x9591 0xe8889e
菜 0x3a5a 0x8dd8 0xe88f9c
香 0x3961 0x8d81 0xe9a699

実行例は以下のとおり。

> 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("石見舞菜香", to = "ISO-2022-JP")
[1] "\033$B@P8+Iq:Z9a"
> iconv("石見舞菜香", to = "ISO-2022-JP", toRaw = TRUE)
[[1]]
[1] 1b 24 42 40 50 38 2b 49 71 3a 5a 39 61
> iconv("石見舞菜香", to = "SJIS", toRaw = TRUE)
[[1]]
[1] 90 ce 8c a9 95 91 8d d8 8d 81
> iconv("石見舞菜香", to = "UTF-8", toRaw = TRUE)
[[1]]
[1] e7 9f b3 e8 a6 8b e8 88 9e e8 8f 9c e9 a6 99

iconv関数は現在の環境の文字コードに関係なく、引数に与えた任意の文字コードからなる文字列を、任意の文字コードまたは任意の文字コードによる文字列に変換する関数。よって、以下の例のようにUTF-8環境下でシフトJISに相当するバイト列をロウ型で与えてfromオプションにシフトJISを指定することで、任意の文字コードを変換することができる。最後の例では、符号化文字集合の切り替えを示すエスケープシーケンス(0x1b 0x24 0x42)が先頭に付けられていることに注意。

> ra <- as.raw(c(0x95, 0x91, 0x8d, 0xd8, 0x8d, 0x81))
> iconv(rawToChar(ra), from = "SJIS", to = "UTF-8")
[1] "舞菜香"
> iconv(rawToChar(ra), from = "SJIS", to = "UTF-8", toRaw = TRUE)
[[1]]
[1] e8 88 9e e8 8f 9c e9 a6 99
> iconv(rawToChar(ra), from = "SJIS", to = "ISO-2022-JP", toRaw = TRUE)
[[1]]
[1] 1b 24 42 49 71 3a 5a 39 61

iconv関数の第1引数にベクトルで文字列を与えると、それぞれ変換した値を返す。

> iconv(c("石見舞菜香", "和多田美咲"), to = "SJIS", toRaw = TRUE)
[[1]]
[1] 90 ce 8c a9 95 91 8d d8 8d 81
[[2]]
[1] 98 61 91 bd 93 63 94 fc 8d e7

2024年1月 4日 (木)

[R]ベクトルの要素をランダムに抽出する

sample関数を使う。引数にベクトルだけを与えると、要素をランダムに並び替えたベクトルを返す。第2引数に数値を指定すると、その数だけランダムに要素を選んだベクトルを返す。デフォルトでは非復元抽出(一度抜き取った標本を元に戻さずに次の標本を抽出すること)のため、与えたベクトルの要素数以上の値を指定すると、選べないためエラーが発生する。

> s <- c("A", "B", "C", "D", "E")
> sample(s)
[1] "D" "B" "A" "E" "C"
> sample(s)
[1] "B" "C" "E" "D" "A"
> sample(s, 3)
[1] "B" "E" "A"
> sample(s, 3)
[1] "D" "E" "C"
> sample(s, 6)
sample.int(length(x), size, replace, prob) でエラー:
'replace = FALSE' なので、母集団以上の大きさの標本は取ることができません

replaceオプションにTRUEを指定すると復元抽出(一度抜き取った標本を元に戻してから次の標本を抽出すること)になる。そのため、返されるベクトルの要素には重複が生じる。また、与えたベクトル以上の要素数を超える数値を指定してもランダムに選ばれて返される。

> sample(s, 3, replace = TRUE)
[1] "C" "E" "D"
> sample(s, 3, replace = TRUE)
[1] "B" "E" "E"
> sample(s, 6, replace = TRUE)
[1] "A" "D" "B" "C" "A" "A"

sample関数は乱数の処理に基づいて要素を抽出しており、特に指定をしなければ要素はランダムに選ばれ続ける。他の乱数の関数と同様に動作確認で乱数の発生を再現したい場合は、実行前にset.seed関数に適当な数値を指定して実行すればよい。同じ数値を指定してset.seed関数を実行すれば、乱数の発生を再現することができる。

> sample(s, 3, replace = TRUE)
[1] "E" "B" "E"
> sample(s, 3, replace = TRUE)
[1] "E" "D" "A"
> set.seed(3)
> sample(s, 3, replace = TRUE)
[1] "E" "B" "D"
> sample(s, 3, replace = TRUE)
[1] "D" "B" "C"
> set.seed(3)
> sample(s, 3, replace = TRUE)
[1] "E" "B" "D"
> sample(s, 3, replace = TRUE)
[1] "D" "B" "C"

2024年1月 3日 (水)

[R]leafletを使用して表示場所とズームレベルを指定して地図を表示する

addTiles関数とsetView関数を組み合わせて使う。

> library(magrittr)
> library(leaflet)
> library(magrittr)
> leaflet() %>% addTiles() %>% setView(lng = 139.962, lat = 35.725, zoom = 16)

R_leaflet_zoom

2024年1月 2日 (火)

[R]leafletを使用して地図を表示する

addTiles関数を使う。

> library(leaflet)
> library(magrittr)
> leaflet() %>% addTiles()

R_leaflet_map

2024年1月 1日 (月)

[R]ファイルへの高速な書き込み

data.tableパッケージのfwrite関数を使う。以下は、行数が100万のデータフレームを、write.table関数とfwrite関数を使ってそれぞれTSV形式のテキストファイルに書き込んだ例。書き込んだ行数はヘッダー行も含むため100万1行であることに注意。それぞれ2回繰り返し行ったが、fwrite関数による書き込みのほうが10倍以上速いことがわかる。

> library(data.table)
> n <- 10 ^ 6
> no <- 1:n
> s <- c("カナメ", "フレイア", "美雲", "マキナ", "レイナ")
> name <- sample(s, n, replace = TRUE)
> shoe_size <- round(rnorm(n, 23.5, 1), 1)
> dtf <- data.frame(no, name, shoe_size)
> head(dtf, 3)
no name shoe_size
1 1 美雲 22.6
2 2 マキナ 22.5
3 3 レイナ 23.5
> system.time(
+ write.table(dtf, "temp.tsv", sep = "\t", row.name = FALSE, quote = FALSE)
+ )
ユーザ システム 経過
3.48 0.18 3.71
> system.time(
+ write.table(dtf, "temp.tsv", sep = "\t", row.name = FALSE, quote = FALSE)
+ )
ユーザ システム 経過
3.45 0.09 3.54
> system.time(
+ fwrite(dtf, "temp.tsv", sep = "\t", row.name = FALSE, quote = FALSE)
+ )
ユーザ システム 経過
0.07 0.02 0.04
> system.time(
+ fwrite(dtf, "temp.tsv", sep = "\t", row.name = FALSE, quote = FALSE)
+ )
ユーザ システム 経過
0.16 0.00 0.05

« 2023年12月 | トップページ | 2024年2月 »

無料ブログはココログ

■■

■■■