2017-04-26 2 views
3

Ich möchte eine Liste von Werten erhalten, die zwischen mehreren Bereichen liegen.Subset nach mehreren Bereichen

library(data.table) 
values <- data.table(value = c(1:100)) 
range <- data.table(start = c(6, 29, 87), end = c(10, 35, 92)) 

Ich brauche die Ergebnisse nur die Werte enthalten, die zwischen diesen Bereichen liegen, in:

results <- c(6, 7, 8, 9, 10, 29, 30, 31, 32, 33, 34, 35, 87, 88, 89, 90, 91, 92) 

ich derzeit tue dies mit einer for-Schleife,

results <- data.table(NULL) 
for (i in 1:NROW(range){ 
      results <- rbind(results, 
       data.table(result = values[value >= range[i, start] & 
       value <= range[i, end], value]))} 

jedoch die tatsächliche Dataset ist ziemlich groß und ich suche nach einem effizienteren Weg.

Alle Vorschläge sind willkommen! Vielen Dank!

Antwort

5

Mit dem Nicht-equi beitreten Möglichkeit data.table:

values[range, on = .(value >= start, value <= end), .(results = x.value)] 

die gibt:

results 
1:  6 
2:  7 
3:  8 
4:  9 
5:  10 
6:  29 
7:  30 
8:  31 
9:  32 
10:  33 
11:  34 
12:  35 
13:  87 
14:  88 
15:  89 
16:  90 
17:  91 
18:  92 

Oder nach dem Vorschlag von @ Henrik: values[value %inrange% range]. Das funktioniert sehr gut auch auf data.table die mit mehreren Spalten:

# create new data 
set.seed(26042017) 
values2 <- data.table(value = c(1:100), let = sample(letters, 100, TRUE), num = sample(100)) 

> values2[value %inrange% range] 
    value let num 
1:  6 v 70 
2:  7 f 77 
3:  8 u 21 
4:  9 x 66 
5: 10 g 58 
6: 29 f 7 
7: 30 w 48 
8: 31 c 50 
9: 32 e 5 
10: 33 c 8 
11: 34 y 19 
12: 35 s 97 
13: 87 j 80 
14: 88 o 4 
15: 89 h 65 
16: 90 c 94 
17: 91 k 22 
18: 92 g 46 
+1

Werde ich etwas übersehen oder wäre dies für den Fall von mehreren/vielen Spalten in der Tabelle "values" unpraktisch? d.h. gibt es einen einfachen Weg ohne die Spalten aufzulisten? –

+2

Verwendung der Komfortfunktion '% inrange%': 'Werte [Wert% im Bereich% Bereich]' – Henrik

+0

@Henrik sehr nett! in meiner Antwort enthalten (wenn Sie es als Antwort posten wollen: gehen Sie voran, ich werde es dann hier entfernen) – Jaap

5

Wenn Sie die neueste CRAN-Version von data.table haben, können Sie nicht-equi-Joins verwenden. Zum Beispiel können Sie einen Index erstellen, die Sie dann Ihre Originaldaten verwenden können, um Teilmenge:

idx <- values[range, on = .(value >= start, value <= end), which = TRUE] 
# [1] 6 7 8 9 10 29 30 31 32 33 34 35 87 88 89 90 91 92 
values[idx] 
2

Hier wird ein Verfahren lapply und %between% mit

rbindlist(lapply(seq_len(nrow(range)), function(i) values[value %between% range[i]])) 

Diese Methode Schleifen durch die Bereiche data.table und Teilmengen Werte in jeder Iteration entsprechend der Variablen in Bereichen. lapply gibt eine Liste zurück, die rbindlist in eine data.table konstruiert. Wenn Sie einen Vektor möchten, ersetzen Sie rbindlist durch unlist.


Benchmarks

Nur die Geschwindigkeiten der einzelnen Vorschlag für die gegebenen Daten zu überprüfen, lief ich einen schnellen Vergleich

microbenchmark(
    lmo=rbindlist(lapply(seq_len(nrow(range)), function(i) values[value %between% range[i]])), 
    dd={idx <- values[range, on = .(value >= start, value <= end), which = TRUE]; values[idx]}, 
    jaap=values[range, on = .(value >= start, value <= end), .(results = x.value)], 
    inrange=values[value %inrange% range]) 

Diese

Unit: microseconds 
    expr  min  lq  mean median  uq  max neval cld 
    lmo 1238.472 1460.5645 1593.6632 1520.8630 1613.520 3101.311 100 c 
     dd 688.230 766.7750 885.1826 792.8615 825.220 3609.644 100 b 
    jaap 798.279 897.6355 935.9474 921.7265 970.906 1347.380 100 b 
inrange 463.002 518.3110 563.9724 545.5375 575.758 1944.948 100 a 

Wie zurückgekehrt zu erwarten war Meine Looping-Lösung ist ein wenig langsamer als die anderen. Der klare Gewinner ist jedoch %inrange%, was im Wesentlichen eine vektorisierte Erweiterung von %between% ist.

Verwandte Themen