R data.table 中缺失值的高效移位
Effcient shifting in R data.table with missing values
我有一个 data.table 具有以下三个变量
- receptionist = 接待人员id
- week.no = 本周的数字(1 表示第一周)
- absent.thisweek = 本周缺席天数
请注意,我们只有接待员来上班的那几周的数据,如果缺少一周,则表示该人该周不在
例如,我们有两个接待处 1 和 2,工作模式如下:
dt <- data.table(receptionist = c(1,1,1,2,2,2,2), week.no = c(1,3,4,5,8,10,11), absent.thisweek = c(1,2,3,4,5,6,6))
>dt receptionist week.no absent.thisweek
1: 1 1 1
2: 1 3 2
3: 1 4 3
4: 2 5 4
5: 2 8 5
6: 2 10 6
7: 2 11 6
第 1 步:我需要找出下周接待员缺席的天数,为此我将 absent.thisweek 移动一周(如果该信息在数据中可用),请注意这几周接待员还没来,这个值将是 NA,我的数据集有 100 万行,这是效率最低的部分。
dt[order(receptionist, week.no), absent.nextweek := dt$absent.thisweek[dt$receptionist==receptionist & dt$week.no==week.no+1], by = .(receptionist, week.no)]
>dt receptionist week.no absent.thisweek absent.nextweek
1: 1 1 1 NA
2: 1 3 2 3
3: 1 4 3 NA
4: 2 5 4 NA
5: 2 8 5 NA
6: 2 10 6 6
7: 2 11 6 NA
第 2 步:如果数据缺失,则必须缺席 7 天。在上周,我们将有 absent.nextweek = NA
dt[is.na(absent.nextweek) & week.no != max(week.no, na.rm=T), absent.nextweek:=7]
>dt receptionist week.no absent.thisweek absent.nextweek
1: 1 1 1 7
2: 1 3 2 3
3: 1 4 3 7
4: 2 5 4 7
5: 2 8 5 7
6: 2 10 6 6
7: 2 11 6 NA
我面临两个问题:
-在第 1 步中,它非常低效并且需要很长时间(大约一个小时)才能 运行
- 在步骤 2 中,第 3 行的最后一列应该有 NA,但事实并非如此
任何提高效率的建议(最好是data.table)都会非常有帮助并指出步骤2中的错误。
正确答案是:
>dt receptionist week.no absent.thisweek absent.nextweek
1: 1 1 1 7
2: 1 3 2 3
3: 1 4 3 NA
4: 2 5 4 7
5: 2 8 5 7
6: 2 10 6 6
7: 2 11 6 NA
从头开始思考这个问题,也许你可以直接用 fifelse()
和 shift()
?
# We assume data is ordered by week. Otherwise you can run
# setorder(dt, receptionist, week.no)
dt[,
absent.nextweek :=
fifelse(week.no+1L == shift(week.no, -1L), shift(absent.thisweek, -1L), 7),
by = receptionist]
# receptionist week.no absent.thisweek absent.nextweek
# 1: 1 1 1 7
# 2: 1 3 2 3
# 3: 1 4 3 NA
# 4: 2 5 4 7
# 5: 2 8 5 7
# 6: 2 10 6 6
# 7: 2 11 6 NA
我有一个 data.table 具有以下三个变量
- receptionist = 接待人员id
- week.no = 本周的数字(1 表示第一周)
- absent.thisweek = 本周缺席天数
请注意,我们只有接待员来上班的那几周的数据,如果缺少一周,则表示该人该周不在
例如,我们有两个接待处 1 和 2,工作模式如下:
dt <- data.table(receptionist = c(1,1,1,2,2,2,2), week.no = c(1,3,4,5,8,10,11), absent.thisweek = c(1,2,3,4,5,6,6))
>dt receptionist week.no absent.thisweek
1: 1 1 1
2: 1 3 2
3: 1 4 3
4: 2 5 4
5: 2 8 5
6: 2 10 6
7: 2 11 6
第 1 步:我需要找出下周接待员缺席的天数,为此我将 absent.thisweek 移动一周(如果该信息在数据中可用),请注意这几周接待员还没来,这个值将是 NA,我的数据集有 100 万行,这是效率最低的部分。
dt[order(receptionist, week.no), absent.nextweek := dt$absent.thisweek[dt$receptionist==receptionist & dt$week.no==week.no+1], by = .(receptionist, week.no)]
>dt receptionist week.no absent.thisweek absent.nextweek
1: 1 1 1 NA
2: 1 3 2 3
3: 1 4 3 NA
4: 2 5 4 NA
5: 2 8 5 NA
6: 2 10 6 6
7: 2 11 6 NA
第 2 步:如果数据缺失,则必须缺席 7 天。在上周,我们将有 absent.nextweek = NA
dt[is.na(absent.nextweek) & week.no != max(week.no, na.rm=T), absent.nextweek:=7]
>dt receptionist week.no absent.thisweek absent.nextweek
1: 1 1 1 7
2: 1 3 2 3
3: 1 4 3 7
4: 2 5 4 7
5: 2 8 5 7
6: 2 10 6 6
7: 2 11 6 NA
我面临两个问题: -在第 1 步中,它非常低效并且需要很长时间(大约一个小时)才能 运行 - 在步骤 2 中,第 3 行的最后一列应该有 NA,但事实并非如此
任何提高效率的建议(最好是data.table)都会非常有帮助并指出步骤2中的错误。
正确答案是:
>dt receptionist week.no absent.thisweek absent.nextweek
1: 1 1 1 7
2: 1 3 2 3
3: 1 4 3 NA
4: 2 5 4 7
5: 2 8 5 7
6: 2 10 6 6
7: 2 11 6 NA
从头开始思考这个问题,也许你可以直接用 fifelse()
和 shift()
?
# We assume data is ordered by week. Otherwise you can run
# setorder(dt, receptionist, week.no)
dt[,
absent.nextweek :=
fifelse(week.no+1L == shift(week.no, -1L), shift(absent.thisweek, -1L), 7),
by = receptionist]
# receptionist week.no absent.thisweek absent.nextweek
# 1: 1 1 1 7
# 2: 1 3 2 3
# 3: 1 4 3 NA
# 4: 2 5 4 7
# 5: 2 8 5 7
# 6: 2 10 6 6
# 7: 2 11 6 NA