この記事をたまたまみてRでやってみようと思った。
こういう仕様のファイルは見たことがなかったが、任意の文字(値)で挟まれた範囲の行でデータフレームを分割したいという欲求はあり得るかと思った。
とりあえず今回の仕様について考えてみる。
データはもとのブログの記事からダウンロードできる。
ちなみに次のようにするとデータを再現できる。
df <- tibble::tibble(X1 = rep(c("Name", "Time", 1:25), 4), X2 = sapply(1:4, \(x) c(letters[x], "Date", seq(2, 50, 2))) |> as.vector())
データを読み込むところから書いていく。
# csvファイルの読み書き用にread_csvとwrite_csvを使うために # {readr}を読み込んでおく library(readr) # データの中に含まれている1行目のNameを鍵にするために値として読み込みたいので # 1行目が列名として読み込まれないようにcol_namesをFALSEとしておく df <- read_csv("./Measurement.csv", col_names = FALSE, show_col_types = FALSE) # Name(データフレームの1行目1列目の値)が含まれる行番号をwhichで調べ値を持っておく start_col <- which(df[1] == df[[1,1]]) # 分割したい範囲の最後の行番号を値として持っておく # start_colの2つ目の値からのベクトルに1引いたものと、もとのデータフレームの最後の行番号(データフレームの行数)になる end_col <- c(start_col[-1] - 1, nrow(df))
これで必要な値は作れたのであとは切り出していく。
まずはfor
ループで書いてみる。csv
ファイルに書き出すというのも次にのせるが、Rで直接このあといじるならリストに各データフレームを入れておくほうが扱いやすそうだと思ったのでリストに格納する方法でやってみる。
# 分割したデータフレームを入れるリストを作っておく df_split <- list() # start_colの値の数だけループを回す for(i in 1:length(start_col)){ # リストのi番目に分割したデータフレームを入れる df_split[[i]] <- df[start_col[i]:end_col[i], ] # 名前をつけておく names(df_split)[i] <- df[[start_col[i], 2]] }
こうするとdf_split
のリストの中に分割された4つのデータフレームがあるので、名前もつけたのでほしいデータフレームを取り出せる。
# Nameがbのデータフレームを取り出すなら次のように取り出せる # もちろん番号でも取り出せる df_split["b"]
## $b
## # A tibble: 28 × 2
## X1 X2
## <chr> <chr>
## 1 Name b
## 2 Time Date
## 3 1 2
## 4 2 4
## 5 3 6
## 6 4 8
## 7 5 10
## 8 6 12
## 9 7 14
## 10 8 16
## # … with 18 more rows
データフレームをcsv
ファイルに書き出すのはfor
ループの場合は次のようにするとできる。
for(i in 1:length(start_col)){ write_csv(df[start_col[i]:end_col[i], ], # ファイル名をMeasurement_名前のアルファベットにしておく file = paste0("Measurement_", df[[start_col[i], 2]], ".csv"), # 行名はread_csvでつけられたものなのでcsvファイルに書き込まないようにする col_names = FALSE) }
次に{purrr}
でやってみる。ますはリスト形式でもつ方法を考える。map2
をつかって、start_col
とend_col
の値をつかって分割していく。
library(purrr) df_split <- map2(start_col, end_col, \(x, y) df[x:y, ])
こうすると分割できる。次にデータフレームをcsv
ファイルに書き出す方法を考える。map2
のままでもいいが、標準出力に切り出したデータフレームが出力されるのでwalk2
を使う。
write_csv
はfor
ループのときと変わらない。
walk2(start_col, end_col, \(x, y) write_csv(df[x:y, ], file = paste0("Measurement_", df[[x, 2]], ".csv"), col_names = FALSE))
これで書き出せる。