Je veux compter le nombre d'arêtes uniques dans un réseau non dirigé, par exemple net
x y 1 A B 2 B A 3 A B
Il ne devrait y avoir qu'un seul bord pour cette matrice, car les arêtes AB et BA sont identiques pour le réseau non dirigé.
Pour le réseau dirigé, je peux obtenir le nombre d'arêtes uniques par:
nrow (unique (net [ c ("x", "y"]))
Mais cela ne fonctionne pas pour le réseau non dirigé.
4 Réponses :
Essayez ceci,
library(tidyverse) rev <- function(x){ unname(sapply(x, function(x) { paste(sort(trimws(strsplit(x[1], ',')[[1]])), collapse=',')} )) } df <- data.frame(x=c("A", "B", "A"), y = c("B", "A", "B")) rows <- df %>% mutate(both = c(paste(x, y, sep = ", "))) unique(rev(rows$both))
Comment cela fonctionne-t-il?
Nous appliquons une fonction à chaque ligne de la trame de données, afin que nous puissions prendre chaque ligne à la fois. Prenez la deuxième rangée du df,
df <- data.frame(x=c("A", "BC", "A"), y = c("B", "A", "BC")) df x y 1 A B 2 BC A 3 A BC unique(apply(df, 1, function(x) paste(sort(unlist(strsplit(x, " "))),collapse = " "))) [1] "A B" "A BC"
Nous avons ensuite divisé ( strsplit
) ceci, et unlist
en un vecteur de chaque lettre, (Nous utilisons as.matrix
pour isoler les éléments)
df <- data.frame(x=c("A", "B", "A"), y = c("B", "A", "B"), z = c("C", "D", "D")) unique(apply(df, 1, function(x) paste(sort(unlist(strsplit(x, " "))),collapse = " "))) [1] "A B C" "A B D"
Utilisez la fonction de tri pour les mettre par ordre alphabétique, puis collez-les ensemble,
paste(sort(unlist(strsplit(as.matrix(df[2,]), " "))), collapse = " ") [1] "A B"
Ensuite, la fonction apply
fait cela pour toutes les lignes, car nous définissons l'index sur 1, puis utilisons la fonction unique
pour identifier les arêtes uniques.
Cela peut être étendu à n variables, par exemple n = 3,
unlist(strsplit(as.matrix(df[2,]), " ")) [1] "B" "A"
Si vous avez besoin de plus de lettres, combinez simplement deux lettres comme suit,
df[2,] x y 1 B A
Ancienne version
En utilisant le tidyverse
, créez une fonction appelée rev
qui peut ordonner nos arêtes, puis utilisez muter
pour créer une nouvelle colonne combinant les colonnes x et y, dans un tel comme cela fonctionne bien avec la fonction rev
, puis exécutez la nouvelle colonne via la fonction et trouvez les paires uniques.
df <- data.frame(x=c("A", "B", "A"), y = c("B", "A", "B")) unique(apply(df, 1, function(x) paste(sort(unlist(strsplit(x, " "))),collapse = " "))) [1] "A B"
Merci beaucoup! J'ai essayé votre ancienne version et cela a bien fonctionné :)
Étant donné que vous travaillez avec des réseaux, une solution igraph
:
dat %>% graph_from_data_frame(., directed=FALSE) %>% # convert to undirected graph simplify %>% # remove loops / multiple edges as_data_frame # return remaining edges
Ensuite, utilisez nrow
Explication
library(igraph) as_data_frame(simplify(graph_from_data_frame(dat, directed=FALSE)))
Merci! Cette méthode est plus simple :) et moins fastidieuse :)
Et ce qui est préférable d'utiliser cette méthode, c'est qu'elle exclut les arêtes auto-liées, ce qui est super pour moi.
@ZQu; oui, la valeur par défaut est de supprimer les boucles automatiques, bien que vous puissiez choisir de les conserver. Voir l'argument donc f ? Igraph :: simplify
Voici une solution sans l'intervention de igraph
, le tout dans un seul tube:
df %>% group_by(x, y) %>% mutate(edge_id = paste(sort(unique(c(x,y))), collapse=" "))
Il est possible d'utiliser group_by () code > puis
sort ()
combinaisons de valeurs et paste ()
dans la nouvelle colonne via mutate ()
. unique ()
est utilisé si vous avez de "vrais" doublons (AB, AB entreront dans un groupe).
df = tibble(x=c("A", "B", "A"), y = c("B", "A", "B"))
Lorsque vous avez correctement trié noms d'arêtes dans une nouvelle colonne, il est assez simple de compter les valeurs uniques ou de filtrer les doublons hors de votre bloc de données.
Si vous avez des variables supplémentaires pour les arêtes, ajoutez-les simplement dans le groupe.
Si vous n'utilisez pas {igraph}
ou si vous voulez simplement savoir comment le faire proprement sans aucune dépendance ...
Voici vos données ...
simplify_edgelist <- function(el, directed = TRUE, drop_loops = TRUE) { stopifnot(ncol(el) == 2) if (drop_loops) { el <- el[el[, 1] != el[, 2], ] } if (directed) { out <- unique(el) } else { out <- unique(t(apply(el, 1, sort))) } colnames(out) <- colnames(el) if (is.data.frame(el)) { as.data.frame(out, stringsAsFactors = FALSE) } else { out } } el2 <- rbind(your_edge_list, data.frame(x = c("C", "C"), y = c("C", "A"), stringsAsFactors = FALSE)) el2 #> x y #> 1 A B #> 2 B A #> 3 A B #> 4 C C #> 5 C A simplify_edgelist(el2, directed = FALSE) #> x y #> 1 A B #> 5 A C
et voici une ventilation étape par étape ...
unique(t(apply(your_edge_list, 1, sort))) #> [,1] [,2] #> [1,] "A" "B"
Si nous lâchons les tuyaux, le noyau ressemble à ceci. .
`%>%` <- magrittr::`%>%` your_edge_list %>% apply(1L, sort) %>% # sort dyads t() %>% # transpose resulting matrix to get the original shape back unique() %>% # get the unique rows as.data.frame() %>% # back to data frame setNames(names(your_edge_list)) # reset column names #> x y #> 1 A B
Et nous pouvons l'envelopper dans une fonction qui 1) gère à la fois dirigée et non dirigée, 2) gère les blocs de données et les matrices (les plus courantes), et 3) peut déposer des boucles ...
your_edge_list <- data.frame(x = c("A", "B", "A"), y = c("B", "A", "B"), stringsAsFactors = FALSE) your_edge_list #> x y #> 1 A B #> 2 B A #> 3 A B
Vous pouvez tous les classer par ordre alphabétique, puis faire la même analyse, essayer stackoverflow.com/questions/47337732/...