зацикливание и проверка состояния последовательных записей и замена в R - PullRequest
0 голосов
/ 25 мая 2018

У меня есть набор данных, состоящий из user, time и condition.Я хочу заменить время для последовательностей, начинающихся с FALSE, за которыми следуют более двух последовательных TRUE с time из последних последовательных TRUE.

скажем, 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?

Ответы [ 3 ]

0 голосов
/ 25 мая 2018

Вот один из вариантов использования 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]
}
0 голосов
/ 25 мая 2018

Вот решение 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] удаляет первый NA перед выполнением cumsum.

Я бы порекомендовал вам запустить код by в j для просмотра.

data:

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)
0 голосов
/ 25 мая 2018

Это решение должно сработать, см. Комментарии в коде для получения более подробной информации.

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

Я хотел бы распараллелить этот нижний цикл, если это возможно, но изо всех сил пыталсясделайте это.

Суть в том, чтобы определить, где находятся все наши ложные значения, а затем определить, где находится начало всех наших цепочек, поскольку у нас есть только ИСТИНА и ЛОЖЬ, мы можем сделать это, посмотрев, насколько далеко друг от друга наши ЛОЖИare!

Как только мы узнаем, где начинаются наши цепочки (так как они являются первыми FALSE, где FALSE достаточно далеко друг от друга), мы можем получить конец нашей цепочки, посмотрев на элемент перед следующим FALSE в спискевсе FALSES, которые мы уже создали.

Теперь у нас есть начало и конец наших цепочек, мы можем просто посмотреть на конец цепочки, чтобы получить желаемое время, а затем заполнить значения времени!

Я надеюсь, что это относительно быстрый способ сделать то, что вы хотите, хотя :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...