我正在使用一个数据集,其中每一行代表个人使用服务的位置。这还隐式地跟踪某人是否使用服务,因为如果他们没有使用服务,则该月的列值为
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)
预期输出: 在示例数据中,对于 ID 1、4 和 5,Breaks_in_Service 应为
TRUE
,对于 ID 2 和 3,则 Breaks_in_Service 应为 FALSE
。
我尝试构建一个
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
您希望为每个 ID 执行此操作。一种方法是在每一行上使用
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()
并创建二进制字符串,接下来 sub
去掉所有起始 1
,最后 grepl
用于模式 10
。
> (tmp <- apply(+is.na(simp_2021[-1]), 1, paste, collapse=''))
[1] "01100" "10110" "10000" "01010" "00100"
> (tmp <- sub(tmp, pat='^1+', rep=0))
[1] "01100" "00110" "00000" "01010" "00100"
> (tmp <- grepl(tmp, pat='10'))
[1] TRUE TRUE FALSE TRUE TRUE
完全在一个漂亮的管道中。
> transform(simp_2021,
+ Breaks_in_Service=apply(+is.na(simp_2021[-1]), 1, paste, collapse='') |>
+ sub(pat='^1+', rep=0) |>
+ grepl(pat='10'))
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> TRUE
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))