Поиск шаблонов разрывов в строковых последовательностях значений и NA

Я работаю с набором данных, где каждая строка показывает, где человек использовал услуги. Это также неявно отслеживает, использует ли кто-то услуги, поскольку в противном случае значение столбца за месяц будет NA. Я хочу выявить случаи перерывов (периоды отсутствия с последующим возвращением) в службе отдельного лица на основе переходов между последовательными столбцами месяцев.

Другими словами, меня особенно интересует определение того, когда кто-то переходит от использования услуг (значения в столбце месяца) к отказу от использования услуг (значение 1 + последующий месяц равно NA), а затем снова к использованию услуг (значения в 1 + столбцы последующих месяцев, которым предшествуют NAs). Я надеюсь, что у меня будет двоичный столбец TRUE/FALSE («Breaks_in_Service»). Когда кто-то начинает оказывать услуги, не имеет значения (то есть NA имеет значение только после первого месяца — столбца со значением в нем).

Вот упрощенная версия моего набора данных:

# Sample Data
simp_2021 <- data.frame(
  ID = c(1, 2, 3, 4, 5),
  jan21_ORG_NAME = c("Org A", NA, NA, "Org B", "Org B"),
  feb21_ORG_NAME = c(NA, "Org A", "Org B", NA, "Org B"),
  mar21_ORG_NAME = c(NA, NA, "Org B", "Org D", NA),
  apr21_ORG_NAME = c("Org B", NA, "Org C", NA, "Org E")
)

# Initialize Breaks_in_Service column as FALSE
simp_2021$Breaks_in_Service <- FALSE

# View
print(simp_2021)

Ожидаемый результат: в примере данных Breaks_in_Service должен быть TRUE для идентификаторов 1, 4 и 5 и FALSE для идентификаторов 2 и 3.

Я попробовал создать цикл for, но он запутался и не работает:

# Loop over each row to check for breaks in service
for (i in 1:nrow(simp_2021)) {
  row_values <- simp_2021[i, 2:ncol(simp_2021)]  # Extract service columns for the current row
  
  # Initialize flags to track service usage
  in_service <- FALSE
  found_break <- FALSE
  
  # Check transitions within the row
  for (j in 1:(length(row_values) - 1)) {
    current_value <- row_values[[j]]
    next_value <- row_values[[j + 1]]
    
    if (is.na(current_value) && !is.na(next_value)) {
      # Transition from not using service to using service
      in_service <- TRUE
    } else if (!is.na(current_value) && is.na(next_value)) {
      # Transition from using service to not using service
      if (in_service) {
        found_break <- TRUE
        break  # Found a break, no need to check further
      }
    }
  }
  
  # Set Breaks_in_Service based on found breaks
  if (found_break) {
    simp_2021$Breaks_in_Service[i] <- TRUE
  }
}

# View the updated dataframe with the new 'Breaks_in_Service' column
print(simp_2021)
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Для какого-то вектора x

x <- c("Org A", NA, NA, "Org B")

Можно вычислить «кодирование длины серии» значений, отличных от NA.

> rle(!is.na(x))
Run Length Encoding
  lengths: int [1:3] 1 2 1
  values : logi [1:3] TRUE FALSE TRUE

и если произойдет перерыв в обслуживании, будет более 1 значения TRUE. Итак, вот функция, которая проверяет перерыв в работе

break_in_service <- function(x)
    sum(rle(!is.na(x))$values) > 1

Вы хотели бы сделать это для каждого идентификатора. Один из способов — использовать apply() в каждой строке, исключая первый столбец.

> apply(simp_2021[,-1], 1, break_in_service)
[1]  TRUE FALSE FALSE  TRUE  TRUE

Мне нравится использовать «аккуратный» подход с dplyr/tidyr.

library(tidyr); library(dplyr)
simp_2021 |>
    ## convert to 'long' format, where each row is an ID, name, value tuple
    pivot_longer(ends_with("ORG_NAME")) |>
    ## identify the groups in your data
    group_by(ID) |>
    ## summarize each group
    summarize(has_break_in_service = break_in_service(value))

Результат

> simp_2021 |>
+     ## convert to 'long' format
+     pivot_longer(ends_with("ORG_NAME")) |>
+     ## identify the groups in your data
+     group_by(ID) |>
+     ## summarize each group
+     summarize(has_break_in_service = break_in_service(value))
# A tibble: 5 × 2
     ID has_break_in_service
  <dbl> <lgl>
1     1 TRUE
2     2 FALSE
3     3 FALSE
4     4 TRUE
5     5 TRUE

Это сработало как шарм, и обозначения/объяснения были очень полезны. Большое спасибо!

Violet Brooks 18.04.2024 04:56
Ответ принят как подходящий

Сначала вы можете приравнять столбцы месяца к двоичным !NA, используя +!is.na() и paste к двоичным строкам, затем sub убрать все начальные 0 и, наконец, grepl для шаблона 01.

> (tmp <- apply(+!is.na(simp_2021[-1]), 1, paste, collapse=''))
[1] "1001" "0100" "0111" "1010" "1101"
> (tmp <- sub(tmp, pat='^0+', rep=''))
[1] "1001" "100"  "111"  "1010" "1101"
> (tmp <- grepl(tmp, pat='01'))
[1]  TRUE FALSE FALSE  TRUE  TRUE

В целом в хорошей трубке:

> transform(simp_2021, 
+           Breaks_in_Service=apply(+!is.na(simp_2021[-1]), 1, paste, collapse='') |> 
+             sub(pat='^0+', rep='') |> 
+             grepl(pat='01'))
  ID jan21_ORG_NAME feb21_ORG_NAME mar21_ORG_NAME apr21_ORG_NAME Breaks_in_Service
1  1          Org A           <NA>           <NA>          Org B              TRUE
2  2           <NA>          Org A           <NA>           <NA>             FALSE
3  3           <NA>          Org B          Org B          Org C             FALSE
4  4          Org B           <NA>          Org D           <NA>              TRUE
5  5          Org B          Org B           <NA>          Org E              TRUE

Примечание: этот подход также использует построчный apply, но для "matrix", для которого он предназначен и, следовательно, эффективен.

> is.matrix(+!is.na(simp_2021[-1]))
[1] TRUE

Данные:

> dput(simp_2021)
structure(list(ID = c(1, 2, 3, 4, 5), jan21_ORG_NAME = c("Org A", 
NA, NA, "Org B", "Org B"), feb21_ORG_NAME = c(NA, "Org A", "Org B", 
NA, "Org B"), mar21_ORG_NAME = c(NA, NA, "Org B", "Org D", NA
), apr21_ORG_NAME = c("Org B", NA, "Org C", NA, "Org E")), class = "data.frame", row.names = c(NA, 
-5L))

Другие вопросы по теме

Сохраните первый экземпляр столбца и удалите остальные, используя частичный текст в имени столбца
Как найти первую строку, соответствующую условиям маски для каждой группы?
Объедините два фрейма данных с масштабируемыми столбцами, разместив второй фрейм данных непосредственно под первым фреймом данных, не удаляя ключи
Применение strsplit() к data.frame приводит к неожиданному выводу
Более быстрый способ конвертировать результаты DuckDB в фрейм данных, который поддерживается потоком?
Как деструктурировать вложенные структуры в полярах (python api)?
Могу ли я выбрать столбцы в фрейме данных с помощью цикла for?
Как разбить массив, используя его минимальную запись
Поляры: замените части фрейма данных другими частями фрейма данных
Как разбить несколько списков, каждый из которых имеет случайную длину, а столбцы имеют случайные имена ключей?