2014-10-06 41 views
6

Ho un set di dati simile a questo.Mutare più colonne in un dataframe

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 

e voglio calcolare alcuni rapporti in base ai bilanci delle banche. e voglio il set di dati a guardare come questo

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 

Ecco il codice di replicare i dati

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) 

In primo luogo, mi sono liberato di AN nei bilanci.

cols <- c("totass", "cash", "bond", "loans") 
bankdata[cols][is.na(bankdata[cols])] <- 0 

Poi ho calcolare rapporti

library(dplyr) 
bankdata<-mutate(bankdata,CashtoAsset = cash/totass) 
bankdata<-mutate(bankdata,BondtoAsset = bond/totass) 
bankdata<-mutate(bankdata,loanstoAsset =loans/totass) 

Ma, invece di calcolare tutti questi rapporti linea per linea, voglio creare un look per farlo tutto in una volta. In Stata, vorrei fare

foreach x of varlist cash bond loans { 
by bankid: gen `x'toAsset = `x'/ totass 
} 

Come farei questo?

+1

Meta-commenti: quando si traduce da una lingua all'altra, è necessario non essere troppo letterale. I loop in Stata funzionano spesso meglio come calcoli basati su array in R. (Anche il contrario può essere vero: i nuovi arrivati ​​in Stata da altre lingue spesso provano loop su osservazioni, che sono raramente necessari.) –

+0

Semplifico le mie variabili qui, ma nel mio set di dati, ho oltre 20 categorie di risorse, quindi avere un ciclo è utile. –

+0

Non ho niente contro i loop; allo stesso modo il tipico utente R è sicuramente felice con 20 colonne .... –

risposta

0

Si potrebbe rendere questo un po 'più difficile del necessario. Basta provare questo e vedere se produce ciò di cui hai bisogno.

bankdata$CashtoAsset <- bankdata$cash/bankdata$totass 
bankdata$BondtoAsset <- bankdata$bond/bankdata$totass 
bankdata$loantoAsset <- bankdata$loans/bankdata$totass 
bankdata 

rendimenti questo:

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 

Questo dovrebbe iniziare nella giusta direzione.

0

Questo è uno dei grandi aspetti negativi di dplyr: per quanto ne so, non esiste un modo semplice per utilizzarlo a livello di programmazione piuttosto che in modo interattivo senza alcun tipo di "hack" come il deplorevole idioma eval(parse(text=foo)).

L'approccio più semplice è lo stesso del metodo Stata, ma la manipolazione delle stringhe è un po 'più prolissa in R che in Stata (o in qualsiasi altro linguaggio di scripting, peraltro).

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. 
} 

Per rendere il tutto più Stata simile, si può avvolgere il tutto in within in questo modo:

bankdata <- within(bankdata, for (x in c("cash", "bond", "loans")) { 
    assign(x, get(x)/totass) 
}) 

ma questo comporta alcuni hacker con le get e assign funzioni che non sono come sicuro da usare in generale, anche se nel tuo caso non è probabilmente un grosso problema. Non consiglierei di provare trucchi simili con dplyr, ad esempio, perché dplyr abusa delle funzionalità di valutazione non standard di R ed è probabilmente più un problema che non ne vale la pena. Per una soluzione più rapida e probabilmente superiore, controlla il pacchetto data.table che (penso) ti consentirebbe di utilizzare la sintassi di loop simile a Stata ma con la velocità di dplyr -like. Controlla la vignetta del pacchetto su CRAN.

Inoltre, sei davvero, davvero sicuro di voler riassegnare le voci NA a 0?

14

Update (come del 2 Dicembre 2017)

Da quando ho risposto a questa domanda, ho capito che alcuni modo che gli utenti sono stati controllando questa risposta.Il pacchetto dplyr è cambiato da allora. Pertanto, lascio il seguente aggiornamento. Spero che questo possa aiutare alcuni utenti R a imparare come usare mutate_at().

mutate_each() è ora obsoleto. Si desidera utilizzare mutate_at(), invece. È possibile specificare quali colonne si desidera applicare la propria funzione in .vars. Un modo è utilizzare vars(). Un altro consiste nell'utilizzare un vettore di caratteri contenente i nomi di colonna, che si desidera applicare la funzione personalizzata in .fun. L'altro è specificare colonne con numeri (ad es. 5: 7 in questo caso). Notare che, se si utilizza una colonna per group_by(), è necessario modificare i numeri delle posizioni delle colonne. Dai un'occhiata a this 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 

ho volutamente dato toAsset alla funzione personalizzata in .fun dal momento che questo mi aiuterà a organizzare nuovi nomi delle colonne. Precedentemente, ho usato rename(). Ma penso che sia molto più facile ripulire i nomi delle colonne con gsub() nell'approccio attuale. Se il risultato sopra riportato viene salvato come out, si desidera eseguire il seguente codice per rimuovere _ nei nomi delle colonne.

names(out) <- gsub(names(out), pattern = "_", replacement = "") 

risposta originale

penso che si può risparmiare un po 'di battitura in questo modo con dplyr. Il rovescio della medaglia è di sovrascrivere denaro, obbligazioni e prestiti.

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 

Se si preferisce il risultato previsto, penso che sia necessaria una digitazione. La parte del rinominare sembra essere qualcosa che devi fare.

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

Ciao, sto provando tutte le diverse opzioni pubblicate qui. Quando ho provato i tuoi codici, ho capito. 'Errore: oggetto 'ana' non trovato. Mi spiegheresti cosa sta succedendo? Grazie. –

+0

@HPark Sto assegnando un'uscita all'oggetto, ana nel processo di piping. Se questo approccio non funziona per te, potresti farlo, 'ana <- bankdata%>% group_by (bankname)%>% summarise_each (funs (whatever = ./totass), cash: loans)%>% rename (cashtoAsset = contanti, bondtoAsset = bond, loanstoAsset = prestiti); ana%>% merge (bankdata,., by = "bank name") ' – jazzurro

0

Prova:

for(i in 5:7){ 
    bankdata[,(i+3)] = bankdata[,i]/bankdata[,4] 
} 
names(bankdata)[(5:7)+3] = paste0(names(bankdata)[5:7], 'toAssest') 

uscita:

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 
2

Ecco una soluzione 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 
2

Apply e 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 
Problemi correlati