Трассировка назад - найти, встречается ли строковое значение перед другим заданным c строковым значением - dplyr / R - PullRequest
0 голосов
/ 17 января 2020

У меня есть общая проблема в R. Интересно, есть ли способ определить, происходит ли указанное c строковое значение после другого указанного c строкового значения в группе. Набор данных является временным рядом. Каждая группа состоит из 10 лет.

Я хочу что-то похожее на приведенное ниже, но вместо лага я буду смотреть sh каждый год перед "stringvalue1" в группе.

mutate(new_var = if_else(stringvar = "stringvalue1" & lag(stringvar) %in% c("stringvalue2", "stringvalue3"), "Match", "Not match"))  

Помощь будет высоко ценится!

library(dplyr)

match_if_precedes <- function(column, this_string, preceded_by)
{
  matches    <- which(column == this_string)
  if (length(matches) == 0) return(rep("No Match", length(column)))
  last_match = matches[length(matches) - 1]
  if (last_match == 0) return(rep("No Match", length(column)))
  any_matches <- !is.na(preceded_by %in% column[1:last_match])
  if(length(any_matches) == 0) return(rep("No Match", length(column)))
  any_matches <- any(any_matches)
  if(any_matches) return(rep("Match", length(column)))
  return(rep("No Match", length(column)))
}

df1 <- structure(list(group = c("A", "A", "A", "A", "A",  
                               "B", "B", "B", "B", "B", 
                               "C", "C", "C", "C", "C"), 
                     stringvar = c("stringvalue4", "stringvalue2", "stringvalue1", "stringvalue1", "stringvalue1", 
                                   "stringvalue1", "stringvalue1", "stringvalue1", "stringvalue1","stringvalue4", 
                                   "stringvalue4", "stringvalue2", "stringvalue3", "stringvalue3", "stringvalue4")),
                                   row.names = c(NA, -15L), class = "data.frame")
df1 %>% 
  group_by(group) %>% 
  mutate(newvar = match_if_precedes(stringvar, "stringvalue1", 
                                    c("stringvalue2", "stringvalue3"))) 

   group stringvar    newvar  
   <chr> <chr>        <chr>   
 1 A     stringvalue4 Match   
 2 A     stringvalue2 Match   
 3 A     stringvalue1 Match   
 4 A     stringvalue1 Match   
 5 A     stringvalue1 Match   
 6 B     stringvalue1 Match   
 7 B     stringvalue1 Match   
 8 B     stringvalue1 Match   
 9 B     stringvalue1 Match   
10 B     stringvalue4 Match   
11 C     stringvalue4 No Match
12 C     stringvalue2 No Match
13 C     stringvalue3 No Match
14 C     stringvalue3 No Match
15 C     stringvalue4 No Match

1 Ответ

1 голос
/ 17 января 2020

Вы можете определить функцию, которая будет возвращать вектор «Соответствия», если критерии выполнены, и вектор «Нет соответствия», если критерии не выполнены. Они будут иметь ту же длину, что и столбец ввода.

Я добавил подробные комментарии, чтобы показать, как работает функция:

# This function takes a vector of strings called `column`. It looks for any instances of the
# single string `this_string` and any of the vector of strings `preceded_by` within 
# `column`. If it finds any member of `preceded_by` in the vector before the last instance
# of `this_string` it returns a vector of the string "Match" with the same length as 
# the original `column` vector. In all other cases it returns a vector of "No Match"

match_if_precedes <- function(column, this_string, preceded_by)
{
  # Find instances of this_string. If there are no instances of this_string then
  # we want to return a vector of "No Match"
  matches    <- which(column == this_string)
  if (length(matches) == 0) return(rep("No Match", length(column)))

  # If there is more than one instance of this_string, we want to choose the last one
  last_match = matches[length(matches)] - 1

  # If the only instance of this_string is at position 1, there can't be any
  # instances of preceded_by before it, so return a vector of NA
  if (last_match == 0) return(rep("No Match", length(column)))

  # Now find the instances of preceded_by in the part of the column before the
  # last instance of this_string and remove any NA values
  any_matches <- preceded_by %in% column[1:last_match]
  any_matches <- any_matches[!is.na(any_matches)]

  # If no matches are valid, we return all NAs
  if(length(any_matches) == 0) return(rep("No Match", length(column)))

  # If any of our matches are TRUE, we return a vector of "Match"
  if(any(any_matches)) return(rep("Match", length(column)))

  # The only remaining possibility is that we had no matches, so return "No Match"
  return(rep("No Match", length(column)))
}

Мы можем проверить это, используя данные из вашего вопроса в измененном виде. по вашим комментариям:

df <- structure(list(group = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 
2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L), .Label = c("A", "B", "C"), class = "factor"), 
    stringvar = c("stringvalue4", "stringvalue2", "stringvalue1", 
    "stringvalue1", "stringvalue1", "stringvalue1", "stringvalue1", 
    "stringvalue1", "stringvalue1", "stringvalue4", "stringvalue4", 
    "stringvalue2", "stringvalue3", "stringvalue3", "stringvalue1"
    )), row.names = c(NA, -15L), class = "data.frame")

find_these  <- c("stringvalue2", "stringvalue3")
before_this <- "stringvalue1"

Теперь я могу использовать group_by и mutate, чтобы применить эту функцию к каждой из групп во фрейме данных:

library(dplyr)

df                                                                     %>%
group_by(group)                                                        %>% 
mutate(newvar = match_if_precedes(stringvar, before_this, find_these)) %>% 
as.data.frame()

Результат:

#>    group    stringvar   newvar
#> 1      A stringvalue4    Match
#> 2      A stringvalue2    Match
#> 3      A stringvalue1    Match
#> 4      A stringvalue1    Match
#> 5      A stringvalue1    Match
#> 6      B stringvalue1 No Match
#> 7      B stringvalue1 No Match
#> 8      B stringvalue1 No Match
#> 9      B stringvalue1 No Match
#> 10     B stringvalue4 No Match
#> 11     C stringvalue4    Match
#> 12     C stringvalue2    Match
#> 13     C stringvalue3    Match
#> 14     C stringvalue3    Match
#> 15     C stringvalue1    Match
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...