這是一個關于將大型資料集傳遞給類應用函式時的資源和效率的問題。
例子
[編輯:更改示例和描述以說明使用多個表和每個@UWE 評論的計算步驟]
library(dplyr)
#> Warning: package 'dplyr' was built under R version 4.0.5 ...[snip]...
set.seed(10)
# Objective: Add period-cost per portion of person's selected fruit
df.A <- data.frame(
period = rep(1:3, times = 1, each = 4),
Name = rep(c("John", "Paul","Ringo", "George"), times = 3),
Fruit = sample(c("Apple", "Pear", "Banana", "Apple"), size = 12, replace = TRUE)
) # extend to many people, many periods, many fruit
df.B <- data.frame(
Fruit = c("Pear", "Apple", "Banana"),
id = c(1, 2, 3),
pound.per.portion = c(0.396832,0.440925,0.299829)
) # one entry per fruit
df.C <- data.frame(
id = rep(1:3, times=3),
period = rep(1:3, times = 1, each = 3),
price.pound = c(2.33, 0.99, 2.15, 2.38, 1.01, 2.20, 2.42, 1.04, 2.25)
) # one entry per fruit per period
df.A
#> period Name Fruit
#> 1 1 John Banana
#> 2 1 Paul Apple
#> 3 1 Ringo Pear
#> 4 1 George Apple
#> 5 2 John Apple
#> 6 2 Paul Banana
#> 7 2 Ringo Apple
#> 8 2 George Pear
#> 9 3 John Banana
#> 10 3 Paul Banana
#> 11 3 Ringo Banana
#> 12 3 George Apple
df.B
#> Fruit id pound.per.portion
#> 1 Pear 1 0.396832
#> 2 Apple 2 0.440925
#> 3 Banana 3 0.299829
df.C
#> id period price.pound
#> 1 1 1 2.33
#> 2 2 1 0.99
#> 3 3 1 2.15
#> 4 1 2 2.38
#> 5 2 2 1.01
#> 6 3 2 2.20
#> 7 1 3 2.42
#> 8 2 3 1.04
#> 9 3 3 2.25
df.A$portion.price <- apply(df.A, MARGIN = 1,
FUN = function(x, legend, prices){
# please ignore efficiency of this function
# the internal function is not the focus of the question
fruit.info <- df.B[df.B$Fruit == x[["Fruit"]],]
cost <- df.C %>%
filter(period == x[["period"]],
id == fruit.info[["id"]]) %>%
select(price.pound) %>%
`*`(fruit.info$pound.per.portion)
cost[[1]]
},
legend = df.B, prices = df.C)
# Question relates to passing of legend and prices
# if `apply` passes df.B and df.C many times
# and df.B, df.C are large - is this inefficient, is there a better way
head(df.A, 5)
#> period Name Fruit portion.price
#> 1 1 John Banana 0.6446323
#> 2 1 Paul Apple 0.4365157
#> 3 1 Ringo Pear 0.9246186
#> 4 1 George Apple 0.4365157
#> 5 2 John Apple 0.4453343
由reprex 包于 2022-05-20 創建(v2.0.1)
此示例中的目標是向 df.A 添加一列,顯示特定人在特定時期內選擇的水果的部分成本。
存在三組資料,但沒有一個單獨具有計算所需的所有資訊。
df.A包含他們選擇的人、時期和水果的名稱。每個時期的每個人都有一個條目。
df.C具有按時期劃分的水果價格資訊,但價格表示為每磅價格,而不是部分價格,并且資料集不識別水果名稱(只有 id 號)。每個時期的每個水果都有一個條目。
df.B提供缺失的資訊。首先,它定義df.C$id了df.A$Name,它提供了一個將每磅成本轉換為每份成本的因素。每個水果只需要一個條目。
對于 的每一行df.A,apply將人的水果名稱和期間以及兩個參考集 (df.B和df.C) 傳遞給函式。該函式查找df.B用于參考來自 的資料的必要資訊df.C,然后用于計算每部分的成本(回傳)。
該函式本身對這個問題并不重要,只是為了說明使用多個資料集來為每一行查找一個值。
這個例子很簡單(四個人,三個時期,三個水果)并且很容易處理apply;但是,理論上,這些資料集中的每一個都可以包含數千行。
討論主題從這里開始
如果我理解正確,r傳遞值而不是參考。我相信這意味著apply上面示例df.B中的函式df.C為df.A. 假設這是正確的,這感覺效率不高,尤其是在資料集很大的情況下。
在使用大型資料集時,是否有比apply這種查找/處理更好的解決方案?
我知道rcpp函式可以使用參考而不是值。是否會構建一個僅使用參考的自定義rccp函式apply,或者是否有標準的現成方法?
uj5u.com熱心網友回復:
將apply()函式與 data.frames 一起使用有一個主要缺點,因為在繼續之前apply()將 data.frame 強制為矩陣(請參閱Patrick Burns 的The R Inferno 的第 8.2.38 節)。
由于矩陣的所有元素都需要屬于同一型別,因此 data.frame 的所有列都被強制轉換為一種常見的資料型別。
這可以通過以下方式驗證
apply(df.A, MARGIN = 2, str)
chr [1:12] "1" "1" "1" "1" "2" "2" "2" "2" "3" "3" "3" "3" chr [1:12] "John" "Paul" "Ringo" "George" "John" "Paul" "Ringo" "George" "John" "Paul" "Ringo" "George" chr [1:12] "Banana" "Apple" "Pear" "Apple" "Apple" "Banana" "Apple" "Pear" "Banana" "Banana" "Banana" ...
在這里,整數列period也被強制輸入字符。這是昂貴的,并且可能會創建所有資料的副本。
那么我們可以做些什么來實作 OP 的目標:
此示例中的目標是向 df.A 添加一列,顯示特定人在特定時期內選擇的水果的部分成本。
恕我直言,實作目標的最佳方式是加入兩次。
首先,創建一個查找表 lut,其中包含portion.pricefor eachFruit和period。然后,使用更新連接將列附加到df.A:
library(data.table)
lut <- setDT(df.B)[df.C, on = .(id)][, portion.price := pound.per.portion * price.pound][]
setDT(df.A)[lut, on = .(Fruit, period), portion.price := i.portion.price][]
period Name Fruit portion.price 1: 1 John Banana 0.6446323 2: 1 Paul Apple 0.4365157 3: 1 Ringo Pear 0.9246186 4: 1 George Apple 0.4365157 5: 2 John Apple 0.4453343 6: 2 Paul Banana 0.6596238 7: 2 Ringo Apple 0.4453343 8: 2 George Pear 0.9444602 9: 3 John Banana 0.6746152 10: 3 Paul Banana 0.6746152 11: 3 Ringo Banana 0.6746152 12: 3 George Apple 0.4585620
data.table旨在有效處理大型資料集。
或者,可以使用 SQL:
sqldf::sqldf("
select period, Name, Fruit, `portion.price` from `df.A`
left join (
select Fruit, period,
`pound.per.portion` * `price.pound` as `portion.price` from `df.B`
join `df.C` using(id)
) using(period, Fruit)
")
period Name Fruit portion.price 1 1 John Banana 0.6446323 2 1 Paul Apple 0.4365157 3 1 Ringo Pear 0.9246186 4 1 George Apple 0.4365157 5 2 John Apple 0.4453343 6 2 Paul Banana 0.6596238 7 2 Ringo Apple 0.4453343 8 2 George Pear 0.9444602 9 3 John Banana 0.6746152 10 3 Paul Banana 0.6746152 11 3 Ringo Banana 0.6746152 12 3 George Apple 0.4585620
請注意,某些表和列名包含在反引號中,因為句點在 SQL 中具有特殊含義
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/479062.html
