Суммирование по строкам с несколькими меняющимися условиями R data.table
Я пытаюсь создать столбец в data.frame
или же data.table
с двумя условиями. Отличие от постов, которые я видел и которые я пытался изменить ниже, состоит в том, что у меня нет "значения" для условий, но условия зависят от других переменных в data.frame
,
Давайте предположим, что это мой фрейм данных:
mydf <- data.frame (Year = c(2000, 2001, 2002, 2004, 2005,
2007, 2000, 2001, 2002, 2003,
2003, 2004, 2005, 2006, 2006, 2007),
Name = c("Tom", "Tom", "Tom", "Fred", "Gill",
"Fred", "Gill", "Gill", "Tom", "Tom",
"Fred", "Fred", "Gill", "Fred", "Gill", "Gill"))
Я хочу узнать, сколько раз 3 субъекта испытывали событие за последние 5 лет. Тем не менее, если даты проведения мероприятия превышают 5 лет, я не хочу его включать. Я думал, что мог бы сделать сумму переменной индикатора (установленной в 1, если субъект испытал событие в году), указав что-то вроде Year < Year & Year >= Year-5
, Таким образом, в основном суммируйте события за год, меньший, чем целевой год, и превышающий или равный 5 годам, предшествующим целевому году.
У меня есть индикатор для суммирования и переменная для целевого года - 5
mydf$Ind <- 1
mydf$Yearm5 <- mydf$Year-5
Затем я конвертирую в таблицу данных для скорости (исходный df имеет +60k obs)
library(data.table)
mydf <- data.table(mydf)
Проблема в том, что я не могу заставить эти два условия работать. Пост, который я видел, кажется, все знает конкретное значение для поднабора (например, поднабор R data.table для нескольких условий.), Но в моем случае значение изменяется от наблюдения к наблюдению (не уверен, означает ли это, что мне нужно сделать какая-то петля?).
Я думал, что мне нужно что-то вроде:
mydf[, c("Exp"):= sum(Ind), by = c("Name")][Year < Year & Year >= Yearm5]
дает:
Empty data.table (0 rows) of 5 cols: Year,Name,Ind,Yearm5,Exp
Используя только одно условие
mydf1 <- mydf[, c("Exp"):= sum(Ind), by = c("Name")][Year >= Yearm5]
дает общий опыт, поэтому я предполагаю, что что-то не так с Year < Year
состояние.
Я не совсем уверен, что, хотя. Я также пытался изменить предложения в: как кумулятивно добавлять значения в один вектор в R, и опять же не повезло, что что-то не так с тем, как я задаю условия.
library(dplyr)
mytest1 <- mydf %>%
group_by(Name, Year) %>%
filter(Year < Year & Year >= Yearm5) %>%
mutate(Exp = sum(Ind))
Результат должен выглядеть следующим образом:
myresult <- data.frame (Year = c(2003, 2004, 2004, 2006,
2007, 2000, 2001, 2005,
2005, 2006, 2007, 2000,
2001, 2002, 2002, 2003),
Name = c("Fred", "Fred", "Fred", "Fred",
"Fred", "Gill", "Gill", "Gill",
"Gill", "Gill", "Gill", "Tom",
"Tom", "Tom", "Tom", "Tom"),
Ind = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
Exp = c(0, 1, 1, 3, 4, 0, 1, 1, 1, 2, 3, 0, 1, 2, 2, 4),
Yearm5 = c(1998, 1999, 1999, 2001, 2002,
1995, 1996, 2000, 2000, 2001,
2002, 1995, 1996, 1996, 1997, 1998))
Любая помощь или указатели будут оценены!
3 ответа
Вот подход с использованием rollapply
а также data.table
library(zoo)
setDT(mydf)
setkey(mydf, Name,Year)
# create a data.table that has all Years and incidences including the 5 year window
# and sum up the number of incidences per year for each subject
m <- mydf[CJ(unique(Name),seq(min(Year)-5, max(Year))),allow.cartesian=TRUE][,
list(Ind = unique(Ind), I2 = sum(Ind,na.rm=TRUE)),
keyby=list(Name,Year)]
# use rollapply over this larger data.table to get the number of
# incidences in the previous 5 years (not including this year (hence head(x,-1))
m[,Exp := rollapply(I2, 5, function(x) sum(head(x,-1)),
align = 'right', fill=0),by=Name]
# join with the original to create your required data
m[mydf,!c('I2'),with=FALSE]
Name Year Ind Exp
# 1: Fred 2003 1 0
# 2: Fred 2004 1 1
# 3: Fred 2004 1 1
# 4: Fred 2006 1 3
# 5: Fred 2007 1 4
# 6: Gill 2000 1 0
# 7: Gill 2001 1 1
# 8: Gill 2005 1 1
# 9: Gill 2005 1 1
# 10: Gill 2006 1 2
# 11: Gill 2007 1 3
# 12: Tom 2000 1 0
# 13: Tom 2001 1 1
# 14: Tom 2002 1 2
# 15: Tom 2002 1 2
# 16: Tom 2003 1 4
Вот еще data.table
подход, используя roll
,
setDT(mydf)
# this is our desired end point
boundary = mydf[, list(Name, year.end = Year + 4)]
# set the key for the following merges
setkey(mydf, Name, Year)
setkey(boundary, Name, year.end)
# add indices that will keep track of the positions to compute deltas
mydf[, idx := .I]
boundary[, idx := .I]
# merge, rolling to match the end correctly, and then subtract the indices
# to get the desired delta.
# Note that we need to unique data because of duplicates.
# Depending on data you may also need to add `allow.cartesian = TRUE`.
# Final note - in data.table <= 1.9.2 you should omit the `by = .EACHI` part.
mydf[unique(boundary)[unique(mydf), list(Exp = i.idx - idx),
roll = -Inf, by = .EACHI]]
# Year Name idx Exp
# 1: 2003 Fred 1 0
# 2: 2004 Fred 2 1
# 3: 2004 Fred 3 1
# 4: 2006 Fred 4 3
# 5: 2007 Fred 5 4
# 6: 2000 Gill 6 0
# 7: 2001 Gill 7 1
# 8: 2005 Gill 8 1
# 9: 2005 Gill 9 1
#10: 2006 Gill 10 2
#11: 2007 Gill 11 3
#12: 2000 Tom 12 0
#13: 2001 Tom 13 1
#14: 2002 Tom 14 2
#15: 2002 Tom 15 2
#16: 2003 Tom 16 4
С data.table
Я думаю, синтаксис, который вы ищете, должен быть таким:
setDT(mydf)
mydf[ , Exp := rank(x=Year,ties.method="min")-1, by=Name]