ความแตกต่างระหว่าง lapply และ do.call คืออะไร


143

ฉันเรียนรู้ R เร็ว ๆ นี้และสับสนโดยทั้งสองฟังก์ชั่นและlapply do.callดูเหมือนว่าพวกเขาจะคล้ายกับmapฟังก์ชั่นใน Lisp แต่ทำไมมีสองฟังก์ชั่นที่มีชื่อต่างกัน? ทำไม R ไม่ใช้ฟังก์ชั่นที่เรียกว่าmap?

คำตอบ:


126

มีฟังก์ชั่นที่เรียกMapว่าอาจคล้ายกับแผนที่ในภาษาอื่น ๆ :

  • lapply ส่งคืนรายการความยาวเดียวกันกับ X แต่ละองค์ประกอบซึ่งเป็นผลลัพธ์ของการใช้ความสนุกกับองค์ประกอบที่สอดคล้องกันของ X

  • do.call สร้างและดำเนินการเรียกใช้ฟังก์ชั่นจากชื่อหรือฟังก์ชั่นและรายการของข้อโต้แย้งที่จะส่งผ่านไปยังมัน

  • Mapใช้ฟังก์ชันกับองค์ประกอบที่สอดคล้องกันของเวกเตอร์ที่ให้ ... Mapเป็นตัวห่อหุ้มอย่างง่ายmapplyซึ่งไม่ได้พยายามทำให้ผลลัพธ์ง่ายขึ้นคล้ายกับ mapcar ทั่วไปของ Lisp (โดยมีข้อโต้แย้งที่ถูกนำมารีไซเคิล) เวอร์ชันในอนาคตอาจอนุญาตการควบคุมประเภทผลลัพธ์บางอย่าง


  1. Map เป็นเสื้อคลุมรอบ ๆ mapply
  2. lapply เป็นกรณีพิเศษของ mapply
  3. ดังนั้นMapและlapplyจะคล้ายกันในหลายกรณี

ตัวอย่างเช่นนี่คือlapply:

lapply(iris, class)
$Sepal.Length
[1] "numeric"

$Sepal.Width
[1] "numeric"

$Petal.Length
[1] "numeric"

$Petal.Width
[1] "numeric"

$Species
[1] "factor"

และเช่นเดียวกันโดยใช้Map:

Map(class, iris)
$Sepal.Length
[1] "numeric"

$Sepal.Width
[1] "numeric"

$Petal.Length
[1] "numeric"

$Petal.Width
[1] "numeric"

$Species
[1] "factor"

do.callใช้ฟังก์ชั่นเป็นอินพุตและกระจายข้อโต้แย้งอื่น ๆ ไปยังฟังก์ชั่น มันถูกใช้กันอย่างแพร่หลายตัวอย่างเช่นการรวบรวมรายการเป็นโครงสร้างที่เรียบง่าย (มักจะมีrbindหรือcbind)

ตัวอย่างเช่น:

x <- lapply(iris, class)
do.call(c, x)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
   "numeric"    "numeric"    "numeric"    "numeric"     "factor" 

4
จริง ๆ แล้วฉันพบว่าdo.callเกือบจะเหมือนกับapplyใน Lisp
Hanfei Sun

ไม่ได้ว่าตัวอย่างที่ผ่านมาควรจะเป็นdo.call(cbind, x)รุ่นปัจจุบันให้ฉันError in do.call(c, x) : 'what' must be a function or character string...
sindri_baldur

1
@snoram ตัวอย่างนั้นยังคงใช้ได้ ฟังก์ชั่นcbind()นั้นแตกต่างจากฟังก์ชั่นc()และแม้ว่าจะใช้งานได้ แต่ก็ให้ผลลัพธ์ที่แตกต่าง
Andrie

61

lapplyใช้ฟังก์ชั่นมากกว่ารายการdo.callเรียกใช้ฟังก์ชั่นที่มีรายการข้อโต้แย้ง ดูเหมือนว่าจะแตกต่างสำหรับฉัน ...

ในการให้ตัวอย่างกับรายการ:

X <- list(1:3,4:6,7:9)

ด้วย lapply คุณจะได้รับค่าเฉลี่ยของทุกองค์ประกอบในรายการดังนี้:

> lapply(X,mean)
[[1]]
[1] 2

[[2]]
[1] 5

[[3]]
[1] 8

do.call ให้ข้อผิดพลาดตามที่คาดว่าอาร์กิวเมนต์ "trim" จะเป็น 1

ในทางกลับกันการrbindโยงข้อโต้แย้งทั้งหมด ดังนั้นในการผูก X rowwise คุณต้อง:

> do.call(rbind,X)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

หากคุณต้องการใช้lapplyR จะใช้rbindกับทุกองค์ประกอบของรายการโดยให้เรื่องไร้สาระนี้แก่คุณ:

> lapply(X,rbind)
[[1]]
     [,1] [,2] [,3]
[1,]    1    2    3

[[2]]
     [,1] [,2] [,3]
[1,]    4    5    6

[[3]]
     [,1] [,2] [,3]
[1,]    7    8    9

ในการมีบางสิ่งบางอย่างเช่นแผนที่คุณต้องการ?mapplyซึ่งเป็นสิ่งที่แตกต่างกันโดยสิ้นเชิง วิธีรับเช่นค่าเฉลี่ยของทุกองค์ประกอบใน X แต่ด้วยการตัดแต่งที่แตกต่างกันคุณสามารถใช้:

> mapply(mean,X,trim=c(0,0.5,0.1))
[1] 2 5 8

34

lapplyคือการที่คล้ายกันmap, do.callไม่ได้ lapplyใช้ฟังก์ชันกับองค์ประกอบทั้งหมดของรายการdo.callเรียกใช้ฟังก์ชันที่อาร์กิวเมนต์ของฟังก์ชันทั้งหมดอยู่ในรายการ ดังนั้นสำหรับnรายการองค์ประกอบlapplyมีnการเรียกใช้ฟังก์ชันและdo.callมีการเรียกใช้ฟังก์ชันเพียงครั้งเดียว ดังนั้นค่อนข้างแตกต่างจากdo.call lapplyหวังว่านี่จะช่วยชี้แจงปัญหาของคุณ

ตัวอย่างรหัส:

do.call(sum, list(c(1, 2, 4, 1, 2), na.rm = TRUE))

และ:

lapply(c(1, 2, 4, 1, 2), function(x) x + 1)

25

ด้วยคำพูดที่ง่ายที่สุด:

  1. lapply ()ใช้ฟังก์ชันที่กำหนดสำหรับแต่ละองค์ประกอบในรายการดังนั้นจะมีการเรียกใช้ฟังก์ชันหลายอย่าง

  2. do.call ()ใช้ฟังก์ชั่นที่กำหนดให้กับรายการโดยรวมดังนั้นจึงมีการเรียกใช้ฟังก์ชั่นเดียวเท่านั้น

วิธีที่ดีที่สุดในการเรียนรู้คือเล่นกับตัวอย่างฟังก์ชั่นในเอกสาร R


12

lapply()เป็นฟังก์ชั่นคล้ายแผนที่ do.call()แตกต่างกัน มันถูกใช้สำหรับการส่งผ่านข้อโต้แย้งไปยังฟังก์ชั่นในรูปแบบรายการแทนที่จะให้พวกเขาระบุ ตัวอย่างเช่น

> do.call("+",list(4,5))
[1] 9

10

แม้ว่าจะมีคำตอบมากมาย แต่นี่คือตัวอย่างของฉันสำหรับการอ้างอิง สมมติว่าเรามีรายการข้อมูลดังนี้:

L=list(c(1,2,3), c(4,5,6))

ฟังก์ชันส่งคืนรายการอย่าง lapply

lapply(L, sum) 

ด้านบนหมายถึงสิ่งที่ต้องการด้านล่าง

list( sum( L[[1]]) , sum( L[[2]]))

ตอนนี้ให้เราทำสิ่งเดียวกันสำหรับ do.call

do.call(sum, L) 

มันหมายถึง

sum( L[[1]], L[[2]])

ในตัวอย่างของเรามันจะคืนค่า 21 ในระยะสั้น lapply ส่งคืนรายการเสมอในขณะที่ชนิดส่งคืนของ do.call ขึ้นอยู่กับฟังก์ชันที่ถูกเรียกใช้


5

ความแตกต่างระหว่างทั้งสองคือ:

lapply(1:n,function,parameters)

=> สิ่งนี้จะส่ง 1, พารามิเตอร์ของฟังก์ชั่น => สิ่งนี้จะส่ง 2, พารามิเตอร์ของฟังก์ชันและอื่น ๆ

do.call 

เพียงแค่ส่ง 1 … n เป็นเวกเตอร์และพารามิเตอร์ไปยังฟังก์ชัน

ดังนั้นในการสมัครคุณมีการเรียกใช้ฟังก์ชัน n ใน do.call คุณมีเพียงครั้งเดียว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.