138-一个参数导致R读入数据后会改变列名?

刘小泽写于19.9.13 从一个小问题也能学到很多

原本是一件很简单的事情

用R读取一个数据,本来是一件很简单的事情,但用两种方法探索时发现了一些端倪

例如:

我们可以清楚看到原来的列名中是有-

接下来,用R读入试试。我之所以发现这个问题,是因为教程中使用了read.delim,它的代码如下:

# 第一种read.delim
plate1 <- read.delim(file.path(tempdir(), "counts_Calero_20160113.tsv"), 
    header=TRUE, row.names=1, check.names=FALSE)

> dim(plate1)
[1] 46703    97

> head(names(plate1))
[1] "Length"                              
[2] "SLX-9555.N701_S502.C89V9ANXX.s_1.r_1"
[3] "SLX-9555.N701_S503.C89V9ANXX.s_1.r_1"
[4] "SLX-9555.N701_S504.C89V9ANXX.s_1.r_1"
[5] "SLX-9555.N701_S505.C89V9ANXX.s_1.r_1"
[6] "SLX-9555.N701_S506.C89V9ANXX.s_1.r_1"

可以看到它复现了原来数据的-

但问题来了

我在使用read.csv的时候,出现了一点问题,本来想的是就是简单验证一下,结果报了一个FALSE

# 第二种read.csv
tmp1 <- read.csv(file.path(tempdir(), "counts_Calero_20160113.tsv"),
                row.names = 1, sep = '\t',header = T )

> dim(tmp1)
[1] 46703    97

# 它们竟然不一样!
> identical(plate1,tmp1)
[1] FALSE

到底是哪里不一样?我也不清楚,于是使用了另外一个函数来获得更多的信息:

# 这个函数除了判断二者是否一样,还能给出一点提示信息
> all.equal(tmp1,plate1)
[1] "Names: 96 string mismatches"

根据结果提示:应该是有96个列名不一样,因为names()是取列名的意思

所以我又检查了一下列名:

> head(names(tmp1))
[1] "Length"                              
[2] "SLX.9555.N701_S502.C89V9ANXX.s_1.r_1"
[3] "SLX.9555.N701_S503.C89V9ANXX.s_1.r_1"
[4] "SLX.9555.N701_S504.C89V9ANXX.s_1.r_1"
[5] "SLX.9555.N701_S505.C89V9ANXX.s_1.r_1"
[6] "SLX.9555.N701_S506.C89V9ANXX.s_1.r_1"

结果和read.delim()结果确实差在了- 变成.

重新检查

我又怀疑是不是这两个函数得到不一样,但是使用help()查看的时候,发现它们又都是属于read.table {utils} ,按说得到的东西应该是一样的。那么 是不是参数的设置?

于是发现我使用的read.csv相比于read.delim缺少了一个参数:check.names ,而这个参数我平常是忽视的,那么在read.csv()中添加上check.names参数会怎样呢?

tmp2 <- read.csv(file.path(tempdir(), "counts_Calero_20160113.tsv"),
                row.names = 1, sep = '\t',header = T ,check.names = F)

> head(names(tmp2))
[1] "Length"                              
[2] "SLX-9555.N701_S502.C89V9ANXX.s_1.r_1"
[3] "SLX-9555.N701_S503.C89V9ANXX.s_1.r_1"
[4] "SLX-9555.N701_S504.C89V9ANXX.s_1.r_1"
[5] "SLX-9555.N701_S505.C89V9ANXX.s_1.r_1"
[6] "SLX-9555.N701_S506.C89V9ANXX.s_1.r_1"

发现确实保持了和原来的一致性!

探索

那么,问题来了,这个参数check.names做了什么?它为什么会私自改变列名?

再次搜索帮助文档:

image-20190913132719773

发现这个函数确实是说,如果有必要,它会进行make.names()处理,而这个函数所做的的事情,也就是把不识别的字符变成.

**怎么体现不识别呢?**简单用$取一下列名就能看到:

# 如果是用原来数据的列名
> head(plate1$`SLX-9555.N701_S502.C89V9ANXX.s_1.r_1`)
[1] 0 0 0 0 0 0
# 而如果用R私自修改过的
> head(tmp$SLX.9555.N701_S502.C89V9ANXX.s_1.r_1)
[1] 0 0 0 0 0 0

很明显可以看到,R对第一个列名加上了反引号,再次验证了R不识别列名中包含的-

因此,这次得到的结论就是

  • R认为列名中的短横线-是无效的(当然还会有其他字符),所以如果读入的列名中包含了-,它会默认将其替换为. ,目的是确保能及时检验是否出现重复的列名

  • 如果原本数据中的列名包括-或者其他R不识别的字符,自己还想让读到R的数据与原数据保持一致,就需要使用check.names = FALSE 不让read.csv或者read.delim读入的数据框中列名的-变成.

Yunze Liu
Yunze Liu
Bioinformatics Sharer

Co-founder of Bioinfoplanet(生信星球)

Next
Previous

Related