2 votes

R data table outer join by function

J'ai un tableau de données avec les colonnes suivantes :

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

Je veux joindre cette table avec elle-même, en gardant chaque ligne où name != name et exécuter une fonction de distance sur le x y y valeurs de chaque côté. Le résultat devrait être au format :

name1, name2, distance

J'ai écrit la fonction de distance comme ceci :

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

J'ai essayé d'utiliser le outer mais elle ne prend que des vecteurs, pas des tableaux de données et j'ai essayé d'utiliser les différentes jointures dans dplyr mais sans succès.

7voto

David Robinson Points 33371

Supposons que votre jeu de données ressemble à :

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

Une façon générale d'essayer toutes les combinaisons de deux trames de données (ou toutes les combinaisons avec elles-mêmes) est la méthode tidyr's crossing (mais vous devrez faire attention à ne pas renommer les colonnes). Ensuite, vous pourrez calculer la distance et effectuer votre filtrage :

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)

Dans ce cas particulier, vous pourriez utiliser mon fuzzyjoin paquet, notamment distance_join (vous aurez besoin de la dernière version de développement depuis GitHub). Cela joint deux cadres de données (dans ce cas, une auto-jonction) sur la base d'un seuil de distance, et ajoute une colonne supplémentaire avec la distance :

library(fuzzyjoin)

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

Cela donnera :

# 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

Vous pouvez définir max_dist à un autre seuil, non infini, si vous savez que vous ne vous souciez pas des correspondances éloignées.

3voto

lmo Points 31046

Voici une méthode R de base qui utilise cbind y dist (la fonction mentionnée par @mrflick). nous avons un data.frame nommé df créé au bas de ce post.

Notez que dist renvoie une matrice triangulaire inférieure :

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

Nous pouvons utiliser combn pour créer des comparaisons par paires de la variable des noms, puis combinez le résultat dans un data.frame et donnez des noms aux colonnes avec setNames .

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"))

qui renvoie

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

Notez que la variable names doit être un caractère ou vous devez l'envelopper dans la balise as.character pour que cela fonctionne.

datos

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X