我想创建一个"自诊断时间"变量,该变量以我的数据中的其他两个现有变量为条件。
下面是一些示例数据:
id <- c("0001", "0001", "0001", "0002", "0002", "0002", "0003", "0003", "0003", "0003")
dementia <- c(0, 0, 1, 0, 1, 1, 0, 1, 0, 1)
age_visit <- c("80", "81", "82","50", "51", "52","60", "61", "62", "63")
ds <- data.frame(id, dementia, age_visit)
我在长格式数据集中有一个二进制诊断变量dementia
。
如下所示:
id dementia age_visit
1 0001 0 80
2 0001 0 81
3 0001 1 82
4 0002 0 50
5 0002 1 51
6 0002 1 52
7 0003 0 60
8 0003 1 61
9 0003 0 62
我想要一个age_at_diagnosis
变量,当它第一次等于1时,为痴呆诊断的第一个实例粘贴age_visit
。如果有一种方法可以直接跳到最后一步,也就是第一次诊断后的时间,那么这一步就不重要了。主要的问题是,个人可能被诊断出来,然后进行另一项无效的评估。我想要第一个病例,然后从第一次评估到诊断的时间。
所以最终结果看起来像这样,time_sincedx
是age_visit - age_at_dx
:
id dementia age_visit age_at_dx time_sincedx
1 0001 0 80 NA NA
2 0001 0 81 NA NA
3 0001 1 82 82 0
4 0002 0 50 NA NA
5 0002 1 51 51 0
6 0002 1 52 51 1
7 0003 0 60 NA NA
8 0003 1 61 61 0
9 0003 0 62 61 1
10 0003 1 63 61 2
是否有办法做到这一点与dplyr?我试过了,但不太对。它在每次情况下粘贴每个年龄,在time_since_dx
列下留下0。
df <- mutate(df, age_at_dx = ifelse(dementia==1, age_at_visit, NA))
df$time_sincedx<- df$age_at_visit - df$age_atdx
任何想法都非常感谢!
一个小子集和tidyr::fill
来处理多余的NA
值会让你到达那里:
library(tidyverse)
ds %>% group_by(id) %>% # evaluate patients individually
mutate(age_visit = as.integer(as.character(age_visit)), # factor to integer
# if no dementia, NA else min age where dementia == 1
age_at_dx = ifelse(dementia == 0, NA, min(age_visit[dementia == 1]))) %>%
fill(age_at_dx) %>% # fill in NAs after non-NA (where dx == 1, then 0 like line 9)
mutate(time_since_dx = age_visit - age_at_dx)
## Source: local data frame [10 x 5]
## Groups: id [3]
##
## id dementia age_visit age_at_dx time_since_dx
## <fctr> <dbl> <int> <int> <int>
## 1 0001 0 80 NA NA
## 2 0001 0 81 NA NA
## 3 0001 1 82 82 0
## 4 0002 0 50 NA NA
## 5 0002 1 51 51 0
## 6 0002 1 52 51 1
## 7 0003 0 60 NA NA
## 8 0003 1 61 61 0
## 9 0003 0 62 61 1
## 10 0003 1 63 61 2
或跳过age_at_dx
列,
ds %>% group_by(id) %>%
mutate(age_visit = as.integer(as.character(age_visit)),
time_since_dx = age_visit - min(age_visit[dementia == 1]),
time_since_dx = ifelse(time_since_dx < 0, NA, time_since_dx)) # make negatives NA
## Source: local data frame [10 x 4]
## Groups: id [3]
##
## id dementia age_visit time_since_dx
## <fctr> <dbl> <int> <int>
## 1 0001 0 80 NA
## 2 0001 0 81 NA
## 3 0001 1 82 0
## 4 0002 0 50 NA
## 5 0002 1 51 0
## 6 0002 1 52 1
## 7 0003 0 60 NA
## 8 0003 1 61 0
## 9 0003 0 62 1
## 10 0003 1 63 2
这里有另一种方法。首先,我将age_visit
转换为整数。然后,我将数据按id
分组。我使用索引创建了age_at_dx
,用于逻辑检查。我使用which()
确定了痴呆== 1出现的第一行(行号)。任何小于该行号的行号都应该是NA。其余行应该具有标识行中的数字。此逻辑用于创建age_at_dx
。然后,我使用另一个逻辑检查创建time_sincedx
。在这种情况下,我检查了age_at_dx
的每个元素是否为NA。如果元素不是NA,则在time_sincedx
中使用cumsum()
创建索引号。否则,我在time_sincedx
中创建NA。
library(dplyr)
mutate(ds, age_visit = as.integer(as.character(age_visit))) %>%
group_by(id) %>%
mutate(age_at_dx = if_else(row_number() < which(dementia == 1)[1],
NA_integer_, age_visit[dementia == 1][1]),
time_sincedx = if_else(!is.na(age_at_dx), cumsum(!is.na(age_at_dx))-1, NA_real_))
# id dementia age_visit age_at_dx time_sincedx
# <fctr> <dbl> <int> <int> <dbl>
#1 0001 0 80 NA NA
#2 0001 0 81 NA NA
#3 0001 1 82 82 0
#4 0002 0 50 NA NA
#5 0002 1 51 51 0
#6 0002 1 52 51 1
#7 0003 0 60 NA NA
#8 0003 1 61 61 0
#9 0003 0 62 61 1
#10 0003 1 63 61 2
在这个解决方案中没有什么花哨的技巧:只有几个透明的split, apply, combine方法。
第一步是确保您收到的数据集被正确安排,以便在遗漏的诊断年份稍后进行处理。然后,我们基本上填补了缺失的年份:检查落后于前一年阳性诊断的无效诊断。该逻辑计算出,如果当年的诊断为Null,而同一患者ID在前一个就诊年份的诊断为阳性,则将该阳性诊断值在dementiaCorr列中向下交换。对这个逻辑的警告是,它只能覆盖一年的空白:注意-我非常好奇调查tidyr::fill()的功能[顺便说一句:谢谢你,@alistaire帮助我发现这一点!)
则,在填补诊断空白后,我们可以根据诊断的最小年份进行分组并收集。之后,我们将数据绑定到完整的清单中,按照指示计算持续时间,并根据最终表达式的需要选择/排序数据帧。
df <-
ds %>%
arrange(id, age_visit) %>%
mutate(dementiaCorr = ifelse((lag(id)==id)&lag(dementia == 1)|dementia == 1, 1, 0)) %>%
group_by(id) %>%
filter(dementiaCorr == 1) %>%
mutate(age_at_dx = min(as.integer(age_visit))) %>%
select(-dementia) %>%
right_join(ds, by = c('id', 'age_visit')) %>%
mutate(time_sincedx = as.integer(age_visit)-as.integer(age_at_dx)) %>%
select(id, dementia, age_visit, age_at_dx, time_sincedx)
或者,您可以切换操作,并以更接近您在问题中向我们介绍的数据开发进程的顺序处理数据。
在这种方法中,我们再次从确保数据正确排列以填补空白开始。然后我们分组并选出诊断的最小年龄。在对其进行修剪并将其连接回原始数据集之后,我们删除了由于数据集中出现的模糊诊断值而产生的重复。然后我们填补空白:警告是1年的空白,除非你重新执行空白填补。最后,根据整数转换值计算自第一次诊断以来经过的时间,然后选择的列是order/selected。
df <-
ds %>%
arrange(id, age_visit) %>%
filter(dementia == 1) %>%
mutate(minageofDx = age_visit) %>%
group_by(id) %>%
mutate(agedxPrep = min(minageofDx)) %>%
select(id, dementia, agedxPrep) %>%
right_join(ds) %>% ungroup %>% distinct %>%
mutate(age_at_dx = ifelse(is.na(agedxPrep) & (lag(id)==id) & lag(dementia == 1), # Conditional
lag(agedxPrep), agedxPrep), # trueVal, falseVal
time_sincedx = as.integer(age_visit)-as.integer(age_at_dx)) %>%
select(id, dementia, age_visit, age_at_dx, time_sincedx)
希望这些简单、直接的方法及其开放、扁平的逻辑可能比更高级编程风格的嵌套和复杂的代码对您更有帮助。