ใช้ฟังก์ชันกับทุกแถวของเมทริกซ์หรือกรอบข้อมูล


130

สมมติว่าฉันมีเมทริกซ์คูณ 2 และฟังก์ชันที่ใช้เวกเตอร์ 2 ตัวเป็นหนึ่งในอาร์กิวเมนต์ของมัน ฉันต้องการใช้ฟังก์ชันกับแต่ละแถวของเมทริกซ์และรับ n-vector วิธีการทำใน R?

ตัวอย่างเช่นฉันต้องการคำนวณความหนาแน่นของการแจกแจงแบบปกติมาตรฐาน 2 มิติในสามจุด:

bivariate.density(x = c(0, 0), mu = c(0, 0), sigma = c(1, 1), rho = 0){
    exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+x[2]^2/sigma[2]^2-2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2))
}

out <- rbind(c(1, 2), c(3, 4), c(5, 6))

วิธีการใช้ฟังก์ชันกับแต่ละแถวout?

จะส่งผ่านค่าสำหรับอาร์กิวเมนต์อื่น ๆ นอกเหนือจากจุดไปยังฟังก์ชันตามที่คุณระบุได้อย่างไร?

คำตอบ:


181

คุณเพียงแค่ใช้apply()ฟังก์ชัน:

R> M <- matrix(1:6, nrow=3, byrow=TRUE)
R> M
     [,1] [,2]
[1,]    1    2
[2,]    3    4
[3,]    5    6
R> apply(M, 1, function(x) 2*x[1]+x[2])
[1]  4 10 16
R> 

สิ่งนี้ใช้เมทริกซ์และใช้ฟังก์ชัน (โง่) กับแต่ละแถว คุณผ่านการขัดแย้งเป็นพิเศษในการทำงานเป็นสี่ห้า ... apply()ข้อโต้แย้ง


ขอบคุณ! จะเกิดอะไรขึ้นถ้าแถวของเมทริกซ์ไม่ใช่อาร์กิวเมนต์แรกของฟังก์ชัน จะระบุได้อย่างไรว่าอาร์กิวเมนต์ใดของฟังก์ชันแต่ละแถวของเมทริกซ์ถูกกำหนดให้
ทิม

อ่านวิธีใช้apply()- มันกวาดทีละแถว (เมื่ออาร์กิวเมนต์ที่สองคือ 1 อื่น ๆ ตามคอลัมน์) และแถวปัจจุบัน (หรือ col) จะเป็นอาร์กิวเมนต์แรกเสมอ นั่นคือวิธีการกำหนดสิ่งต่างๆ
Dirk Eddelbuettel

@ Tim: ถ้าคุณใช้ฟังก์ชัน R ภายในและแถวนั้นไม่ใช่อาร์กิวเมนต์แรกให้ทำตามที่ Dirk ทำและสร้างฟังก์ชันที่กำหนดเองโดยที่แถวเป็นอาร์กิวเมนต์แรก
Joris Meys

3
แพ็คเกจ plyr มีฟังก์ชันการใช้งานที่หลากหลายเหล่านี้ นอกจากนี้ยังมีฟังก์ชันเพิ่มเติมรวมถึงการประมวลผลแบบขนาน
Paul Hiemstra

6
@ cryptic0 คำตอบนี้ช้า แต่สำหรับ googler อาร์กิวเมนต์ที่สองที่ใช้คือMARGINอาร์กิวเมนต์ ในที่นี้หมายถึงใช้ฟังก์ชันกับแถว (มิติแรกในdim(M)) ถ้าเป็น 2 มันจะใช้ฟังก์ชันกับคอลัมน์
De Novo

17

ในกรณีที่คุณต้องการใช้ฟังก์ชันทั่วไปเช่น sum หรือ mean คุณควรใช้rowSumsหรือrowMeansเนื่องจากมันเร็วกว่าapply(data, 1, sum)วิธีการ apply(data, 1, fun)มิฉะนั้นติดกับ คุณสามารถส่งผ่านอาร์กิวเมนต์เพิ่มเติมหลังจากอาร์กิวเมนต์ FUN (ตามที่ Dirk แนะนำแล้ว):

set.seed(1)
m <- matrix(round(runif(20, 1, 5)), ncol=4)
diag(m) <- NA
m
     [,1] [,2] [,3] [,4]
[1,]   NA    5    2    3
[2,]    2   NA    2    4
[3,]    3    4   NA    5
[4,]    5    4    3   NA
[5,]    2    1    4    4

จากนั้นคุณสามารถทำสิ่งนี้:

apply(m, 1, quantile, probs=c(.25,.5, .75), na.rm=TRUE)
    [,1] [,2] [,3] [,4] [,5]
25%  2.5    2  3.5  3.5 1.75
50%  3.0    2  4.0  4.0 3.00
75%  4.0    3  4.5  4.5 4.00

15

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

หมายเหตุ:ผลที่ได้จากapply()จะต้องมีการขนย้ายโดยใช้เพื่อให้ได้รูปแบบเดียวกับเมทริกซ์การป้อนข้อมูลt()A

A <- matrix(c(
  0, 1, 1, 2,
  0, 0, 1, 3,
  0, 0, 1, 3
), nrow = 3, byrow = TRUE)

t(apply(A, 1, function(x) x / sum(x) ))

ผลลัพธ์:

     [,1] [,2] [,3] [,4]
[1,]    0 0.25 0.25 0.50
[2,]    0 0.00 0.25 0.75
[3,]    0 0.00 0.25 0.75

6

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

bvnormdens <- function(x=c(0,0),mu=c(0,0), sigma=c(1,1), rho=0){
     exp(-1/(2*(1-rho^2))*(x[1]^2/sigma[1]^2+
                           x[2]^2/sigma[2]^2-
                           2*rho*x[1]*x[2]/(sigma[1]*sigma[2]))) * 
     1/(2*pi*sigma[1]*sigma[2]*sqrt(1-rho^2))
     }
 out=rbind(c(1,2),c(3,4),c(5,6));

 bvout<-matrix(NA, ncol=1, nrow=3)
 bvout[] <-apply(out, 1, bvnormdens)
 bvout
             [,1]
[1,] 1.306423e-02
[2,] 5.931153e-07
[3,] 9.033134e-15

หากคุณต้องการใช้นอกเหนือจากพารามิเตอร์เริ่มต้นของคุณการเรียกควรรวมอาร์กิวเมนต์ที่ตั้งชื่อไว้หลังฟังก์ชัน:

bvout[] <-apply(out, 1, FUN=bvnormdens, mu=c(-1,1), rho=0.6)

apply () ยังสามารถใช้กับอาร์เรย์มิติที่สูงขึ้นได้และอาร์กิวเมนต์ MARGIN สามารถเป็นเวกเตอร์และจำนวนเต็มเดียว


4

สมัครได้ผลดี แต่ค่อนข้างช้า การใช้ sapply และ vapply อาจมีประโยชน์ rowwise ของ dplyr อาจมีประโยชน์มาดูตัวอย่างวิธีการทำแถวผลิตภัณฑ์ที่ชาญฉลาดของกรอบข้อมูลใด ๆ

a = data.frame(t(iris[1:10,1:3]))
vapply(a, prod, 0)
sapply(a, prod)

โปรดทราบว่าการกำหนดให้ตัวแปรก่อนใช้ vapply / sapply / apply เป็นแนวทางปฏิบัติที่ดีเนื่องจากจะช่วยลดเวลาได้มาก มาดูผลลัพธ์ microbenchmark

a = data.frame(t(iris[1:10,1:3]))
b = iris[1:10,1:3]
microbenchmark::microbenchmark(
    apply(b, 1 , prod),
    vapply(a, prod, 0),
    sapply(a, prod) , 
    apply(iris[1:10,1:3], 1 , prod),
    vapply(data.frame(t(iris[1:10,1:3])), prod, 0),
    sapply(data.frame(t(iris[1:10,1:3])), prod) ,
    b %>%  rowwise() %>%
        summarise(p = prod(Sepal.Length,Sepal.Width,Petal.Length))
)

ดูวิธีการใช้ t () อย่างรอบคอบ


มันอาจจะเป็นธรรมมากขึ้นเพื่อนำไปใช้เปรียบเทียบในครอบครัวถ้าคุณใช้และb <- t(iris[1:10, 1:3]) apply(b, 2 prod)
DaSpeeg

2

rollapply(data, width, FUN, ...)อีกวิธีหนึ่งถ้าคุณต้องการที่จะใช้เป็นส่วนที่แตกต่างของชุดข้อมูลที่แทนค่าเดียวคือการใช้งาน การใช้เวกเตอร์ของความกว้างช่วยให้คุณสามารถใช้ฟังก์ชันบนหน้าต่างที่แตกต่างกันของชุดข้อมูลได้ ฉันใช้สิ่งนี้เพื่อสร้างขั้นตอนการกรองแบบปรับได้แม้ว่าจะไม่ได้มีประสิทธิภาพมากนัก

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