J'essaie d'appliquer une fonction uniquement sur certaines colonnes en utilisant mutate_at.
Voici les données:
# A tibble: 12 x 14 # Groups: LoB [1] LoB AY R_0 R_1 R_2 R_3 R_4 R_5 R_6 R_7 R_8 R_9 R_10 R_11 <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 1 1994 50135 76631 28763 0 16433 6301 0 0 0 0 0 0 2 1 1995 96665 81539 31397 1409 16433 6301 0 0 0 0 0 0 3 1 1996 134960 111966 31771 1879 16869 6301 0 0 0 0 0 0 4 1 1997 146993 116234 31771 1879 16869 6301 0 0 0 0 0 0 5 1 1998 160325 118228 31987 2082 17071 6480 147 135 0 0 0 0 6 1 1999 195389 166654 31987 2082 19186 6480 147 135 0 0 0 0 7 1 2000 211084 171239 32542 2082 19186 6480 147 135 0 0 0 0 8 1 2001 252311 186817 32542 2082 19186 6480 147 135 0 0 0 0 9 1 2002 340671 194929 39703 2082 19186 6663 147 135 0 0 0 0 10 1 2003 370171 225874 41895 3638 20457 7715 1129 1042 902 833 800 684 11 1 2004 400329 234015 41895 3638 20457 7715 1129 3398 902 833 800 684 12 1 2005 447918 245609 42667 4313 20992 7715 1129 3398 902 833 800 684
qui ressemble à ce qui suit:
XXX
Disons que je voudrais créer une somme cumulée de la colonne qui commence par R_
. Pour ce faire, j'ai écrit:
df %>% mutate_at(vars(contains("R_")), funs(cumsum))
qui me donne le résultat suivant:
# A tibble: 12 x 14 # Groups: LoB [1] LoB AY R_0 R_1 R_2 R_3 R_4 R_5 R_6 R_7 R_8 R_9 R_10 R_11 <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 1 1994 50135 76631 28763 0 16433 6301 0 0 0 0 0 0 2 1 1995 46530 4908 2634 1409 0 0 0 0 0 0 0 0 3 1 1996 38295 30427 374 470 436 0 0 0 0 0 0 0 4 1 1997 12033 4268 0 0 0 0 0 0 0 0 0 0 5 1 1998 13332 1994 216 203 202 179 147 135 0 0 0 0 6 1 1999 35064 48426 0 0 2115 0 0 0 0 0 0 0 7 1 2000 15695 4585 555 0 0 0 0 0 0 0 0 0 8 1 2001 41227 15578 0 0 0 0 0 0 0 0 0 0 9 1 2002 88360 8112 7161 0 0 183 0 0 0 0 0 0 10 1 2003 29500 30945 2192 1556 1271 1052 982 907 902 833 800 684 11 1 2004 30158 8141 0 0 0 0 0 2356 0 0 0 0 12 1 2005 47589 11594 772 675 535 0 0 0 0 0 0 0
Le problème ici est que la somme cumulée a été faite verticalement (par variable) plutôt qu'horizontalement. Comment puis-je y parvenir dans dplyr?
3 Réponses :
Je ne suis pas sûr qu'il existe un moyen de le faire sans utiliser rassembler
et spread
. Voici comment je le ferais. Tout d'abord, je remodelerais les données pour qu'elles soient «longues», puis nous devons utiliser group_by
afin que nous ne calculions que le cumsum
pour chaque ligne du data.frame
(si ce n'est pas suffisamment groupé, nous pouvons ajouter un row_number
aux données, et group_by
cela). Après cela, nous mutons
puis répandons
pour que les données redeviennent «larges». Enfin, nous ajoutons un select (names (df))
comme suggéré par @Gregor pour conserver l'ordre d'origine des colonnes.
df %>% gather(variable, value, contains('R_')) %>% # reshape wide to long group_by(LoB, AY) %>% # group by for each row in original data mutate(value = cumsum(value)) %>% # calculate cumsum spread(variable, value) %>% # reshape back from long to wide select(names(df)) # added to retain original column order # LoB AY R_0 R_1 R_2 R_3 ... # <fct> <dbl> <dbl> <dbl> <dbl> <dbl> ... # 1 1 1994 50135 126766 155529 155529 ... # 2 1 1995 46530 51438 54072 55481 ... # 3 1 1996 38295 68722 69096 69566 ... # 4 1 1997 12033 16301 16301 16301 ...
Je pense que cela ne calcule pas correctement le cumulé, car l'ordre des lignes après le rassembler
ne correspond pas à l'ordre d'origine des colonnes (R_10 vient avant R_2).
Je ne suis pas d'accord. L'ordre des colonnes dans le résultat final est ce qui est différent, pas l'ordre des lignes. Le sperme calcule correctement. Vous pouvez facilement vérifier cela en inspectant les données après la ligne rassembler
.
Vrai. Désolé, mon erreur. J'ai supposé que rassembler () trierait les lignes par ordre alphabétique.
Réponse similaire à celle du bouncyball, mais elle garde l'ordre des colonnes et utilise un identifiant générique au cas où (LoB, AY) ne serait pas une clé primaire:
df %>% mutate(id = 1:n()) %>% gather(old_name, value, starts_with("R_")) %>% arrange(id, nchar(old_name), old_name) %>% group_by(id) %>% mutate(value = cumsum(value)) %>% ungroup() %>% select(-id) %>% spread(old_name, value) %>% select(names(df)) %>% select(AY, everything())
En effet, c'est beaucoup plus agréable! J'ai oublié que l'ordre correct est toujours présent dans la variable d'origine. Merci!
avec select (names (df))
, je pense que le select (AY, tout ())
est également redondant
Les opérations par ligne fonctionnent souvent mieux sur les matrices. Pour éviter les tracas de rassembler / répandre
, j'extraire les colonnes R_
, utiliser apply
(conversion implicite en matrice), puis attribuer le résultat retour aux données d'origine:
Cela dit, les données ne semblent pas très ordonnées. Vous feriez peut-être mieux de rassembler
le format long et de le conserver longtemps.
result = dd %>% ungroup %>% select(starts_with("R_")) %>% apply(1, cumsum) %>% t dd[, grepl("^R_", names(dd))] = result dd # # A tibble: 12 x 14 # # Groups: LoB [1] # LoB AY R_0 R_1 R_2 R_3 R_4 R_5 R_6 R_7 R_8 R_9 R_10 R_11 # <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> # 1 1 1994 50135 126766 155529 155529 171962 178263 178263 178263 178263 178263 178263 178263 # 2 1 1995 46530 51438 54072 55481 55481 55481 55481 55481 55481 55481 55481 55481 # 3 1 1996 38295 68722 69096 69566 70002 70002 70002 70002 70002 70002 70002 70002 # 4 1 1997 12033 16301 16301 16301 16301 16301 16301 16301 16301 16301 16301 16301 # 5 1 1998 13332 15326 15542 15745 15947 16126 16273 16408 16408 16408 16408 16408 # 6 1 1999 35064 83490 83490 83490 85605 85605 85605 85605 85605 85605 85605 85605 # 7 1 2000 15695 20280 20835 20835 20835 20835 20835 20835 20835 20835 20835 20835 # 8 1 2001 41227 56805 56805 56805 56805 56805 56805 56805 56805 56805 56805 56805 # 9 1 2002 88360 96472 103633 103633 103633 103816 103816 103816 103816 103816 103816 103816 # 10 1 2003 29500 60445 62637 64193 65464 66516 67498 68405 69307 70140 70940 71624 # 11 1 2004 30158 38299 38299 38299 38299 38299 38299 40655 40655 40655 40655 40655 # 12 1 2005 47589 59183 59955 60630 61165 61165 61165 61165 61165 61165 61165 61165