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

时间:2018-08-29 04:44:43

标签: r replace dplyr conditional mutate

我有以下数据帧,并且我想根据波长值是否落入确定为不良测量值的特定范围的组(badData向量)中,用NA替换反射率值。

错误数据的范围可能会随时间变化,因此我希望解决方案尽可能通用。

  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

4 个答案:

答案 0 :(得分:6)

第一步是要认识到定义整数范围将不起作用。相反,我将列出一个数字对列表:

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和第三个参数上运行相同的函数。它存储它,然后在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

第一个返回是第一个未经修改的向量。第二个元素是原始[[2]]向量和上一个返回值与 this [[1]]向量(与原始[[1]]相同)进行或运算。第三个元素是原始[[3]]向量与上一个返回值,即 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

此输出看起来很熟悉。

  

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

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

,它也一样有效。为此,尽管如此,您需要意识到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

编辑:或者使用您对dplyr的第一个想法(不是我的第一个习惯,没有理由){}:

replace

或基数R:

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

注意:

  • 我专门使用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时会抱怨/ fail,如果“ true”和“ false”参数的类不同(dplyr::if_else在技术上是NA,而不是logicalnumeric相同); < / li>
  • 在第一个示例中,我使用reflectance,因为您已经在使用dplyr::if_else,但是如果您选择放弃dplyr(或其他人这样做),则基准- R dplyr也有效。 (它有责任,但在这里似乎可以正常工作。)

答案 1 :(得分:1)

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
    )
  )

答案 2 :(得分:0)

我认为这会有所帮助。

    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

答案 3 :(得分:0)

这是基于为TextInputLayoutclass CustomersTable extends Table { public function initialize(array $config) { $this->belongsTo('Countries') ->setForeignKey('primary_country'); } } 创建数据框的解决方案。使用badData,我们可以获得两个数据框之间的所有组合。

tidyr::crossing