You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取数据框每行首尾非NA值的列名及日期间隔

解决方法:提取每个用户的首尾非NA日期及间隔

首先注意你的数据框里有重复的列名2012-02-01,这会导致R自动重命名列(比如变成2012-02-01.1),建议先修正列名,以下示例将第三个日期列改为2012-03-01:

# 构建示例数据框
df <- data.frame(
  `User ID` = c("Cell 1", "Cell 3"),
  `2012-01-01` = c(NA, 1),
  `2012-02-01` = c(2, NA),
  `2012-03-01` = c(NA, 5),
  check.names = FALSE  # 保留原始日期列名
)

基础R实现

先提取日期列名称,再逐行计算首尾非NA对应的日期:

# 获取日期列的名称集合
date_cols <- colnames(df)[-1]

# 提取首个非NA值对应的日期
df$first_date <- apply(df[-1], 1, function(x) {
  non_na_pos <- which(!is.na(x))
  # 无NA值时返回NA,否则取对应位置的列名
  if (length(non_na_pos) == 0) NA else date_cols[min(non_na_pos)]
})

# 提取末个非NA值对应的日期
df$last_date <- apply(df[-1], 1, function(x) {
  non_na_pos <- which(!is.na(x))
  if (length(non_na_pos) == 0) NA else date_cols[max(non_na_pos)]
})

# 计算日期间隔(转成Date类型后计算)
df$date_diff <- as.Date(df$last_date) - as.Date(df$first_date)

运行后结果:

User ID 2012-01-01 2012-02-01 2012-03-01 first_date last_date date_diff
1  Cell 1         NA          2         NA 2012-02-01 2012-02-01    0 days
2  Cell 3          1         NA          5 2012-01-01 2012-03-01   59 days

你的代码问题分析

  • 第一段代码:x[which.min(which(is.na(x) == FALSE))] 取的是非NA值本身,不是列名;且which.min多余——which(!is.na(x))直接返回非NA值的位置,取min()就是第一个非NA的位置。
  • 第二段代码:x是行向量,没有列名属性,colnames(x)会返回NULL,应该从原数据框的date_cols里提取对应位置的名称。

tidyverse实现(简洁直观)

用宽转长的方式处理,适合大规模数据:

library(tidyverse)

result <- df %>%
  # 宽表转长表,过滤NA值
  pivot_longer(-`User ID`, names_to = "date", values_to = "value", values_drop_na = TRUE) %>%
  mutate(date = as.Date(date)) %>%
  # 按用户分组计算首尾日期和间隔
  group_by(`User ID`) %>%
  summarise(
    first_date = as.character(min(date)),
    last_date = as.character(max(date)),
    date_diff = max(date) - min(date)
  ) %>%
  # 合并回原数据框,保留无NA值的用户
  right_join(df, by = "User ID")

内容的提问来源于stack exchange,提问作者Nat Guess

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 23:30:47