Определите двусторонние комбинации уровней в столбце для каждого идентификатора - PullRequest
1 голос
/ 01 ноября 2019

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

У меня есть датафрейм, который выглядит следующим образом:

in_df <- data.frame(id = c(1,1,1,1,1,2,2,3), 
                    Date = as.Date(c("2019-01-01", "2019-01-01", "2019-01-01", "2019-01-02", "2019-01-02", "2019-01-01", "2019-01-01", "2019-01-01")), 
                    letter = c("A", "B", "C", "A", "B", "A", "D", "B")) 

in_df
  id       Date letter
1  1 2019-01-01      A
2  1 2019-01-01      B
3  1 2019-01-01      C
4  1 2019-01-02      A
5  1 2019-01-02      B
6  2 2019-01-01      A
7  2 2019-01-01      D
8  3 2019-01-01      B

И я хочу такой, который выглядит так:

out_df
  id       Date letter_1 letter_2
1  1 2019-01-01        A        B
2  1 2019-01-01        A        C
3  1 2019-01-01        B        C
4  1 2019-01-02        A        B
5  2 2019-01-01        A        D
6  3 2019-01-01        B        NA

Итак, первый id и первые Date имеют буквы A, B и C. Я хочу каждую уникальную пару из трех. Порядок не имеет значения, поэтому переключение на letter_1 и letter_2 было бы одним и тем же.

Я играл с expand.grid и combn, но ни один из них не кажется вполне подходящим для этой задачи,

РЕДАКТИРОВАТЬ

У меня также есть случаи, когда в id / Date есть только одна строка, поэтому использование combn дает мне Error in combn(letter, m = 2) : n < m. Как я могу добавить регистр if, чтобы letter_2 получил NA? (Я также обновил dfs выше для решения этой проблемы)

Ответы [ 3 ]

1 голос
/ 01 ноября 2019

Мы можем использовать split и combn:

do.call('rbind', 
        lapply(split(in_df, list(in_df$id, in_df$Date), drop = TRUE), 
               FUN = function(d) 
                 cbind.data.frame(unique(d[c('id', 'Date')]), 
                                  data.frame(t(
                                    if(length(d$letter) > 1){
                                      combn(d$letter, 2)    
                                    }else{
                                      matrix(c(d$letter, NA), nrow = 2)
                                    })))))


#                id       Date X1 X2
# 1.2019-01-01.1  1 2019-01-01  A  B
# 1.2019-01-01.2  1 2019-01-01  A  C
# 1.2019-01-01.3  1 2019-01-01  B  C
# 2.2019-01-01    2 2019-01-01  A  D
# 1.2019-01-02    1 2019-01-02  A  B

Может быть полезно пройти через это. Изучите вывод:

(ss <- split(in_df, list(in_df$id, in_df$Date), drop = TRUE))

Затем проверьте:

lapply(ss, FUN = function(d) data.frame(t(combn(d$letter, 2))))

В остальном мы просто объединяем данные. Возможно, вы захотите немного изменить имена столбцов.

0 голосов
/ 01 ноября 2019

Использование data.table:

require(data.table); setDT(in_df)

dt = in_df[, data.table(t(combn(letter, m = 2))), .(id, Date)]

Вывод:

> dt
   id       Date V1 V2
1:  1 2019-01-01  A  B
2:  1 2019-01-01  A  C
3:  1 2019-01-01  B  C
4:  1 2019-01-02  A  B
5:  2 2019-01-01  A  D
0 голосов
/ 01 ноября 2019

Я думаю, что следующий код работает:

library("dplyr")
in_df %>% 
  group_by(id, Date) %>% 
  mutate(
    letter_1 = combn(letter, 2)[1, ],
    letter_2 = combn(letter, 2)[2, ]
  ) %>% 
  distinct(letter_1, letter_2)


# # A tibble: 5 x 4
# # Groups:   id, Date [3]
#   letter_1 letter_2    id Date      
#   <fct>    <fct>    <dbl> <date>    
# 1 A        B            1 2019-01-01
# 2 A        C            1 2019-01-01
# 3 B        C            1 2019-01-01
# 4 A        B            1 2019-01-02
# 5 A        D            2 2019-01-01
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...