J'ai un ensemble de données qui ressemble à ceci.
bankname bankid year totass cash bond loans
Bank A 1 1881 244789 7250 20218 29513
Bank B 2 1881 195755 10243 185151 2800
Bank C 3 1881 107736 13357 177612 NA
Bank D 4 1881 170600 35000 20000 5000
Bank E 5 1881 3200000 351266 314012 NA
et je veux calculer des ratios basés sur les bilans des banques. et je veux que l'ensemble de données ressemble à ceci
bankname bankid year totass cash bond loans CashtoAsset BondtoAsset LoanstoAsset
Bank A 1 1881 2447890 7250 202100 951300 0.002 0.082 0.388
Bank B 2 1881 195755 10243 185151 2800 0.052 0.945 0.014
Bank C 3 1881 107736 13357 177612 NA 0.123 1.648585431 NA
Bank D 4 1881 170600 35000 20000 5000 0.205 0.117 0.029
Bank E 5 1881 32000000 351266 314012 NA 0.0109 0.009 NA
Voici le code pour répliquer les données
bankname <- c("Bank A","Bank B","Bank C","Bank D","Bank E")
bankid <- c( 1, 2, 3, 4, 5)
year<- c( 1881, 1881, 1881, 1881, 1881)
totass <- c(244789, 195755, 107736, 170600, 32000000)
cash<-c(7250,10243,13357,35000,351266)
bond<-c(20218,185151,177612,20000,314012)
loans<-c(29513,2800,NA,5000,NA)
bankdata<-data.frame(bankname, bankid,year,totass, cash, bond, loans)
Premièrement, je me suis débarrassé des NA dans les bilans.
cols <- c("totass", "cash", "bond", "loans")
bankdata[cols][is.na(bankdata[cols])] <- 0
Puis je calcule des ratios
library(dplyr)
bankdata<-mutate(bankdata,CashtoAsset = cash/totass)
bankdata<-mutate(bankdata,BondtoAsset = bond/totass)
bankdata<-mutate(bankdata,loanstoAsset =loans/totass)
Mais, au lieu de calculer tous ces ratios ligne par ligne, je souhaite créer un look permettant de faire tout cela à la fois. À Stata, je ferais
foreach x of varlist cash bond loans {
by bankid: gen `x'toAsset = `x'/ totass
}
Comment je ferais ça?
Depuis que j'ai répondu à cette question, je me suis rendu compte que certains SO utilisateurs vérifiaient cette réponse. Le paquet dplyr a changé depuis. Par conséquent, je laisse la mise à jour suivante. J'espère que cela aidera certains utilisateurs de R à apprendre à utiliser mutate_at()
.
mutate_each()
est maintenant obsolète. Vous souhaitez utiliser mutate_at()
à la place. Vous pouvez spécifier les colonnes auxquelles vous souhaitez appliquer votre fonction dans .vars
. Une solution consiste à utiliser vars()
. Une autre consiste à utiliser un vecteur de caractères contenant des noms de colonnes, auquel vous souhaitez appliquer votre fonction personnalisée dans .fun
. L'autre consiste à spécifier des colonnes avec des nombres (par exemple, 5: 7 dans ce cas). Notez que si vous utilisez une colonne pour group_by()
, vous devez modifier le nombre de positions de colonne. Jetez un oeil sur cette question .
bankdata %>%
mutate_at(.funs = funs(toAsset = ./totass), .vars = vars(cash:loans))
bankdata %>%
mutate_at(.funs = funs(toAsset = ./totass), .vars = c("cash", "bond", "loans"))
bankdata %>%
mutate_at(.funs = funs(toAsset = ./totass), .vars = 5:7)
# bankname bankid year totass cash bond loans cash_toAsset bond_toAsset loans_toAsset
#1 Bank A 1 1881 244789 7250 20218 29513 0.02961734 0.082593581 0.12056506
#2 Bank B 2 1881 195755 10243 185151 2800 0.05232561 0.945830247 0.01430359
#3 Bank C 3 1881 107736 13357 177612 NA 0.12397899 1.648585431 NA
#4 Bank D 4 1881 170600 35000 20000 5000 0.20515826 0.117233294 0.02930832
#5 Bank E 5 1881 32000000 351266 314012 NA 0.01097706 0.009812875 NA
J'ai volontairement donné toAsset
à la fonction personnalisée dans .fun
, car cela m'aidera à organiser les nouveaux noms de colonne. Auparavant, j'utilisais rename()
. Mais je pense qu'il est beaucoup plus facile de nettoyer les noms de colonnes avec gsub()
dans l'approche actuelle. Si le résultat ci-dessus est enregistré en tant que out
, vous souhaitez exécuter le code suivant afin de supprimer _
dans les noms de colonne.
names(out) <- gsub(names(out), pattern = "_", replacement = "")
Je pense que vous pouvez économiser un peu de frappe de cette manière avec dplyr. L'inconvénient est que vous écrasez de l'argent, des obligations et des prêts.
bankdata %>%
group_by(bankname) %>%
mutate_each(funs(whatever = ./totass), cash:loans)
# bankname bankid year totass cash bond loans
#1 Bank A 1 1881 244789 0.02961734 0.082593581 0.12056506
#2 Bank B 2 1881 195755 0.05232561 0.945830247 0.01430359
#3 Bank C 3 1881 107736 0.12397899 1.648585431 NA
#4 Bank D 4 1881 170600 0.20515826 0.117233294 0.02930832
#5 Bank E 5 1881 32000000 0.01097706 0.009812875 NA
Si vous préférez le résultat escompté, je pense qu’il est nécessaire de taper. La partie renommée semble être quelque chose que vous devez faire.
bankdata %>%
group_by(bankname) %>%
summarise_each(funs(whatever = ./totass), cash:loans) %>%
rename(cashtoAsset = cash, bondtoAsset = bond, loanstoAsset = loans) -> ana;
ana %>%
merge(bankdata,., by = "bankname")
# bankname bankid year totass cash bond loans cashtoAsset bondtoAsset loanstoAsset
#1 Bank A 1 1881 244789 7250 20218 29513 0.02961734 0.082593581 0.12056506
#2 Bank B 2 1881 195755 10243 185151 2800 0.05232561 0.945830247 0.01430359
#3 Bank C 3 1881 107736 13357 177612 NA 0.12397899 1.648585431 NA
#4 Bank D 4 1881 170600 35000 20000 5000 0.20515826 0.117233294 0.02930832
#5 Bank E 5 1881 32000000 351266 314012 NA 0.01097706 0.009812875 NA
Apply
et cbind
cbind(bankdata,apply(bankdata[,5:7],2, function(x) x/bankdata$totass))
names(bankdata)[8:10] <- paste0(names(bankdata)[5:7], 'toAssest’)
> bankdata
bankname bankid year totass cash bond loans cashtoAssest bondtoAssest loanstoAssest
1 Bank A 1 1881 244789 7250 20218 29513 0.02961734 0.082593581 0.12056506
2 Bank B 2 1881 195755 10243 185151 2800 0.05232561 0.945830247 0.01430359
3 Bank C 3 1881 107736 13357 177612 NA 0.12397899 1.648585431 NA
4 Bank D 4 1881 170600 35000 20000 5000 0.20515826 0.117233294 0.02930832
5 Bank E 5 1881 32000000 351266 314012 NA 0.01097706 0.009812875 NA
Voici une solution data.table
.
library(data.table)
setDT(bankdata)
bankdata[, paste0(names(bankdata)[5:7], "toAsset") :=
lapply(.SD, function(x) x/totass), .SDcols=5:7]
bankdata
# bankname bankid year totass cash bond loans cashtoAsset bondtoAsset loanstoAsset
# 1: Bank A 1 1881 244789 7250 20218 29513 0.02961734 0.082593581 0.12056506
# 2: Bank B 2 1881 195755 10243 185151 2800 0.05232561 0.945830247 0.01430359
# 3: Bank C 3 1881 107736 13357 177612 0 0.12397899 1.648585431 0.00000000
# 4: Bank D 4 1881 170600 35000 20000 5000 0.20515826 0.117233294 0.02930832
# 5: Bank E 5 1881 32000000 351266 314012 0 0.01097706 0.009812875 0.00000000
C’est l’un des gros inconvénients de dplyr
: pour autant que je sache, il n’existe aucun moyen simple de l’utiliser par programme plutôt que de manière interactive, sans une sorte de «bidouille» comme l’idiot déplorable eval(parse(text=foo))
.
L'approche la plus simple est la même que dans la méthode Stata, mais la manipulation de chaîne est un peu plus détaillée dans R que dans Stata (ou dans tout autre langage de script, d'ailleurs).
for (x in c("cash", "bond", "loans")) {
bankdata[sprintf("%stoAsset", x)] <- bankdata[x] / bankdata$totass # or, equivalently, bankdata["totass"] for a consistent "look"
## can also replace `sprintf("%stoAsset", x)` with `paste0(c(x, "toAsset"))` or even `paste(x, "toAsset", collapse="") depending on what makes more sense to you.
}
Pour rendre le tout plus semblable à Stata, vous pouvez envelopper le tout dans within
comme suit:
bankdata <- within(bankdata, for (x in c("cash", "bond", "loans")) {
assign(x, get(x) / totass)
})
mais cela implique quelques manipulations avec les fonctions get
et assign
qui ne sont pas aussi sûres à utiliser en général, bien que dans votre cas ce ne soit probablement pas un gros problème. Par exemple, je ne recommanderais pas d'essayer des trucs similaires avec dplyr
, parce que dplyr
abuse des fonctionnalités d'évaluation non standard de R et qu'il cause probablement plus de problèmes qu'il n'en vaut la peine. Pour une solution plus rapide et probablement supérieure, consultez le paquetage data.table
qui (je pense) vous permettrait d’utiliser la syntaxe en boucle de type Stata, mais avec une vitesse de type dplyr
. Découvrez la vignette du paquet sur CRAN.
De plus, êtes-vous vraiment très sûr de vouloir réaffecter les entrées NA
à 0?
Essayer:
for(i in 5:7){
bankdata[,(i+3)] = bankdata[,i]/bankdata[,4]
}
names(bankdata)[(5:7)+3] = paste0(names(bankdata)[5:7], 'toAssest')
Sortie:
bankdata
bankname bankid year totass cash bond loans cashtoAssest bondtoAssest loanstoAssest
1 Bank A 1 1881 244789 7250 20218 29513 0.02961734 0.082593581 0.12056506
2 Bank B 2 1881 195755 10243 185151 2800 0.05232561 0.945830247 0.01430359
3 Bank C 3 1881 107736 13357 177612 0 0.12397899 1.648585431 0.00000000
4 Bank D 4 1881 170600 35000 20000 5000 0.20515826 0.117233294 0.02930832
5 Bank E 5 1881 32000000 351266 314012 0 0.01097706 0.009812875 0.00000000
Vous pourriez rendre cela un peu plus difficile que nécessaire. Essayez ceci et voyez si cela donne ce dont vous avez besoin.
bankdata$CashtoAsset <- bankdata$cash / bankdata$totass
bankdata$BondtoAsset <- bankdata$bond / bankdata$totass
bankdata$loantoAsset <- bankdata$loans / bankdata$totass
bankdata
Donne ceci:
bankname bankid year totass cash bond loans CashtoAsset BondtoAsset loantoAsset
1 Bank A 1 1881 244789 7250 20218 29513 0.02961734 0.082593581 0.12056506
2 Bank B 2 1881 195755 10243 185151 2800 0.05232561 0.945830247 0.01430359
3 Bank C 3 1881 107736 13357 177612 0 0.12397899 1.648585431 0.00000
4 Bank D 4 1881 170600 35000 20000 5000 0.20515826 0.117233294 0.02930832
5 Bank E 5 1881 32000000 351266 314012 0 0.01097706 0.009812875 0.00000000
Cela devrait vous aider à démarrer dans la bonne direction.