3
5

More than 5 years have passed since last update.

Rのwrite.csvで列名指定を有効にする

Posted at

Rでcsvを出力する関数であるwrite.csvで、たまに以下のような警告が出て困っていたので対策して見ます。

エラー
write.csv(df, pipe("pbcopy"), row.names=F, col.names=F)

警告メッセージ: 
 write.csv(df, pipe("pbcopy"), row.names = F, col.names = F) : 
   'col.names' への変更の試みは無視されました 

前提

write.csvは名前の通り、データをcsv形式で出力する関数です。
ファイルを指定して書き出したり、クリップボードに出力することもできるのでわりと重宝します。

write.csv
# 適当データ
df <- data.frame("ID" = c("A1","A2","A3"),
                 "Value" = c("abc", 123, T))
# ファイル出力
write.csv(df, "output.csv")

# クリップボードにコピー
## Windowsの場合
write.csv(df, "clipboard")
## Macの場合
write.csv(df, pipe("pbcopy"))

出力形式を色々設定できるwrite.tableのラッパーです。
親であるwrite.tableには行名・列名の出力を制御するrow.names=(TRUE/FALSE/ベクトル)col.names=(TRUE/FALSE/NA/ベクトル)がありますが、write.csvに指定してみると上記のようなエラーが出ます。

仕様です

マニュアルを読むとちゃんと書いていました。
write.csvwrite.csv2は出力したcsvを他のソフト(Excelなど)に貼り付けた時にcsvの行や列が崩れるのを防止するために制約をきつくしてるよ、"col.names"や"sep"は制限してるよ、とのこと。

確かに試しに制限を受けないwrite.tableのほうにwrite.table(...,row.names=T, col.names=T)のような指定をすると

"ID" "Value"
"1" "A1" "abc"
"2" "A2" "123"
"3" "A3" "TRUE"

こんな感じにいかにも列がずれそうな出力になります。

ああ、なるほど仕様でしたか。

とは言うものの、write.csvからクリップボード経由でExcelに貼れるのを非常に重宝している身としては、列名なしでコピーとかをしたいわけです。
そんな訳で改造して見ます。

col.namesが有効になるよう改造してみる

Rでは関数のオーバーライドが簡単にできてしまうので、おもむろにwrite.csvを上書きします。

write.csv <- function (...) {
    Call <- match.call(expand.dots = TRUE)
    # チェック対象から外す
    for (argname in c("append", "sep", "dec", "qmethod")) if (!is.null(Call[[argname]])) 
        warning(gettextf("attempt to set '%s' ignored", argname), 
            domain = NA)
    isFALSE <- function(x) identical(F, x)
    rn <- eval.parent(Call$row.names)
    cn <- eval.parent(Call$col.names)
    Call$append <- NULL
    # 列がずれるパターンは回避
    Call$col.names <- 
      if (!is.logical(rn) && !isFALSE(cn))
        NA
      else if (isTRUE(rn) && !isFALSE(cn))
        NA
      else if (isFALSE(rn) && is.na(cn))
        F
      else 
        cn
    Call$sep <- ","
    Call$dec <- "."
    Call$qmethod <- "double"
    Call[[1L]] <- as.name("write.table")
    eval.parent(Call)
}

列のズレが起こる以下のパターン

  • row.names=c("行名", "を", "指定"), col.names=T
  • row.names=c("行名", "を", "指定"), col.names=c("列名", "指定")
  • row.names=T, col.names=T
  • row.names=T, col.names=c("列名", "指定")

と、そもそも指定ができない

  • row.names=F, col.names=NA

のパターンの場合は回避するようにしています。
これで列名なしでコピペもできますね。

出力
write.csv(df, pipe("pbcopy"), row.names=T, col.names=T)
"","ID","Value"
"1","A1","abc"
"2","A2","123"
"3","A3","TRUE"

write.csv(df, pipe("pbcopy"), row.names=T, col.names=F)
"1","A1","abc"
"2","A2","123"
"3","A3","TRUE"

write.csv(df, pipe("pbcopy"), row.names=F, col.names=F)
"A1","abc"
"A2","123"
"A3","TRUE"

果たしてニーズあるのかと言われると、、、微妙。

なお、write.csvを元に戻す場合は、utilsパッケージに元のがあるので上書きすれば大丈夫です。

元に戻す
write.csv <- utils::write.csv
3
5
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
3
5