【问题标题】:Replace values in one column based on a vector conditionally matching another column根据有条件地匹配另一列的向量替换一列中的值
【发布时间】:2019-02-03 18:45:31
【问题描述】:

我有以下数据框,我想用 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

【问题讨论】:

  • 您将badData 定义为整数和小数之间的范围,这是没有意义的(冒号本质上表示“从firstlast i> 按个”)。那你希望用%in%看一个数值是否在这个准整数向量内,严重受制于R FAQ 7.31(浮点数的比较)。我怀疑您打算将badData 中的号码设为“lo:hi”,对吗?
  • 我更新了我的帖子以添加预期的输出@RonakShah
  • 抱歉@r2evans 由于我的实际数据集与我为堆栈溢出创建的虚拟数据集之间存在差异,存在一些拼写错误。它们现已更新。
  • 这并没有解决我的数字问题:从技术上讲,any 浮点数被测试为%in% integers 的向量将 总是失败。好的,所以它有时可能会起作用,但你永远不能相信它。
  • @r2evans 非常抱歉,感谢您的来信。我以为我已经接受了。

标签: r replace conditional dplyr


【解决方案1】:

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

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( 部分在前两个参数上调用第一个参数,一个函数,保存返回值,然后在返回值和第三个参数上运行相同的函数。它存储它,然后在返回和第四个参数(如果存在)上运行相同的函数。它会在提供的列表的整个长度上重复此操作。

在这个例子中,函数是文字|(因为它是特殊的而被转义),所以它是将[[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]] 向量 ORed 与先前的返回,即 this [[1]] 向量(与原始 [[1]] 相同)。第三个元素是原始的[[3]] 向量与前一个返回的ORed,即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

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

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 中的数据行数一样多,如果您不熟悉,那就奇怪了。)

现在,我们可以通过dplyrNAify 我们需要的东西:

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 时,如果“真”和“假”参数不一样(NA 在技术上是 logical,而不是 numeric,因为您的 reflectance 是);
  • 我在第一个例子中使用dplyr::if_else,因为你已经在使用dplyr,但是如果你选择放弃dplyr(或者其他人这样做),那么base-R ifelse可以工作,也。 (它有它的责任,但它似乎在这里工作得很好。)

【讨论】:

  • 精彩的回应。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    这是一个基于为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
    

    【讨论】:

    • 哦,当然,我在测试中错过了一步,对不起......忽略“相同”的评论。话虽如此,这会很糟糕,你不觉得吗?
    • @r2evans 没关系。关于缩放是的,我也害怕,但我希望 OP 有几个间隔来测试。
    【解决方案4】:

    我认为这会有所帮助。

        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
    

    【讨论】:

      猜你喜欢
      • 2012-11-06
      • 1970-01-01
      • 2016-11-22
      • 2017-03-04
      • 2021-10-01
      • 2020-05-20
      • 2019-09-02
      • 2018-09-08
      • 1970-01-01
      相关资源
      最近更新 更多