我有这个示例数据框
df <- data.frame(New=c("X2", "k5", "N30","N40", "K5", "S12", "K5", "K5"),
K_10=c(NA, NA, 3, 4,0,2,NA, NA),
K_11=c(NA, NA, NA, 4,0,3,NA, NA),
K_12=c(NA, 2, NA, NA,0,NA,NA,0),
K_13=c(0, 3, 5, NA,0,5,NA,NA),
K_14=c(NA, 3, 1, 2,10,10,NA,NA),
K_15=c(NA, 2, 3, 5,15,10,NA,2),
K_16=c(NA, 10, 1, 6,43,10,NA,56),
K_17=c(NA, 5, 1, 3,1,10,NA,23),
K_18=c(NA, 6, 4, 2,0,10,NA,12),
K_19=c(NA, 3, 8, NA,3,10,NA,90),
K_20=c(NA, 3, 19, 2,6,10,NA,59),
K_21=c(NA, 3, 10, 2,8,10,NA,11),
K_22=c(NA, 3, NA, 2,9,10,NA,10),
K_23=c(NA, 3, NA, 2,90,10,NA,9))
df
New K_10 K_11 K_12 K_13 K_14 K_15 K_16 K_17 K_18 K_19 K_20 K_21 K_22 K_23
1 X2 NA NA NA 0 NA NA NA NA NA NA NA NA NA NA
2 k5 NA NA 2 3 3 2 10 5 6 3 3 3 3 3
3 N30 3 NA NA 5 1 3 1 1 4 8 19 10 NA NA
4 N40 4 4 NA NA 2 5 6 3 2 NA 2 2 2 2
5 K5 0 0 0 0 10 15 43 1 0 3 6 8 9 90
6 S12 2 3 NA 5 10 10 10 10 10 10 10 10 10 10
7 K5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA
8 K5 NA NA 0 NA NA 2 56 23 12 90 59 11 10 9
我想创建一个列,逐行检查从 K_11 到 K_18 是否有至少 6 个连续的非 NA
此外,我想创建第二列,仅当 New=k5 时,该列才会逐行检查从 K_11 到 K_18 是否存在至少 6 个连续的非 NA
最后,我想创建第三列,仅当该行中至少有 6 个连续的非 Na 时,才按行计算从 K_11 到 K_18 有多少个 Na
所以我的输出应该是这个
df_new <- data.frame(New=c("X2", "k5", "N30","N40", "K5", "S12", "K5", "K5"),
K_10=c(NA, NA, 3, 4,0,2,NA, NA),
K_11=c(NA, NA, NA, 4,0,3,NA, NA),
K_12=c(NA, 2, NA, NA,0,NA,NA,0),
K_13=c(0, 3, 5, NA,0,5,NA,NA),
K_14=c(NA, 3, 1, 2,10,10,NA,NA),
K_15=c(NA, 2, 3, 5,15,10,NA,2),
K_16=c(NA, 10, 1, 6,43,10,NA,56),
K_17=c(NA, 5, 1, 3,1,10,NA,23),
K_18=c(NA, 6, 4, 2,0,10,NA,12),
K_19=c(NA, 3, 8, NA,3,10,NA,90),
K_20=c(NA, 3, 19, 2,6,10,NA,59),
K_21=c(NA, 3, 10, 2,8,10,NA,11),
K_22=c(NA, 3, NA, 2,9,10,NA,10),
K_23=c(NA, 3, NA, 2,90,10,NA,9),
Con_11_18=c(FALSE, TRUE, TRUE, FALSE,TRUE,TRUE,FALSE,FALSE),
Con_11_18_New=c("Not applicable", TRUE, "Not applicable", "Not applicable",TRUE, "Not applicable","FALSE","FALSE"),
count_NA_11_18_New=c("Not applicable", 1,2, 2,0,1,"Not applicable","Not applicable"))
df_new
df_new
New K_10 K_11 K_12 K_13 K_14 K_15 K_16 K_17 K_18 K_19 K_20 K_21 K_22 K_23 Con_11_18 Con_11_18_New count_NA_11_18_New
1 X2 NA NA NA 0 NA NA NA NA NA NA NA NA NA NA FALSE Not applicable Not applicable
2 k5 NA NA 2 3 3 2 10 5 6 3 3 3 3 3 TRUE TRUE 1
3 N30 3 NA NA 5 1 3 1 1 4 8 19 10 NA NA TRUE Not applicable 2
4 N40 4 4 NA NA 2 5 6 3 2 NA 2 2 2 2 FALSE Not applicable 2
5 K5 0 0 0 0 10 15 43 1 0 3 6 8 9 90 TRUE TRUE 0
6 S12 2 3 NA 5 10 10 10 10 10 10 10 10 10 10 TRUE Not applicable 1
7 K5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA FALSE FALSE Not applicable
8 K5 NA NA 0 NA NA 2 56 23 12 90 59 11 10 9 FALSE FALSE Not applicable
例如,在第 2 行中,我有至少 6 个连续的非 NA 值,从 K_11 到 K18,因此列“Con_11_18”的值为“TRUE”。对于第一行,情况并非如此,因此该列的值为“FALSE”。
“Con_11_18_New”列是“con_11_18”列的重复,唯一的区别是它仅关注 New=k5 的行,而对于所有其他行,它取值“不适用”。
第三行仅关注 K_11 和 K_18 之间至少有 6 个连续非 Na 的行,并计算该间隔内的 NA。例如,在第 2 行中,我们有至少 6 个连续非 Na,在 K_11 和 K_18 之间有 1 个 NA。在第 3 行中,我们有 2 个这样的 NA。对于没有 6 个连续非 Na 的行,它们被分配值“不适用”
我知道我问的问题有点难,但如果你能提供一些通用代码就太好了。请注意,我的真实数据集包含数千行。
说实话,我甚至不知道如何开始解决这个问题,所以我需要你的帮助
最佳答案
3
这是一个基本的 R 方法:
fn <- function(x){
idx <- !is.na(x)
max(rle(diff(cumsum(idx)))$lengths) * (max(idx)>0)
}
nms <- names(df)
d <- df[, which(nms == 'K_11') : which(nms == 'K_18')]
within(df,
{Con_11_18 <- apply(d , 1, fn) >= 6
count_NA_11_18_New <- rowSums(is.na(d))
Con_11_18_New <- ifelse(tolower(df$New)== 'k5', Con_11_18, 'not applicable')
count_NA_11_18_New <- ifelse(Con_11_18, count_NA_11_18_New, 'not applicable')}
)
New K_10 K_11 K_12 K_13 K_14 K_15 K_16 K_17 K_18 K_19 K_20 K_21 K_22 K_23 Con_11_18_New count_NA_11_18_New Con_11_18
1 X2 NA NA NA 0 NA NA NA NA NA NA NA NA NA NA not applicable not applicable FALSE
2 k5 NA NA 2 3 3 2 10 5 6 3 3 3 3 3 TRUE 1 TRUE
3 N30 3 NA NA 5 1 3 1 1 4 8 19 10 NA NA not applicable 2 TRUE
4 N40 4 4 NA NA 2 5 6 3 2 NA 2 2 2 2 not applicable not applicable FALSE
5 K5 0 0 0 0 10 15 43 1 0 3 6 8 9 90 TRUE 0 TRUE
6 S12 2 3 NA 5 10 10 10 10 10 10 10 10 10 10 not applicable 1 TRUE
7 K5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA FALSE not applicable FALSE
8 K5 NA NA 0 NA NA 2 56 23 12 90 59 11 10 9 FALSE not applicable FALSE
以下是 tidyverse 的相同方法:
df %>%
mutate( Con_11_18 = matrixStats::colCumsums(t(!is.na(pick(K_11:K_18))))|>
apply(2, \(x)max(rle(diff(x))$lengths) * (max(x)>0)) >= 6,
Con_11_18_New = ifelse(tolower(New)== 'k5', Con_11_18, 'not applicable'),
count_NA_11_18_New = rowSums(is.na(pick(K_11:K_18))),
count_NA_11_18_New = ifelse(Con_11_18, count_NA_11_18_New, 'not applicable')
)
10
-
@Iroha 你确定吗?检查结果。你可以
sum
在找到滞后 1 的差异后使用它来确定连续的数字。再次检查代码
– -
@Iroha。问题不是出在 colSums 上,而是因为我没有去掉 0。让我更新解决方案,仍然使用相同的
– -
1@Iroha 问题不在于 colCumsums,而在于 colSums。感谢您的意见
– -
非常感谢 Onyambu。如果我想修改上述代码以获取最多 5 个连续的非 NA 值,该如何修改?我问这个问题是因为知道如何使用代码是件好事。再次感谢!
– -
@newfinder 将代码中的 6 改为 5
–
|
您可以尝试any
一起使用rle
。将布尔值TRUE
与字符串组合起来没有意义,只需使用布尔值即可。
> chk_con <- \(x, cols, min) {
+ any(with(rle(x), lengths[values == 'FALSE']) > min - 1L)
+ }
> df |>
+ transform(cns_11_18=apply(is.na(df[paste('K', 11:18, sep='_')]), 1, chk_con, min=6L))
New K_10 K_11 K_12 K_13 K_14 K_15 K_16 K_17 K_18 K_19 K_20 K_21 K_22 K_23 cns_11_18
1 X2 NA NA NA 0 NA NA NA NA NA NA NA NA NA NA FALSE
2 k5 NA NA 2 3 3 2 10 5 6 3 3 3 3 3 TRUE
3 N30 3 NA NA 5 1 3 1 1 4 8 19 10 NA NA TRUE
4 N40 4 4 NA NA 2 5 6 3 2 NA 2 2 2 2 FALSE
5 K5 0 0 0 0 10 15 43 1 0 3 6 8 9 90 TRUE
6 S12 2 3 NA 5 10 10 10 10 10 10 10 10 10 10 TRUE
7 K5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA FALSE
8 K5 NA NA 0 NA NA 2 56 23 12 90 59 11 10 9 FALSE
更新
> chk_upto <- \(x, cols, max) {
+ rl <- rle(x)
+ fls <- with(rl, lengths[!values])
+ if (length(fls)) {
+ all(fls > 1) & all(fls < max + 1L)
+ } else {
+ FALSE
+ }
+ }
> df2 |>
+ transform(cns_11_18=apply(is.na(df2[-1]), 1, chk_upto, max=6L))
New K_10 K_11 K_12 K_13 K_14 K_15 K_16 K_17 K_18 K_19 K_20 K_21 K_22 K_23 cns_11_18
1 X2 NA NA NA 0 NA NA NA NA NA NA NA NA NA NA FALSE
2 k5 2 2 2 3 3 2 2 5 6 3 3 3 3 3 FALSE
3 N30 3 NA NA 5 1 3 1 1 4 8 19 10 NA NA FALSE
4 N40 4 4 NA NA 2 5 6 3 2 2 NA 2 2 2 TRUE
5 K5 0 0 0 0 10 NA 43 1 NA 3 6 NA 9 90 TRUE
6 S12 2 3 NA 5 10 10 10 NA 10 10 10 10 NA 10 FALSE
7 K5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA FALSE
8 K5 NA NA 0 NA NA 2 56 23 12 90 59 11 10 9 FALSE
数据:
> dput(df2)
structure(list(New = c("X2", "k5", "N30", "N40", "K5", "S12",
"K5", "K5"), K_10 = c(NA, 2L, 3L, 4L, 0L, 2L, NA, NA), K_11 = c(NA,
2L, NA, 4L, 0L, 3L, NA, NA), K_12 = c(NA, 2L, NA, NA, 0L, NA,
NA, 0L), K_13 = c(0L, 3L, 5L, NA, 0L, 5L, NA, NA), K_14 = c(NA,
3L, 1L, 2L, 10L, 10L, NA, NA), K_15 = c(NA, 2L, 3L, 5L, NA, 10L,
NA, 2L), K_16 = c(NA, 2L, 1L, 6L, 43L, 10L, NA, 56L), K_17 = c(NA,
5L, 1L, 3L, 1L, NA, NA, 23L), K_18 = c(NA, 6L, 4L, 2L, NA, 10L,
NA, 12L), K_19 = c(NA, 3L, 8L, 2L, 3L, 10L, NA, 90L), K_20 = c(NA,
3L, 19L, NA, 6L, 10L, NA, 59L), K_21 = c(NA, 3L, 10L, 2L, NA,
10L, NA, 11L), K_22 = c(NA, 3L, NA, 2L, 9L, NA, NA, 10L), K_23 = c(NA,
3L, NA, 2L, 90L, 10L, NA, 9L)), class = "data.frame", row.names = c("1",
"2", "3", "4", "5", "6", "7", "8"))
9
-
非常感谢 Jay.sf。如果我想修改上述代码以获取最多 5 个连续的非 NA 值,该如何修改?我问这个问题是因为知道如何使用代码是件好事。再次感谢!
– -
@newfinder 您可以简单地使用
min=6L
。实际上,我加入了这个参数以使其更灵活,但忘了解释!
– -
您的意思是 min=5L?我试过了,但它给出的是“至少 5”。如果我想要最多 5 个连续的数值呢?
– -
@newfinder 不确定,目前不在机器上,也许你可以调整它来得到你想要的。
– -
如果你碰巧在机器旁,别忘了我。如果有必要,我会提出一个新问题!
–
|
我就是喜欢这类问题。我不知道它是否像第一个答案一样有效,但感觉相当透明。我刚刚从rle()
另一个问题中了解到这一点。
# Helper function
max.non.na.span <- function(x) {
# return max non-NA length <numeric>
# or return NA if all NA
if (all(is.na(x))) return(NA)
rle.res <- rle(!(is.na(x)))
non.na.lengths <- rle.res$lengths[rle.res$values]
max(non.na.lengths)
}
# Iterate through "New" column and a list of the 11->18 row
# slices of the data.frame simultaneously, returning a list of
# data.frame rows that we'll put together with rbind
new.df.cols <- base::Map(
df$New,
split(as.matrix(df[, paste0("K_", 11:18)]), row(df[, paste0("K_", 11:18)])),
f = \(x, y) {
Con_11_18 <- local({
max.non.na.len <- max.non.na.span(y)
if (is.na(max.non.na.len)) return(FALSE)
ifelse(max.non.na.len >= 6, TRUE, FALSE)
})
Con_11_18_New <- local({
if (tolower(x) %in% "k5") return(Con_11_18)
NA
})
count_NA_11_18_New <- local({
if (Con_11_18) return(sum(is.na(y)))
NA
})
data.frame(
Con_11_18 = Con_11_18,
Con_11_18_New = Con_11_18_New,
count_NA_11_18_New = count_NA_11_18_New
)
}
) |> do.call(rbind, args = _) |> base::transform(row.names = NULL)
df_new_new <- cbind(df, new.df.cols)
df_new_new
New K_10 K_11 K_12 K_13 K_14 K_15 K_16 K_17 K_18 K_19 K_20 K_21 K_22 K_23 Con_11_18 Con_11_18_New count_NA_11_18_New
1 X2 NA NA NA 0 NA NA NA NA NA NA NA NA NA NA FALSE NA NA
2 k5 NA NA 2 3 3 2 10 5 6 3 3 3 3 3 TRUE TRUE 1
3 N30 3 NA NA 5 1 3 1 1 4 8 19 10 NA NA TRUE NA 2
4 N40 4 4 NA NA 2 5 6 3 2 NA 2 2 2 2 FALSE NA NA
5 K5 0 0 0 0 10 15 43 1 0 3 6 8 9 90 TRUE TRUE 0
6 S12 2 3 NA 5 10 10 10 10 10 10 10 10 10 10 TRUE NA 1
7 K5 NA NA NA NA NA NA NA NA NA NA NA NA NA NA FALSE FALSE NA
8 K5 NA NA 0 NA NA 2 56 23 12 90 59 11 10 9 FALSE FALSE NA
|
|