1
votes

Identifier les combinaisons bidirectionnelles de niveaux dans une colonne pour chaque ID

Je souhaite identifier les combinaisons bidirectionnelles de niveaux dans une colonne regroupées par les variables id et Date . En gros, je veux les paires de lettres uniques quotidiennes pour chaque personne.

J'ai un dataframe qui ressemble à ceci:

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

Et j'en veux un qui ressemble à ceci:

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

Ainsi, le premier id et la première Date ont les lettres A, B et C. Je veux chaque paire unique des trois. L'ordre n'a pas d'importance, donc changer ce qui va à letter_1 et letter_2 serait la même chose.

J'ai joué avec expand. grid et combn , mais ni l'un ni l'autre ne semble tout à fait approprié pour cette tâche.

EDIT

J'ai aussi des cas où il n'y a qu'une seule ligne par id / Date donc l'utilisation de combn me donne Erreur dans combn (lettre, m = 2): n . Comment puis-je ajouter un cas si tel que la letter_2 obtienne un NA? (J'ai également mis à jour le dfs ci-dessus pour résoudre ce problème)

r

2 commentaires

Pouvez-vous expliquer comment la deuxième et la troisième ligne de la sortie ont-elles été créées? Je veux dire les valeurs dans letter_1 et letter_2 .


@tmfmnk Merci pour la réponse rapide! J'ai essayé de clarifier un peu plus, est-ce que cela aide?


3 Réponses :


1
votes

Nous pouvons utiliser split et combn:

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

Il peut être utile de suivre cette étape. Examinez la sortie de:

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

Ensuite, vérifiez:

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

Le reste du temps, nous combinons simplement les données. Vous voudrez peut-être ajuster un peu les noms des colonnes.


3 commentaires

Merci!! J'ai aussi des cas où il n'y a qu'une seule ligne par id / Date donc lorsque j'utilise cette réponse, j'obtiens Erreur dans combn (lettre, m = 2) : n . Comment puis-je ajouter un cas si tel que la letter_2 obtienne un NA?


voir modifier ... cela rend le code un peu plus compliqué, mais cela devrait gérer votre situation


Il fonctionne depuis environ une heure. Peut-être que mes données sont trop volumineuses pour cette méthode. Cela a été vraiment utile, merci!



0
votes

Je pense que le code suivant fonctionne:

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


0 commentaires

1
votes

Utilisation de data.table:

> 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

Output:

require(data.table); setDT(in_df)

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


1 commentaires

Toutes les réponses répondaient adéquatement à ma question initiale, mais c'était la seule que je pouvais me mettre au travail pour mes données réelles un peu plus compliquées. Merci!