Как переписать мой код с использования tidyverse на data.table?

#r #data.table

#r #data.table

Вопрос:

Я написал код:

 df_all<- df1 %>%
  mutate(type = factor(type, levels = df3$type)) %>%
  group_by(ID, date) %>%
  complete(type, fill = list(value = 0)) %>%
  left_join(df3)
 

Как переписать его с помощью data.table? Я новичок в этом, поэтому я не знаю? Было бы просто здорово, если бы вы помогли мне с этим.

Вот для чего я его использую:

У меня есть одноколоночный фрейм данных df3 с типом столбца со всеми возможными «типами»:

 comment       type

used         enter 
used         open
used         close
used         update
not_used     delete
 

Я получаю фрейм данных из своей базы данных. Но в этом фрейме данных могут быть не все «типы». Вот пример этой таблицы:

 ID    date            type           value
a1    2020-09-01       enter          18
a1    2020-09-01       close          15
a1    2020-09-02       enter          4
a2    2020-09-01       close          10
b1    2020-09-02       update         10
 

Как вы видите, идентификатор a1 имеет только два типа: ввод и закрытие. у a2 есть только close, у b1 есть только update.

Я хочу связать эти две таблицы таким образом, чтобы «типы», которых не было в моей таблице, имели нулевое значение для каждого идентификатора и даты. Итак, как связать эти две таблицы, чтобы получить это:

 comment            ID    date            type           value
used               a1    2020-09-01       enter          18
used               a1    2020-09-01       open           0
used               a1    2020-09-01       close          15
used               a1    2020-09-01       update         0
not_used           a1    2020-09-01       delete         0
used               a1    2020-09-02       enter          4
used               a1    2020-09-02       open           0
used               a1    2020-09-02       close          0
used               a1    2020-09-02       update         0
not_used           a1    2020-09-02       delete         0
used               a2    2020-09-01       enter          0
used               a2    2020-09-01       open           0
used               a2    2020-09-01       close          10
used               a2    2020-09-01       update         0
not_used           a2    2020-09-01       delete         0
used               b1    2020-09-01       enter          0
used               b1    2020-09-01       open           0
used               b1    2020-09-01       close          0
used               b1    2020-09-01       update         10
not_used           b1    2020-09-01       delete         0
 

Как вы видите, я также сохранил столбец «комментарий». Как я мог переписать код как data.table?

Комментарии:

1. В коде у вас есть count столбец. Этого нет в примере

2. Что df3 в коде

3. @akrun я отредактировал. его первый фрейм данных

4. @akrun я удалил первую часть rbind, теперь это просто df1

5. В примере это второй пример ‘df1’ и первый ‘df3’?

Ответ №1:

Вот краткая data.table версия кода OP:

 setDT(df1)[, .SD[df3, on = .(type)], by = .(ID, date)]
 

который возвращает ожидаемый результат (для ясности, NA s в value столбце преобразуются на втором шаге — см. Ниже)

     ID       date   type value  comment
 1: a1 2020-09-01  enter    18     used
 2: a1 2020-09-01   open    NA     used
 3: a1 2020-09-01  close    15     used
 4: a1 2020-09-01 update    NA     used
 5: a1 2020-09-01 delete    NA not_used
 6: a1 2020-09-02  enter     4     used
 7: a1 2020-09-02   open    NA     used
 8: a1 2020-09-02  close    NA     used
 9: a1 2020-09-02 update    NA     used
10: a1 2020-09-02 delete    NA not_used
11: a2 2020-09-01  enter    NA     used
12: a2 2020-09-01   open    NA     used
13: a2 2020-09-01  close    10     used
14: a2 2020-09-01 update    NA     used
15: a2 2020-09-01 delete    NA not_used
16: b1 2020-09-02  enter    NA     used
17: b1 2020-09-02   open    NA     used
18: b1 2020-09-02  close    NA     used
19: b1 2020-09-02 update    10     used
20: b1 2020-09-02 delete    NA not_used
 

Для каждой группы уникальных ID date комбинаций подмножество строк из df1 соединяется с правой df3 type , которая завершает недостающие строки для каждого подмножества. Поскольку вместо tidyr::complete() используется правое соединение, здесь нет необходимости принудительно type учитывать все уровни факторов. Кроме того, data.table был сохранен порядок df3 строк во время объединения.

Для преобразования NA s в value столбце доступно 4 разных подхода, которые все возвращают один и тот же результат:

 setDT(df1)[, .SD[df3, on = .(type)], by = .(ID, date)][is.na(value), value := 0L][]
setDT(df1)[, .SD[df3, on = .(type)], by = .(ID, date)][, value := fcoalesce(value, 0L)][]
setDT(df1)[, .SD[df3, on = .(type)], by = .(ID, date)][, value := nafill(value, fill = 0L)][]
setnafill(setDT(df1)[, .SD[df3, on = .(type)], by = .(ID, date)], fill = 0L, cols = "value")[]
 
      ID       date   type value  comment
 1: a1 2020-09-01  enter    18     used
 2: a1 2020-09-01   open     0     used
 3: a1 2020-09-01  close    15     used
 4: a1 2020-09-01 update     0     used
 5: a1 2020-09-01 delete     0 not_used
 6: a1 2020-09-02  enter     4     used
 7: a1 2020-09-02   open     0     used
 8: a1 2020-09-02  close     0     used
 9: a1 2020-09-02 update     0     used
10: a1 2020-09-02 delete     0 not_used
11: a2 2020-09-01  enter     0     used
12: a2 2020-09-01   open     0     used
13: a2 2020-09-01  close    10     used
14: a2 2020-09-01 update     0     used
15: a2 2020-09-01 delete     0 not_used
16: b1 2020-09-02  enter     0     used
17: b1 2020-09-02   open     0     used
18: b1 2020-09-02  close     0     used
19: b1 2020-09-02 update    10     used
20: b1 2020-09-02 delete     0 not_used
 

Ответ №2:

Мы можем преобразовать type из factor , использовать CJ (перекрестное соединение) для расширения по идентификатору, дате и типу

 library(data.table)
setDT(df1)[, type := factor(type, levels = unique(df3$type))][,
   CJ(ID, date, type = type, unique = TRUE)][df1,
    value := value, on = .(ID, date, type)][is.na(value),
     value := 0][df3, on = .(type)]
 

Или это можно сделать с помощью split

 setDT(df1)[, type := factor(type, levels = unique(df3$type))]
rbindlist(lapply(split(df1, df1[, .(ID, date)], drop = TRUE), 
   function(x) x[, CJ(ID, date, type = levels(x$type), unique = TRUE)][x, 
     value := value, on = .(ID, date, type)][is.na(value), value := 0][]))[df3, on = .(type)]))
 

-вывод

 #    ID       date   type value  comment
# 1: a1 2020-09-01  enter    18     used
# 2: a1 2020-09-01   open     0     used
# 3: a1 2020-09-01  close    15     used
# 4: a1 2020-09-01 update     0     used
# 5: a1 2020-09-01 delete     0 not_used
# 6: a2 2020-09-01  enter     0     used
# 7: a2 2020-09-01   open     0     used
# 8: a2 2020-09-01  close    10     used
# 9: a2 2020-09-01 update     0     used
#10: a2 2020-09-01 delete     0 not_used
#11: a1 2020-09-02  enter     4     used
#12: a1 2020-09-02   open     0     used
#13: a1 2020-09-02  close     0     used
#14: a1 2020-09-02 update     0     used
#15: a1 2020-09-02 delete     0 not_used
#16: b1 2020-09-02  enter     0     used
#17: b1 2020-09-02   open     0     used
#18: b1 2020-09-02  close     0     used
#19: b1 2020-09-02 update    10     used
#20: b1 2020-09-02 delete     0 not_used
 

Комментарии:

1. он говорит об ошибке в [.data.table (rbindlist(list(df1, df2))[, : Когда i является data.table (или символьным вектором), столбцы, к которым нужно присоединиться, должны быть указаны с помощью аргумента ‘on =’ (см. ?data.table), путем ввода x (т.Е. отсортированных и, помеченный как сортированный, см. ?setkey), или путем совместного использования имен столбцов между x и i (т. Е. Естественного соединения). Соединения с ключом могут иметь дополнительные преимущества в скорости работы с очень большими данными из-за сортировки x в ОЗУ.

2. @french_fries это должно быть из last step for CJ `. Я не тестировал это, потому что не было примера. Можете ли вы опубликовать пример для тестирования. Спасибо

3. я добавил пример и объяснение

4. @french_fries вы уверены, что синтаксис dplyr работал для вас с rbind

5. дает ли ваш код желаемый результат с примером dataframe?