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 letter_2 obtienne un NA? (J'ai également mis à jour le dfs ci-dessus pour résoudre ce problème)
3 Réponses :
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.
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 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!
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
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)]
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!
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_1etletter_2.@tmfmnk Merci pour la réponse rapide! J'ai essayé de clarifier un peu plus, est-ce que cela aide?