我有这个示例数据框

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