小伙伴说他写个CSV文件,名字就变了,无缘无故|就被改了,还有些名字被强加了X。我注意到了他写的对象是data.frame(d),这锅绝对是data.frame的。

我们可以试一下:

> d = matrix(rnorm(4), ncol=2)
> colnames(d) = c("A|B", "123")
> write.csv(data.frame(d))
"","A.B","X123"
"1",-0.601247669017546,0.802654193340092
"2",-0.128752028398414,0.430829592244036

没错,自己给变了。

其实如果我们不data.frame强转的话,一切都是OK的。

> write.csv(d)
"","A|B","123"
"1",-0.601247669017546,0.802654193340092
"2",-0.128752028398414,0.430829592244036

我们不防再试一下,自己转为data.frame,看看是不是这样,结果必须也是,所以这锅就是data.frame的。

> d = data.frame(d)
> colnames(d)
[1] "A.B"  "X123"

解决方案

既然转成data.frame之后,它变了,那我们就再变回去呗。

> colnames(d) = c("A|B", "123")
> colnames(d)
[1] "A|B" "123"
> write.csv(d)
"","A|B","123"
"1",-0.601247669017546,0.802654193340092
"2",-0.128752028398414,0.430829592244036

问题到底出在那里?

因为|是保留字符,还有数字不能当变量名这些,几乎是共识,所以data.frame()这个函数就不允许它们当名字。但是我们想一想,保留字符出现在字符串中是OK的吧?数字开头的字符串是OK的吧?必须是,而名字必须就只是单纯的字符串吧?必须也是,所以这是两码事,你看我们可以给d的colnames设置回去就知道了,这些都是合法的!到了这里,我们只能说data.frame()这个函数死蠢!

第二点,你可能以为是matrix转data.frame才会有这问题, 那我们可以再试一下,data.frame转data.frame,你以为它啥都不干吧,不是的,data.frame()这个函数,就是会给你改名字,给你惊喜。

> write.csv(data.frame(d))
"","A.B","X123"
"1",-0.601247669017546,0.802654193340092
"2",-0.128752028398414,0.430829592244036
> class(d)
[1] "data.frame"

如何能够避免这个坑

使用data.frame(d, check.names=FALSE)或者使用tibble

> tibble::as_data_frame(d) %>% write.csv
"","A|B","123"
"1",-0.601247669017546,0.802654193340092
"2",-0.128752028398414,0.430829592244036

看完你还想看