ฟังก์ชั่นการจัดกลุ่ม (tapply, by, aggregate) และ * ใช้ตระกูล


1040

เมื่อใดก็ตามที่ฉันต้องการทำอะไรบางอย่าง "map" py ใน R ฉันมักจะพยายามใช้ฟังก์ชั่นในapplyครอบครัว

อย่างไรก็ตามฉันไม่เคยเข้าใจความแตกต่างระหว่างพวกเขา - วิธี { sapply, lapplyฯลฯ } ใช้ฟังก์ชันกับอินพุต / จัดกลุ่มอินพุตเอาต์พุตจะเป็นอย่างไรหรือแม้กระทั่งอินพุตที่เป็นดังนั้นฉันจึงมักจะ เพียงผ่านพวกเขาทั้งหมดจนกว่าฉันจะได้รับสิ่งที่ฉันต้องการ

ใครสามารถอธิบายวิธีการใช้อันไหนได้บ้าง

ความเข้าใจในปัจจุบันของฉัน (อาจไม่ถูกต้อง / ไม่สมบูรณ์) คือ ...

  1. sapply(vec, f): input เป็นเวกเตอร์ เอาท์พุทเป็นเวกเตอร์ / เมทริกซ์ที่องค์ประกอบiคือf(vec[i])ให้คุณเมทริกซ์ถ้าfมีเอาท์พุทหลายองค์ประกอบ

  2. lapply(vec, f): เหมือนกันsapplyแต่เอาท์พุทเป็นรายการ?

  3. apply(matrix, 1/2, f): input เป็นเมทริกซ์ เอาต์พุตเป็นเวกเตอร์โดยที่อิลิเมนต์iคือ f (แถว / col i ของเมทริกซ์)
  4. tapply(vector, grouping, f): output เป็นเมทริกซ์ / อาร์เรย์ที่องค์ประกอบในเมทริกซ์ / อาร์เรย์เป็นค่าของfที่การจัดกลุ่มgของเวกเตอร์และgได้รับการผลักดันให้ชื่อแถว / col
  5. by(dataframe, grouping, f): ให้gเป็นกลุ่ม ใช้fกับแต่ละคอลัมน์ของกลุ่ม / dataframe พิมพ์การจัดกลุ่มและค่าของfแต่ละคอลัมน์
  6. aggregate(matrix, grouping, f): คล้ายกับbyแต่แทนที่จะพิมพ์ผลลัพธ์ทั้งหมดจะรวมทุกอย่างไว้ใน dataframe

คำถามด้าน: ฉันยังไม่ได้เรียนรู้ plyr หรือก่อร่างใหม่ - จะplyrหรือreshapeแทนที่ทั้งหมดเหล่านี้ทั้งหมดหรือไม่


33
คำถามด้านข้างของคุณ: สำหรับหลายสิ่ง plyr เป็นแทนโดยตรงและ*apply() byplyr (อย่างน้อยสำหรับฉัน) ดูเหมือนจะสอดคล้องกันมากขึ้นในการที่ฉันมักจะรู้ว่าสิ่งที่รูปแบบข้อมูลที่คาดหวังและสิ่งที่มันจะคายออก ที่ช่วยให้ฉันยุ่งยากมาก
JD Long

12
นอกจากนี้ผมอยากแนะนำให้เพิ่ม: และการเลือกและใช้ความสามารถของdoBy data.table
Iterator

7
sapplyเป็นเพียงlapplyการเพิ่มsimplify2arrayในการส่งออก applyไม่เชื่อมโยงกับอะตอมมิกเวกเตอร์ แต่เอาต์พุตสามารถเป็นเวกเตอร์หรือรายการ byแยก dataframes ออกเป็น sub-dataframe แต่มันไม่ได้ใช้fกับคอลัมน์แยกต่างหาก เฉพาะในกรณีที่มีวิธีสำหรับ 'data.frame' ระดับอาจได้รับคอลัมน์ฉลาดใช้โดย f เป็นวิธีการทั่วไปที่แตกต่างกันสำหรับคลาสที่แตกต่างกันของอาร์กิวเมนต์แรก byaggregate
IRTFM

8
ช่วยในการจำ: l ใช้สำหรับ 'list', s ใช้สำหรับ 'simplifying', t ใช้สำหรับ 'per type' (แต่ละระดับของการจัดกลุ่มเป็นแบบ)
Lutz Prechelt

นอกจากนี้ยังมีฟังก์ชั่นบางอย่างในแพ็กเกจ Rfast เช่น: eachcol.apply, Apply.condition และอื่น ๆ ซึ่งเร็วกว่าการเทียบเท่า R
Stefanos

คำตอบ:


1330

R มีฟังก์ชั่นการใช้มากมายซึ่งอธิบายด้วยความช่วยเหลือในไฟล์ช่วยเหลือ (เช่น?apply) แม้ว่าจะมีเพียงพอแล้วว่าการใช้งานครั้งแรกอาร์เอสอาจมีปัญหาในการตัดสินใจเลือกสิ่งที่เหมาะสมกับสถานการณ์หรือจำได้ทั้งหมด พวกเขาอาจมีความรู้สึกทั่วไปว่า "ฉันควรใช้ * ใช้ฟังก์ชั่นที่นี่" แต่มันอาจเป็นเรื่องยากที่จะทำให้พวกเขาตรงทั้งหมดในตอนแรก

แม้จะมีข้อเท็จจริง (ระบุไว้ในคำตอบอื่น ๆ ) ว่าฟังก์ชันการทำงานส่วนใหญ่ของตระกูล * นำไปใช้นั้นได้รับการคุ้มครองโดยplyrแพ็คเกจยอดนิยมอย่างมากแต่ฟังก์ชันพื้นฐานยังคงมีประโยชน์และคุ้มค่าที่จะรู้

คำตอบนี้มีจุดประสงค์เพื่อทำหน้าที่เป็นป้ายบอกทางสำหรับ useRs ใหม่เพื่อช่วยนำพวกเขาไปยังฟังก์ชันการใช้ * ที่ถูกต้องสำหรับปัญหาเฉพาะของพวกเขา หมายเหตุนี่ไม่ได้มีวัตถุประสงค์เพื่อทำการสำรอกหรือเปลี่ยนเอกสาร R! ความหวังก็คือคำตอบนี้จะช่วยให้คุณตัดสินใจได้ว่า * ใช้ฟังก์ชันใดที่เหมาะสมกับสถานการณ์ของคุณแล้วมันขึ้นอยู่กับคุณที่จะทำการวิจัยเพิ่มเติม ด้วยข้อยกเว้นหนึ่งข้อแตกต่างของประสิทธิภาพจะไม่ได้รับการจัดการ

  • ใช้ - เมื่อคุณต้องการใช้ฟังก์ชั่นกับแถวหรือคอลัมน์ของเมทริกซ์ (และแอนะล็อกมิติที่สูงกว่า); โดยทั่วไปไม่แนะนำให้ใช้กับเฟรมข้อมูลเนื่องจากจะเชื่อมโยงกับเมทริกซ์ก่อน

    # Two dimensional matrix
    M <- matrix(seq(1,16), 4, 4)
    
    # apply min to rows
    apply(M, 1, min)
    [1] 1 2 3 4
    
    # apply max to columns
    apply(M, 2, max)
    [1]  4  8 12 16
    
    # 3 dimensional array
    M <- array( seq(32), dim = c(4,4,2))
    
    # Apply sum across each M[*, , ] - i.e Sum across 2nd and 3rd dimension
    apply(M, 1, sum)
    # Result is one-dimensional
    [1] 120 128 136 144
    
    # Apply sum across each M[*, *, ] - i.e Sum across 3rd dimension
    apply(M, c(1,2), sum)
    # Result is two-dimensional
         [,1] [,2] [,3] [,4]
    [1,]   18   26   34   42
    [2,]   20   28   36   44
    [3,]   22   30   38   46
    [4,]   24   32   40   48
    

    หากคุณต้องการหมายถึงแถว / คอลัมน์หรือเงินก้อนสำหรับเมทริกซ์ 2D ให้แน่ใจว่าได้ตรวจสอบปรับสูงฟ้าผ่าอย่างรวดเร็วcolMeans, rowMeans, ,colSumsrowSums

  • lapply - เมื่อคุณต้องการใช้ฟังก์ชั่นกับแต่ละองค์ประกอบของรายการในทางกลับกันและรับรายการกลับ

    นี่คือภาระหน้าที่ของฟังก์ชั่นการใช้งานอื่น ๆ ลอกรหัสของพวกเขาออกและคุณมักจะพบlapplyภายใต้

    x <- list(a = 1, b = 1:3, c = 10:100) 
    lapply(x, FUN = length) 
    $a 
    [1] 1
    $b 
    [1] 3
    $c 
    [1] 91
    lapply(x, FUN = sum) 
    $a 
    [1] 1
    $b 
    [1] 6
    $c 
    [1] 5005
    
  • sapply - เมื่อคุณต้องการใช้ฟังก์ชั่นกับแต่ละองค์ประกอบของรายการในทางกลับกัน แต่คุณต้องการให้เวกเตอร์กลับมาแทนที่จะเป็นรายการ

    หากคุณพบว่าตัวเองพิมพ์หยุดและพิจารณาunlist(lapply(...)) sapply

    x <- list(a = 1, b = 1:3, c = 10:100)
    # Compare with above; a named vector, not a list 
    sapply(x, FUN = length)  
    a  b  c   
    1  3 91
    
    sapply(x, FUN = sum)   
    a    b    c    
    1    6 5005 
    

    ในการใช้งานขั้นสูงมากขึ้นของsapplyมันจะพยายามบีบบังคับให้เกิดผลลัพธ์กับอาเรย์หลายมิติถ้าเหมาะสม ตัวอย่างเช่นถ้าฟังก์ชันของเราคืนค่าเวกเตอร์ที่มีความยาวเท่ากันsapplyจะใช้มันเป็นคอลัมน์ของเมทริกซ์:

    sapply(1:5,function(x) rnorm(3,x))

    หากฟังก์ชันของเราส่งคืนเมทริกซ์ 2 มิติsapplyจะทำสิ่งเดียวกันโดยให้ถือว่าเมทริกซ์ที่คืนมาแต่ละตัวเป็นเวกเตอร์ยาวเดี่ยว:

    sapply(1:5,function(x) matrix(x,2,2))

    ถ้าเราไม่ระบุsimplify = "array"ในกรณีนี้มันจะใช้เมทริกซ์ส่วนบุคคลเพื่อสร้างอาร์เรย์หลายมิติ:

    sapply(1:5,function(x) matrix(x,2,2), simplify = "array")

    พฤติกรรมเหล่านี้แต่ละอย่างนั้นแน่นอนขึ้นอยู่กับฟังก์ชันที่เราคืนค่าเวกเตอร์หรือเมทริกซ์ที่มีความยาวหรือมิติเดียวกัน

  • vapply - เมื่อคุณต้องการใช้sapplyแต่อาจจำเป็นต้องเร่งความเร็วให้เร็วขึ้นจากโค้ดของคุณ

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

    x <- list(a = 1, b = 1:3, c = 10:100)
    #Note that since the advantage here is mainly speed, this
    # example is only for illustration. We're telling R that
    # everything returned by length() should be an integer of 
    # length 1. 
    vapply(x, FUN = length, FUN.VALUE = 0L) 
    a  b  c  
    1  3 91
    
  • mapply - สำหรับเมื่อคุณมีโครงสร้างข้อมูลหลายอย่าง (เช่นรายการเวกเตอร์) และคุณต้องการใช้ฟังก์ชันกับองค์ประกอบที่ 1 ของแต่ละองค์ประกอบจากนั้นองค์ประกอบที่สองของแต่ละ ฯลฯ บังคับให้ผลลัพธ์กับเวกเตอร์ / อาเรย์sapply.

    นี่คือหลายตัวแปรในแง่ที่ว่าฟังก์ชั่นของคุณต้องยอมรับการขัดแย้งหลายครั้ง

    #Sums the 1st elements, the 2nd elements, etc. 
    mapply(sum, 1:5, 1:5, 1:5) 
    [1]  3  6  9 12 15
    #To do rep(1,4), rep(2,3), etc.
    mapply(rep, 1:4, 4:1)   
    [[1]]
    [1] 1 1 1 1
    
    [[2]]
    [1] 2 2 2
    
    [[3]]
    [1] 3 3
    
    [[4]]
    [1] 4
    
  • แผนที่ - เสื้อคลุมไปmapplyด้วยSIMPLIFY = FALSEดังนั้นจึงรับประกันว่าจะส่งคืนรายการ

    Map(sum, 1:5, 1:5, 1:5)
    [[1]]
    [1] 3
    
    [[2]]
    [1] 6
    
    [[3]]
    [1] 9
    
    [[4]]
    [1] 12
    
    [[5]]
    [1] 15
    
  • rapply - สำหรับเมื่อคุณต้องการใช้ฟังก์ชันกับแต่ละองค์ประกอบของโครงสร้างรายการแบบซ้อน

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

    # Append ! to string, otherwise increment
    myFun <- function(x){
        if(is.character(x)){
          return(paste(x,"!",sep=""))
        }
        else{
          return(x + 1)
        }
    }
    
    #A nested list structure
    l <- list(a = list(a1 = "Boo", b1 = 2, c1 = "Eeek"), 
              b = 3, c = "Yikes", 
              d = list(a2 = 1, b2 = list(a3 = "Hey", b3 = 5)))
    
    
    # Result is named vector, coerced to character          
    rapply(l, myFun)
    
    # Result is a nested list like l, with values altered
    rapply(l, myFun, how="replace")
    
  • tapply - สำหรับเมื่อคุณต้องการใช้ฟังก์ชั่นกับเซตย่อยของเวกเตอร์และเซตย่อยถูกกำหนดโดยเวกเตอร์อื่น ๆ ซึ่งมักเป็นปัจจัย

    แกะดำของตระกูล * นำไปใช้แปลก ๆ การใช้วลีไฟล์ช่วยเหลือ "ragged array" อาจทำให้เกิดความสับสนเล็กน้อยแต่จริงๆแล้วมันค่อนข้างง่าย

    เวกเตอร์:

    x <- 1:20

    ปัจจัยกำหนดความยาวเท่ากัน!

    y <- factor(rep(letters[1:5], each = 4))

    เพิ่มค่าxภายในแต่ละกลุ่มย่อยที่กำหนดโดยy:

    tapply(x, y, sum)  
     a  b  c  d  e  
    10 26 42 58 74 
    

    ตัวอย่างที่ซับซ้อนมากขึ้นสามารถจัดการได้ที่กลุ่มย่อยจะถูกกำหนดโดยชุดค่าผสมที่ไม่ซ้ำกันของรายการของปัจจัยหลายประการ tapplyจะคล้ายกันในจิตวิญญาณที่จะสมัคร-รวมแยกฟังก์ชั่นที่ใช้กันทั่วไปใน R (คนaggregate, by, ave, ddplyฯลฯ ) ดังนั้นสถานะแกะสีดำ


32
เชื่อว่าคุณจะพบว่าbyบริสุทธิ์แยกจากกันและaggregateอยู่tapplyที่แกนของพวกเขา ฉันคิดว่าแกะดำทำผ้าที่ยอดเยี่ยม
IRTFM

21
การตอบสนองที่ยอดเยี่ยม! นี่ควรเป็นส่วนหนึ่งของเอกสาร R ทางการ :) หนึ่งข้อเสนอแนะเล็ก ๆ : อาจเพิ่มกระสุนบางอย่างเกี่ยวกับการใช้aggregateและbyเช่นกัน? (ในที่สุดผมก็เข้าใจพวกเขาหลังจากที่คำอธิบายของคุณ !, แต่พวกเขากำลังพบรักดังนั้นมันอาจจะมีประโยชน์ที่จะแยกออกมาและมีตัวอย่างที่เฉพาะเจาะจงบางอย่างสำหรับทั้งสองฟังก์ชั่น.)
grautur

3
@grautur ฉันกำลังตัดสิ่งต่าง ๆ จากคำตอบนี้อย่างแข็งขันเพื่อหลีกเลี่ยงการถูก (ก) ยาวเกินไปและ (b) การเขียนเอกสารอีกครั้ง ฉันตัดสินใจในขณะที่aggregate, byฯลฯ จะขึ้นอยู่กับ * ใช้ฟังก์ชั่นในแบบที่คุณวิธีการใช้พวกเขาเป็นพอที่แตกต่างกันจากมุมมองของผู้ใช้ที่พวกเขาควรจะได้สรุปไว้ในคำตอบที่แยกต่างหาก ฉันอาจลองว่าถ้าฉันมีเวลาหรือบางทีคนอื่นจะเอาชนะฉันและรับ upvote ของฉัน
joran

4
ยัง?Mapเป็นญาติของmapply
ติส

3
@jsanders - ฉันไม่เห็นด้วยเลย data.frames เป็นส่วนสำคัญอย่างยิ่งของ R และเป็นlistวัตถุมีการจัดการโดยใช้บ่อยโดยlapplyเฉพาะอย่างยิ่ง พวกมันยังทำหน้าที่เป็นภาชนะสำหรับจัดกลุ่มเวกเตอร์ / ปัจจัยหลายชนิดด้วยกันในชุดข้อมูลรูปสี่เหลี่ยมผืนผ้าแบบดั้งเดิม ในขณะที่data.tableและplyrอาจเพิ่มไวยากรณ์บางประเภทที่บางคนอาจพบว่าสะดวกสบายกว่าพวกเขากำลังขยายและดำเนินการกับdata.frames ตามลำดับ
thelatemail

191

ในหมายเหตุด้านข้างนี่คือความหลากหลายของplyrฟังก์ชั่นที่สอดคล้องกับ*applyฟังก์ชั่นพื้นฐาน(จากบทนำถึงเอกสาร plyr จากหน้าเว็บ plyr http://had.co.nz/plyr/ )

Base function   Input   Output   plyr function 
---------------------------------------
aggregate        d       d       ddply + colwise 
apply            a       a/l     aaply / alply 
by               d       l       dlply 
lapply           l       l       llply  
mapply           a       a/l     maply / mlply 
replicate        r       a/l     raply / rlply 
sapply           l       a       laply 

หนึ่งในเป้าหมายของplyrคือการจัดให้มีการประชุมการตั้งชื่อที่สอดคล้องกันสำหรับแต่ละฟังก์ชั่นการเข้ารหัสประเภทอินพุตและเอาต์พุตข้อมูลในชื่อฟังก์ชั่น นอกจากนี้ยังให้ความสม่ำเสมอในเอาต์พุตในเอาต์พุตdlply()นั้นสามารถผ่านไปได้อย่างง่ายดายldply()เพื่อผลิตเอาต์พุตที่มีประโยชน์ ฯลฯ

แนวคิดการเรียนรู้plyrไม่ยากไปกว่าการทำความเข้าใจ*applyฟังก์ชั่นพื้นฐาน

plyrและreshapeฟังก์ชั่นได้แทนที่ฟังก์ชั่นเหล่านี้เกือบทั้งหมดในการใช้งานทุกวันของฉัน แต่จากเอกสาร Intro to Plyr ด้วย:

ฟังก์ชั่นที่เกี่ยวข้องtapplyและsweepไม่มีฟังก์ชั่นที่เกี่ยวข้องplyrและยังคงมีประโยชน์ mergeมีประโยชน์สำหรับการรวมข้อมูลสรุปเข้ากับข้อมูลดั้งเดิม


13
เมื่อฉันเริ่มเรียนรู้ R จากศูนย์ฉันพบว่าเรียนรู้ได้ง่ายกว่า*apply()ครอบครัวของหน้าที่ต่างๆ สำหรับฉันddply()มันใช้งานง่ายมากเพราะฉันคุ้นเคยกับฟังก์ชั่นการรวม SQL ddply()กลายเป็นค้อนของฉันสำหรับการแก้ปัญหาต่าง ๆ ซึ่งบางอย่างสามารถแก้ไขได้ดีกว่าด้วยคำสั่งอื่น ๆ
JD Long

1
ฉันคิดว่าฉันคิดว่าแนวคิดที่อยู่เบื้องหลังplyrฟังก์ชั่นนั้นคล้ายกับ*applyฟังก์ชั่นดังนั้นหากคุณสามารถทำสิ่งหนึ่งได้คุณสามารถทำสิ่งอื่น ๆ ได้ แต่plyrฟังก์ชั่นนั้นง่ายต่อการจดจำ แต่ฉันเห็นด้วยกับddply()ค้อนโดยสิ้นเชิง!
JoFrhwld

1
แพ็คเกจ plyr มีjoin()ฟังก์ชันที่ทำงานคล้ายกับการรวม บางทีมันอาจจะมากกว่าที่จะพูดถึงในบริบทของ plyr
marbel

ให้เราอย่าลืมคนจนลืมeapply
JDL

คำตอบที่ดีในทั่วไป แต่ฉันคิดว่ามัน downplays ยูทิลิตี้และข้อเสียของvapply sapplyข้อได้เปรียบที่สำคัญของvapplyมันคือมันบังคับใช้ประเภทและความยาวของเอาต์พุตดังนั้นคุณจะได้ผลลัพธ์ที่คาดหวังหรือข้อผิดพลาดที่ให้ข้อมูล ในทางกลับกันsapplyจะพยายามทำให้ผลลัพธ์ต่อไปนี้เป็นไปตามกฎที่ไม่ชัดเจนเสมอไปและกลับไปที่รายการอื่น sapply(list(1:5, 6:10, matrix(1:4, 2)), function(x) head(x, 1))ยกตัวอย่างเช่นพยายามที่จะทำนายประเภทของการส่งออกนี้จะผลิต: เกี่ยวกับsapply(list(matrix(1:4, 2), matrix(1:4, 2)), ...)อะไร
Alexey Shiklomanov

133

จากสไลด์ 21 จากhttp://www.slideshare.net/hadley/plyr-one-data-analytic-strategy :

ใช้, ไพเราะ, lapply, โดย, รวม

(หวังว่าเป็นที่ชัดเจนว่าapplyสอดคล้องกับ @ Hadley's aaplyและaggregateสอดคล้องกับ @ Hadley's ddplyและอื่น ๆ สไลด์ 20 ของสไลด์โชว์เดียวกันจะชี้แจงหากคุณไม่ได้รับจากภาพนี้)

(ด้านซ้ายคืออินพุตด้านบนคือเอาต์พุต)


4
มีการพิมพ์ผิดในสไลด์หรือไม่ เซลล์ด้านซ้ายบนควรจะเป็น aaply
JHowIX

100

เริ่มแรกด้วยคำตอบที่ยอดเยี่ยมของ Joran - สงสัยอะไรก็ได้ดีกว่า

ตัวช่วยจำดังต่อไปนี้อาจช่วยให้จำความแตกต่างระหว่างกันได้ ในขณะที่บางคนเห็นได้ชัดคนอื่นอาจน้อยกว่า --- สำหรับสิ่งเหล่านี้คุณจะพบเหตุผลในการพูดคุยของ Joran

วิชาว่าด้วยความจำ

  • lapplyเป็นรายการที่ใช้ซึ่งทำหน้าที่ในรายการหรือเวกเตอร์และส่งคืนรายการ
  • sapplyเป็นแบบง่าย lapply (ค่าเริ่มต้นของฟังก์ชันจะคืนค่าเวกเตอร์หรือเมทริกซ์เมื่อทำได้)
  • vapplyเป็นการตรวจสอบที่ใช้แล้ว (อนุญาตให้มีชนิดของวัตถุส่งคืนที่ได้รับการกำหนดล่วงหน้า)
  • rapplyเป็นการสมัครซ้ำสำหรับรายการที่ซ้อนกันคือรายการภายในรายการ
  • tapplyคือการติดแท็กที่แท็กระบุชุดย่อย
  • apply เป็น แบบทั่วไป : ใช้ฟังก์ชั่นกับแถวหรือคอลัมน์ของเมทริกซ์ (หรือโดยทั่วไปกับมิติของอาร์เรย์)

สร้างพื้นหลังที่เหมาะสม

หากการใช้applyครอบครัวยังคงรู้สึกว่าคุณเป็นคนต่างด้าวอยู่เล็กน้อยคุณอาจพลาดมุมมองสำคัญ ๆ

บทความทั้งสองนี้สามารถช่วยได้ พวกเขามีพื้นฐานที่จำเป็นในการกระตุ้นให้เกิดเทคนิคการเขียนโปรแกรมการทำงานที่มีให้โดยapplyครอบครัวของฟังก์ชั่น

ผู้ใช้ Lisp จะรับรู้กระบวนทัศน์ทันที หากคุณไม่คุ้นเคยกับ Lisp เมื่อคุณมุ่งหน้าไปที่ FP คุณจะได้รับมุมมองที่ทรงพลังสำหรับใช้ใน R - และapplyจะทำให้เข้าใจได้มากขึ้น


51

เนื่องจากฉันตระหนักว่าคำตอบที่ยอดเยี่ยมมากของการขาดโพสต์byและaggregateคำอธิบาย นี่คือผลงานของฉัน

จำแนกตาม

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

ct <- tapply(iris$Sepal.Width , iris$Species , summary )
cb <- by(iris$Sepal.Width , iris$Species , summary )

 cb
iris$Species: setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 
-------------------------------------------------------------- 
iris$Species: versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 
-------------------------------------------------------------- 
iris$Species: virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 


ct
$setosa
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.300   3.200   3.400   3.428   3.675   4.400 

$versicolor
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.000   2.525   2.800   2.770   3.000   3.400 

$virginica
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.200   2.800   3.000   2.974   3.175   3.800 

ถ้าเราพิมพ์ทั้งสองวัตถุctและcbเรา "หลัก" มีผลที่เหมือนกันและแตกต่างเพียง แต่ในวิธีการที่พวกเขาจะแสดงให้เห็นที่แตกต่างกันและclassคุณลักษณะตามลำดับbyสำหรับcbและสำหรับarrayct

เป็นฉันได้กล่าวว่าอำนาจของbyเกิดขึ้นเมื่อเราไม่สามารถใช้tapply; รหัสต่อไปนี้เป็นตัวอย่างหนึ่ง:

 tapply(iris, iris$Species, summary )
Error in tapply(iris, iris$Species, summary) : 
  arguments must have same length

R บอกว่าอาร์กิวเมนต์ต้องมีความยาวเท่ากันพูดว่า "เราต้องการคำนวณsummaryตัวแปรทั้งหมดirisตามปัจจัยSpecies": แต่ R ไม่สามารถทำได้เพราะไม่ทราบวิธีจัดการ

ด้วยbyฟังก์ชั่น R ส่งวิธีการเฉพาะสำหรับการdata frameเรียนแล้วปล่อยให้summaryฟังก์ชั่นการทำงานแม้ว่าความยาวของอาร์กิวเมนต์แรก (และประเภทเกินไป) จะแตกต่างกัน

bywork <- by(iris, iris$Species, summary )

bywork
iris$Species: setosa
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:4.800   1st Qu.:3.200   1st Qu.:1.400   1st Qu.:0.200   versicolor: 0  
 Median :5.000   Median :3.400   Median :1.500   Median :0.200   virginica : 0  
 Mean   :5.006   Mean   :3.428   Mean   :1.462   Mean   :0.246                  
 3rd Qu.:5.200   3rd Qu.:3.675   3rd Qu.:1.575   3rd Qu.:0.300                  
 Max.   :5.800   Max.   :4.400   Max.   :1.900   Max.   :0.600                  
-------------------------------------------------------------- 
iris$Species: versicolor
  Sepal.Length    Sepal.Width     Petal.Length   Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.00   Min.   :1.000   setosa    : 0  
 1st Qu.:5.600   1st Qu.:2.525   1st Qu.:4.00   1st Qu.:1.200   versicolor:50  
 Median :5.900   Median :2.800   Median :4.35   Median :1.300   virginica : 0  
 Mean   :5.936   Mean   :2.770   Mean   :4.26   Mean   :1.326                  
 3rd Qu.:6.300   3rd Qu.:3.000   3rd Qu.:4.60   3rd Qu.:1.500                  
 Max.   :7.000   Max.   :3.400   Max.   :5.10   Max.   :1.800                  
-------------------------------------------------------------- 
iris$Species: virginica
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400   setosa    : 0  
 1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800   versicolor: 0  
 Median :6.500   Median :3.000   Median :5.550   Median :2.000   virginica :50  
 Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026                  
 3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300                  
 Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500     

มันใช้งานได้จริงและผลลัพธ์ก็น่าประหลาดใจมาก มันเป็นวัตถุของคลาสbyที่ตามSpecies(พูดสำหรับแต่ละคน) คำนวณsummaryของแต่ละตัวแปร

โปรดทราบว่าหากอาร์กิวเมนต์แรกคือ a data frameฟังก์ชันที่ถูกส่งจะต้องมีวิธีการสำหรับคลาสของวัตถุนั้น ตัวอย่างเช่นเราใช้รหัสนี้กับmeanฟังก์ชั่นเราจะมีรหัสนี้ที่ไม่มีความหมายเลย:

 by(iris, iris$Species, mean)
iris$Species: setosa
[1] NA
------------------------------------------- 
iris$Species: versicolor
[1] NA
------------------------------------------- 
iris$Species: virginica
[1] NA
Warning messages:
1: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
2: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA
3: In mean.default(data[x, , drop = FALSE], ...) :
  argument is not numeric or logical: returning NA

AGGREGATE

aggregateสามารถถูกมองว่าเป็นอีกวิธีหนึ่งในการใช้งานที่แตกต่างกันtapplyหากเราใช้ในลักษณะดังกล่าว

at <- tapply(iris$Sepal.Length , iris$Species , mean)
ag <- aggregate(iris$Sepal.Length , list(iris$Species), mean)

 at
    setosa versicolor  virginica 
     5.006      5.936      6.588 
 ag
     Group.1     x
1     setosa 5.006
2 versicolor 5.936
3  virginica 6.588

ทั้งสองแตกต่างทันทีที่อาร์กิวเมนต์ที่สองของการaggregate ต้องเป็นรายการในขณะที่tapply สามารถ (ไม่จำเป็น) เป็นรายการและการส่งออกของaggregateกรอบข้อมูลในขณะที่หนึ่งในเป็นtapplyarray

พลังของaggregateมันคือมันสามารถจัดการกับส่วนย่อยของข้อมูลได้อย่างง่ายดายด้วยการsubsetโต้แย้งและมันมีวิธีการสำหรับtsวัตถุและformulaเช่นกัน

องค์ประกอบเหล่านี้ทำให้aggregateการทำงานกับมันง่ายขึ้นtapplyในบางสถานการณ์ นี่คือตัวอย่าง (มีอยู่ในเอกสารประกอบ):

ag <- aggregate(len ~ ., data = ToothGrowth, mean)

 ag
  supp dose   len
1   OJ  0.5 13.23
2   VC  0.5  7.98
3   OJ  1.0 22.70
4   VC  1.0 16.77
5   OJ  2.0 26.06
6   VC  2.0 26.14

เราสามารถบรรลุผลได้เหมือนกันtapplyแต่ไวยากรณ์ยากขึ้นเล็กน้อยและเอาต์พุต (ในบางสถานการณ์) อ่านได้น้อยลง:

att <- tapply(ToothGrowth$len, list(ToothGrowth$dose, ToothGrowth$supp), mean)

 att
       OJ    VC
0.5 13.23  7.98
1   22.70 16.77
2   26.06 26.14

มีครั้งอื่น ๆ เมื่อเราไม่สามารถใช้เป็นbyหรือและเราจะต้องใช้tapplyaggregate

 ag1 <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, mean)

 ag1
  Month    Ozone     Temp
1     5 23.61538 66.73077
2     6 29.44444 78.22222
3     7 59.11538 83.88462
4     8 59.96154 83.96154
5     9 31.44828 76.89655

เราไม่สามารถรับผลลัพธ์ก่อนหน้าด้วยการtapplyโทรครั้งเดียว แต่เราต้องคำนวณค่าเฉลี่ยMonthสำหรับแต่ละองค์ประกอบแล้วรวมเข้าด้วยกัน (โปรดทราบว่าเราต้องเรียกใช้na.rm = TRUEเพราะformulaวิธีการaggregateทำงานมีค่าเริ่มต้นna.action = na.omit):

ta1 <- tapply(airquality$Ozone, airquality$Month, mean, na.rm = TRUE)
ta2 <- tapply(airquality$Temp, airquality$Month, mean, na.rm = TRUE)

 cbind(ta1, ta2)
       ta1      ta2
5 23.61538 65.54839
6 29.44444 79.10000
7 59.11538 83.90323
8 59.96154 83.96774
9 31.44828 76.90000

ในขณะที่byเราไม่สามารถบรรลุผลได้ในความเป็นจริงการเรียกใช้ฟังก์ชันต่อไปนี้ส่งคืนข้อผิดพลาด (แต่ส่วนใหญ่จะเกี่ยวข้องกับฟังก์ชันที่ให้มาmean):

by(airquality[c("Ozone", "Temp")], airquality$Month, mean, na.rm = TRUE)

บางครั้งผลลัพธ์จะเหมือนกันและความแตกต่างอยู่ในคลาส (และวิธีแสดง / พิมพ์และไม่เพียง - ตัวอย่างเช่นวิธีการย่อย) วัตถุ:

byagg <- by(airquality[c("Ozone", "Temp")], airquality$Month, summary)
aggagg <- aggregate(cbind(Ozone, Temp) ~ Month, data = airquality, summary)

รหัสก่อนหน้านี้บรรลุเป้าหมายและผลลัพธ์เดียวกันในบางจุดเครื่องมือที่ใช้เป็นเรื่องของรสนิยมและความต้องการส่วนบุคคล วัตถุสองชิ้นก่อนหน้ามีความต้องการที่แตกต่างกันมากในแง่ของการย่อย


อย่างที่ฉันได้พูดไปแล้วพลังของการเกิดขึ้นเมื่อเราไม่สามารถใช้แทปไทด์ได้ รหัสต่อไปนี้เป็นตัวอย่างหนึ่ง: นี่คือคำศัพท์ที่คุณเคยใช้มาก่อน และคุณได้ยกตัวอย่างการคำนวณสรุป สมมติว่าสถิติสรุปสามารถคำนวณได้เพียงว่าจะต้องทำความสะอาด: เช่น data.frame(tapply(unlist(iris[,-5]),list(rep(iris[,5],ncol(iris[-5])),col(iris[-5])),summary))นี่เป็นการใช้ tapply . With the right splitting there is nothing you cant do with tapply . The only thing is it returns a matrix. Please be careful by saying we cant use tapply`
Onyambu

35

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

ฉันได้ทดสอบสองฟังก์ชั่นที่แตกต่างกันsumและlengthในครั้งเดียว ปริมาณที่ทดสอบคือ 50M สำหรับอินพุตและ 50K สำหรับเอาต์พุต ฉันยังได้รวมสองแพคเกจที่เป็นที่นิยมในขณะนี้ซึ่งไม่ได้ใช้กันอย่างแพร่หลายในขณะที่คำถามที่ถูกถามและdata.table dplyrทั้งสองอย่างคุ้มค่าที่จะดูว่าคุณมีเป้าหมายเพื่อผลงานที่ดี

library(dplyr)
library(data.table)
set.seed(123)
n = 5e7
k = 5e5
x = runif(n)
grp = sample(k, n, TRUE)

timing = list()

# sapply
timing[["sapply"]] = system.time({
    lt = split(x, grp)
    r.sapply = sapply(lt, function(x) list(sum(x), length(x)), simplify = FALSE)
})

# lapply
timing[["lapply"]] = system.time({
    lt = split(x, grp)
    r.lapply = lapply(lt, function(x) list(sum(x), length(x)))
})

# tapply
timing[["tapply"]] = system.time(
    r.tapply <- tapply(x, list(grp), function(x) list(sum(x), length(x)))
)

# by
timing[["by"]] = system.time(
    r.by <- by(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)

# aggregate
timing[["aggregate"]] = system.time(
    r.aggregate <- aggregate(x, list(grp), function(x) list(sum(x), length(x)), simplify = FALSE)
)

# dplyr
timing[["dplyr"]] = system.time({
    df = data_frame(x, grp)
    r.dplyr = summarise(group_by(df, grp), sum(x), n())
})

# data.table
timing[["data.table"]] = system.time({
    dt = setnames(setDT(list(x, grp)), c("x","grp"))
    r.data.table = dt[, .(sum(x), .N), grp]
})

# all output size match to group count
sapply(list(sapply=r.sapply, lapply=r.lapply, tapply=r.tapply, by=r.by, aggregate=r.aggregate, dplyr=r.dplyr, data.table=r.data.table), 
       function(x) (if(is.data.frame(x)) nrow else length)(x)==k)
#    sapply     lapply     tapply         by  aggregate      dplyr data.table 
#      TRUE       TRUE       TRUE       TRUE       TRUE       TRUE       TRUE 

# print timings
as.data.table(sapply(timing, `[[`, "elapsed"), keep.rownames = TRUE
              )[,.(fun = V1, elapsed = V2)
                ][order(-elapsed)]
#          fun elapsed
#1:  aggregate 109.139
#2:         by  25.738
#3:      dplyr  18.978
#4:     tapply  17.006
#5:     lapply  11.524
#6:     sapply  11.326
#7: data.table   2.686

เป็นเรื่องปกติหรือไม่ที่ dplyr ต่ำกว่าฟังก์ชั่น applt?
Mostafa

1
@DimitriPetrenko ฉันไม่คิดอย่างนั้นไม่แน่ใจว่าทำไมถึงอยู่ที่นี่ เป็นการดีที่สุดที่จะทดสอบกับข้อมูลของคุณเองเนื่องจากมีหลายปัจจัยที่เข้ามาเล่น
jangorecki

28

แม้จะมีคำตอบที่ยอดเยี่ยมทั้งหมดที่นี่ แต่มีฟังก์ชั่นพื้นฐานอีก 2 ฟังก์ชั่นที่ควรกล่าวถึงouterฟังก์ชั่นที่มีประโยชน์และeapplyฟังก์ชั่นปิดบัง

ด้านนอก

outerเป็นฟังก์ชั่นที่มีประโยชน์มากซ่อนอยู่ในฐานะที่เป็นทางโลกมากขึ้น หากคุณอ่านความช่วยเหลือสำหรับouterคำอธิบายพูดว่า:

The outer product of the arrays X and Y is the array A with dimension  
c(dim(X), dim(Y)) where element A[c(arrayindex.x, arrayindex.y)] =   
FUN(X[arrayindex.x], Y[arrayindex.y], ...).

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

 A<-c(1,3,5,7,9)
 B<-c(0,3,6,9,12)

mapply(FUN=pmax, A, B)

> mapply(FUN=pmax, A, B)
[1]  1  3  6  9 12

outer(A,B, pmax)

 > outer(A,B, pmax)
      [,1] [,2] [,3] [,4] [,5]
 [1,]    1    3    6    9   12
 [2,]    3    3    6    9   12
 [3,]    5    5    6    9   12
 [4,]    7    7    7    9   12
 [5,]    9    9    9    9   12

ฉันใช้มันเป็นการส่วนตัวเมื่อฉันมีเวกเตอร์ของค่าและเวกเตอร์ของเงื่อนไขและต้องการดูว่าค่าใดตรงตามเงื่อนไขใด

eapply

eapplyมันเหมือนกับlapplyว่าแทนที่จะใช้ฟังก์ชั่นกับทุกองค์ประกอบในรายการมันใช้ฟังก์ชั่นกับทุกองค์ประกอบในสภาพแวดล้อม ตัวอย่างเช่นหากคุณต้องการค้นหารายการฟังก์ชันที่ผู้ใช้กำหนดในสภาพแวดล้อมแบบโกลบอล:

A<-c(1,3,5,7,9)
B<-c(0,3,6,9,12)
C<-list(x=1, y=2)
D<-function(x){x+1}

> eapply(.GlobalEnv, is.function)
$A
[1] FALSE

$B
[1] FALSE

$C
[1] FALSE

$D
[1] TRUE 

ตรงไปตรงมาฉันไม่ได้ใช้สิ่งนี้มาก แต่ถ้าคุณกำลังสร้างแพ็คเกจจำนวนมากหรือสร้างสภาพแวดล้อมมากมายมันอาจมีประโยชน์


25

aveมันอาจจะเป็นมูลค่าการกล่าวขวัญ aveเป็นtapplyของญาติที่เป็นมิตร มันส่งคืนผลลัพธ์ในรูปแบบที่คุณสามารถเสียบกลับเข้าไปในกรอบข้อมูลของคุณ

dfr <- data.frame(a=1:20, f=rep(LETTERS[1:5], each=4))
means <- tapply(dfr$a, dfr$f, mean)
##  A    B    C    D    E 
## 2.5  6.5 10.5 14.5 18.5 

## great, but putting it back in the data frame is another line:

dfr$m <- means[dfr$f]

dfr$m2 <- ave(dfr$a, dfr$f, FUN=mean) # NB argument name FUN is needed!
dfr
##   a f    m   m2
##   1 A  2.5  2.5
##   2 A  2.5  2.5
##   3 A  2.5  2.5
##   4 A  2.5  2.5
##   5 B  6.5  6.5
##   6 B  6.5  6.5
##   7 B  6.5  6.5
##   ...

ไม่มีอะไรในแพ็กเกจฐานที่ใช้งานได้ดีกับaveเฟรมข้อมูลทั้งหมด (เช่นbyเดียวtapplyกับเฟรมข้อมูล) แต่คุณสามารถทำให้เหลวไหลได้:

dfr$foo <- ave(1:nrow(dfr), dfr$f, FUN=function(x) {
    x <- dfr[x,]
    sum(x$m*x$m2)
})
dfr
##     a f    m   m2    foo
## 1   1 A  2.5  2.5    25
## 2   2 A  2.5  2.5    25
## 3   3 A  2.5  2.5    25
## ...

12

ฉันเพิ่งค้นพบsweepฟังก์ชั่นที่มีประโยชน์มากกว่าและเพิ่มไว้ที่นี่เพื่อความสมบูรณ์:

กวาด

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

สมมติว่าคุณมีเมทริกซ์และต้องการทำให้คอลัมน์เป็นมาตรฐาน :

dataPoints <- matrix(4:15, nrow = 4)

# Find means per column with `apply()`
dataPoints_means <- apply(dataPoints, 2, mean)

# Find standard deviation with `apply()`
dataPoints_sdev <- apply(dataPoints, 2, sd)

# Center the points 
dataPoints_Trans1 <- sweep(dataPoints, 2, dataPoints_means,"-")
print(dataPoints_Trans1)
##      [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,]  0.5  0.5  0.5
## [4,]  1.5  1.5  1.5
# Return the result
dataPoints_Trans1
##      [,1] [,2] [,3]
## [1,] -1.5 -1.5 -1.5
## [2,] -0.5 -0.5 -0.5
## [3,]  0.5  0.5  0.5
## [4,]  1.5  1.5  1.5
# Normalize
dataPoints_Trans2 <- sweep(dataPoints_Trans1, 2, dataPoints_sdev, "/")

# Return the result
dataPoints_Trans2
##            [,1]       [,2]       [,3]
## [1,] -1.1618950 -1.1618950 -1.1618950
## [2,] -0.3872983 -0.3872983 -0.3872983
## [3,]  0.3872983  0.3872983  0.3872983
## [4,]  1.1618950  1.1618950  1.1618950

NB: สำหรับตัวอย่างง่ายๆนี้ผลลัพธ์ที่แน่นอนสามารถบรรลุได้ง่ายขึ้นโดย
apply(dataPoints, 2, scale)


1
สิ่งนี้เกี่ยวข้องกับการจัดกลุ่มหรือไม่
Frank

2
@ Frank: จริง ๆ แล้วการซื่อสัตย์กับคุณชื่อของโพสต์นี้ค่อนข้างทำให้เข้าใจผิด: เมื่อคุณอ่านคำถามตัวเองมันเป็นเรื่องเกี่ยวกับ "ครอบครัวใช้" sweepเป็นฟังก์ชั่นขั้นสูงเหมือนคนอื่น ๆ ทั้งหมดที่กล่าวถึงที่นี่เช่นapply, sapply, lapplyดังนั้นคำถามเดียวกันอาจจะถามเกี่ยวกับคำตอบที่ได้รับการยอมรับที่มีมากกว่า 1,000 upvotes และตัวอย่างที่กำหนดนั้น เพียงแค่ดูตัวอย่างที่ให้applyไว้
vonjd

2
การกวาดมีชื่อที่ทำให้เข้าใจผิดค่าเริ่มต้นที่ทำให้เข้าใจผิดและชื่อพารามิเตอร์ที่ทำให้เข้าใจผิด :) มันง่ายกว่าที่ฉันจะเข้าใจด้วยวิธีนี้: 1) STATS คือเวกเตอร์หรือค่าเดียวที่จะทำซ้ำเพื่อสร้างเมทริกซ์ที่มีขนาดเท่ากันกับอินพุตแรก 2) ความสนุกจะถูกนำไปใช้กับอินพุตแรกและเมทริกซ์ใหม่นี้ อาจแสดงได้ดีขึ้นโดย: sweep(matrix(1:6,nrow=2),2,7:9,list). มันมักจะมีประสิทธิภาพมากกว่าapplyเพราะที่applyลูปsweepสามารถใช้ฟังก์ชั่นเวกเตอร์ได้
Moody_Mudskipper

2

ในแพ็คเกจยุบที่เพิ่งเปิดตัวใน CRAN ฉันได้พยายามบีบอัดฟังก์ชั่นการใช้งานทั่วไปส่วนใหญ่ให้เป็น 2 ฟังก์ชั่น:

  1. dapply(Data-Apply) ใช้ฟังก์ชั่นกับแถวหรือ (ค่าเริ่มต้น) คอลัมน์ของเมทริกซ์และ data.frames และ (ค่าเริ่มต้น) ส่งคืนออบเจ็กต์ประเภทเดียวกันและมีคุณสมบัติเดียวกัน (ยกเว้นผลลัพธ์ของการคำนวณแต่ละครั้งคืออะตอมและdrop = TRUE) ประสิทธิภาพเทียบได้กับlapplyคอลัมน์ data.frame และเร็วกว่า 2x ประมาณสองเท่าapplyสำหรับแถวหรือคอลัมน์เมทริกซ์ การขนานสามารถใช้ได้ผ่านmclapply(สำหรับ MAC เท่านั้น)

ไวยากรณ์:

dapply(X, FUN, ..., MARGIN = 2, parallel = FALSE, mc.cores = 1L, 
       return = c("same", "matrix", "data.frame"), drop = TRUE)

ตัวอย่าง:

# Apply to columns:
dapply(mtcars, log)
dapply(mtcars, sum)
dapply(mtcars, quantile)
# Apply to rows:
dapply(mtcars, sum, MARGIN = 1)
dapply(mtcars, quantile, MARGIN = 1)
# Return as matrix:
dapply(mtcars, quantile, return = "matrix")
dapply(mtcars, quantile, MARGIN = 1, return = "matrix")
# Same for matrices ...
  1. BYเป็น S3 ทั่วไปสำหรับการคำนวณแบบแยกส่วนใช้ร่วมกับวิธีเวกเตอร์เมทริกซ์และ data.frame มันเป็นอย่างเร็วกว่าtapply, byและaggregate(ยังเร็วกว่าplyrบนข้อมูลขนาดใหญ่dplyrได้เร็วขึ้นแม้ว่า)

ไวยากรณ์:

BY(X, g, FUN, ..., use.g.names = TRUE, sort = TRUE,
   expand.wide = FALSE, parallel = FALSE, mc.cores = 1L,
   return = c("same", "matrix", "data.frame", "list"))

ตัวอย่าง:

# Vectors:
BY(iris$Sepal.Length, iris$Species, sum)
BY(iris$Sepal.Length, iris$Species, quantile)
BY(iris$Sepal.Length, iris$Species, quantile, expand.wide = TRUE) # This returns a matrix 
# Data.frames
BY(iris[-5], iris$Species, sum)
BY(iris[-5], iris$Species, quantile)
BY(iris[-5], iris$Species, quantile, expand.wide = TRUE) # This returns a wider data.frame
BY(iris[-5], iris$Species, quantile, return = "matrix") # This returns a matrix
# Same for matrices ...

gรายการของตัวแปรการจัดกลุ่มนอกจากนี้ยังสามารถจ่ายให้กับ

การพูดคุยเกี่ยวกับประสิทธิภาพ: เป้าหมายหลักของการล่มสลายคือการส่งเสริมการเขียนโปรแกรมที่มีประสิทธิภาพสูงใน R และเพื่อก้าวต่อไป เพื่อจุดประสงค์นี้แพคเกจมีชุดเต็มรูปแบบของภาษา C ++ ตามฟังก์ชั่นทั่วไปอย่างรวดเร็ว: fmean, fmedian, fmode, fsum, fprod, fsd, fvar, fmin, fmax, ffirst, flast, fNobs, fNdistinct, fscale, fbetween, fwithin, fHDbetween, fHDwithin, flag, และfdiff fgrowthพวกเขาทำการคำนวณแบบกลุ่มในการส่งผ่านครั้งเดียวผ่านข้อมูล (เช่นไม่มีการแยกและรวบรวมข้อมูลใหม่)

ไวยากรณ์:

fFUN(x, g = NULL, [w = NULL,] TRA = NULL, [na.rm = TRUE,] use.g.names = TRUE, drop = TRUE)

ตัวอย่าง:

v <- iris$Sepal.Length
f <- iris$Species

# Vectors
fmean(v)             # mean
fmean(v, f)          # grouped mean
fsd(v, f)            # grouped standard deviation
fsd(v, f, TRA = "/") # grouped scaling
fscale(v, f)         # grouped standardizing (scaling and centering)
fwithin(v, f)        # grouped demeaning

w <- abs(rnorm(nrow(iris)))
fmean(v, w = w)      # Weighted mean
fmean(v, f, w)       # Weighted grouped mean
fsd(v, f, w)         # Weighted grouped standard-deviation
fsd(v, f, w, "/")    # Weighted grouped scaling
fscale(v, f, w)      # Weighted grouped standardizing
fwithin(v, f, w)     # Weighted grouped demeaning

# Same using data.frames...
fmean(iris[-5], f)                # grouped mean
fscale(iris[-5], f)               # grouped standardizing
fwithin(iris[-5], f)              # grouped demeaning

# Same with matrices ...

ในบทความสั้น ๆ ฉันให้การวัดประสิทธิภาพ การเขียนโปรแกรมด้วยฟังก์ชั่นที่รวดเร็วนั้นเร็วกว่าการเขียนโปรแกรมด้วยdplyrหรือdata.tableโดยเฉพาะกับข้อมูลที่มีขนาดเล็กลง

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