2017-09-27 2 views
2

J'ai une trame de données qui ressemble à ceci:La formation d'une matrice de co-occurence d'une trame de données

id val 
1 a 
1 b 
2 a 
2 c 
2 d 
3 a 
3 a 

penser à chaque ligne comme une étiquette, val, qui a été donné à une observation avec un id .

Ce que je veux finalement arriver à est une matrice « cooccurrence » qui ressemble à quelque chose comme ça où je reçois un compte de combien de fois chaque lettre apparaît dans le même identifiant les uns avec les autres lettres:

a b c d 
a 1 1 1 1 
b 1 0 0 0 
c 1 0 0 1 
d 1 0 1 0 

Je me suis foutu le cerveau à la recherche de moyens de le faire, mais je suis resté vide jusqu'à présent. Des indices? De préférence en utilisant des outils tidyverse, mais ouvert à d'autres options aussi bien à ce stade.

EDIT: les solutions à la question liée en tant que doublon possible ne fonctionnent pas dans ce cas. Je ne sais pas pourquoi, mais je soupçonne que cela a à voir avec cette question ayant un cadre de données avec 3 colonnes.

+2

double possible de [Création matrice de co-occurrence] (https://stackoverflow.com/questions/13281303/creating-co-occurrence-matrix) – missuse

+1

Cette question a 5 ans, alors j'espère qu'il y a une solution plus simple maintenant. J'ai essayé 2 ou 3 des solutions là et aucun d'eux ne fonctionne, donc ma question est différente (peut-être parce qu'il manque une troisième colonne?) –

+0

J'ai juste noté que ma solution originale est très semblable à @ d.b, ainsi Je l'ai changé pour ajouter de la valeur. – useR

Répondre

1

est ici une solution dans R. base Pas tout à fait élégante mais semble fonctionner

temp = data.frame(do.call(cbind, lapply(split(df, df$id), function(a) 
    combn(a$val, 2))), stringsAsFactors = FALSE) 
sapply(sort(unique(df$val)), function(rows) 
    sapply(sort(unique(df$val)), function(cols) 
     sum(sapply(temp, function(x) 
      identical(sort(x), sort(c(rows, cols))))))) 
# a b c d 
#a 1 1 1 1 
#b 1 0 0 0 
#c 1 0 0 1 
#d 1 0 1 0 

OU avec igraph

temp = t(do.call(cbind, lapply(split(df, df$id), function(a) combn(a$val, 2)))) 
library(igraph) 
as.matrix(get.adjacency(graph(temp, directed = FALSE))) 
# a c b d 
#a 1 1 1 1 
#c 1 0 0 1 
#b 1 0 0 0 
#d 1 1 0 0 

DONNÉES

df = structure(list(id = c(1L, 1L, 2L, 2L, 2L, 3L, 3L), 
        val = c("a", "b", "a", "c", "d", "a", "a")), 
       .Names = c("id", "val"), 
       class = "data.frame", 
       row.names = c(NA, -7L)) 
0

Une solution avec dplyr + purrr:

library(dplyr) 
library(purrr) 
df %>% 
    split(.$id) %>% 
    map_dfr(function(x){ 
    t(combn(x$val, 2)) %>% 
     data.frame(stringsAsFactors = FALSE) 
    }) %>% 
    mutate_all(funs(factor(., levels = c("a", "b", "c", "d")))) %>% 
    table() %>% 
    pmax(., t(.)) 

Résultat:

X2 
X1 a b c d 
    a 1 1 1 1 
    b 1 0 0 0 
    c 1 0 0 1 
    d 1 0 1 0 

Notes:

  1. Je découpez d'abord la df par id, puis utilisé map_dfr de purrr pour cartographier la fonction combn à chaque groupe d'id.
  2. combn trouve toutes les combinaisons d'éléments dans un vecteur (longueur (vec) choisir 2) et renvoie une matrice. À la fin de map_dfr signifie que le résultat sera une trame de données par ligne liant chaque élément de la liste. Donc, c'est effectivement do.call(rbind, lapply()).
  3. mutate_all fait que table conserve tous les niveaux nécessaires même si une lettre n'existe pas dans une colonne.
  4. Enfin, étant donné que, après l'étape table, une matrice triangulaire supérieure est produite, je nourris que matrice et sa transposée dans pmax
  5. pmax trouve les maxima parallèle à partir des deux entrées et renvoie une matrice symétrique comme on le souhaite.

données:

df = read.table(text= "id val 
       1 a 
       1 b 
       2 a 
       2 c 
       2 d 
       3 a 
       3 a", header = TRUE, stringsAsFactors = FALSE)