เข้าถึงชื่อดัชนี lapply ภายใน FUN


162

มีวิธีรับชื่อดัชนีรายการในฟังก์ชัน lapply () ของฉันหรือไม่

n = names(mylist)
lapply(mylist, function(list.elem) { cat("What is the name of this list element?\n" })

ฉันถามก่อนว่าเป็นไปได้หรือไม่ที่จะรักษาชื่อดัชนีในรายการคืน lapply () แต่ฉันยังไม่รู้ว่ามีวิธีง่าย ๆ ในการดึงชื่อองค์ประกอบแต่ละรายการภายในฟังก์ชันที่กำหนดเองหรือไม่ ฉันต้องการหลีกเลี่ยงการโทรไปที่ชื่อตัวเองอย่าง lapply ฉันควรได้รับชื่อในพารามิเตอร์ฟังก์ชัน


มีเคล็ดลับอีกอย่างหนึ่งพร้อมแอตทริบิวต์ ดูที่นี่: stackoverflow.com/questions/4164960/…ซึ่งคล้ายกับสิ่งที่ DWIN มี แต่แตกต่างกัน :)
Roman Luštrik

คำตอบ:


161

น่าเสียดายที่lapplyให้องค์ประกอบของเวกเตอร์ที่คุณส่งผ่านไปเท่านั้น วิธีแก้ไขคือการส่งชื่อหรือดัชนีของเวกเตอร์แทนเวกเตอร์เอง

แต่โปรดทราบว่าคุณสามารถส่งผ่านอาร์กิวเมนต์พิเศษไปยังฟังก์ชันดังนั้นการทำงานต่อไปนี้:

x <- list(a=11,b=12,c=13) # Changed to list to address concerns in commments
lapply(seq_along(x), function(y, n, i) { paste(n[[i]], y[[i]]) }, y=x, n=names(x))

นี่ผมใช้lapplyกว่าดัชนีของxแต่ยังผ่านและชื่อของx xอย่างที่คุณเห็นลำดับของฟังก์ชันอากิวเมนต์สามารถเป็นอะไรก็ได้ - lapplyจะผ่านใน "องค์ประกอบ" (นี่คือดัชนี) ไปยังอาร์กิวเมนต์แรกที่ไม่ได้ระบุในอาร์กิวเมนต์พิเศษ ในกรณีนี้ฉันระบุyและnดังนั้นiเหลือเพียง...

ซึ่งผลิตสิ่งต่อไปนี้:

[[1]]
[1] "a 11"

[[2]]
[1] "b 12"

[[3]]
[1] "c 13"

อัปเดตตัวอย่างที่เรียบง่ายผลลัพธ์เดียวกัน:

lapply(seq_along(x), function(i) paste(names(x)[[i]], x[[i]]))

ฟังก์ชันใช้ตัวแปร "ส่วนกลาง" ที่นี่xและแยกชื่อในการโทรแต่ละครั้ง


พารามิเตอร์ 'i' ถูกเตรียมใช้งานในฟังก์ชันที่กำหนดเองอย่างไร
Robert Kubrick

เข้าใจแล้วดังนั้น lapply () ใช้กับองค์ประกอบที่ส่งคืนโดย seq_along จริงๆ ฉันสับสนเพราะพารามิเตอร์ฟังก์ชันที่กำหนดเองถูกจัดลำดับใหม่ โดยปกติอิลิเมนต์รายการที่ทำซ้ำคือพารามิเตอร์แรก
Robert Kubrick

อัปเดตคำตอบและเปลี่ยนฟังก์ชั่นแรกที่จะใช้yแทนxเพื่อที่จะได้ (หวังว่า) ชัดเจนยิ่งขึ้นว่าฟังก์ชั่นนี้สามารถเรียกว่าเป็นอาร์กิวเมนต์อะไรก็ได้ 11,12,13ยังมีการเปลี่ยนแปลงค่าเวกเตอร์
Tommy

@RobertKubrick - ใช่ฉันอาจพยายามแสดงหลายสิ่งหลายอย่างพร้อมกัน ... คุณสามารถตั้งชื่อข้อโต้แย้งอะไรก็ได้และมีให้ในลำดับใดก็ได้
Tommy

@DWin - ฉันคิดว่ามันถูกต้อง (และนำไปใช้กับรายการเช่นกัน) ;-) ... แต่โปรดพิสูจน์ฉันผิด!
Tommy

48

วิธีนี้ใช้วิธีแก้ปัญหาแบบเดียวกับ Tommy แต่Map()ไม่จำเป็นต้องเข้าถึงตัวแปรทั่วโลกที่เก็บชื่อของรายการส่วนประกอบ

> x <- list(a=11, b=12, c=13)
> Map(function(x, i) paste(i, x), x, names(x))
$a
[1] "a 11"

$b
[1] "b 12"

$c
[1] "c 13

หรือถ้าคุณต้องการ mapply()

> mapply(function(x, i) paste(i, x), x, names(x))
     a      b      c 
"a 11" "b 12" "c 13"

นี่เป็นทางออกที่ดีที่สุดของพวง
emilBeBri

เมื่อใช้mapply()งานให้สังเกตSIMPLIFYตัวเลือกที่มีค่าเริ่มต้นเป็นจริง ในกรณีของฉันนั่นทำให้ทั้งหมดกลายเป็นเมทริกซ์ขนาดใหญ่เมื่อฉันต้องการใช้รายการแบบง่าย การตั้งค่าเป็นF(ภายในmapply()) ทำให้มันทำงานได้ตามที่ต้องการ
JJ เพื่อความโปร่งใสและโมนิก้า

39

อัพเดทสำหรับ R เวอร์ชั่น 3.2

คำเตือน: นี่เป็นกลลวงที่แฮ็คและอาจหยุดทำงานในรุ่นถัดไป

คุณสามารถรับดัชนีโดยใช้สิ่งนี้:

> lapply(list(a=10,b=20), function(x){parent.frame()$i[]})
$a
[1] 1

$b
[1] 2

หมายเหตุ: สิ่ง[]นี้เป็นสิ่งจำเป็นสำหรับการทำงานเนื่องจากมันหลอกให้คิดว่าสัญลักษณ์i(ที่อยู่ในกรอบการประเมินของlapply) อาจมีการอ้างอิงมากกว่าดังนั้นจึงเป็นการเปิดใช้งานการทำสำเนาที่ขี้เกียจของมัน หากไม่มีมัน R จะไม่แยกสำเนาของi:

> lapply(list(a=10,b=20), function(x){parent.frame()$i})
$a
[1] 2

$b
[1] 2

เทคนิคที่แปลกใหม่อื่น ๆ ที่สามารถนำมาใช้เหมือนหรือfunction(x){parent.frame()$i+0}function(x){--parent.frame()$i}

ผลกระทบต่อประสิทธิภาพ

การทำสำเนาบังคับจะทำให้ประสิทธิภาพลดลงหรือไม่ ใช่ นี่คือมาตรฐาน:

> x <- as.list(seq_len(1e6))

> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.38 0.00 2.37
> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.45 0.00 2.45
> system.time( y <- lapply(x, function(x){parent.frame()$i[]}) )
user system elapsed
2.41 0.00 2.41
> y[[2]]
[1] 2

> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
1.92 0.00 1.93
> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
2.07 0.00 2.09
> system.time( y <- lapply(x, function(x){parent.frame()$i}) )
user system elapsed
1.89 0.00 1.89
> y[[2]]
[1] 1000000

ข้อสรุป

คำตอบนี้แสดงให้เห็นว่าคุณไม่ควรใช้สิ่งนี้ ... ไม่เพียง แต่รหัสของคุณจะสามารถอ่านได้มากขึ้นหากคุณพบวิธีการแก้ปัญหาอื่น ๆ เช่น Tommy ด้านบนและเข้ากันได้กับรุ่นต่อไปในอนาคต พัฒนา!


กลโกงรุ่นเก่าไม่ทำงานอีกต่อไป:

> lapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])

ผลลัพธ์:

$a
[1] 1

$b
[1] 2

$c
[1] 3

คำอธิบาย: lapplyสร้างสายของรูปแบบFUN(X[[1L]], ...), FUN(X[[2L]], ...)ฯลฯ ดังนั้นการโต้แย้งมันผ่านไปเป็นX[[i]]ที่iเป็นดัชนีปัจจุบันในวง หากเราได้รับสิ่งนี้ก่อนที่จะถูกประเมิน (เช่นถ้าเราใช้substitute) เราจะได้รับนิพจน์ที่X[[i]]ไม่ได้ประเมินค่า นี่คือการเรียกใช้[[ฟังก์ชันโดยมีอาร์กิวเมนต์X(สัญลักษณ์) และi (จำนวนเต็ม) ดังนั้นsubstitute(x)[[3]]ส่งกลับจำนวนเต็มนี้อย่างแม่นยำ

มีดัชนีคุณสามารถเข้าถึงชื่อได้เล็กน้อยถ้าคุณบันทึกไว้ก่อนหน้านี้:

L <- list(a=10,b=10,c=10)
n <- names(L)
lapply(L, function(x)n[substitute(x)[[3]]])

ผลลัพธ์:

$a
[1] "a"

$b
[1] "b"

$c
[1] "c"

หรือใช้เคล็ดลับที่สองนี้ :-)

lapply(list(a=10,b=10,c=10), function(x)names(eval(sys.call(1)[[2]]))[substitute(x)[[3]]])

(ผลลัพธ์เหมือนกัน)

คำอธิบายที่ 2: sys.call(1)ผลตอบแทนlapply(...)เพื่อให้เป็นสำนวนที่ใช้เป็นอาร์กิวเมนต์รายการsys.call(1)[[2]] lapplyผ่านสิ่งนี้เพื่อevalสร้างวัตถุที่ถูกต้องตามกฎหมายที่namesสามารถเข้าถึงได้ ยุ่งยาก แต่ก็ใช้ได้ผล

โบนัส: วิธีที่สองในการรับชื่อ:

lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])

ทราบว่าXเป็นวัตถุที่ถูกต้องในกรอบแม่ของFUNและการอ้างอิงอาร์กิวเมนต์รายการเพื่อให้เราสามารถได้รับไปด้วยlapplyeval.parent


2
รหัสlapply(list(a=10,b=10,c=10), function(x)substitute(x)[[3]])กำลังส่งคืนทั้งหมดให้เป็น 3 คุณจะอธิบายวิธีเลือก 3 รายการนี้ได้อย่างไร และเหตุผลสำหรับความแตกต่าง? มันเท่ากับความยาวของรายการในกรณีนี้ 3. ขออภัยถ้านี่เป็นคำถามพื้นฐาน แต่ต้องการทราบวิธีการใช้สิ่งนี้ในกรณีทั่วไป
Anusha

@Anusha รูปแบบนั้นไม่ทำงานอีกต่อไป ... แต่lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])ผลงาน ... ฉันจะตรวจสอบสิ่งที่เกิดขึ้น
Ferdinand.kraft

@ Ferdinand.kraft lapply(list(a=10,b=10,c=10), function(x)eval.parent(quote(names(X)))[substitute(x)[[3]]])ไม่ทำงานอีกต่อไปและให้ข้อผิดพลาดError in eval.parent(quote(names(X)))[substitute(x)[[3]]] : invalid subscript type 'symbol'มีวิธีง่าย ๆ ในการแก้ไขปัญหานี้หรือไม่
ทำนาย

ขอบคุณมาก @ Ferdinand.kraft
ทำนาย

18

ฉันมีปัญหาเดียวกันหลายครั้ง ... ฉันเริ่มใช้วิธีอื่น ... แทนที่จะใช้lapplyฉันเริ่มใช้mapply

n = names(mylist)
mapply(function(list.elem, names) { }, list.elem = mylist, names = n)

2
ฉันยังชอบนี้ แต่คำตอบนี้เป็นซ้ำของหนึ่งก่อนหน้านี้
merv

13

คุณสามารถลองใช้imap()จากpurrrแพ็คเกจ

จากเอกสาร:

imap (x, ... ) นั้นย่อมาจาก map2 (x, names (x), ... ) ถ้า x มีชื่อหรือ map2 (x, seq_along (x), ... ) หากไม่มี

ดังนั้นคุณสามารถใช้วิธีนี้ได้:

library(purrr)
myList <- list(a=11,b=12,c=13) 
imap(myList, function(x, y) paste(x, y))

ซึ่งจะให้ผลลัพธ์ดังต่อไปนี้:

$a
[1] "11 a"

$b
[1] "12 b"

$c
[1] "13 c"

10

แค่วนในชื่อ

sapply(names(mylist), function(n) { 
    doSomething(mylist[[n]])
    cat(n, '\n')
}

นี่เป็นทางออกที่ง่ายที่สุดอย่างแน่นอน
บิน

1
@flies: ใช่ยกเว้นมันเป็นการปฏิบัติที่ไม่ดีกับตัวแปรรหัสยากmylistๆ ยังดีกว่าที่จะทำfunction(mylist, nm) ...
smci

5

คำตอบของทอมมี่นำไปใช้กับเวกเตอร์ที่มีชื่อ แต่ฉันมีความคิดที่คุณสนใจในรายการ และดูเหมือนว่าเขากำลังทำสิ่งรอบตัวเพราะเขาอ้างถึง "x" จากสภาพแวดล้อมการโทร ฟังก์ชั่นนี้ใช้เฉพาะพารามิเตอร์ที่ถูกส่งผ่านไปยังฟังก์ชั่นดังนั้นจึงไม่มีข้อสมมติฐานเกี่ยวกับชื่อของวัตถุที่ถูกส่งผ่าน:

x <- list(a=11,b=12,c=13)
lapply(x, function(z) { attributes(deparse(substitute(z)))$names  } )
#--------
$a
NULL

$b
NULL

$c
NULL
#--------
 names( lapply(x, function(z) { attributes(deparse(substitute(z)))$names  } ))
#[1] "a" "b" "c"
 what_is_my_name <- function(ZZZ) return(deparse(substitute(ZZZ)))
 what_is_my_name(X)
#[1] "X"
what_is_my_name(ZZZ=this)
#[1] "this"
 exists("this")
#[1] FALSE

ฟังก์ชั่นของคุณจะกลับมาNULL?! ดังนั้นlapply(x, function(x) NULL)จะช่วยให้คำตอบเดียวกัน ...
ทอมมี่

โปรดทราบว่าlapplyจะเพิ่มชื่อจากxในผลลัพธ์ภายหลังเสมอ
Tommy

ใช่. ยอมรับว่าเป็นบทเรียนของการออกกำลังกายนี้
IRTFM

4

คำตอบของฉันไปในทิศทางเดียวกับ Tommy และ caracals แต่หลีกเลี่ยงการบันทึกรายการเป็นวัตถุเพิ่มเติม

lapply(seq(3), function(i, y=list(a=14,b=15,c=16)) { paste(names(y)[[i]], y[[i]]) })

ผลลัพธ์:

[[1]]
[1] "a 14"

[[2]]
[1] "b 15"

[[3]]
[1] "c 16"

สิ่งนี้ทำให้รายการเป็นอาร์กิวเมนต์ที่ตั้งชื่อเพื่อความสนุก (แทนที่จะเป็น lapply) lapply จะต้องวนซ้ำองค์ประกอบของรายการเท่านั้น (โปรดระมัดระวังในการเปลี่ยนอาร์กิวเมนต์แรกนี้เป็น lapply เมื่อเปลี่ยนความยาวของรายการ)

หมายเหตุ: การให้รายการโดยตรงไปยัง lapply เนื่องจากอาร์กิวเมนต์เพิ่มเติมยังใช้งานได้:

lapply(seq(3), function(i, y) { paste(names(y)[[i]], y[[i]]) }, y=list(a=14,b=15,c=16))

3

ทั้ง @caracals และ @Tommy เป็นคำตอบที่ดีและนี่คือตัวอย่างรวมถึงlist´s และdata.frame.s
rเป็นlistของlistands และdata.frame´s ( dput(r[[1]]ในตอนท้าย)

names(r)
[1] "todos"  "random"
r[[1]][1]
$F0
$F0$rst1
   algo  rst  prec  rorac prPo pos
1  Mean 56.4 0.450 25.872 91.2 239
6  gbm1 41.8 0.438 22.595 77.4 239
4  GAM2 37.2 0.512 43.256 50.0 172
7  gbm2 36.8 0.422 18.039 85.4 239
11 ran2 35.0 0.442 23.810 61.5 239
2  nai1 29.8 0.544 52.281 33.1 172
5  GAM3 28.8 0.403 12.743 94.6 239
3  GAM1 21.8 0.405 13.374 68.2 239
10 ran1 19.4 0.406 13.566 59.8 239
9  svm2 14.0 0.385  7.692 76.2 239
8  svm1  0.8 0.359  0.471 71.1 239

$F0$rst5
   algo  rst  prec  rorac prPo pos
1  Mean 52.4 0.441 23.604 92.9 239
7  gbm2 46.4 0.440 23.200 83.7 239
6  gbm1 31.2 0.416 16.421 79.5 239
5  GAM3 28.8 0.403 12.743 94.6 239
4  GAM2 28.2 0.481 34.815 47.1 172
11 ran2 26.6 0.422 18.095 61.5 239
2  nai1 23.6 0.519 45.385 30.2 172
3  GAM1 20.6 0.398 11.381 75.7 239
9  svm2 14.4 0.386  8.182 73.6 239
10 ran1 14.0 0.390  9.091 64.4 239
8  svm1  6.2 0.370  3.584 72.4 239

มีวัตถุประสงค์เพื่อunlistทุกรายการโดยใส่ลำดับของlistชื่อเป็นคอลัมน์เพื่อระบุกรณีและปัญหา

r=unlist(unlist(r,F),F)
names(r)
[1] "todos.F0.rst1"  "todos.F0.rst5"  "todos.T0.rst1"  "todos.T0.rst5"  "random.F0.rst1" "random.F0.rst5"
[7] "random.T0.rst1" "random.T0.rst5"

ไม่แสดงรายการ แต่ไม่ใช่data.frameของ

ra=Reduce(rbind,Map(function(x,y) cbind(case=x,y),names(r),r))

Mapใส่ลำดับของชื่อเป็นคอลัมน์ Reduceเข้าร่วมdata.frame´s ทั้งหมด

head(ra)
            case algo  rst  prec  rorac prPo pos
1  todos.F0.rst1 Mean 56.4 0.450 25.872 91.2 239
6  todos.F0.rst1 gbm1 41.8 0.438 22.595 77.4 239
4  todos.F0.rst1 GAM2 37.2 0.512 43.256 50.0 172
7  todos.F0.rst1 gbm2 36.8 0.422 18.039 85.4 239
11 todos.F0.rst1 ran2 35.0 0.442 23.810 61.5 239
2  todos.F0.rst1 nai1 29.8 0.544 52.281 33.1 172

ป.ล. r[[1]]:

    structure(list(F0 = structure(list(rst1 = structure(list(algo = c("Mean", 
    "gbm1", "GAM2", "gbm2", "ran2", "nai1", "GAM3", "GAM1", "ran1", 
    "svm2", "svm1"), rst = c(56.4, 41.8, 37.2, 36.8, 35, 29.8, 28.8, 
    21.8, 19.4, 14, 0.8), prec = c(0.45, 0.438, 0.512, 0.422, 0.442, 
    0.544, 0.403, 0.405, 0.406, 0.385, 0.359), rorac = c(25.872, 
    22.595, 43.256, 18.039, 23.81, 52.281, 12.743, 13.374, 13.566, 
    7.692, 0.471), prPo = c(91.2, 77.4, 50, 85.4, 61.5, 33.1, 94.6, 
    68.2, 59.8, 76.2, 71.1), pos = c(239L, 239L, 172L, 239L, 239L, 
    172L, 239L, 239L, 239L, 239L, 239L)), .Names = c("algo", "rst", 
    "prec", "rorac", "prPo", "pos"), row.names = c(1L, 6L, 4L, 7L, 
    11L, 2L, 5L, 3L, 10L, 9L, 8L), class = "data.frame"), rst5 = structure(list(
        algo = c("Mean", "gbm2", "gbm1", "GAM3", "GAM2", "ran2", 
        "nai1", "GAM1", "svm2", "ran1", "svm1"), rst = c(52.4, 46.4, 
        31.2, 28.8, 28.2, 26.6, 23.6, 20.6, 14.4, 14, 6.2), prec = c(0.441, 
        0.44, 0.416, 0.403, 0.481, 0.422, 0.519, 0.398, 0.386, 0.39, 
        0.37), rorac = c(23.604, 23.2, 16.421, 12.743, 34.815, 18.095, 
        45.385, 11.381, 8.182, 9.091, 3.584), prPo = c(92.9, 83.7, 
        79.5, 94.6, 47.1, 61.5, 30.2, 75.7, 73.6, 64.4, 72.4), pos = c(239L, 
        239L, 239L, 239L, 172L, 239L, 172L, 239L, 239L, 239L, 239L
        )), .Names = c("algo", "rst", "prec", "rorac", "prPo", "pos"
    ), row.names = c(1L, 7L, 6L, 5L, 4L, 11L, 2L, 3L, 9L, 10L, 8L
    ), class = "data.frame")), .Names = c("rst1", "rst5")), T0 = structure(list(
        rst1 = structure(list(algo = c("Mean", "ran1", "GAM1", "GAM2", 
        "gbm1", "svm1", "nai1", "gbm2", "svm2", "ran2"), rst = c(22.6, 
        19.4, 13.6, 10.2, 9.6, 8, 5.6, 3.4, -0.4, -0.6), prec = c(0.478, 
        0.452, 0.5, 0.421, 0.423, 0.833, 0.429, 0.373, 0.355, 0.356
        ), rorac = c(33.731, 26.575, 40, 17.895, 18.462, 133.333, 
        20, 4.533, -0.526, -0.368), prPo = c(34.4, 52.1, 24.3, 40.7, 
        37.1, 3.1, 14.4, 53.6, 54.3, 116.4), pos = c(195L, 140L, 
        140L, 140L, 140L, 195L, 195L, 140L, 140L, 140L)), .Names = c("algo", 
        "rst", "prec", "rorac", "prPo", "pos"), row.names = c(1L, 
        9L, 3L, 4L, 5L, 7L, 2L, 6L, 8L, 10L), class = "data.frame"), 
        rst5 = structure(list(algo = c("gbm1", "ran1", "Mean", "GAM1", 
        "GAM2", "svm1", "nai1", "svm2", "gbm2", "ran2"), rst = c(17.6, 
        16.4, 15, 12.8, 9, 6.2, 5.8, -2.6, -3, -9.2), prec = c(0.466, 
        0.434, 0.435, 0.5, 0.41, 0.8, 0.44, 0.346, 0.345, 0.337), 
            rorac = c(30.345, 21.579, 21.739, 40, 14.754, 124, 23.2, 
            -3.21, -3.448, -5.542), prPo = c(41.4, 54.3, 35.4, 22.9, 
            43.6, 2.6, 12.8, 57.9, 62.1, 118.6), pos = c(140L, 140L, 
            195L, 140L, 140L, 195L, 195L, 140L, 140L, 140L)), .Names = c("algo", 
        "rst", "prec", "rorac", "prPo", "pos"), row.names = c(5L, 
        9L, 1L, 3L, 4L, 7L, 2L, 8L, 6L, 10L), class = "data.frame")), .Names = c("rst1", 
    "rst5"))), .Names = c("F0", "T0"))

0

สมมติว่าเราต้องการคำนวณความยาวของแต่ละองค์ประกอบ

mylist <- list(a=1:4,b=2:9,c=10:20)
mylist

$a
[1] 1 2 3 4

$b
[1] 2 3 4 5 6 7 8 9

$c
 [1] 10 11 12 13 14 15 16 17 18 19 20

หากเป้าหมายคือการติดป้ายองค์ประกอบที่เกิดขึ้นผลงานนั้นlapply(mylist,length)หรือต่ำกว่านั้น

sapply(mylist,length,USE.NAMES=T)

 a  b  c 
 4  8 11 

หากเป้าหมายคือการใช้ฉลากภายในฟังก์ชั่นก็mapply()มีประโยชน์โดยการวนลูปมากกว่าสองวัตถุ องค์ประกอบรายการและชื่อรายการ

fun <- function(x,y) paste0(length(x),"_",y)
mapply(fun,mylist,names(mylist))

     a      b      c 
 "4_a"  "8_b" "11_c" 

0

@ ferdinand-Kraft ให้เคล็ดลับที่ยอดเยี่ยมแก่เราแล้วบอกเราว่าเราไม่ควรใช้เพราะมันไม่มีเอกสารและเพราะค่าใช้จ่ายด้านประสิทธิภาพ

ฉันไม่สามารถโต้เถียงได้มากในประเด็นแรก แต่ฉันอยากทราบว่าค่าใช้จ่ายไม่น่าเป็นห่วง

ลองกำหนดฟังก์ชั่นที่ใช้งานอยู่เพื่อที่เราจะได้ไม่ต้องเรียกใช้นิพจน์ที่ซับซ้อน parent.frame()$i[]แต่เพียงอย่างเดียว.i()เราจะสร้าง.n()การเข้าถึงชื่อซึ่งควรจะใช้กับทั้งฟังก์ชันพื้นฐานและฟังก์ชันpurrr (และอาจเป็นฟังก์ชันอื่น ๆ เช่นกัน)

.i <- function() parent.frame(2)$i[]
# looks for X OR .x to handle base and purrr functionals
.n <- function() {
  env <- parent.frame(2)
  names(c(env$X,env$.x))[env$i[]]
}

sapply(cars, function(x) paste(.n(), .i()))
#>     speed      dist 
#> "speed 1"  "dist 2"

ตอนนี้เรามาลองเปรียบเทียบฟังก์ชันที่เรียบง่ายซึ่งวางรายการของเวกเตอร์ลงในดัชนีโดยใช้วิธีการที่แตกต่างกัน paste(vec, seq_along(vec))ได้

เรากำหนดฟังก์ชันการเปรียบเทียบและฟังก์ชั่นการพล็อตและพล็อตผลลัพธ์ด้านล่าง:

library(purrr)
library(ggplot2)
benchmark_fun <- function(n){
  vec <- sample(letters,n, replace = TRUE)
  mb <- microbenchmark::microbenchmark(unit="ms",
                                      lapply(vec, function(x)  paste(x, .i())),
                                      map(vec, function(x) paste(x, .i())),
                                      lapply(seq_along(vec), function(x)  paste(vec[[x]], x)),
                                      mapply(function(x,y) paste(x, y), vec, seq_along(vec), SIMPLIFY = FALSE),
                                      imap(vec, function(x,y)  paste(x, y)))
  cbind(summary(mb)[c("expr","mean")], n = n)
}

benchmark_plot <- function(data, title){
  ggplot(data, aes(n, mean, col = expr)) + 
    geom_line() +
    ylab("mean time in ms") +
    ggtitle(title) +
    theme(legend.position = "bottom",legend.direction = "vertical")
}

plot_data <- map_dfr(2^(0:15), benchmark_fun)
benchmark_plot(plot_data[plot_data$n <= 100,], "simplest call for low n")

benchmark_plot(plot_data,"simplest call for higher n")

สร้างเมื่อ 2019-11-15 โดยแพ็คเกจ reprex (v0.3.0)

การลดลงที่จุดเริ่มต้นของแผนภูมิแรกนั้นเป็นความบังเอิญโปรดละเว้นมัน

เราเห็นว่าคำตอบที่เลือกนั้นเร็วกว่าและสำหรับการทำซ้ำในปริมาณที่เหมาะสม.i()โซลูชั่นของเราจะช้ากว่าค่าใช้จ่ายเมื่อเปรียบเทียบกับคำตอบที่เลือกคือประมาณ 3 เท่าของค่าใช้จ่ายในการใช้purrr::imap()และจำนวนประมาณ 25 มิลลิวินาทีสำหรับการทำซ้ำ 30k ฉันเลยเสียประมาณ 1 ms ต่อ 1,000 รอบซ้ำ 1 วินาทีต่อล้าน นั่นเป็นค่าใช้จ่ายเล็กน้อยเพื่อความสะดวกในความคิดของฉัน


-1

เพียงแค่เขียนlapplyฟังก์ชั่นที่กำหนดเองของคุณเอง

lapply2 <- function(X, FUN){
  if( length(formals(FUN)) == 1 ){
    # No index passed - use normal lapply
    R = lapply(X, FUN)
  }else{
    # Index passed
    R = lapply(seq_along(X), FUN=function(i){
      FUN(X[[i]], i)
    })
  }

  # Set names
  names(R) = names(X)
  return(R)
}

จากนั้นใช้ดังนี้:

lapply2(letters, function(x, i) paste(x, i))

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