2014-03-29 105 views
5

我想將不同貨幣的一些價格轉換爲特定貨幣。 假設我有這樣的:在R data.table乘以列名基於另一列的值

library(data.table) 
set.seed(100) 
DT <- data.table(day=1:10, price=runif(10), currency=c("aud","eur"), 
       aud=runif(10) + 1, eur=runif(10) + 1.5) 
DT 
    day  price currency  aud  eur 
1: 1 0.30776611  aud 1.624996 2.035811 
2: 2 0.25767250  eur 1.882166 2.210804 
3: 3 0.55232243  aud 1.280354 2.038349 
4: 4 0.05638315  eur 1.398488 2.248972 
5: 5 0.46854928  aud 1.762551 1.920101 
6: 6 0.48377074  eur 1.669022 1.671420 
7: 7 0.81240262  aud 1.204612 2.270302 
8: 8 0.37032054  eur 1.357525 2.381954 
9: 9 0.54655860  aud 1.359475 2.049097 
10: 10 0.17026205  eur 1.690291 1.777724 

每天的價格在貨幣列中顯示相應的貨幣表示。所以第一天的0.30776611是澳元(澳元),0.25767250是歐元(歐元)。列audeur以美元顯示各個貨幣的匯率。如何以data.table的方式創建以美元表示的新價格列?

我需要多price基於currency相應的列名,以獲得此:

DT 
    day  price currency  aud  eur price.in.usd 
1: 1 0.30776611  aud 1.624996 2.035811 0.5001187 
2: 2 0.25767250  eur 1.882166 2.210804 0.5696634 
3: 3 0.55232243  aud 1.280354 2.038349 0.7071682 
4: 4 0.05638315  eur 1.398488 2.248972 0.1268041 
5: 5 0.46854928  aud 1.762551 1.920101 0.825842 
6: 6 0.48377074  eur 1.669022 1.671420 0.8085841 
7: 7 0.81240262  aud 1.204612 2.270302 0.9786299 
8: 8 0.37032054  eur 1.357525 2.381954 0.8820865 
9: 9 0.54655860  aud 1.359475 2.049097 0.7430328 
10: 10 0.17026205  eur 1.690291 1.777724 0.3026789 

所以對於第一天我乘price * aud = 0.30776611 * 1.624996,因爲價格是audcurrency列,而在第二個price * eur = 0.25767250 * 2.210804出於同樣的原因。

實際數據包括大約40升的貨幣,從而多個ifelse()創建箭頭反模式都不是很方便。

就目前而言,我的數據的一個子樣本,我有這樣的:

DT.all[, price := ifelse(curcdd=="AUD", adj.price * AUD, 
         ifelse(curcdd=="BEF", adj.price * BEF, 
           ifelse(curcdd=="BGN", adj.price * BGN, 
            ifelse(curcdd=="CHF", adj.price * CHF, 
              ifelse(curcdd=="CZK", adj.price * CZK, 
                ifelse(curcdd=="DEM", adj.price * DEM, 
                  ifelse(curcdd=="EUR", adj.price * EUR, 
                   ifelse(curcdd=="FRF", adj.price * FRF, 
                     ifelse(curcdd=="GBP", adj.price * GBP, 
                       ifelse(curcdd=="ILS", adj.price * ILS, 
                         ifelse(curcdd=="JPY", adj.price * JPY, 
                          ifelse(curcdd=="NLG", adj.price * NLG, 
                            ifelse(curcdd=="NOK", adj.price * NOK, 
                              ifelse(curcdd=="PLN", adj.price * PLN, 
                                ifelse(curcdd=="SEK", adj.price * SEK, 
                                 ifelse(curcdd=="SGD", adj.price * SGD, 
                                   ifelse(curcdd=="USD", adj.price, NA)))))))))))))))))] 

其作品,但它只有大約20貨幣,做所有的人(〜40)肯定是不優雅。 ..

非常感謝!

+0

我認爲如果將價格和coverage表單獨分開,這會更好。想必你有轉換表,然後根據日期和貨幣合併,對吧? –

+0

準確。我有一個價格'data.table'和一個貨幣'data.table',然後我將它們與merge(DT,curDT)'合併。所以我們通過以下方式到達同一個表:'DT < - data.table(day = 1:10,price = runif(10))','currency.DT < - data.table(day = 1:10,currency = c (「aud」,「eur」),aud = runif(10)+ 1,eur = runif(10)+ 1.5)','setkey(DT,day); setkey(currrency.DT,day)','DT < - merge(DT,currency.DT)' – pidosaurus

回答

3

[編輯]使用get由我從馬修Dowle一個答案看到列名的參考價值拉動的想法工作,這似乎是有效的:

setkey(DT, currency) 
DT[ , cvt := .SD[, get(currency)]*price, by=currency] 
DT 

    day  price currency  aud  eur  cvt 
1: 1 0.30776611  aud 1.624996 2.035811 0.5001188 
2: 3 0.55232243  aud 1.280354 2.038349 0.7071681 
3: 5 0.46854928  aud 1.762551 1.920101 0.8258420 
4: 7 0.81240262  aud 1.204612 2.270302 0.9786301 
5: 9 0.54655860  aud 1.359475 2.049097 0.7430328 
6: 2 0.25767250  eur 1.882166 2.210804 0.5696634 
7: 4 0.05638315  eur 1.398488 2.248972 0.1268041 
8: 6 0.48377074  eur 1.669022 1.671420 0.8085842 
9: 8 0.37032054  eur 1.357525 2.381954 0.8820863 
10: 10 0.17026205  eur 1.690291 1.777724 0.3026789 

這裏有一個方法,雖然它不「T推廣以及較大數量的貨幣:

DT[ , cvt := ifelse (currency == 'aud', price*aud, price*eur) ] 
> DT 
    day  price currency  aud  eur  cvt 
1: 1 0.30776611  aud 1.624996 2.035811 0.5001188 
2: 2 0.25767250  eur 1.882166 2.210804 0.5696634 
3: 3 0.55232243  aud 1.280354 2.038349 0.7071681 
4: 4 0.05638315  eur 1.398488 2.248972 0.1268041 
5: 5 0.46854928  aud 1.762551 1.920101 0.8258420 
6: 6 0.48377074  eur 1.669022 1.671420 0.8085842 
7: 7 0.81240262  aud 1.204612 2.270302 0.9786301 
8: 8 0.37032054  eur 1.357525 2.381954 0.8820863 
9: 9 0.54655860  aud 1.359475 2.049097 0.7430328 
10: 10 0.17026205  eur 1.690291 1.777724 0.3026789 

你得到一個警告(和不同的結果,如果你嘗試機智if(.){.}else{.}有:

DT[ , cvt := if (currency == 'aud'){price*aud}else{price*eur}] 

這與data.frames發生的情況完全類似。但是...在data.table中使用ifelse已知很慢。

+0

非常感謝您的回答。我應該注意到我有20多種貨幣,所以我會編輯我的問題。 – pidosaurus

+0

'get()'是我一直在尋找的東西。非常感謝您的支持。 – pidosaurus

1

在這個解決方案,你需要指定不同的貨幣的數量(在這種情況下2)和觀察的數量(在這種情況下10),並且還假定貨幣值('aud','eur'等)是最後一個幾列。

> B_msk <- matrix(rep(DT$currency,2), ncol=2, byrow=TRUE)==matrix(rep(colnames(DT)[-(1:3)], 10), ncol=2) 
> DF <- data.frame(DT) 
> DF$in_USD <- rowSums(DF[colnames(DT)[-(1:3)]]*B_msk*DF$price) 
> DF #or data.table(DF) 
    day  price currency  aud  eur in_USD 
1 1 0.30776611  aud 1.624996 2.035811 0.5001188 
2 2 0.25767250  eur 1.882166 2.210804 0.5696634 
3 3 0.55232243  aud 1.280354 2.038349 0.7071681 
4 4 0.05638315  eur 1.398488 2.248972 0.1268041 
5 5 0.46854928  aud 1.762551 1.920101 0.8258420 
6 6 0.48377074  eur 1.669022 1.671420 0.8085842 
7 7 0.81240262  aud 1.204612 2.270302 0.9786301 
8 8 0.37032054  eur 1.357525 2.381954 0.8820863 
9 9 0.54655860  aud 1.359475 2.049097 0.7430328 
10 10 0.17026205  eur 1.690291 1.777724 0.3026789 

編輯:

希望這個解決方案解決了內存問題,(但仍然需要有一個data.frame數據)

> Idx=cbind(1:10,match(DT[,currency], colnames(DT))) #replace 10 with the actually np. of obs. 
> DF=data.frame(DT) 
> DF 
    day  price currency  aud  eur 
1 1 0.30776611  aud 1.624996 2.035811 
2 2 0.25767250  eur 1.882166 2.210804 
3 3 0.55232243  aud 1.280354 2.038349 
4 4 0.05638315  eur 1.398488 2.248972 
5 5 0.46854928  aud 1.762551 1.920101 
6 6 0.48377074  eur 1.669022 1.671420 
7 7 0.81240262  aud 1.204612 2.270302 
8 8 0.37032054  eur 1.357525 2.381954 
9 9 0.54655860  aud 1.359475 2.049097 
10 10 0.17026205  eur 1.690291 1.777724 
> DF$price*as.numeric(DF[Idx]) #assign it as 'DF$P_in_USD' 
[1] 0.5001187 0.5696634 0.7071682 0.1268041 0.8258420 0.8085841 0.9786299 0.8820865 0.7430327 0.3026789 
+0

非常感謝您的回覆。我非常喜歡創造一個面具矩陣。然而,觀察結果太多(超過100萬),我遇到了內存分配問題。 :(我想知道如果有一種方法基於在'data.table'方式幣值.... – pidosaurus

+0

歡迎您剛剛'lookup'相應的列。看看新的解決方案能夠適應你的RAM 。 –

+0

它很合適,謝謝!然而data.table()的'get()'要快得多,掩碼的想法是我不會忘記的,謝謝! – pidosaurus

1

你有沒有考慮簡單地遍歷貨幣,過濾主要數據框以僅保持給定貨幣的價格,在子集數據框中執行轉換並最終堆疊所有貨幣數據框(或逐步填充在主數據框中的列)