2016-07-20 17 views
0

Ich hatte gehofft, jemand könnte helfen, ich versuche, eine Anwendungsfunktion zu beschleunigen, und ich habe ein paar Tricks versucht, aber es ist immer noch sehr langsam und ich fragte mich, ob Jeder hatte noch mehr Vorschläge.Beschleunigung der Anwendung einer Funktion auf eindeutige Werte in R

Ich habe Daten wie folgt:

myData= data.frame(ident=c(3,3,4,4,4,4,4,4,4,4,4,7,7,7,7,7,7,7), 
group=c(7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8), 
significant=c(1,1,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0), 
year=c(2003,2002,2001,2008,2010,2007,2007,2008,2006,2012,2008, 
2012,2006,2001,2014,2012,2004,2007), 
month=c(1,1,9,12,3,2,4,3,9,5,12,8,11,3,1,6,3,1), 
subReport=c(0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0), 
prevReport=c(1,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1)) 

und ich möchte mit einem Datenrahmen wie dies am Ende:

results=data.frame(ident=c(3,4,7), 
significant=c(1,0,1), 
prevReports=c(2,6,7), 
subReport=c(0,1,0), 
group=c(7,7,8)) 

Um dies zu tun, ich den Code unten geschrieben und es zu tun schnell i habe versucht, in Datentabellen umzuwandeln und rbindlist anstelle von rbind zu verwenden, was ich in einigen Threads vorgeschlagen habe. Ich habe auch versucht, ParLapply, aber ich finde immer noch, dass der Prozess ziemlich langsam ist, (Ich mache das auf etwa 250.000 Datenpunkte).

dt<-data.table(myData) 

results<-NULL 

ApplyModel <- function (id,data) { 

dtTemp<-dt[dt$ident== id,] 

if(nrow(dtTemp)>=1){ 

prevReport = if(sum(dtTemp$prevReport)>=1) sum(dtTemp$prevReport) else 0 

subsequentReport = if(sum(dtTemp$subReport)>=1) 1 else 0 

significant = as.numeric(head(dtTemp$sig,1)) 

group = head(dtTemp$group,1) 

id= as.numeric(head(dtTemp$id,1)) 

output<-cbind(id, significant ,prevReport,subsequentReport ,group) 

output<-output[!duplicated(output[,1]),] 
print(output) 
results <- rbindlist(list(as.list(output))) 

} 
} 


results<-lapply(unique(dt$ident), ApplyModel) 
results<-as.data.frame(do.call(rbind, results)) 

Alle Vorschläge, wie dies beschleunigt werden könnte, wären sehr willkommen! Ich denke, dass es mit der Teilmenge zu tun ist, möchte ich die Funktion auf eine Teilmenge basierend auf einem eindeutigen Wert anwenden, aber ich denke, dass lapply ist wirklich mehr für die Anwendung einer Funktion auf jeden Wert, so Subsetting besiegt das Objekt etwas ...

Antwort

2

Hier erzeugt der Code einen Fehler:

results<-lapply(unique(dt$ident), ApplyModel) Error in dt$ident : object of type 'closure' is not subsettable

Es erscheint mir, dass Sie für tapply suchen statt lapply. Mit tapply könnte man etwa die oben in viel prägnanter Weise ausdrücken:

results2 <- data.frame(significant = tapply(myData$significant, myData$ident, function(x) return(x[1])), 
         prevreports = tapply(myData$prevReport, myData$ident, sum), 
         subReports = tapply(myData$subReport, myData$ident, function(x) as.numeric(any(x==1))), 
         group = tapply(myData$group, myData$ident, function(x) return(x[1]))) 

über die gleiche Arbeit tun sollten, aber viel besser lesbar sein. Jetzt sollte das wirklich schnell sein, außer für riesige Datensätze. In den meisten Fällen sollte es schneller sein zu warten, bis R den Auftrag abgeschlossen hat, als mehr Zeit für die Programmierung aufzuwenden. Eine Möglichkeit, dies noch schneller zu machen, wäre die Nutzung der Leistung des data.table-Pakets, aber es genügt nicht, es aufzurufen. Sie müssen lernen, es ist sehr spezielle Syntax. Bitte überprüfe vorher, dass der Code auf diese Weise wirklich zu langsam ist. Wenn es wirklich zu langsam ist, überprüfen Sie dies:

library(data.table) 

first <- function(x) x[1] 
myAny <- function(x) as.numeric(any(x==1)) 
myData <- data.table(myData) 

myData[, .(significant=first(significant), 
      prevReports=sum(prevReport), 
      subReports=myAny(subReport), 
      group=first(group)), ident] 
+0

Erstellen 'first' scheint übertrieben; 'myAny' sollte auf Integer anstelle von Float erzwingen; und der korrekte Weg, um in eine data.table zu transformieren, ist 'setDT (mydata)' (ohne irgendeine '<-'-Zuweisung). Schließlich ist die Syntax, die für dieses Beispiel benötigt wird, nicht besonders; Sie machen es nur schwerer als nötig zu lesen, indem Sie Whitespace weglassen und 'by = ident' nach Position statt nach name übergeben. Das sind keine großen Probleme; nur um dich wissen zu lassen. Auch bezüglich des Erlernens der Syntax könntest du zu den Vignetten verlinken: https://github.com/Rdatatable/data.table/wiki/Getting-started – Frank

+2

Vielen Dank für die Hinweise und dafür, mich auf setDT() zu verweisen. Ob zuerst definiert werden soll und ob Argumentnamen angegeben werden und wo Leerzeichen gesetzt werden, ist Geschmackssache. Ich denke, dass myAny wahrscheinlich überhaupt nicht gezwungen werden sollte, da booleans wahrscheinlich die Werte am besten widerspiegeln. Zur Frage der Syntax von data.tables: Wir werden wahrscheinlich zustimmen, dass data.table eine eigene DSL hat, die kompakt, auf den Punkt und nützlich ist. Das ursprüngliche Poster scheint mit grundlegenden R zu kämpfen, und ich rate Anfängern, grundlegendes R zuerst zu lernen, bevor Sie in DSLs innerhalb von R wie Datentabelle, ggplot2, reguläre Ausdrücke tauchen. – Bernhard

+0

weiter: Das ist wieder eine Frage des Geschmacks, aber ich glaube, dass Lernen data.table sollte kommen, nachdem man Basis-R wie tapply gelernt hat, aggregieren, doBy und dergleichen. Die Verknüpfung mit den Vignetten ist eine großartige Ergänzung. – Bernhard

2

Sie dplyr verwenden:

require(dplyr) 

new <- myData %>% group_by(ident) %>% 
summarise(first(significant),sum(prevReport),(n_distinct(subReport)-1), first(group)) %>% 
data.frame() 
+0

'max (signifikant)' führt zu falschen Ergebnissen, wenn Null zuerst und 1 Sekunde ist. Besser zu verwenden 'dplyr :: first' –

+0

@ Piere Lafortune Funktioniert immer noch, wenn 0 das erste Element der gruppierten Variable ist. Oder fehlt mir etwas? – count

+2

Bearbeiten Sie die Daten mit 'myData [4,3] <- 1' und überprüfen Sie die Antwort. Ihre Syntax führt zu dem falschen Ergebnis von '1' für die' ident' '4'. Das korrekte Ergebnis ist "0" gemäß der OP-Bedingung. –