Я работаю с набором данных, где каждая строка показывает, где человек использовал услуги. Это также неявно отслеживает, использует ли кто-то услуги, поскольку в противном случае значение столбца за месяц будет NA
. Я хочу выявить случаи перерывов (периоды отсутствия с последующим возвращением) в службе отдельного лица на основе переходов между последовательными столбцами месяцев.
Другими словами, меня особенно интересует определение того, когда кто-то переходит от использования услуг (значения в столбце месяца) к отказу от использования услуг (значение 1 + последующий месяц равно NA
), а затем снова к использованию услуг (значения в 1 + столбцы последующих месяцев, которым предшествуют NA
s). Я надеюсь, что у меня будет двоичный столбец 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)
Для какого-то вектора 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
Сначала вы можете приравнять столбцы месяца к двоичным !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))
Это сработало как шарм, и обозначения/объяснения были очень полезны. Большое спасибо!