【问题标题】:looping and check condition of consecutive records and replacing in R循环和检查连续记录的条件并在R中替换
【发布时间】:2018-11-04 07:03:00
【问题描述】:

我的数据集由usertimecondition 组成。我想用最后一个连续TRUEtime 替换以 FALSE 开头的序列的时间,然后是两个以上连续的TRUEs。

假设df:

df <- read.csv(text="user,time,condition
11,1:05,FALSE
11,1:10,TRUE
11,1:10,FALSE
11,1:15,TRUE
11,1:20,TRUE
11,1:25,TRUE
11,1:40,FALSE
22,2:20,FALSE
22,2:30,FALSE
22,2:35,TRUE
22,2:40,TRUE", header=TRUE)

我想要的结果:第 6 行的时间被复制到第 3 到 6 行的时间,因为连续的 TRUE 从 4 到 6 开始。这同样适用于最后三个记录。

user time   condition
11  1:05    FALSE
11  1:10    TRUE
11  1:25    FALSE
11  1:25    TRUE
11  1:25    TRUE
11  1:25    TRUE
11  1:40    FALSE
22  2:20    FALSE
22  2:40    FALSE
22  2:40    TRUE
22  2:40    TRUE

如何在 R 中做到这一点?

【问题讨论】:

    标签: r loops replace


    【解决方案1】:

    这个解决方案应该可以解决问题,更多详细信息请参见代码中的 cmets

    false_positions <- which(!c(df$condition, FALSE)) #Flag the position of each of the FALSE occurences
                                                      #A dummy FALSE is put on the end to check for end of dataframe
    
    false_differences <- diff(false_positions, 1)     #Calculate how far each FALSE occurence is from the last
    
    false_starts <- which(false_differences > 2)      #Grab which of these FALSE differences are more than 2 apart
                                                      #Greater than 2 indicates 2 or more TRUEs as the first FALSE 
                                                      #counts as one position
    
    #false_starts stores the beginning of each chain we want to update
    
    #Go through each of the FALSE starts which have more than one consecutive TRUE
    for(false_start in false_starts){
    
      false_first <- false_positions[false_start]     #Gets the position of the start of our chain
    
      true_last <- false_positions[false_start+1]-1   #Gets the position of the end of our chain, which is the
                                                      #the item before (thus the -1) the false after our
                                                      #initial FALSE (thus the +1)
    
      time_override <- df$time[true_last]             #Now we know the position of the end of our chain (the last TRUE)
                                                      #We can get the time we want to use
    
      df$time[false_first:true_last] <- time_override #Update all the times from the start to end of our chain with
                                                      #the time we just determined
    
    }
    
    > df
       user time condition
    1    11 1:05     FALSE
    2    11 1:10      TRUE
    3    11 1:25     FALSE
    4    11 1:25      TRUE
    5    11 1:25      TRUE
    6    11 1:25      TRUE
    7    11 1:40     FALSE
    8    22 2:20     FALSE
    9    22 2:40     FALSE
    10   22 2:40      TRUE
    11   22 2:40      TRUE
    

    如果可能的话,我想并行化那个底部循环,但我一时想不通。

    要点是确定我们所有的错误在哪里,然后确定我们所有链的起点在哪里,因为我们只有 TRUE 和 FALSE,我们可以通过查看 FALSE 的距离来做到这一点!

    一旦我们知道我们的链从哪里开始(因为它们是第一个 FALSE,其中 FALSE 相距足够远),我们可以通过查看我们已经创建的所有 FALSES 列表中下一个 FALSE 之前的元素来结束我们的链.

    现在我们有了链的开头和结尾,我们可以只看链的结尾来获取我们想要的时间,然后填写时间值!

    我希望这提供了一种相对快速的方式来做你想做的事:)

    【讨论】:

      【解决方案2】:

      这是使用rle的一个选项

      ## Run length encoding of df
      df_rle <- rle(df$condition)
      ## Locations of 2 or more consecutive TRUEs in RLE
      seq_changes <- which(df_rle$lengths >= 2 & df_rle$value == TRUE)
      ## End-point index in original data frame
      df_ind <- cumsum(df_rle$lengths)
      
      ## Loop over breakpoints to change
      for (i in seq_changes){
        i1 <- df_ind[i-1]
        i2 <- df_ind[i]
        df$time[i1:i2] <- df$time[i2]
      }
      

      【讨论】:

        【解决方案3】:

        这是一个data.table 解决方案,它在运行时应该更快。

        library(data.table)
        setDT(df)
        df[, time := if (.N > 2) time[.N] else time, 
            by=cumsum(!shift(c(condition, FALSE))[-1L])]
        
        #    user time condition
        # 1:   11 1:05     FALSE
        # 2:   11 1:10      TRUE
        # 3:   11 1:25     FALSE
        # 4:   11 1:25      TRUE
        # 5:   11 1:25      TRUE
        # 6:   11 1:25      TRUE
        # 7:   11 1:40     FALSE
        # 8:   22 2:20     FALSE
        # 9:   22 2:40     FALSE
        #10:   22 2:40      TRUE
        #11:   22 2:40      TRUE
        

        想法是切割成以F开头的序列。

        [-1L] 在执行cumsum 之前删除第一个 NA。

        我建议您在j 中运行一些by 代码来看看。

        数据:

        df <- read.csv(text="user,time,condition
        11,1:05,FALSE
        11,1:10,TRUE
        11,1:10,FALSE
        11,1:15,TRUE
        11,1:20,TRUE
        11,1:25,TRUE
        11,1:40,FALSE
        22,2:20,FALSE
        22,2:30,FALSE
        22,2:35,TRUE
        22,2:40,TRUE", header=TRUE)
        

        【讨论】:

          猜你喜欢
          • 2020-10-01
          • 2020-05-16
          • 2013-07-13
          • 1970-01-01
          • 1970-01-01
          • 2021-05-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多