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形式で出力する関数です。
ファイルを指定して書き出したり、クリップボードに出力することもできるのでわりと重宝します。
# 適当データ
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.csv
やwrite.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