このようなデータセットがあります。
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
そして、銀行のバランスシートに基づいていくつかの比率を計算したいと思います。データセットを次のようにしたい
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
データを複製するコードは次のとおりです
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)
まず、バランスシートのNAを取り除きました。
cols <- c("totass", "cash", "bond", "loans")
bankdata[cols][is.na(bankdata[cols])] <- 0
次に比率を計算します
library(dplyr)
bankdata<-mutate(bankdata,CashtoAsset = cash/totass)
bankdata<-mutate(bankdata,BondtoAsset = bond/totass)
bankdata<-mutate(bankdata,loanstoAsset =loans/totass)
しかし、これらすべての比率を1行ずつ計算するのではなく、これを一度に実行するための外観を作成したいと思います。スタタでは、
foreach x of varlist cash bond loans {
by bankid: gen `x'toAsset = `x'/ totass
}
どうすればいいですか?
変更がありました。 _.funs
_(funs()
)ではfuns(name = f(.)
を使用しています。しかし、これは変更されています(上記のdplyr 0.8.0)。 funs
の代わりに、list
(list(name = ~f(.))
)を使用します。以下の新しい例を参照してください。
_bankdata %>%
mutate_at(.funs = list(toAsset = ~./totass), .vars = vars(cash:loans))
bankdata %>%
mutate_at(.funs = list(toAsset = ~./totass), .vars = c("cash", "bond", "loans"))
bankdata %>%
mutate_at(.funs = list(toAsset = ~./totass), .vars = 5:7)
_
この質問に回答してから、一部のSOユーザーがこの回答を確認しています。それ以降、dplyrパッケージが変更されました。そのため、次の更新を残します。 Rユーザーはmutate_at()
の使用方法を学びます。
mutate_each()
は非推奨になりました。代わりにmutate_at()
を使用します。 _.vars
_で、関数を適用する列を指定できます。 1つの方法は、vars()
を使用することです。もう1つは、カスタム関数を_.fun
_で適用する列名を含む文字ベクトルを使用することです。もう1つは、列を数値で指定することです(この例では5:7)。 group_by()
に列を使用する場合、列の位置の数を変更する必要があることに注意してください。 この質問 をご覧ください。
_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
_
_.fun
_のカスタム関数に意図的にtoAsset
を与えました。これは、新しい列名を配置するのに役立つためです。以前は、rename()
を使用していました。しかし、現在のアプローチでは、gsub()
を使用して列名をクリーンアップする方がはるかに簡単だと思います。上記の結果がout
として保存されている場合、列名から__
_を削除するために次のコードを実行します。
_names(out) <- gsub(names(out), pattern = "_", replacement = "")
_
この方法でdplyrを使用すると、タイピングを節約できると思います。欠点は、現金、債券、ローンを上書きすることです。
_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
_
もしあなたが期待した結果を好むなら、私はいくつかのタイピングが必要だと思います。名前を変更する部分は、あなたがやらなければならないことのようです。
_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
および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
がここにあります 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
これはdplyr
の大きな欠点の1つです。私が知っている限り、嘆かわしいeval(parse(text=foo))
熟語。
最も単純なアプローチはStataメソッドと同じですが、文字列操作は、RではStata(または他のスクリプト言語)よりも少し冗長です。
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.
}
全体をよりStataのようにするために、次のようにwithin
で全体をラップできます。
bankdata <- within(bankdata, for (x in c("cash", "bond", "loans")) {
assign(x, get(x) / totass)
})
しかし、これにはget
関数とassign
関数を使用したハッキングが伴いますが、これらの関数は一般的に安全ではありませんが、あなたの場合はそれほど大したことではありません。たとえば、dplyr
がRの非標準の評価機能を悪用しているため、価値があるよりもトラブルが多いため、dplyr
で同様のトリックを試すことはお勧めしません。より速く、おそらくより優れたソリューションについては、data.table
パッケージ(私は思います)は、Stataのようなループ構文を使用できますが、dplyr
のような速度です。 CRANのパッケージビネットを確認してください。
また、本当にNA
エントリを0に再割り当てしてもよろしいですか?
試してください:
for(i in 5:7){
bankdata[,(i+3)] = bankdata[,i]/bankdata[,4]
}
names(bankdata)[(5:7)+3] = 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 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
あなたはこれを必要以上に少し難しくしているかもしれません。これを試して、必要なものが得られるかどうかを確認してください。
bankdata$CashtoAsset <- bankdata$cash / bankdata$totass
bankdata$BondtoAsset <- bankdata$bond / bankdata$totass
bankdata$loantoAsset <- bankdata$loans / bankdata$totass
bankdata
これをもたらす:
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
これにより、正しい方向に進むことができます。