6
votes

Comment faire en R une matrice d'intersections et d'unions sur des catégories?

Prenons ces données

> allt <- data.frame(day = rep(c("mon", "tue", "wed"), each =3), id = c(1:3,2:4,3:5))
> allt
  day id
1 mon  1
2 mon  2
3 mon  3
4 tue  2
5 tue  3
6 tue  4
7 wed  3
8 wed  4
9 wed  5

Dans la trame de données finale, nous pouvons voir que pour le jour "mon" nous avons des identifiants [1,2,3] et pour "mar" nous avons [ 2,3,4]. Donc, si nous faisons l'intersection de ces vecteurs, nous obtenons [2,3] et si nous faisons l'union, nous obtenons [1,2,3,4]. Les longueurs de ces vecteurs sont de 2 respectivement 4 et le rapport est de 0,5. C'est le chiffre que je veux obtenir.

Je cherche donc un moyen généralisé d'obtenir ce rapport sur plus de catégories pour toutes les combinaisons possibles.

Le résultat pourrait être dans un format quelque chose comme une matrice de corrélation. Juste pour être clair, je suis intéressé par les intersections et l'union de 2 catégories, donc par exemple, je n'ai pas besoin d'une intersection à 4 voies (lun, mar, mer, jeu) - juste chaque intersection de 2 jours.

r

1 commentaires

Quel serait votre résultat attendu pour l'exemple donné?


4 Réponses :


9
votes

Peut-être quelque chose comme ça?

days <- levels(allt$day)

f <- function(x, y) {
  xids <- allt$id[allt$day == x]
  yids <- allt$id[allt$day == y]
  length(intersect(xids, yids)) / length(union(xids, yids))
}
f <- Vectorize(f)

outer(days, days, f)

#      [,1] [,2] [,3]
# [1,]  1.0  0.5  0.2
# [2,]  0.5  1.0  0.5
# [3,]  0.2  0.5  1.0

éventuellement canaliser cela dans set_colnames (days) et set_rownames(days)


2 commentaires

Que fait le "Vectorize"?


Le f non vectorisé ne fonctionne pas correctement sur les vecteurs qui lui sont fournis (les fonctions à l'intérieur peuvent également prendre des vecteurs, f ne fait que passer le vecteur). Vectorize encapsule essentiellement la fonction dans sapply pour chaque élément de x et y appliquant des règles de recyclage et de telle sorte que chaque invocation soit appelée avec des arguments scalaires.



1
votes

Ceci devrait faire l'affaire, et vous pouvez également faire plus de combinaisons mais en changeant la valeur de la fonction combn.

# Creating your dataset
monday<-data.frame(day=rep("mon",3),id=c(1:3))
tuesday<-data.frame(day=rep("tue",3),id=c(2:4))
saturday<-data.frame(day=rep("sat",3),id=c(3:5))
allt<-rbind(monday,tuesday,saturday)

# Creating a list of values pr day    
library(dplyr)
aggregated_form <- allt %>%
  group_by(day) %>%
  summarise(ids = list(id))

# Function takes a list with two vectors and make intersect/join    
intersecter <- function(list_of_lists) {
  vec1 <- unlist(list_of_lists[1])
  vec2 <- unlist(list_of_lists[2])
  my_intersect <- intersect(vec1, vec2)
  my_union <- union(vec1, vec2)
  ratio <- length(my_intersect)/length(my_union)
  return(ratio)
}

# Creates strings with all combinations
combination <- sapply(combn(aggregated_form$day,2, simplify = FALSE), paste, collapse = "-")

# Calculates you value for all combinations
values <- combn(aggregated_form$ids, 2, FUN = intersecter)

# Generates a dataframe with results
results <- data.frame(comb = combination,
                      value = values)
results
     comb value
1 mon-tue   0.5
2 mon-sat   0.2
3 tue-sat   0.5


0 commentaires

1
votes
lapply(combn(unique(allt$day), 2, ,F), paste, collapse = "-")

[[1]]
[1] "mon-tue"

[[2]]
[1] "mon-sat"

[[3]]
[1] "tue-sat"

3 commentaires

Je ne le suis pas, pas besoin d'être impoli. Vous pourriez dire que nous avons adopté une approche comparable (même si cela pourrait être discuté également), mais je n'ai même pas vu votre message avant de publier le mien.


Ou s'agit-il simplement de créer les «noms de combinaisons de jours»? Oui, je vois maintenant que cette seule ligne est presque la même, mais pas de soucis, j'ai copié-collé votre réponse. Parfois, les grands esprits se ressemblent;)


Vous ne pouvez pas épeler grand sans 8: - |



1
votes

commencez par créer une matrice pour les résultats des intersections:

int/un

     mon tue sat
 mon 1.0 0.5 0.2
 tue 0.5 1.0 0.5
 sat 0.2 0.5 1.0

répliquer la matrice pour les unions:

for(col in colnames(int)){
  for(row in colnames(int)){
    int[row,col]<-length(intersect(allt[allt$day==col,"id"],allt[allt$day==row,"id"]))
    un[row,col]<-length(union(allt[allt$day==col,"id"],allt[allt$day==row,"id"]))
  }
}

calculer les intersections et les unions: p>

un<-int

Ensuite, il vous suffit de diviser les deux matrices:

int<-mat.or.vec(nr=length(unique(allt$day)),nc=length(unique(allt$day)))
colnames(int)<-unique(allt$day)
rownames(int)<-unique(allt$day)


0 commentaires