【问题标题】:Find previous same value in a vector and apply certain conditions在向量中查找先前相同的值并应用某些条件
【发布时间】:2017-12-14 14:21:07
【问题描述】:

对于myvector1 的每个值,我想知道myvector1 中前一个相同值的mycategory 值,因为mystatusON,否则我会看对应下一个相同的值,直到它为 ON

说明如下:

  1. 对于“myvector”的给定位置,给我他的值。
  2. 在中查找中间前一个相同值的位置 “我的向量”
  3. 检查关联状态。如果它是 ON 给我他的关联 “我的类别”。如果它是关闭的,重复到第 2 点。
  4. 将获得的“mycategory”分配给新的向量“mysolution”。

给定数据集mydf,我要查找的是DesiredSolution(我手动填写的)。

mydf <- structure(list(myvector1 = structure(c(1L, 2L, 3L, 4L, 5L, 1L, 
2L, 4L, 5L, 2L, 3L, 4L, 5L, 2L, 3L, 5L, 1L, 2L, 3L, 4L, 5L, 1L, 
2L, 4L, 5L, 1L, 1L, 2L, 3L, 4L, 5L, 3L), .Label = c("0", "1", 
"2", "3", "4"), class = "factor"), mystatus = structure(c(2L, 
1L, 2L, 2L, 1L, 2L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 1L, 2L, 2L, 2L, 
1L, 2L, 2L, 1L, 1L, 2L, 1L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 2L), .Label = c("OFF", 
"ON"), class = "factor"), mycategory = structure(c(2L, 2L, 3L, 
1L, 1L, 1L, 1L, 3L, 3L, 1L, 2L, 2L, 3L, 1L, 1L, 1L, 1L, 1L, 2L, 
2L, 3L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L), .Label = c("bye", 
"hi", "stay"), class = "factor"), DesiredSolution = structure(c(3L, 
3L, 3L, 3L, 3L, 2L, 3L, 1L, 3L, 1L, 4L, 4L, 4L, 1L, 2L, 4L, 1L, 
1L, 1L, 2L, 1L, 1L, 1L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 1L, 2L), .Label = c("bye", 
"hi", "NA", "stay"), class = "factor")), .Names = c("myvector1", 
"mystatus", "mycategory", "DesiredSolution"), row.names = c(NA, 
-32L), class = "data.frame")

【问题讨论】:

    标签: r for-loop


    【解决方案1】:

    使用 data.table...

    library(data.table)
    setDT(mydf)
    mydf[, r := .I]
    mydf[, v := mydf[mystatus == "ON"][mydf, on=.(r < r, myvector1), mult="last", x.mycategory]]
    

    给了

        myvector1 mystatus mycategory DesiredSolution  r    v
     1:         0       ON         hi              NA  1   NA
     2:         1      OFF         hi              NA  2   NA
     3:         2       ON       stay              NA  3   NA
     4:         3       ON        bye              NA  4   NA
     5:         4      OFF        bye              NA  5   NA
     6:         0       ON        bye              hi  6   hi
     7:         1       ON        bye              NA  7   NA
     8:         3       ON       stay             bye  8  bye
     9:         4       ON       stay              NA  9   NA
    10:         1      OFF        bye             bye 10  bye
    11:         2       ON         hi            stay 11 stay
    12:         3       ON         hi            stay 12 stay
    13:         4       ON       stay            stay 13 stay
    14:         1      OFF        bye             bye 14  bye
    15:         2       ON        bye              hi 15   hi
    16:         4       ON        bye            stay 16 stay
    17:         0       ON        bye             bye 17  bye
    18:         1      OFF        bye             bye 18  bye
    19:         2       ON         hi             bye 19  bye
    20:         3       ON         hi              hi 20   hi
    21:         4      OFF       stay             bye 21  bye
    22:         0      OFF        bye             bye 22  bye
    23:         1       ON        bye             bye 23  bye
    24:         3      OFF        bye              hi 24   hi
    25:         4       ON        bye             bye 25  bye
    26:         0      OFF        bye             bye 26  bye
    27:         0      OFF         hi             bye 27  bye
    28:         1      OFF         hi             bye 28  bye
    29:         2      OFF         hi              hi 29   hi
    30:         3      OFF         hi              hi 30   hi
    31:         4      OFF       stay             bye 31  bye
    32:         2       ON       stay              hi 32   hi
        myvector1 mystatus mycategory DesiredSolution  r    v
    

    它是如何工作的:mydf[mystatus == "ON"] 中查找行号r 较低且myvector1 匹配的行。返回mycategory,如果有多个匹配则取最后一个匹配行。

    【讨论】:

    • 你能解释一下'last'参数和mult参数吗?
    • @bringtheheat 在连接x[i, on=] 中,我们在x 中查找i 的每一行。如果i 的给定行有多行x,则mult= 确定返回其中的哪一行。如果设置为“last”,则仅返回最后匹配的x 行(按x 中的行顺序)。
    • 啊,明白了。这是一个优雅的解决方案。通常使用data.table 进行就地操作,因此mult= 参数会很好用。我可以看到它对于涉及时间戳的数据非常好(假设数据使用setorder().......)
    【解决方案2】:

    另一种可能的方式,使用 data.framezoo::na.locf 的“切片”

    首先,设置数据的位置,以便之后可以取回它

    mydf$pos <- seq_len(nrow(mydf))
    

    然后split你的data.frame根据myvector1:

    spl_mydf <- split(mydf, mydf$myvector1)
    

    然后对每个“切片”(仅myvector1 的一个值)应用一个函数,当mystatus 为ON 时,该函数仅保留mycategory 的值,将其余部分作为NA 并将NA 替换为以前不是NA 值。第一项是NA,您不要保留最后一项(为了获得所需的延迟)。

    my_out <- lapply(spl_mydf,
                     function(sl_df){
                        out <- sl_df$mycategory
                        out[sl_df$mystatus=="OFF"] <- NA
                        data.frame(pos=sl_df$pos, 
                                   out=c(NA, head(na.locf(as.character(out), na.rm=FALSE), -1))) # as.character is to avoid getting the factors levels
     })
    

    最后,根据位置放回所有内容并抑制pos列:

    out <- do.call(rbind, my_out)
    mydf$output <- out$out[order(out$pos)]
    mydf$pos <- NULL
    
    head(mydf, 10)
       myvector1 mystatus mycategory DesiredSolution output
    1          0       ON         hi              NA   <NA>
    2          1      OFF         hi              NA   <NA>
    3          2       ON       stay              NA   <NA>
    4          3       ON        bye              NA   <NA>
    5          4      OFF        bye              NA   <NA>
    6          0       ON        bye              hi     hi
    7          1       ON        bye              NA   <NA>
    8          3       ON       stay             bye    bye
    9          4       ON       stay              NA   <NA>
    10         1      OFF        bye             bye    bye
    

    检查一切正常:

    all(mydf$DesiredSolution==mydf$output, na.rm=TRUE) # TRUE
    all((as.character(mydf$DesiredSolution)=="NA")==is.na(mydf$output)) # TRUE 
    

    (NA 被视为您的 data.frame 中的级别之一)

    【讨论】:

      【解决方案3】:

      试试这个。我相信你可以让它更简洁。

      DesiredSolution = vector()
      for (i in 1:length(myvector1)) {
        step_1 = myvector1[i]
        step_2 = grep(step_1, myvector1)
        step_2 = step_2[step_2 < i]
        step_2a = sort(step_2, decreasing = T)
      
        if (length(step_2) != 0) {
          for (d in 1:length(step_2a)) {
            k = step_2a[d]
            step_3 = mystatus[k]
            if (step_3 == 'ON') {
              step_4 = mycategory[k]
              break
            } else if (step_3 == 'OFF' & d == length(step_2a)) {
              step_4 = NA
            }
          }
        } else {
            step_4 = NA
        }
        DesiredSolution = c(DesiredSolution, step_4)
      
      }
      

      【讨论】:

      • 感谢您的回答!它似乎工作正常。它创建了一个长度为 n+1 的向量,NA 在最后一个位置很艰难,但我只是删除了最后一个位置。
      • 试试我发布的新版本。我误解了OP中的前提
      • 你应该看看@Frank 发布的解决方案,它更简洁、更快、内存效率更高(所有有根据的猜测,没有实际的基准测试)
      猜你喜欢
      • 2017-11-01
      • 2019-09-19
      • 2021-11-08
      • 1970-01-01
      • 2021-08-17
      • 1970-01-01
      • 1970-01-01
      • 2018-06-17
      • 1970-01-01
      相关资源
      最近更新 更多