web-dev-qa-db-fra.com

Comparer deux vecteurs de caractère dans R

J'ai deux vecteurs de caractères d'identifiants.

Je voudrais comparer les deux vecteurs de caractères, en particulier les chiffres suivants m'intéressent:

  • Combien d'identifiants sont à la fois dans A et B
  • Combien d'ID sont en A mais pas en B
  • Combien d'ID sont en B mais pas en A

J'aimerais aussi dessiner un diagramme de Venn.

50
Aslan986

Voici quelques bases à essayer:

> A = c("Dog", "Cat", "Mouse")
> B = c("Tiger","Lion","Cat")
> A %in% B
[1] FALSE  TRUE FALSE
> intersect(A,B)
[1] "Cat"
> setdiff(A,B)
[1] "Dog"   "Mouse"
> setdiff(B,A)
[1] "Tiger" "Lion" 

De même, vous pouvez obtenir des comptes simplement comme:

> length(intersect(A,B))
[1] 1
> length(setdiff(A,B))
[1] 2
> length(setdiff(B,A))
[1] 2
72
Mittenchops

Je travaille généralement avec des ensembles de grande taille, j'utilise donc un tableau au lieu d'un diagramme de Venn:

xtab_set <- function(A,B){
    both    <-  union(A,B)
    inA     <-  both %in% A
    inB     <-  both %in% B
    return(table(inA,inB))
}

set.seed(1)
A <- sample(letters[1:20],10,replace=TRUE)
B <- sample(letters[1:20],10,replace=TRUE)
xtab_set(A,B)

#        inB
# inA     FALSE TRUE
#   FALSE     0    5
#   TRUE      6    3
20
Frank

Pourtant, une autre façon, en utilisant % in% et des vecteurs booléens d’éléments communs au lieu de intersection et setdiff. Je suppose que vous voulez réellement comparer deux vecteurs, pas deux listes - a liste est une classe R pouvant contenir tout type d'élément, alors que les vecteurs contiennent toujours des éléments d'un seul type, il est donc plus facile de comparer ce qui est vraiment égal. Ici, les éléments sont transformés en chaînes de caractères, car il s'agissait du type d'élément le plus inflexible présent.

first <- c(1:3, letters[1:6], "foo", "bar")
second <- c(2:4, letters[5:8], "bar", "asd")

both <- first[first %in% second] # in both, same as call: intersect(first, second)
onlyfirst <- first[!first %in% second] # only in 'first', same as: setdiff(first, second)
onlysecond <- second[!second %in% first] # only in 'second', same as: setdiff(second, first)
length(both)
length(onlyfirst)
length(onlysecond)

#> both
#[1] "2"   "3"   "e"   "f"   "bar"
#> onlyfirst
#[1] "1"   "a"   "b"   "c"   "d"   "foo"
#> onlysecond
#[1] "4"   "g"   "h"   "asd"
#> length(both)
#[1] 5
#> length(onlyfirst)
#[1] 6
#> length(onlysecond)
#[1] 4

# If you don't have the 'gplots' package, type: install.packages("gplots")
require("gplots")
venn(list(first.vector = first, second.vector = second))

Comme il a été mentionné, il existe de nombreux choix pour tracer des diagrammes de Venn en R. Voici la sortie utilisant gplots.

venn diagram with gplots

13

Avec sqldf: Plus lent mais très approprié pour les trames de données avec des types mélangés:

t1 <- as.data.frame(1:10)
t2 <- as.data.frame(5:15)
sqldf1 <- sqldf('SELECT * FROM t1 EXCEPT SELECT * FROM t2') # subset from t1 not in t2 
sqldf2 <- sqldf('SELECT * FROM t2 EXCEPT SELECT * FROM t1') # subset from t2 not in t1 
sqldf3 <- sqldf('SELECT * FROM t1 UNION SELECT * FROM t2') # UNION t1 and t2

sqldf1  X1_10
1
2
3
4
sqldf2   X5_15
11
12
13
14
15
sqldf3   X1_10
1
2 
3 
4 
5 
6 
7
8
9
10
11
12
13      
14
15
4
rferrisx

En utilisant les mêmes exemples de données que l'une des réponses ci-dessus.

A = c("Dog", "Cat", "Mouse")
B = c("Tiger","Lion","Cat")

match(A,B)
[1] NA  3 NA

La fonction match renvoie un vecteur avec l'emplacement dans B de toutes les valeurs dans A. Ainsi, cat, le deuxième élément de A, est le troisième élément de B. Il n'y a pas d'autres correspondances.

Pour obtenir les valeurs correspondantes dans A et B, vous pouvez faire:

m <- match(A,B)
A[!is.na(m)]
"Cat"
B[m[!is.na(m)]]
"Cat"

Pour obtenir les valeurs qui ne correspondent pas dans A et B:

A[is.na(m)]
"Dog"   "Mouse"
B[which(is.na(m))]
"Tiger" "Cat"

De plus, vous pouvez utiliser length() pour obtenir le nombre total de valeurs correspondantes et non correspondantes.

1
milan

Si A est un fichier data.table avec le champ a de type list, les entrées étant elles-mêmes des vecteurs de type primitif, par ex. créé comme suit

A<-data.table(a=c(list(c("abc","def","123")),list(c("ghi","zyx"))),d=c(9,8))

et B est une liste avec un vecteur d'entrées primitives, par ex. créé comme suit

B<-list(c("ghi","zyx"))

et vous essayez de trouver quel élément (le cas échéant) de A$a correspond à B

A[sapply(a,identical,unlist(B))]

si vous voulez juste l'entrée dans a

A[sapply(a,identical,unlist(B)),a]

si vous voulez les indices correspondants de a

A[,which(sapply(a,identical,unlist(B)))]

si au lieu de cela B est lui-même une table de données ayant la même structure que A, par ex.

B<-data.table(b=c(list(c("zyx","ghi")),list(c("abc","def",123))),z=c(5,7))

et vous recherchez l'intersection des deux listes par une colonne, où vous avez besoin du même ordre d'éléments vectoriels.

# give the entry in A for in which A$a matches B$b
A[,`:=`(res=unlist(sapply(list(a),function(x,y){
                                      x %in% unlist(lapply(y,as.vector,mode="character"))
                                  },list(B[,b]),simplify=FALSE)))
  ][res==TRUE
  ][,res:=NULL][] 

# get T/F for each index of A
A[,sapply(list(a),function(x,y){
                      x %in% unlist(lapply(y,as.vector,mode="character"))
                  },list(B[,b]),simplify=FALSE)]

Notez que vous ne pouvez pas faire quelque chose d'aussi simple que

setkey(A,a)
setkey(B,b)
A[B]

rejoindre A & B car vous ne pouvez pas saisir de champ de type list dans data.table 1.12.2

de même, vous ne pouvez pas demander

A[a==B[,b]]

même si A et B sont identiques, comme le == L’opérateur n’a pas été implémenté dans R pour le type list

1
mpag