2017-07-13 1 views
4

Ich muss den Unterschied zwischen zwei beliebigen Elementen von zwei Vektor nehmen. Wenn A<-c(1,2) und B<-c(3,4) dann sollte mein Ergebnis Rc(3-1,3-2,4-1,4-2) sein.schnelle Möglichkeit in R, zwei verschachtelte Schleifen zu tun

Mit diesem Code-Schnipsel

myfunction <- function(N) 
{ 
    A = runif(N) 
    B = runif(N) 
    R = c() 
    for(a in A){ 
    for(b in B){ 
     R=c(b-a,R) 
    } 
    } 
    R 
} 
print(system.time(result <- myfunction(300))) 

ich diesmal

user system elapsed 
    14.27 0.01 14.39 

Gibt es einen schnelleren Weg, es zu tun?

+4

'äußeren (A, B,' -') '? Siehe [hier] (https://stackoverflow.com/a/29950048/680068), Zitate werden durcheinander gebracht. – zx8754

+0

nice @ zx8754, hätte nicht gedacht, dass die BaseR Lösung wäre so schnell! – JanLauGe

+0

@ JanLauGe das ist R, alles ist ein Vektor. – zx8754

Antwort

5

Die schnellste Basislösung die Verwendung von outer ist:

as.vector(outer(B,A,"-")) 

Zu meiner eigenen Überraschung, map2_dbl ist eigentlich ziemlich viel schneller als outer:

Nicht zu meiner Überraschung, map2_dbl scheint schneller , aber das liegt daran, dass nicht jede Kombination der Werte in A und B berechnet wird:

 test elapsed relative 
3 CP(A, B) 7.54 47.125 # using expand.grid 
2 JL(A, B) 0.16 1.000 # using map2_dbl 
1 JM(A, B) 3.13 19.563 # using outer 

Aber:

> A <- 1:3 
> B <- 3:1 
> JL(A,B) 
[1] -2 0 2 
> JM(A,B) 
[1] 2 1 0 1 0 -1 0 -1 -2 

Dies ist für zwei Vektoren der Länge 1000 und mit 100 Replikationen. Ich habe nicht Ihre eigene Lösung enthalten, da, dass man aus zwei Gründen unglaublich langsam ist:

  • for Schleifen in R sind um einiges schneller als in den alten Tagen, aber immer noch nicht so optimal wie Funktionen verwenden, die ihre Schleifen codiert in C oder gleichwertig. Das ist der Fall für die Funktionen, die hier im getesteten Code verwendet werden.
  • "wachsen" Sie Ihr Ergebnisobjekt. Jede Schleife durch den Code, die R wird einen Wert größer, so dass R nach einem neuen Platz im Speicher suchen muss, um es zu speichern. Das ist der größte Engpass in Ihrem Code.Versuchen Sie, diese Art von Konstrukt um jeden Preis zu vermeiden, weil es eine der wichtigsten Ursachen für schrecklich langsamen Code ist.

Der Benchmark-Code:

library(tidyverse) 

JM <- function(A,B){ 
    as.vector(outer(B,A,"-")) 
} 

JL <- function(A,B){ 
    map2_dbl(.x = A, 
      .y = B, 
      .f = ~ c(.x - .y)) 
} 

CP <- function(A,B){ 
    as.data.frame(expand.grid(A,B)) %>% 
    mutate(Var3 = Var2-Var1) 
} 

library(rbenchmark) 

A <- runif(1000) 
B <- runif(1000) 

benchmark(JM(A,B), 
      JL(A,B), 
      CP(A,B), 
      replications = 100, 
      columns = c("test","elapsed","relative")) 
+1

Ich versuchte gerade 'N = 100000' gerade jetzt, zu welchem ​​Zeitpunkt 'äußere' zu viel Speicher auf meinem Rechner belegt (' Fehler: kann Vektor der Größe 74,5 Gb' nicht zuordnen). 'map2_dbl' funktioniert immer noch in 0,14 Sekunden. – JanLauGe

+1

@ zx8754 Sie haben den Code, Sie können Ihre eigene CPU rauchen, wenn Sie daran interessiert sind. Ich habe auch herausgefunden, warum 'map2_dbl' so schnell ist: weil es keine Lösung für das Problem ist :-) –

+0

Ah, macht Sinn, warum' map_dbl' schneller ist. Schön, die "äußere" Lösung zu kennen. – CPak

-4

Mit purrr::map2:

library(tidyverse) 

N = 300 
A = runif(N) 
B = runif(N) 
R = c() 

print(
    system.time(
    result <- map(
     .x = A, 
     .f = ~ c(.x - B)) %>% unlist 
) 
) 

Zeit genommen:

user system elapsed 
0.02  0 0.02 

Wenn ich nun Ihre Aufmerksamkeit bekam Besuche this repo für einen schönen Spaziergang durch purrr.

+0

Andere Optionen sind die 'apply' -Funktionsfamilie, aber ich mag' purrr :: map', da ich denke, dass es ein bisschen intuitiver ist. – JanLauGe

+0

Da es um Geschwindigkeit geht und die anderen Antworten auch dies bieten, würde es Ihnen etwas ausmachen ein bisschen über die tatsächliche Zeit, die es dauerte, um diesen Code im Vergleich zu den OPs zu laufen? – JAD

+0

Offensichtlich ist das schneller. Es berechnet nicht alle Kombinationen, also tut es nicht, was OP gefragt hat. Versuchen Sie es mit 'A <- 1: 10' und' B <- 10: 1' und Sie werden sehen. –

2

Sie können expand.grid verwenden den Ansatz vektorisieren:

A <- runif(300) 
B <- runif(300) 

library(dplyr) 
R <- as.data.frame(expand.grid(A,B)) %>% 
     mutate(Var3 = Var2-Var1) 

Die ersten 5 Zeilen der Ausgabe:

 Var1  Var2   Var3 
1 0.8516676 0.325261 -0.5264066246 
2 0.2126453 0.325261 0.1126156694 
3 0.5394620 0.325261 -0.2142010126 
4 0.1364876 0.325261 0.1887734290 
5 0.3248651 0.325261 0.0003958747 

Dies geschah:

user system elapsed 
0.02 0.00 0.02 

Ihre Funktion hat:

user system elapsed 
42.39 0.43 42.90  
+0

'purrr :: map2_dbl' ist schneller. Nicht, dass es auf 300 Werte ankommt, aber ich gehe davon aus, dass das OP darauf aus ist, das zu vergrößern. – JanLauGe

+0

Umm ... ok. Zuerst sollten Sie zeigen, dass Sie den Leistungsunterschied getestet haben (vergleichen Sie es auch mit meinem Ansatz). Zweitens, ich weiß 'purrr: map' ist schneller, weil ich tatsächlich gegen meinen Ansatz getestet habe. Drittens gab ich dir die einzige Upvote, die du im Moment hast. Viertens haben wir zur selben Zeit gepostet, weshalb mein Post überhaupt existiert. Fünftens, Internet-Punkte sind nicht real, und Sie können nichts mit ihnen kaufen. – CPak

+0

Mein Kommentar scheint dich verärgert zu haben. Das war nicht meine Absicht, Entschuldigung. Ich denke gerne, dass ich nur daran interessiert war, die beste Lösung für das Problem zu finden. Ich hätte das deutlicher machen sollen. – JanLauGe

Verwandte Themen