2016-07-29 14 views
2

Ich habe eine Datentabelle mit den folgenden Spalten:R-Datentabelle Außen durch Funktion verbinden

name, x, y 
a, 1, 2 
b, 2, 3 
c, 3, 1 

I mit sich selbst auf diese Tabelle beitreten wollen, jede Zeile zu halten, wo name != name und eine Distanzfunktion auf der x laufen und y Werte von jeder Seite. Das Ergebnis sollte in dem Format:

name1, name2, distance 

Ich schrieb die Abstandsfunktion wie folgt aus:

dist <- function(a, b) sqrt((a$x-b$x)^2 + (a$y-b$y)^2) 

ich die outer Funktion zu nutzen versucht, aber es dauert nur Vektoren, nicht Datentabellen und ich versuchte, Verwenden der verschiedenen Joins in dplyr, war aber nicht erfolgreich.

+0

Sieht aus wie Sie gerade euklidische Abstand verwendet werden.Ich denke, Sie wollen nur die 'dist()' Funktion, um die Berechnung durchzuführen. Es wäre einfacher, mit Beispiel-Eingabedaten mit einem [reproduzierbaren Beispiel] (http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) zu testen. – MrFlick

Antwort

7

Angenommen, Ihre Datenmenge sah aus wie:

d <- data_frame(name = rownames(mtcars), x = mtcars$mpg, y = mtcars$cyl) 

Eine allgemeine Möglichkeit, alle Kombinationen von zwei Datenrahmen (oder alle Kombinationen mit sich selbst), um zu versuchen ist tidyr ist crossing Funktion (obwohl Sie Umbenennung vorsichtig zu sein brauchen die Spalten). Danach werden Sie in der Lage sein, den Abstand zu berechnen und zu tun, um Ihre Filter:

library(dplyr) 
library(tidyr) 

d %>% 
    rename(name1 = name, x1 = x, y1 = y) %>% 
    crossing(d) %>% 
    rename(name2 = name, x2 = x, y2 = y) %>% 
    mutate(distance = sqrt((x1 - x2)^2 + (y1 - y2)^2)) %>% 
    filter(name1 != name2) 

In diesem speziellen Fall könnten Sie mein fuzzyjoin Paket verwenden, speziell distance_join (Sie die neueste Entwicklungsversion von GitHub benötigen). Diese verbindet zwei Datenrahmen (in diesem Fall eine Selbstverknüpfung) basierend auf einer Abstandsschwelle und fügt eine zusätzliche Spalte mit dem Abstand:

library(fuzzyjoin) 

d %>% 
    rename(name1 = name) %>% 
    distance_inner_join(d, max_dist = Inf, distance_col = "distance") %>% 
    rename(name2 = name) %>% 
    filter(name1 != name2) 

Diese geben:

# A tibble: 992 x 7 
     name1 x.x y.x    name2 x.y y.y distance 
     <chr> <dbl> <dbl>    <chr> <dbl> <dbl> <dbl> 
1 Mazda RX4 21  6  Mazda RX4 Wag 21.0  6 0.000000 
2 Mazda RX4 21  6  Datsun 710 22.8  4 2.690725 
3 Mazda RX4 21  6 Hornet 4 Drive 21.4  6 0.400000 
4 Mazda RX4 21  6 Hornet Sportabout 18.7  8 3.047950 
5 Mazda RX4 21  6   Valiant 18.1  6 2.900000 
6 Mazda RX4 21  6  Duster 360 14.3  8 6.992138 
7 Mazda RX4 21  6   Merc 240D 24.4  4 3.944617 
8 Mazda RX4 21  6   Merc 230 22.8  4 2.690725 
9 Mazda RX4 21  6   Merc 280 19.2  6 1.800000 
10 Mazda RX4 21  6   Merc 280C 17.8  6 3.200000 
# ... with 982 more rows 

Sie könnten Setzen Sie max_dist auf einen anderen, nicht-unendlichen Schwellenwert, wenn Sie wissen, dass Sie keine entfernten Spiele interessieren.

+0

fuzzyjoin ist perfekt für das, was ich versuche zu tun –

3

Hier ist eine Basis-R-Methode, die cbind und dist verwendet (die von @mrflick genannte Funktion). Wir haben einen data.frame namens df am unteren Rand dieses Beitrags erstellt.

Beachten Sie, dass dist kehrt eine untere Dreiecksmatrix:

dist(df[,-1]) 
     1  2 
2 1.414214   
3 2.236068 2.236068 

Wir combn können paarweise Vergleiche der Namen Variable erstellen, dann das Ergebnis in eine data.frame kombinieren und die Spaltennamen mit setNames geben.

dfNew <- setNames(data.frame(t(combn(df$name, 2)), 
          combn(df$name, 2, function(i) { 
               dist(df[df$name %in% i, -1])})), 
        c("var1", "var2", "distance")) 

die

dfNew 
     var1 var2 distance 
    1 a b 1.414214 
    2 a c 2.236068 
    3 b c 2.236068 

Hinweis gibt, dass die Namen Variable Zeichen sein muss, oder Sie haben es in der der as.character Funktion wickeln, damit dies funktioniert.

Daten

df <- read.table(header=TRUE, text="name, x, y 
a, 1, 2 
b, 2, 3 
c, 3, 1", sep=",", stringsAsFactors=F) 
Verwandte Themen