匹配数据框中所有列的字符串并估算或替换值
Match strings across all columns in dataframe and impute or replace values
问题
我有一个数据框,df
,有 82 列,在第四列之后,其余列包含相同的起始字符串,一式三份。例如。 mass.mean, mass.stdev, mass.rsd, density.mean, density.stdev, density.rsd
等等 我需要:
1) 将一式三份的列与部分字符串(例如 mass
或 density
)和
匹配
2) 如果满足条件,则用特定计算替换那些匹配列的值(例如,如果 df
中的列包含字符串 mass
,则将 NA 替换为先前的值(估算)zoo::na.locf
或者如果 df
中的列包含字符串 density
则将 NA 替换为零。
对我来说,我似乎需要合并 grepl
lapply
和一个 ifelse
梯子,但我似乎无法将它们组合在一起。如果我可以避免从宽转换为长,那将是最好的,因为我的数据框包含 > 450k 行。
示例数据框
set.seed(123)
df <- data.frame("A" = sample(0:100,8),
"B" = sample(0:100,8),
"C" = sample(0:100,8),
"D" = sample(0:100,8),
"mass.mean" = c(1, NA, 2, 3, NA, NA, 2, 1),
"mass.stdev" = c(1, NA, 1, 1, NA, NA, 2, 1),
"mass.rsd" = c(0, NA, 0.1, 0.1, NA, NA, 0.2, 0.1),
"denisty.mean" = c(6, 5, 7, NA, NA, NA, 6, 4),
"denisty.stdev" = c(3, 1, 1, NA, NA, NA, 2, 1),
"denisty.rsd" = c(0.8,0.2, 2, NA, NA, NA, 0.5, 0.7),
stringsAsFactors = FALSE)
print(df)
A B C D mass.mean mass.stdev mass.rsd denisty.mean denisty.stdev denisty.rsd
1 29 55 24 66 1 1 0.0 6 3 0.8
2 78 45 4 70 NA NA NA 5 1 0.2
3 40 94 32 53 2 1 0.1 7 1 2.0
4 86 44 93 58 3 1 0.1 NA NA NA
5 91 65 86 28 NA NA NA NA NA NA
6 4 54 66 14 NA NA NA NA NA NA
7 50 9 60 91 2 2 0.2 6 2 0.5
8 83 84 97 84 1 1 0.1 4 1 0.7
期望输出
A B C D mass.mean mass.stdev mass.rsd denisty.mean denisty.stdev denisty.rsd
1 29 55 24 66 1 1 0.0 6 3 0.8
2 78 45 4 70 1 1 0.0 5 1 0.2
3 40 94 32 53 2 1 0.1 7 1 2.0
4 86 44 93 58 3 1 0.1 0 0 0.0
5 91 65 86 28 3 1 0.1 0 0 0.0
6 4 54 66 14 3 1 0.1 0 0 0.0
7 50 9 60 91 2 2 0.2 6 2 0.5
8 83 84 97 84 1 1 0.1 4 1 0.7
像这样应该可以解决您的 'density' 列问题:
library(dplyr)
df %>%
mutate_at(vars(starts_with("density")),function(x) {if_else(is.na(x),0,x)})
'mass' 有点棘手,因为您必须获得以前的值,而且您似乎想从上次有一个 non-NA 值的时间开始进行估算。这个解决方案通过保留 NA 来处理第一行包含 NA 的情况,因为我不确定你想要发生什么。
imputePrev <- function(x) {
l <- seq_along(x) # declare vector of appropriate length rather than growing
for (i in seq_along(x)){
if (i == 1){
l[i] <- x[i] # always keep the first row
next
} else if (is.na(x[i])){
for (j in 1:(i-1)) { # get the last non-NA value if one is available
if (!is.na(x[i-j])){
l[i] <- x[i-j]
break
}
}
} else {
l[i] <- x[i]
}
}
return(l)
}
df %>% mutate_at(vars(starts_with("mass")),imputePrev)
定义 is.mass
以识别 mass
列,然后在这些列上定义 运行 na.locf
。 (如果有前导 NA,第二行 na.locf
执行向后填充。如果您知道有 none 或者如果您想保留前导 NA,则可以省略该行。)类似地定义 is.density
指示密度列,然后在这些列上使用 na.fill
。两条 na.locf
行的替代方法是单行 df[is.mass] <- na.approx(df[is.mass], method = "constant", rule = 2)
library(zoo)
df.orig <- df # optional in case you want to keep the input around
is.mass <- grepl("mass", names(df))
df[is.mass] <- na.locf(df[is.mass], na.rm = FALSE)
df[is.mass] <- na.locf(df[is.mass], na.rm = FALSE, fromLast = TRUE)
is.density <- grepl("density", names(df))
df[is.density] <- na.fill(df[is.density], 0)
给予:
> df
A B C D mass.mean mass.stdev mass.rsd density.mean density.stdev density.rsd
1 29 55 24 66 1 1 0.0 6 3 0.8
2 78 45 4 70 1 1 0.0 5 1 0.2
3 40 94 32 53 2 1 0.1 7 1 2.0
4 86 44 93 58 3 1 0.1 0 0 0.0
5 91 65 86 28 3 1 0.1 0 0 0.0
6 4 54 66 14 3 1 0.1 0 0 0.0
7 50 9 60 91 2 2 0.2 6 2 0.5
8 83 84 97 84 1 1 0.1 4 1 0.7
备注
我们用这个作为输入。这与问题中的相同,只是我们更正了密度中的拼写错误。我们还删除了 stringsAsFactors
,因为数据完全是数字。
set.seed(123)
df <- data.frame("A" = sample(0:100,8),
"B" = sample(0:100,8),
"C" = sample(0:100,8),
"D" = sample(0:100,8),
"mass.mean" = c(1, NA, 2, 3, NA, NA, 2, 1),
"mass.stdev" = c(1, NA, 1, 1, NA, NA, 2, 1),
"mass.rsd" = c(0, NA, 0.1, 0.1, NA, NA, 0.2, 0.1),
"density.mean" = c(6, 5, 7, NA, NA, NA, 6, 4),
"density.stdev" = c(3, 1, 1, NA, NA, NA, 2, 1),
"density.rsd" = c(0.8,0.2, 2, NA, NA, NA, 0.5, 0.7))
问题
我有一个数据框,df
,有 82 列,在第四列之后,其余列包含相同的起始字符串,一式三份。例如。 mass.mean, mass.stdev, mass.rsd, density.mean, density.stdev, density.rsd
等等 我需要:
1) 将一式三份的列与部分字符串(例如 mass
或 density
)和
2) 如果满足条件,则用特定计算替换那些匹配列的值(例如,如果 df
中的列包含字符串 mass
,则将 NA 替换为先前的值(估算)zoo::na.locf
或者如果 df
中的列包含字符串 density
则将 NA 替换为零。
对我来说,我似乎需要合并 grepl
lapply
和一个 ifelse
梯子,但我似乎无法将它们组合在一起。如果我可以避免从宽转换为长,那将是最好的,因为我的数据框包含 > 450k 行。
示例数据框
set.seed(123)
df <- data.frame("A" = sample(0:100,8),
"B" = sample(0:100,8),
"C" = sample(0:100,8),
"D" = sample(0:100,8),
"mass.mean" = c(1, NA, 2, 3, NA, NA, 2, 1),
"mass.stdev" = c(1, NA, 1, 1, NA, NA, 2, 1),
"mass.rsd" = c(0, NA, 0.1, 0.1, NA, NA, 0.2, 0.1),
"denisty.mean" = c(6, 5, 7, NA, NA, NA, 6, 4),
"denisty.stdev" = c(3, 1, 1, NA, NA, NA, 2, 1),
"denisty.rsd" = c(0.8,0.2, 2, NA, NA, NA, 0.5, 0.7),
stringsAsFactors = FALSE)
print(df)
A B C D mass.mean mass.stdev mass.rsd denisty.mean denisty.stdev denisty.rsd
1 29 55 24 66 1 1 0.0 6 3 0.8
2 78 45 4 70 NA NA NA 5 1 0.2
3 40 94 32 53 2 1 0.1 7 1 2.0
4 86 44 93 58 3 1 0.1 NA NA NA
5 91 65 86 28 NA NA NA NA NA NA
6 4 54 66 14 NA NA NA NA NA NA
7 50 9 60 91 2 2 0.2 6 2 0.5
8 83 84 97 84 1 1 0.1 4 1 0.7
期望输出
A B C D mass.mean mass.stdev mass.rsd denisty.mean denisty.stdev denisty.rsd
1 29 55 24 66 1 1 0.0 6 3 0.8
2 78 45 4 70 1 1 0.0 5 1 0.2
3 40 94 32 53 2 1 0.1 7 1 2.0
4 86 44 93 58 3 1 0.1 0 0 0.0
5 91 65 86 28 3 1 0.1 0 0 0.0
6 4 54 66 14 3 1 0.1 0 0 0.0
7 50 9 60 91 2 2 0.2 6 2 0.5
8 83 84 97 84 1 1 0.1 4 1 0.7
像这样应该可以解决您的 'density' 列问题:
library(dplyr)
df %>%
mutate_at(vars(starts_with("density")),function(x) {if_else(is.na(x),0,x)})
'mass' 有点棘手,因为您必须获得以前的值,而且您似乎想从上次有一个 non-NA 值的时间开始进行估算。这个解决方案通过保留 NA 来处理第一行包含 NA 的情况,因为我不确定你想要发生什么。
imputePrev <- function(x) {
l <- seq_along(x) # declare vector of appropriate length rather than growing
for (i in seq_along(x)){
if (i == 1){
l[i] <- x[i] # always keep the first row
next
} else if (is.na(x[i])){
for (j in 1:(i-1)) { # get the last non-NA value if one is available
if (!is.na(x[i-j])){
l[i] <- x[i-j]
break
}
}
} else {
l[i] <- x[i]
}
}
return(l)
}
df %>% mutate_at(vars(starts_with("mass")),imputePrev)
定义 is.mass
以识别 mass
列,然后在这些列上定义 运行 na.locf
。 (如果有前导 NA,第二行 na.locf
执行向后填充。如果您知道有 none 或者如果您想保留前导 NA,则可以省略该行。)类似地定义 is.density
指示密度列,然后在这些列上使用 na.fill
。两条 na.locf
行的替代方法是单行 df[is.mass] <- na.approx(df[is.mass], method = "constant", rule = 2)
library(zoo)
df.orig <- df # optional in case you want to keep the input around
is.mass <- grepl("mass", names(df))
df[is.mass] <- na.locf(df[is.mass], na.rm = FALSE)
df[is.mass] <- na.locf(df[is.mass], na.rm = FALSE, fromLast = TRUE)
is.density <- grepl("density", names(df))
df[is.density] <- na.fill(df[is.density], 0)
给予:
> df
A B C D mass.mean mass.stdev mass.rsd density.mean density.stdev density.rsd
1 29 55 24 66 1 1 0.0 6 3 0.8
2 78 45 4 70 1 1 0.0 5 1 0.2
3 40 94 32 53 2 1 0.1 7 1 2.0
4 86 44 93 58 3 1 0.1 0 0 0.0
5 91 65 86 28 3 1 0.1 0 0 0.0
6 4 54 66 14 3 1 0.1 0 0 0.0
7 50 9 60 91 2 2 0.2 6 2 0.5
8 83 84 97 84 1 1 0.1 4 1 0.7
备注
我们用这个作为输入。这与问题中的相同,只是我们更正了密度中的拼写错误。我们还删除了 stringsAsFactors
,因为数据完全是数字。
set.seed(123)
df <- data.frame("A" = sample(0:100,8),
"B" = sample(0:100,8),
"C" = sample(0:100,8),
"D" = sample(0:100,8),
"mass.mean" = c(1, NA, 2, 3, NA, NA, 2, 1),
"mass.stdev" = c(1, NA, 1, 1, NA, NA, 2, 1),
"mass.rsd" = c(0, NA, 0.1, 0.1, NA, NA, 0.2, 0.1),
"density.mean" = c(6, 5, 7, NA, NA, NA, 6, 4),
"density.stdev" = c(3, 1, 1, NA, NA, NA, 2, 1),
"density.rsd" = c(0.8,0.2, 2, NA, NA, NA, 0.5, 0.7))