web-dev-qa-db-ja.com

データフレームの複数の列を変更する

このようなデータセットがあります。

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
}

どうすればいいですか?

17
H Park

アップデート(2019年3月18日現在)

変更がありました。 _.funs_(funs())ではfuns(name = f(.)を使用しています。しかし、これは変更されています(上記のdplyr 0.8.0)。 funsの代わりに、listlist(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)
_

アップデート(2017年12月2日現在)

この質問に回答してから、一部の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
_
43
jazzurro

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
3
hvollmeier

がここにあります 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
3
KFB

これは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に再割り当てしてもよろしいですか?

1
shadowtalker

試してください:

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
0
rnso

あなたはこれを必要以上に少し難しくしているかもしれません。これを試して、必要なものが得られるかどうかを確認してください。

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

これにより、正しい方向に進むことができます。

0
Matt Jolly