Пометить каждый n-й элемент по группе в data.table

avatar
TBP
1 июля 2021 в 18:55
90
3
0

Мои данные состоят из набора наблюдений, сделанных в разных группах. Для каждой группы имеется разное количество наблюдений. Я хотел бы создать переменную, которая помечает наблюдение цифрой «1» для дальнейшего ручного контроля качества/контроля качества. Флаги должны располагаться через равные промежутки внутри группы, но этот интервал может различаться между двумя группами. Интервал получается путем деления длины каждой группы на константу (5 в этом примере).

Данные будут выглядеть примерно так:

dt<-data.table(places=c(rep("A",10), rep("B",20))) #the data
dt2<-data.table(places=c("A","B"), spacing=c(2,4)) #the spacings by group to apply to the data

Затем применяется некоторый код для создания маркировки (или последовательности)

dt$sequence<- ????

Выглядит так:

places  sequence
A       1
A   
A       1
A   
...
B       1
B   
B   
B   

По сути, я хочу, чтобы каждая группа "отсчитывалась" на основе идеального интервала, который был определен для этой группы, и сохраняла только "1" каждый раз, когда подсчет повторяется. Я просто не уверен, как передать data.table эту комбинацию интервалов и групп.

Источник
Greg
1 июля 2021 в 19:06
1

Это просто с dplyr, но мне любопытно, какие точные правила вы используете для определения интервала. Если длина столбца не делится на соответствующее spacing, вы хотите округлить до ближайшего целого числа или округлить до ближайшего целого числа... или что-то еще? Не могли бы вы предоставить функцию f <- function(col_length, spacing_divisor){...}, чтобы точно определить ваше намерение?

TBP
1 июля 2021 в 19:13
0

Конечно, f<-function(col_length){floor(col_length/5} Решение dplyr будет интересно посмотреть, но меня действительно интересует решение data.table.

Greg
1 июля 2021 в 19:14
1

Спасибо! Сразу придет ответ!

Greg
1 июля 2021 в 19:22
0

Я должен отметить, что в вашем примере выходные данные показывают, что 1 встречается каждые 2 строки для "A", а не один раз в начале и один раз на полпути... что f предоставило бы при наличии 2 и попросили разделить группу на 2 равные части.

TBP
1 июля 2021 в 19:30
0

хм не вижу проблемы. Я представлял, как моя функция применяется группой. Я понял, что мое решение проще, чем я его делал. Смотри ниже.

Ответы (3)

avatar
chinsoon12
1 июля 2021 в 23:33
2

Еще один вариант:

dt[, sq := dt2[.SD, on=.(places), +((rowid(i.places)-1) %% spacing == 0L)]]

выход:

    places sq
 1:      A  1
 2:      A  0
 3:      A  1
 4:      A  0
 5:      A  1
 6:      A  0
 7:      A  1
 8:      A  0
 9:      A  1
10:      A  0
11:      B  1
12:      B  0
13:      B  0
14:      B  0
15:      B  1
16:      B  0
17:      B  0
18:      B  0
19:      B  1
20:      B  0
21:      B  0
22:      B  0
23:      B  1
24:      B  0
25:      B  0
26:      B  0
27:      B  1
28:      B  0
29:      B  0
30:      B  0

Вы можете подать данные.таблицу с комбинацией интервалов и групп с помощью объединения dt2[.SD, on=.(places), затем сгенерировать последовательность с помощью rowid, а затем по модулю найти те строки, в которых целое число последовательности делится на число интервал.

Greg
2 июля 2021 в 13:59
0

Спасибо за перевод моего подхода к data.table! Мне было интересно, как это будет выглядеть. Я довольно хорошо вложился в dplyr на данный момент, но рано или поздно придется сдаться грубой силе data.table. :D

avatar
Greg
1 июля 2021 в 20:12
1

Согласно нашему разговору, вот dplyr решения, каждое из которых начинается с

library(data.table)
library(dplyr)


dt <- data.table(places=c(rep("A",10), rep("B",20))) #the data

для обоих обсуждаемых подходов:

  1. Универсальный делитель (здесь 5):
    # The divisor to be applied universally across all groups.
    universal_divisor <- 5
    
    # The vectorized function you specified.
    f <- function(group_length, divisor){
      return(floor(group_length / divisor))
    }
    
    dt_universal <- dt %>%
      # Group in order to index each row WITHIN its group.
      group_by(places) %>%
      # Mark a 1 at each point calculated by the given function 'f' from the group
      # group size, against the universal divisor; otherwise make blank (NA).
      mutate(sequence = if_else(row_number() %% f(n(), universal_divisor) == 0,
                                   1, as.numeric(NA))) %>%
      ungroup() %>% as.data.table()

  1. Пользовательские интервалы:
    # Your spacings by group to apply to the data.
    dt2 <- data.table(places=c("A","B"), spacing=c(2,4))
    
    dt_custom <- dt %>%
      # Match each row to the custom spacing value for its 'place'.
      left_join(dt2, by = "places") %>%
      # Group in order to index each row WITHIN its group.
      group_by(places) %>%
      # Mark with a 1 at the desired spacing; otherwise make blank (NA).
      transmute(places,
                sequence = if_else(row_number() %% spacing == 0,
                                   1, as.numeric(NA))) %>%
      ungroup() %>% as.data.table()

Каждый из подходов выведет data.table ниже. Хотя некоторые из этих операций можно выполнять более эффективно с помощью data.table, лично я нахожу рабочий процесс dplyr очень прозрачным и гибким.

    places sequence
 1:      A       NA
 2:      A        1
 3:      A       NA
 4:      A        1
 5:      A       NA
 6:      A        1
 7:      A       NA
 8:      A        1
 9:      A       NA
10:      A        1
11:      B       NA
12:      B       NA
13:      B       NA
14:      B        1
15:      B       NA
16:      B       NA
17:      B       NA
18:      B        1
19:      B       NA
20:      B       NA
21:      B       NA
22:      B        1
23:      B       NA
24:      B       NA
25:      B       NA
26:      B        1
27:      B       NA
28:      B       NA
29:      B       NA
30:      B        1
    places sequence
avatar
TBP
1 июля 2021 в 19:27
1

Я пришел к решению data.table :

dtest[, sequence := rep(seq_len(floor(.N/5)),length.out=.N), by = places]
dtest[sequence!=1,sequence:=NA]

... никогда раньше не использовал length.out....

Greg
1 июля 2021 в 19:30
0

Ницца! Но разве это не использует один и тот же spacing (здесь 5) для каждой группы? Как насчет отдельных интервалов по группам, заданных spacing = c(2, 4) в dt2?

TBP
1 июля 2021 в 19:34
0

Сейчас я игнорирую dt2, потому что понял, что это не нужно. Было бы проще обернуть определение sequence. Он использует одинаковое количество единиц (5) для каждой группы, но с разным интервалом. .N — сокращение DT для длины группы. A — это длина 10, а B — длина 20, поэтому расчетное расстояние становится равным 2 и 4.

Greg
1 июля 2021 в 19:40
0

Итак, чтобы было ясно, вам не нужны «настраиваемые» интервалы для каждой группы, как указано в отдельном dt2? Например, вы не можете рассчитать интервал как 2 и 13, используя один и тот же делитель.

TBP
1 июля 2021 в 19:44
1

Я вижу, что вы говорите. Если мне не нравится расчетный интервал, я могу указать собственный интервал с помощью dt2. Это может быть полезно. Мне это не нужно для этой версии, но когда происходят отклонения от протокола (а они случаются), это может быть полезно.

Greg
1 июля 2021 в 19:46
0

Хорошо, у меня есть обе версии в очереди с dplyr, если вам интересно.

TBP
1 июля 2021 в 19:51
1

разместите их, любопытно увидеть эти решения. Спасибо @Greg

Greg
1 июля 2021 в 20:21
0

Просто выложил их. :)