【问题标题】:Identifying if a condition is met based on a dynamic range in another column根据另一列中的动态范围识别是否满足条件
【发布时间】:2018-01-31 23:59:10
【问题描述】:

我有一列有很多零值和偶尔的十进制值,例如

c <- c(0,0,0,0.0927,0,0,0.100821,0) 

我有一个单独的列,v,其中包含所有十进制数字:

v <- c(0.091,0.0981,0.101,0.102,0.092,0.1,0.091,0.099)

如果我在 c 中的值 >0,我想查看列 v从该观察向前(即从该行向下),看看是否在该范围内的任何地方,有一个值低于c 的该行/观察值中的特定值。理想情况下,我想返回第一个出现的较小数字实例的值和/或行号/索引。

输出如下所示: (不,不,不,不,5,不,不,不,8,不)

即输出返回 v 中下一个实例的行号,该行号低于 C 中的相应数字。它忽略 v 中的第一个值 .091,即使它低于 C 中的第一个数字 (.0927),因为它发生在 C 中的出现之前(数据按时间顺序排列)。我只想查看后续行。

我真的非常感谢任何帮助。 :-) W

【问题讨论】:

  • 你为什么要在 v 的声明中覆盖 c
  • “从该行向下”是否包括该行本身?
  • 如果可能,最好从下一行开始。但是,如果我从同一行开始,我认为它不会破坏代码。
  • Maurits - 我很抱歉。那是一个复制/粘贴错误。我现在已经纠正了。我不是故意要覆盖 c!!
  • 另外,在 R 中,我们通常避免使用 c 作为变量名,因为它已经是最常用函数的名称。

标签: r


【解决方案1】:

假设可以有多个符合条件的值(即v 中的多个元素低于c 中的正值

positiveMatches <- which(c > 0) #Find the positive values in c

resultList <- list() #Set up a vector to store our data frames

#Loop through each positive match
for(i in positiveMatches){

    elementName <- paste0(i, "_", c[i]) #Name the list element after the index and value in c

    restrictedV <- c(rep(FALSE, i-1), rep(TRUE, length(v)-(i-1)))
    lowerMatches <- (v[i:length(v)] < c[i]) & restrictedV #Find the index of those elements in v which are less than our positive match in c
    resultList[[elementName]] <- data.frame(index = which(lowerMatches),
                                            value = v[lowerMatches]
                                            )
}

这应该给你一个数据框列表,其中每个元素都以 c 中的索引和值命名,并且数据框包含 v 中每个合适元素的 1 行,提供 v 中每个元素的索引和值较低比c中的那个。

这里唯一的技巧是计算出我们的值是真的索引。我使用which 命令来获取c 中的索引。一旦我有了这些值,我就可以遍历它们,并计算出适用于 v 中每个 c 的索引,我使用lowerMatches &lt;- v &lt; c[i] 完成了这项工作。获得所有信息后,我只需将它们绑定到一个数据帧中。

你没有给出你希望输出是什么样的例子,所以我想出了一些我认为很好的东西。不过,您可能需要修改此代码以将 c 的索引和值存储为列表元素名称以外的其他内容。

您可以使用 names(resultList) 取回这些值,但此时您可能应该尝试修改代码以将这些值存储在更有用的地方。

编辑:我意识到我错过了“从这一点向下”部分,所以我在restrictedV 中添加了一个额外的检查,它只生成所有允许值的索引。如果您不希望它包含与 c 中相同的行(即忽略索引 5),只需将 restrictedV 检查中的 i-1 更改为 i

【讨论】:

    【解决方案2】:

    我不完全确定逻辑,但这重现了您的预期结果并且很短(仅限基础 R)。

    sapply(seq_along(c), function(i) {
        idx <- which(v < c[i]);
        if (length(idx) > 0) return(min(idx[idx > i])) else return(NA);
    })
    #[1] NA NA NA  5 NA NA  8 NA
    

    样本数据

    c <- c(0,0,0,0.0927,0,0,0.100821,0)
    v <- c(0.091,0.0981,0.101,0.102,0.092,0.1,0.091,0.099)
    

    【讨论】:

      【解决方案3】:

      我认为这符合您的要求吗?该方法定义了一个自定义函数应用于每一行数据,然后使用purrr::pmap将该函数应用于每一行并制作一个向量。然后,您可以将向量作为新列绑定到您的数据。这避免了for 循环,尽管这是否值得值得怀疑。如果c 不是正数,则结果是一个带有NA 的新列,如果小于v,则为后面的最小值的行索引,如果不是,则为0

      您给定的示例没有两种可能出现的情况。每次c 在这里是正数时,v 都会大于后面的某个值,所以这里永远不会返回0。此外,v 中可能存在相同的值,这使得“最小值索引”的定义不明确。也可能是一种更简单的方法。

      编辑:我看到示例数据发生了变化,您实际上是在寻找第一个较低的值,而不是最小值。如果您不先调整此答案,将尝试返回并修复它!


      library(tidyverse, quietly = TRUE)
      v <- c(0.09199,0.0981,0.101,0.10002,0.0927273,0.1,0.091,0.099,0.105,0.1,0.1,0.1,0.1,0.100821,0.09)
      c <- c(0,0,0,0,0.0927273,0,0,0,0,0,0,0,0,0.100821,0)
      
      tbl <- tibble(c, v) %>% rowid_to_column()
      
      is_v_lower <- function(rowid, c, v){
        if (c <= 0){
          return(NA)
        } else if (v > min(tbl$v[(rowid + 1):nrow(tbl)])) {
          min_index = which.min(tbl$v[(rowid + 1):nrow(tbl)])
          later_rowid = tbl$rowid[(rowid + 1):nrow(tbl)]
          return(later_rowid[min_index])
        } else {
          return(0)
        }
      }
      
      output <- pmap_chr(tbl, is_v_lower)
      
      new_tbl <- bind_cols(tbl, lower_v_index = output) %>% print()
      #> # A tibble: 15 x 4
      #>    rowid      c      v lower_v_index
      #>    <int>  <dbl>  <dbl> <chr>        
      #>  1     1 0      0.0920 <NA>         
      #>  2     2 0      0.0981 <NA>         
      #>  3     3 0      0.101  <NA>         
      #>  4     4 0      0.100  <NA>         
      #>  5     5 0.0927 0.0927 15           
      #>  6     6 0      0.100  <NA>         
      #>  7     7 0      0.0910 <NA>         
      #>  8     8 0      0.0990 <NA>         
      #>  9     9 0      0.105  <NA>         
      #> 10    10 0      0.100  <NA>         
      #> 11    11 0      0.100  <NA>         
      #> 12    12 0      0.100  <NA>         
      #> 13    13 0      0.100  <NA>         
      #> 14    14 0.101  0.101  15           
      #> 15    15 0      0.0900 <NA>
      

      【讨论】:

        【解决方案4】:

        使用“tidyverse”定义小标题 (data.frames) 和 %&gt;% 运算符(管道;请参阅 magrittr 包中的 ?"%&gt;%")。

        library(tidyverse)
        

        对于任何组和向量 x 和 y,我认为您的标准是(根据需要调整...)

        f = function(x, y) { y[1] = x[1]; which.max(y < y[1]) }
        

        从您的原始数据创建一个 tibble,其行索引列为 i。根据出现的非零值 c 对行进行分组

        tbl = tibble(i = seq_along(c), c, v) %>% group_by(grp = cumsum(c != 0))
        

        总结tbl,提取每组的相关信息

        ans = summarize(tbl, i = i[1], idx = i[1] + f(c, v) - 1L)
        

        将原始表加入答案并清理

        > left_join(tbl, ans) %>% ungroup() %>% select(-i, -grp)
        Joining, by = c("i", "grp")
        # A tibble: 8 x 3
               c      v   idx
           <dbl>  <dbl> <int>
        1 0      0.0910     1
        2 0      0.0981    NA
        3 0      0.101     NA
        4 0.0927 0.102      5
        5 0      0.0920    NA
        6 0      0.100     NA
        7 0.101  0.0910     8
        8 0      0.0990    NA
        

        也许将更多的逻辑放在f() 中,例如,

        f = function(i, x, y) {
            if (i[1] == 1L)
                return(NA)
            y[1] = x[1]
            i[1] + which.max(y < y[1]) - 1L
        }
        ans = summarize(tbl, i = i[1], idx = f(i, c, v))
        left_join(tbl, ans) %>% ungroup() %>% select(-i, -grp)
        

        这将有助于,例如,当 v 中没有任何值小于 c 时试图决定结果。

        【讨论】:

        • 非常感谢大家。我正在研究答案。马丁,你能解释一下“%>%”吗?我已经搜索了澄清,但这不是很容易产生搜索结果的东西。
        • magrittr的管道操作员。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多