2 votes

Comment puis-je sélectionner les lignes de la table A dont l'identifiant correspond à celles de la table B, mais dont les valeurs (hors identifiant) sont différentes ?

Considérez ces deux data.tables, foo et bar.

foo <- data.table(id = c(1,2,3,4), f1 = c("a", "b", "c", "d"), f2 = c("a", "b", "c", "d"))
bar <- data.table(id = c(1,2,3,4), f1 = c("a", "a", "c", "d"), f2 = c("a", "b", "c", "e"))

foo
   id f1 f2
1:  1  a  a
2:  2  b  b
3:  3  c  c
4:  4  d  d

bar
   id f1 f2
1:  1  a  a
2:  2  a  b
3:  3  c  c
4:  4  d  e

Je sais que foo et bar ont une relation 1-1.

Je souhaiterais sélectionner les lignes de bar telles que la ligne correspondante dans foo a des valeurs différentes. Par exemple,

  • id 1: les valeurs de f1 et f2 sont les mêmes dans foo et bar, donc excluez celle-ci
  • id 2: la valeur de f1 a changé ! incluez ceci dans le résultat
  • id 3: les valeurs de f1 et f2 sont les mêmes dans foo et bar, donc excluez celle-ci
  • id 4: la valeur de f2 a changé ! incluez ceci dans le résultat

Résultat Attendu

bar[c(2,4)]
   id f1 f2
1:  2  a  b
2:  4  d  e

Ce que j'ai essayé

J'ai pensé qu'une jointure non-équi fonctionnerait bien ici.. Malheureusement, il semble que l'opérateur "différent de" ne soit pas supporté.?

foo[!bar, on = c("id=id", "f1!=f1", "f2!=f2")]
# Opérateurs invalides !=,!=. Seuls les opérateurs autorisés sont ==<=<>=>.

foo[!bar, on = c("id=id", "f1<>f1", "f2<>f2")]
# Plus d'un opérateur trouvé dans la déclaration 'on': f1<>f1. Veuillez spécifier un seul opérateur.

2voto

Waldi Points 22249

Avec data.table:

bar[foo,.SD[i.f1!=x.f1|i.f2!=x.f2],on="id"]

      id     f1     f2

1:     2      a      b
2:     4      d      e

2voto

jblood94 Points 1577

Étalonnage sur un ensemble de données plus large avec quelques options différentes de data.table. L'option mapply fonctionne uniquement si all.equal(foo$id, bar$id) (dépend exactement de ce qui est entendu par "relation 1-1").

library(data.table)
set.seed(123)

foo <- data.table(id = 1:1e5, f1 = 1:1e5, f2 = 1:1e5)
mix <- sample(1e5, 5e4)
bar <- copy(foo)[mix[1:25e3], f1 := 0L][mix[25001:5e4], f2 := 0L]

head(fsetdiff(bar, foo))
#>    id f1 f2
#> 1:  5  0  5
#> 2:  6  6  0
#> 3:  7  0  7
#> 4:  8  0  8
#> 5: 10  0 10
#> 6: 12 12  0

microbenchmark::microbenchmark(join = bar[foo,.SD[i.f1!=x.f1|i.f2!=x.f2],on="id"],
                               antijoin = bar[!foo, on=.(id,f1,f2)],
                               fsetdiff = fsetdiff(bar, foo),
                               duplicated = bar[(!duplicated(rbindlist(list(bar, foo)), fromLast = TRUE))[1:nrow(bar)]],
                               mapply = bar[rowSums(mapply(function(i) foo[[i]] != bar[[i]], 2:length(bar))) > 0,],
                               check = "equal")
#> Unit: milliseconds
#>        expr     min       lq      mean   median       uq     max neval
#>        join 12.2306 14.07125 15.133795 14.76330 15.96645 22.4534   100
#>    antijoin 16.2002 17.60420 19.234747 18.30230 19.44395 59.1581   100
#>    fsetdiff 20.1408 21.76150 23.080961 23.03760 23.73860 30.9594   100
#>  duplicated 17.8954 20.12690 21.673165 21.66795 22.79185 27.3250   100
#>      mapply  3.2440  3.56480  4.346703  3.87415  4.63610 10.2100   100

2voto

langtang Points 453

Je pense que c'est le meilleur (le plus propre, mais peut-être pas le plus rapide ?) :

bar[!foo, on=.(id,f1,f2)]

      id     f1     f2

1:     2      a      b
2:     4      d      e

1voto

TarJae Points 9674

Comme d'autres solutions que data.table sont également les bienvenues. Voici une solution tidyverse:

library(dplyr)
library(tidyr)

left_join(foo, bar, by="id") %>%
  group_by(id) %>% 
  mutate(identical = n_distinct(unlist(cur_data())) == 1) %>% 
  filter(identical == FALSE) %>% 
  select(id, f1=f1.y, f2=f2.y)

 Groupes :   id [2]
     id f1    f2   

1     2 a     b    
2     4 d     e

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