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

Chr*_*Ade 1 conditional replace r dplyr mutate

我有以下数据框,并且我想用NA替换反射率值,这取决于波长值是否落入被确定为不良测量的某个范围的分组(badData vector).

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

  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
Run Code Online (Sandbox Code Playgroud)

我试过了

   Data2 <- df %>% 
  mutate(reflectance = replace(reflectance,wavelength %in% badData, NA))
Run Code Online (Sandbox Code Playgroud)

但是因为我试图用波长范围而不是精确值来做这个,所以这不起作用.我在想我应该使用条件语句,但我不知道如何通过最有效的方式提供具有不同范围分组的向量.

输出数据集将是因为波长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
Run Code Online (Sandbox Code Playgroud)

r2e*_*ans 6

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

badData <- list(c(296,310), c(330,335), c(350,565))
Run Code Online (Sandbox Code Playgroud)

理解我们要检查每个$wavelength范围是否在这三个范围中的任何一个范围内.支持更多范围.

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

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

要了解这是做什么的,让我们调试它,调用它,看看发生了什么.

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]))
# }
Run Code Online (Sandbox Code Playgroud)

让我们运行那个内部部分:

# 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
Run Code Online (Sandbox Code Playgroud)

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

Reduce(部分在前两个参数上调用第一个参数,一个函数,保存返回,然后在返回和第三个参数上运行相同的函数.它存储它,然后在返回和第四个参数(如果存在)上运行相同的函数.它在提供的列表的整个长度上重复这一点.

在这个例子中,函数是文字|(因为它是特殊的,所以是转义的),因此它将[[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
Run Code Online (Sandbox Code Playgroud)

第一个返回是第一个未经修改的向量.第二个元素是原始[[2]]向量与前一个返回值进行OR运算, [[1]]向量是向量(与原始向量相同[[1]]).第三个元素是原始[[3]]向量与前一个返回ORed,就是这个 [[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
Run Code Online (Sandbox Code Playgroud)

好吧,让我们Q离开调试器,并给它一个完整的去:

within_ranges(df$wavelength, badData)
#  [1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE  TRUE
Run Code Online (Sandbox Code Playgroud)

此输出看起来很熟悉.

(顺便说一句:在我们的功能中,我们也可以使用

rowSums(sapply(lims, ...)) > 0
Run Code Online (Sandbox Code Playgroud)

它也会起作用.但是,为此,您需要意识到sapply应该返回一个matrix包含尽可能多列数据的列df,如果您不熟悉则会有奇怪的.)

现在,我们可以NA通过以下方式获得我们需要的东西dplyr:

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
Run Code Online (Sandbox Code Playgroud)

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

df %>%
  mutate(
    reflectance = replace(reflectance, within_ranges(wavelength, badData), NA_real_)
  )
Run Code Online (Sandbox Code Playgroud)

或基数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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我是专门用的NA_real,两者都是为了清晰(你知道有不同类型NA吗?),部分是因为在使用中dplyr::if_else,如果"真"和"假"参数的类不是,它会抱怨/失败相同(NA技术上logical,不是numericreflectance的);
  • 我用dplyr::if_else的是第一个例子,因为你已经在使用了dplyr,但是如果你选择放弃dplyr(或者别人做的话),那么base-R ifelse也可以使用.(它有它的责任,但它似乎在这里工作得很好.)