背景说明
在一些分子/物质的含量检测时候,往往一些样本的含量非常低没有检测到,导致机器给的数值是0,或者是小于检测限(LOD)的值,我们这个时候要建模的话就需要将这些0值进行变换,一些文献推荐将其替换为非0最小值的1/2,也有一些推荐将低于LOD的值替换为LOD/ 2 \sqrt 2 2,其实都是可以的。
0.示例数据
##构建实例数据:5行4列
df <- data.frame(a = 1:5, b = c(3, 0, 4, 2, 3), c = c(0, 2, 3, 0, 4), d = c(4:0))
df
# a b c d
#1 1 3 0 4
#2 2 0 2 3
#3 3 4 3 2
#4 4 2 0 1
#5 5 3 4 0
以下操作都基于此示例数据框
1.按列替换
这里是数据框中的每一列是检测的各种因子或指标,每一行是每个样本
用mutate_all() 函数,这是个函数是基于dplyr包的,属于mutate函数的变种,详细参数介绍见帮助文档。
1.1. 将0替换为最小正值
library(dplyr)
##0至替换为非零最小正值
df1 <-
df %>%
mutate_all(., ~ replace(., . == 0, min(as.numeric(.[.>0]))))
df1
# a b c d
#1 1 3 2 4
#2 2 2 2 3
#3 3 4 3 2
#4 4 2 2 1
#5 5 3 4 1
1.2. 将0替换为最小正值的一半
##0替换为非零最小正值的二分之一
df2 <-
df %>%
mutate_all(., ~ replace(., . == 0, min(as.numeric(.[.>0]))/2))
df2
# a b c d
#1 1 3 1 4.0
#2 2 1 2 3.0
#3 3 4 3 2.0
#4 4 2 1 1.0
#5 5 3 4 0.5
1.3. 将低于检测限(LOD)的值替换为LOD/ 2 \sqrt 2 2
与上面不同的是这里用到了apply()函数,apply函数非常强大
我们把第一行作为每个指标的LOD值
df99 <- apply(df, 2, function(x){
x[x<x[1]]=x[1]/(2^0.5)
return(x)
})
df99
# a b c d
#[1,] 1 3.00000 0 4.000000
#[2,] 2 2.12132 2 2.828427
#[3,] 3 4.00000 3 2.828427
#[4,] 4 2.12132 0 2.828427
#[5,] 5 3.00000 4 2.828427
df #与df对比
# a b c d
#1 1 3 0 4
#2 2 0 2 3
#3 3 4 3 2
#4 4 2 0 1
#5 5 3 4 0
这个时候还不能得意忘形觉得万事大吉可以开始后面分析。仔细的人会发现df99的结果与df不同,这里说的不是数值不同,而是两个数据结构不同。那么我们接下来看看df99是什么的数据结构:
class(df99)
#[1] "matrix" "array"
df99_0 <- data.frame(df99)
class(df99_0)
df99_0
#[1] "data.frame"
# a b c d
#1 1 3.00000 0 4.000000
#2 2 2.12132 2 2.828427
#3 3 4.00000 3 2.828427
#4 4 2.12132 0 2.828427
#5 5 3.00000 4 2.828427
通过class()我们发现df99其实是矩阵/数组,而不是我们需要数据框结构,因此我们需要通过上面的方法将其结构转换。
2. 按行替换
相反,这里是数据框中的每一行是检测的各种因子或指标,每一列是每个样本
这里会用到apply()这个函数
2.1. 将0替换为最小正值
df3 <- df
df3[] <- t(apply(df, 1, function(x)
replace(x, x == 0, min(x[x > 0], na.rm = TRUE))))
df3
# a b c d
#1 1 3 1 4
#2 2 2 2 3
#3 3 4 3 2
#4 4 2 1 1
#5 5 3 4 3
2.2. 将0替换为最小正值的一半
df4 <- df
df4[] <- t(apply(df, 1, function(x)
replace(x, x == 0, min(x[x > 0], na.rm = TRUE)/2)))
df4
# a b c d
#1 1 3 0.5 4.0
#2 2 1 2.0 3.0
#3 3 4 3.0 2.0
#4 4 2 0.5 1.0
#5 5 3 4.0 1.5
2.3. 将低于检测限(LOD)的值替换为LOD/ 2 \sqrt 2 2
这里横向的表(即列是样本,行是变量),需要用t()进行转置,否则得到的是纵向数据
df100 <- t(apply(df, 1, function(x){
x[x<x[1]]=x[1]/(2^0.5)
return(x)
}))
df100
# a b c d
#[1,] 1 3.000000 0.7071068 4.000000
#[2,] 2 1.414214 2.0000000 3.000000
#[3,] 3 4.000000 3.0000000 2.121320
#[4,] 4 2.828427 2.8284271 2.828427
#[5,] 5 3.535534 3.5355339 3.535534
##与前面一样的要进行数据框转换
df100_0 <- data.frame(df100)
df100_0
# a b c d
#1 1 3.000000 0.7071068 4.000000
#2 2 1.414214 2.0000000 3.000000
#3 3 4.000000 3.0000000 2.121320
#4 4 2.828427 2.8284271 2.828427
#5 5 3.535534 3.5355339 3.535534
3.额外的内容
假如我们由一个数据框包含一下NA,要将每列的NA值替换为平均值,我们该怎么做:
##新建一个矩阵1:40(8*5),包含8个随机的NA(数据框也可以,多一个data.frame步骤)
raw <- 1:40
raw[sample(raw,8)] <- NA
dim(raw) <- c(8,5)
raw
# [,1] [,2] [,3] [,4] [,5]
#[1,] 1 9 17 25 NA
#[2,] 2 10 18 26 34
#[3,] NA 11 19 NA 35
#[4,] 4 12 20 28 36
#[5,] NA 13 NA 29 37
#[6,] 6 14 NA 30 38
#[7,] 7 15 23 31 39
#[8,] 8 16 24 32 40
# 按照列,替换每一列的NA值为该列的平均值
new <- apply(raw,2,function(x){
x[is.na(x)]=mean(x,na.rm = T)
return(x)
})
new
# [,1] [,2] [,3] [,4] [,5]
#[1,] 1.000000 9 17.00000 25.00000 37
#[2,] 2.000000 10 18.00000 26.00000 34
#[3,] 4.666667 11 19.00000 28.71429 35
#[4,] 4.000000 12 20.00000 28.00000 36
#[5,] 4.666667 13 20.16667 29.00000 37
#[6,] 6.000000 14 20.16667 30.00000 38
#[7,] 7.000000 15 23.00000 31.00000 39
#[8,] 8.000000 16 24.00000 32.00000 40
##注意:这里的产生的new仍旧是矩阵
其他的还有中位数替换都是类似的方法