根据条件匹配另一列的向量替换一列中的值

Replace values in one column based on a vector conditionally matching another column

我有以下数据框,我想用 NA 替换反射率值,具体取决于波长值是否落在被确定为不良测量值(badData 向量)的特定范围组中。

坏数据的范围可能会随着时间的推移而改变,所以我希望解决方案尽可能通用。

  badData <- c(296:310, 330:335, 350:565)

  df <- data.frame(wavelength = seq(300,360,5.008667),
                  reflectance = seq(-1,-61,-5.008667))

df 

   wavelength reflectance
   300.0000   -1.000000
   305.0087   -6.008667
   310.0173  -11.017334
   315.0260  -16.026001
   320.0347  -21.034668
   325.0433  -26.043335
   330.0520  -31.052002
   335.0607  -36.060669
   340.0693  -41.069336
   345.0780  -46.078003
   350.0867  -51.086670
   355.0953  -56.095337

我试过了

   Data2 <- df %>% 
  mutate(reflectance = replace(reflectance,wavelength %in% badData, NA))

但是因为我试图用波长范围而不是精确值来做到这一点,所以这是行不通的。我在想我应该使用条件语句,但我不知道如何最有效地提供具有不同范围分组的向量。

输出数据集是因为波长 300.000 和 305.0087 介于 296 和 310 之间,波长 330.05620 介于 330 和 335 之间,而 350.0867 和 355.0953 介于 350:565。

 wavelength reflectance
   300.0000   NA
   305.0087   NA
   310.0173  -11.017334
   315.0260  -16.026001
   320.0347  -21.034668
   325.0433  -26.043335
   330.0520  NA
   335.0607  -36.060669
   340.0693  -41.069336
   345.0780  -46.078003
   350.0867  NA
   355.0953  NA

我认为这会有所帮助。

    library(TeachingDemos)
    df$reflectance <- ifelse(296 %<% df$wavelength %<% 310 | 330 %<% df$wavelength %<% 335 | 350 %<% df$wavelength %<% 565, NA, df$reflectance) 

> df
   wavelength reflectance
1    300.0000          NA
2    305.0087          NA
3    310.0173   -11.01733
4    315.0260   -16.02600
5    320.0347   -21.03467
6    325.0433   -26.04333
7    330.0520          NA
8    335.0607   -36.06067
9    340.0693   -41.06934
10   345.0780   -46.07800
11   350.0867          NA
12   355.0953          NA

第一步是要认识到定义整数范围是行不通的。相反,我将使用数字对列表:

badData <- list(c(296,310), c(330,335), c(350,565))

我们要检查每个 $wavelength 是否在这三个范围内。支持更多范围。

我们可以做的第二件事是编写一个函数来检查值向量是否在一对或多对数字内。 (在这个例子中,我们“知道”它不会超过一个,但这并不重要。)

within_ranges <- function(x, lims)  {
  Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
}

要了解它在做什么,让我们调试它,调用它,看看发生了什么。

debugonce(within_ranges)
within_ranges(df$wavelength, badData)
# debugging in: within_ranges(df$wavelength, badData)
# debug at #1: {
#     Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= 
#         lim[2]))
# }

让我们 运行 内部部分:

# Browse[2]> 
lapply(lims, function(lim) lim[1] <= x & x <= lim[2])
# [[1]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
#  [1] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
#  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE

所以第一个元素 (T,T,F,F,...) 是值 (x) 是否落在第一个数字对 (296 到 310) 内;具有第二对的第二个元素(330 到 335);等等

Reduce(部分在前两个参数上调用第一个参数,一个函数,保存return,然后运行在[=99上调用相同的函数=] 和第三个参数。它存储它,然后 运行 在 return 和第四个参数(如果存在)上使用相同的函数。它在提供的列表的整个长度上重复此操作。

在此示例中,函数是文字 |(因为它是特殊的而被转义),因此它是 [[1]] 向量与 [[2]] 向量的“或”运算。如果添加 accumulate=TRUE:

,您实际上可以看到发生了什么
# Browse[2]> 
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]), accumulate=TRUE)
# [[1]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# [[2]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
# [[3]]
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE

第一个return是第一个向量,未修改。第二个元素是原始[[2]]向量与前面的return即this[[1]]向量(与原始[[1]]).第三个元素是原来的[[3]]向量与前面的return进行或运算,即this[[2]]。这会产生您期望的 TRUE (1、2、7、11、12)三个分组。所以我们要[[3]]这个元素,也就是不累加得到的:

# Browse[2]> 
Reduce(`|`, lapply(lims, function(lim) lim[1] <= x & x <= lim[2]))
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE

好的,让我们Q退出调试器,全力以赴:

within_ranges(df$wavelength, badData)
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE

这个输出看起来很眼熟。

(BTW: inside our function, we could also have used

rowSums(sapply(lims, ...)) > 0

and it would have worked just as well. For that, though, you need to realize that sapply should be returning a matrix with as many columns as we have rows of data in df, odd if you aren't familiar.)

现在,我们可以NAdplyr确定我们需要的东西:

df %>%
  mutate(
    reflectance = if_else(within_ranges(wavelength, badData), NA_real_, reflectance)
  )
#    wavelength reflectance
# 1    300.0000          NA
# 2    305.0087          NA
# 3    310.0173   -11.01733
# 4    315.0260   -16.02600
# 5    320.0347   -21.03467
# 6    325.0433   -26.04333
# 7    330.0520          NA
# 8    335.0607   -36.06067
# 9    340.0693   -41.06934
# 10   345.0780   -46.07800
# 11   350.0867          NA
# 12   355.0953          NA

编辑:或另一个dplyr,使用你对replace的第一个想法(不是我的第一个习惯,没有理由):

df %>%
  mutate(
    reflectance = replace(reflectance, within_ranges(wavelength, badData), NA_real_)
  )

或基础 R:

df$reflectance <- ifelse(within_ranges(df$wavelength, badData), NA_real_, df$reflectance)
df
#    wavelength reflectance
# 1    300.0000          NA
# 2    305.0087          NA
# 3    310.0173   -11.01733
# 4    315.0260   -16.02600
# 5    320.0347   -21.03467
# 6    325.0433   -26.04333
# 7    330.0520          NA
# 8    335.0607   -36.06067
# 9    340.0693   -41.06934
# 10   345.0780   -46.07800
# 11   350.0867          NA
# 12   355.0953          NA

备注:

  • 我特别使用 NA_real_,既是为了清楚(你知道有不同类型的 NA 吗?),部分是因为在使用 dplyr::if_else 时,它complain/fail 如果“真”和“假”参数的 类 不相同(NA 在技术上是 logical,而不是 numeric 作为你的 reflectance 是);
  • 我在第一个示例中使用 dplyr::if_else,因为您已经在使用 dplyr,但如果您选择放弃 dplyr(或其他人放弃),那么base-R ifelse 也可以。 (它有它的责任,但它在这里似乎工作得很好。)

这是一个基于为 badDatatidyr::crossing 创建数据框的解决方案。使用 crossing 我们可以获得两个数据帧之间的所有组合。

badData <- data.frame(start= c(296,330,350),end=c(310.01,335,565))

library(dplyr)
library(tidyr)
library(data.table)

df %>% crossing(badData) %>% 
       mutate(Flag=ifelse(data.table::between(wavelength,start,end),1,0)) %>% 
       arrange(wavelength,desc(Flag)) %>% #Make sure 1 'if exist' at the 1st row for each wavelength before run distinct
       distinct(wavelength,.keep_all=T) %>%
       mutate(reflectance_upd=ifelse(Flag==1,NA,reflectance))

    wavelength reflectance start    end Flag reflectance_upd
1    300.0000   -1.000000   296 310.01    1              NA
2    305.0087   -6.008667   296 310.01    1              NA
3    310.0173  -11.017334   296 310.01    0       -11.01733
4    315.0260  -16.026001   296 310.01    0       -16.02600
5    320.0347  -21.034668   296 310.01    0       -21.03467
6    325.0433  -26.043335   296 310.01    0       -26.04333
7    330.0520  -31.052002   330 335.00    1              NA
8    335.0607  -36.060669   296 310.01    0       -36.06067
9    340.0693  -41.069336   296 310.01    0       -41.06934
10   345.0780  -46.078003   296 310.01    0       -46.07800
11   350.0867  -51.086670   350 565.00    1              NA
12   355.0953  -56.095337   350 565.00    1              NA

dplyr::between()怎么样?

library(dplyr)

df %>% 
  mutate(
    reflectance = case_when(
      between(wavelength, 296, 310) ~ NA_real_,
      between(wavelength, 330, 335) ~ NA_real_,
      between(wavelength, 350, 565) ~ NA_real_,
      TRUE                          ~ reflectance
    )
  )