В таблице данных ниже я хочу пометить первую строку каждой группой.
temp_dt <- data.table(date = as.Date(c("2000-01-01","2000-03-31","2000-07-01","2000-09-30",
"2001-01-01","2001-03-31","2001-07-01","2001-09-30",
"2000-01-01","2000-03-31","2000-07-01","2000-09-30",
"2001-01-01","2001-03-31","2001-07-01","2001-09-30",
"2000-01-01","2000-03-31","2000-07-01","2000-09-30",
"2001-01-01","2001-03-31","2001-07-01","2001-09-30")),
group = c(1,1,1,1,1,1,1,1,
2,2,6,6,6,8,8,8,
3,3,3,3,4,4,4,4))
Ниже приведен ожидаемый результат после добавления флага.
> temp_dt
date group flag
1: 2000-01-01 1 1
2: 2000-03-31 1 0
3: 2000-07-01 1 0
4: 2000-09-30 1 0
5: 2001-01-01 1 0
6: 2001-03-31 1 0
7: 2001-07-01 1 0
8: 2001-09-30 1 0
9: 2000-01-01 2 1
10: 2000-03-31 2 0
11: 2000-07-01 6 1
12: 2000-09-30 6 0
13: 2001-01-01 6 0
14: 2001-03-31 8 1
15: 2001-07-01 8 0
16: 2001-09-30 8 0
17: 2000-01-01 3 1
18: 2000-03-31 3 0
19: 2000-07-01 3 0
20: 2000-09-30 3 0
21: 2001-01-01 4 1
22: 2001-03-31 4 0
23: 2001-07-01 4 0
24: 2001-09-30 4 0
date group flag
Вот решение, которое я пробовал (это быстро), но оно не работает должным образом.
temp_dt[, flag := if (identical(.I, 1)) 1 else 0, by = .(group)]
Поскольку я имею дело с миллионами строк, ключевым фактором решения является производительность. Я ищу только решение data.table.
Другие решения, представленные на SO, слишком медленные для моего требования.
Вы можете использовать функцию rowid
. Это даст добавочный индекс, начинающийся с 1
, для каждой группы, определяемой данной переменной (переменными) группировки. Обнаружение первой строки выполняется просто путем сравнения с 1
.
temp_dt[, flag := rowid(group)==1]
В качестве бонуса для обнаружения последней строки группы (.N обозначает количество строк текущей группы)
temp_dt[, flag := rowid(group)==.N]
Я согласен. Типа ответ в процессе. Отредактировано до окончательного ответа. Спасибо
Этот способ находит самую раннюю строку по дате для каждой группы, а затем устанавливает flag == 1
для этих строк.
temp_dt[temp_dt[, .I[date == min(date)], by = .(group)]$V1, flag := 1]
# set the rest of the column to 0
temp_dt[is.na(flag), flag := 0]
Вы можете решить свою проблему следующим образом:
temp_dt[, flag := +!duplicated(group)]
# or
temp_dt[, flag := match(seq_len(.N), 1, 0), by=group]
date group flag
1: 2000-01-01 1 1
2: 2000-03-31 1 0
3: 2000-07-01 1 0
4: 2000-09-30 1 0
5: 2001-01-01 1 0
6: 2001-03-31 1 0
7: 2001-07-01 1 0
8: 2001-09-30 1 0
9: 2000-01-01 2 1
10: 2000-03-31 2 0
11: 2000-07-01 6 1
12: 2000-09-30 6 0
13: 2001-01-01 6 0
14: 2001-03-31 8 1
15: 2001-07-01 8 0
16: 2001-09-30 8 0
17: 2000-01-01 3 1
18: 2000-03-31 3 0
19: 2000-07-01 3 0
20: 2000-09-30 3 0
21: 2001-01-01 4 1
22: 2001-03-31 4 0
23: 2001-07-01 4 0
24: 2001-09-30 4 0
Хотя я считаю, что в конечном итоге принятый ответ является лучшим, мне очень нравятся оба этих варианта.
Я думаю, что Billy34 использует
rowid(group)
лучше всего, но буквальное улучшение вашегоif
кода будетtemp_dt[, flag := +(seq_len(.N) == 1), by = group]
; специальная переменная.I
не учитывает группы, как это видно на примереtemp_dt[, i := .I, by = group]
.