Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #####
- ### Except where otherwise indicated, the code below is written by Benjamin Lind under CC: BY-NC-SA 3.0
- ### If you use or develop this code, please provide proper attribution.
- ### You may contact me at lind.benjamin//at//gmail//d0t//com
- ### You may also find me @benjaminlind on Twitter
- #####
- ### To load these functions into R, enter:
- ### source("http://pastebin.com/raw.php?i=SErkr5sg")
- ### Functions included in this file:
- ### Krackhardt's E-I index
- ### Weak transitivity as measured for a directed network
- ### UMAN random graph generators. They can make random graphs conditioned on the dyad census.
- ### A dyad rewired graph. Like igraph::rewire(), but controls for reciprocity.
- library(Rcpp)
- library(igraph)
- library(compiler)
- cppFunction('
- double ei_index_cpp(IntegerMatrix el, IntegerVector attrib){
- double incount = 0;
- int n = el.nrow();
- for (int i = 0; i < n; ++i){
- int attrib1 = attrib[el(i, 0) - 1];
- int attrib2 = attrib[el(i, 1) - 1];
- if (attrib1 == attrib2){
- incount += 1;
- }
- }
- double excount = n - incount;
- double out = (excount - incount) / n;
- return out;
- }
- ')
- ei_index <- function(g, value){
- # _g_ is an igraph 'graph' object
- # _value_ is a categorical vertex attribute
- if (any(is.na(value)))
- stop("Missing values are not allowed for _value_.")
- if (vcount(g) != length(value))
- stop("The length of _value_ does not equal the number of vertices in _g_.")
- g.el <- get.edgelist(g, names = FALSE)
- allvalues <- unique(value)
- value.int <- match(value, allvalues)
- return(ei_index_cpp(g.el, value.int))
- }
- ei_index <- cmpfun(ei_index)
- transitivity_directed <- function(g, method = "weak"){
- # _g_ is an igraph graph object
- # _method_ is the technique of measuring transitivity.
- # "Weak" is the most common understanding of transitivity, so that's the one used here.
- # "Weak census" returns the counts, not the proportion.
- # Once I find a convenient table showing the weights for vacuous transitive triples and see the formula, I'll include strong transitivity.
- if (is.directed(g) == FALSE){
- warning("Entered an undirected graph into transitivity_directed(). Returned transitivity(g).")
- return(transitivity(g))
- }
- acceptable_methods <- c("weak", "weakcensus")
- if (method %in% acceptable_methods == FALSE)
- stop(paste("_method_ must be in c(\"", paste(acceptable_methods, collapse = "\", \""), "\")", sep = ""))
- # See Wasserman and Faust (1994:573)
- weight_mat <- data.frame(row.names = c("003", "012", "102", "021D", "021U", "021C", "111D", "111U", "030T", "030C", "201", "120D", "120U", "120C", "210", "300"))
- weight_mat$b_in <- c(0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 2, 1, 1, 2, 3)
- weight_mat$b_out <- c(0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 2, 1, 2, 3)
- weight_mat$m <- c(0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 2, 1, 1, 1, 2, 3)
- weight_mat$a <- c(0, 1, 0, 2, 2, 2, 1, 1, 3, 3, 0, 2, 2, 2, 1, 0)
- weight_mat$n <- c(3, 2, 2, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0)
- weight_mat$c <- c(0, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6)
- weight_mat$Trans <- c(0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 1, 3, 6)
- #weight_mat$Vac_trans <- c(6, 6, 6, 6, 6, 5, 5, 5, 5, 3, 4, 4, 4, 3, 2, 0)
- weight_mat$Intrans <- c(0, 0, 0, 0, 0, 1, 1, 1, 0, 3, 2, 0, 0, 2, 1, 0)
- weight_mat$Close_Friends_Disagreeing <- c(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0)
- tc <- triad.census(g)
- if (method == "weakcensus"){
- transcounts <- sum(weight_mat$Trans * tc)
- return(transcounts)
- }
- else if (method == "weak"){
- transcounts <- sum(weight_mat$Trans * tc)
- intranscounts <- sum(weight_mat$Intrans * tc)
- return(transcounts / (transcounts + intranscounts))
- }
- }
- transitivity_directed <- cmpfun(transitivity_directed)
- cppFunction('
- LogicalMatrix uman_game_prob_cpp(int n, NumericVector dc){
- LogicalMatrix out(n, n);
- int adddyadrandint = 0;
- double adddyadranddoub = 0;
- int addmutrandint = 0;
- double addmutranddoub = 0;
- int dironerandint = 0;
- double dironeranddoub = 0;
- bool adddyad = false;
- bool addmut = false;
- bool dirone = false;
- for (int i = 0; i < (n - 1); ++i){
- for (int j = (i + 1); j < n; ++j){
- adddyadrandint = rand() % 10000000 + 1;
- adddyadranddoub = adddyadrandint / 10000000.0;
- adddyad = adddyadranddoub > dc[2];
- if (adddyad){
- addmutrandint = rand() % 10000000 + 1;
- addmutranddoub = addmutrandint / 10000000.0;
- addmut = addmutranddoub <= (dc[0] / (dc[0] + dc[1]));
- if (addmut){
- out(i, j) = true;
- out(j, i) = true;
- }
- else{
- dironerandint = rand() % 10000000 + 1;
- dironeranddoub = dironerandint / 10000000.0;
- dirone = dironeranddoub > .5;
- if (dirone){
- out(i, j) = true;
- out(j, i) = false;
- }
- else{
- out(j, i) = true;
- out(i, j) = false;
- }
- }
- }
- else{
- out(i, j) = false;
- out(j, i) = false;
- }
- }
- }
- return out;
- }
- ')
- cppFunction('
- IntegerMatrix uman_game_exact_cpp(int n, IntegerVector dc){
- IntegerMatrix out((dc[0] * 2) + dc[1], 2);
- LogicalMatrix usededges(n, n);
- int m = 0;
- int a = 0;
- int counter = 0;
- int r = 0;
- int c = 0;
- int addmutint = 0;
- double addmutdoub = 0.0;
- bool addmut = false;
- for (int i = 0; i < n; ++i){
- for (int j = i; j < n; ++j){
- usededges(i, j) = false;
- usededges(j, i) = false;
- }
- }
- while (counter < out.nrow()){
- r = rand() % n;
- c = rand() % n;
- if ((r != c) & (usededges(r, c) == false)){
- usededges(r, c) = true;
- usededges(c, r) = true;
- if ((m < dc[0]) & (a < dc[1])){
- addmutint = rand() % 10000000 + 1;
- addmutdoub = addmutint / 10000000.0;
- addmut = addmutdoub > (dc[1] / (dc[0] + dc[1]));
- }
- else if (m == dc[0]){
- addmut = false;
- }
- else{
- addmut = true;
- }
- if (addmut){
- out(counter, 0) = r + 1;
- out(counter, 1) = c + 1;
- counter += 1;
- out(counter, 1) = r + 1;
- out(counter, 0) = c + 1;
- counter += 1;
- m += 1;
- }
- else{
- out(counter, 0) = r + 1;
- out(counter, 1) = c + 1;
- counter += 1;
- a += 1;
- }
- }
- }
- return out;
- }
- ')
- cppFunction('
- IntegerMatrix uman_game_exact_sparse_cpp(int n, IntegerVector dc){
- IntegerMatrix out((dc[0] * 2) + dc[1], 2);
- int m = 0;
- int a = 0;
- int counter = 0;
- int r = 0;
- int c = 0;
- int addmutint = 0;
- double addmutdoub = 0.0;
- bool addmut = false;
- while (counter < out.nrow()){
- r = rand() % n;
- c = rand() % n;
- bool used = false;
- int j = 0;
- while ((j < counter) & (used == false)){
- if (((out(j, 0) == (r + 1)) & (out(j, 1) == (c + 1))) | ((out(j, 0) == (c + 1)) & (out(j, 1) == (r + 1)))){
- used = true;
- }
- j += 1;
- }
- if ((r != c) & (used == false)){
- if ((m < dc[0]) & (a < dc[1])){
- addmutint = rand() % 10000000 + 1;
- addmutdoub = addmutint / 10000000.0;
- addmut = addmutdoub > (dc[1] / (dc[0] + dc[1]));
- }
- else if (m == dc[0]){
- addmut = false;
- }
- else{
- addmut = true;
- }
- if (addmut){
- out(counter, 0) = r + 1;
- out(counter, 1) = c + 1;
- counter += 1;
- out(counter, 1) = r + 1;
- out(counter, 0) = c + 1;
- counter += 1;
- m += 1;
- }
- else{
- out(counter, 0) = r + 1;
- out(counter, 1) = c + 1;
- counter += 1;
- a += 1;
- }
- }
- }
- return out;
- }
- ')
- .uman_game_cpp <- list(prob = uman_game_prob_cpp, exact = uman_game_exact_cpp, exact_sparse = uman_game_exact_sparse_cpp)
- rm(uman_game_prob_cpp, uman_game_exact_cpp, uman_game_exact_sparse_cpp)
- uman_game <- function(n, dyad.dist, sparse = FALSE){
- # _n_ is the number of vertices in the desired graph
- # _dyad.dist_ is a vector of length three. The values correspond to Mutual, Asymmetric, and Null dyads
- # If the values are all less than or equal to 1 (and greater than 0), they're probabilities
- # If any of the values are greater than 1, they're exact edge counts
- # _sparse_ is good for large networks with low densities. It's poor for dense and small networks.
- n.dyads <- n * (n - 1) / 2
- if (any(dyad.dist > 1) & (sum(round(dyad.dist)) != n.dyads))
- stop("Number of dyads is not possible given the number of vertices. (Multiple edges are not supported.)")
- if (length(dyad.dist) != 3)
- stop("_dyad.dist_ corresponds to the dyad counts or probabilities. Its length must equal three, corresponding to mutual, asymmetric, and null dyads (in that order).")
- if (all(dyad.dist <= 1)){
- if (sparse){
- dyad.dist.round <- round(dyad.dist * n.dyads)
- dyad.dist.diff <- sum(dyad.dist.round) - n.dyads
- # If there's a rounding difference, adjust the nulls.
- dyad.dist.round[3] <- dyad.dist.round[3] - dyad.dist.diff
- g <- as.data.frame(.uman_game_cpp$exact_sparse(n, dyad.dist.round))
- g <- graph.data.frame(g, directed = TRUE, vertices = data.frame(name = 1:n))
- }
- else{
- dyad.dist <- round(dyad.dist, digits = 6)
- g <- .uman_game_cpp$prob(n, dyad.dist)
- g <- graph.adjacency(g, mode = "directed", diag = FALSE)
- }
- }
- else{
- dyad.dist <- round(dyad.dist)
- if (sparse)
- g <- .uman_game_cpp$exact_sparse(n, dyad.dist)
- else
- g <- .uman_game_cpp$exact(n, dyad.dist)
- g <- as.data.frame(g)
- g <- graph.data.frame(g, directed = TRUE, vertices = data.frame(name = 1:n))
- }
- return(g)
- }
- uman_game <- cmpfun(uman_game)
- # Rewires the dyads. It creates a random graph with the same:
- # Indegree and outdegree for each vertex (and density)
- # The level of reciprocity for each vertex (and dyad census)
- # It will not produce multiple edges or loops
- # I would not recommend this function for very large networks, as the matrix will get big.
- # Later, if needed, I can create a sparse version that stores ties as an adjacency list rather than a matrix.
- cppFunction('
- IntegerMatrix dyad_rewire_cpp(IntegerMatrix el, int nswaps){
- int ne = el.nrow();
- IntegerMatrix out(ne, 2);
- int i, j, asymcount, nd, na, d1, d2, s1, r1, s2, r2;
- int nv = 0;
- int counter = 0;
- int nmut = 0;
- int mutcount = 0;
- bool diffind, nonloop, mutswap, nonexist, nonrecipexist;
- for (i = 0; i < ne; ++i){
- if (el(i, 0) > nv){
- nv = el(i, 0);
- }
- if (el(i, 1) > nv){
- nv = el(i, 1);
- }
- }
- LogicalMatrix mat(nv, nv);
- for (i = 0; i < (nv - 1); ++i){
- mat(i, i) = false;
- for (j = (i + 1); j < nv; ++j){
- mat(i, j) = false;
- mat(j, i) = false;
- }
- }
- for (i = 0; i < ne; ++i){
- mat((el(i, 0) - 1), (el(i, 1) - 1)) = true;
- if (mat((el(i, 0) - 1), (el(i, 1) - 1)) & mat((el(i, 1) - 1), (el(i, 0) - 1))){
- nmut += 1;
- }
- }
- nd = ne - nmut;
- na = nd - nmut;
- IntegerMatrix dl(nd, 2);
- asymcount = nmut;
- for (i = 0; i < (nv - 1); ++i){
- for (j = (i + 1); j < nv; ++j){
- if (mat(i, j) & mat(j, i)){
- dl(mutcount, 0) = i + 1;
- dl(mutcount, 1) = j + 1;
- mutcount += 1;
- }
- else if (mat(i, j) & (mat(j, i) == false)){
- dl(asymcount, 0) = i + 1;
- dl(asymcount, 1) = j + 1;
- asymcount += 1;
- }
- else if (mat(j, i) & (mat(i, j) == false)){
- dl(asymcount, 0) = j + 1;
- dl(asymcount, 1) = i + 1;
- asymcount += 1;
- }
- }
- }
- while (counter < nswaps){
- d1 = rand() % nd;
- mutswap = d1 < nmut;
- if (mutswap){
- d2 = rand() % nmut;
- }
- else{
- d2 = rand() % na;
- d2 += nmut;
- }
- s1 = dl(d1, 0);
- r1 = dl(d1, 1);
- s2 = dl(d2, 0);
- r2 = dl(d2, 1);
- diffind = (d1 != d2);
- nonloop = ((s1 != r2) & (s2 != r1));
- nonexist = ((mat(s1 - 1, r2 - 1) == false) & (mat(s2 - 1, r1 - 1) == false));
- nonrecipexist = ((mat(r2 - 1, s1 - 1) == false) & (mat(r1 - 1, s2 - 1) == false));
- if (diffind & nonloop & nonexist & nonrecipexist){
- if (mutswap){
- dl(d1, 1) = r2;
- dl(d2, 1) = r1;
- mat(s1 - 1, r2 - 1) = true;
- mat(s1 - 1, r1 - 1) = false;
- mat(r2 - 1, s1 - 1) = true;
- mat(r1 - 1, s1 - 1) = false;
- mat(s2 - 1, r1 - 1) = true;
- mat(s2 - 1, r2 - 1) = false;
- mat(r1 - 1, s2 - 1) = true;
- mat(r2 - 1, s2 - 1) = false;
- counter += 1;
- }
- else{
- dl(d1, 1) = r2;
- dl(d2, 1) = r1;
- mat(s1 - 1, r2 - 1) = true;
- mat(s1 - 1, r1 - 1) = false;
- mat(s2 - 1, r1 - 1) = true;
- mat(s2 - 1, r2 - 1) = false;
- counter += 1;
- }
- }
- }
- counter = 0;
- for (i = 0; i < nmut; ++i){
- out(counter, 0) = dl(i, 0);
- out(counter, 1) = dl(i, 1);
- counter += 1;
- out(counter, 0) = dl(i, 1);
- out(counter, 1) = dl(i, 0);
- counter += 1;
- }
- for (i = nmut; i < nd; ++i){
- out(counter, 0) = dl(i, 0);
- out(counter, 1) = dl(i, 1);
- counter += 1;
- }
- return out;
- }
- ')
- .dyad_rewire_cpp <- dyad_rewire_cpp
- rm(dyad_rewire_cpp)
- rewire_dyads <- function(g, niter = 100){
- # _g_ is a directed igraph object
- # _niter_ is the number of dyad rewires to perform.
- if (is.directed(g) == FALSE)
- stop("_g_ should be a directed igraph object.")
- if (is.simple(g) == FALSE)
- stop("Graph _g_ must be simple. Multiple edges and loops are not currently supported.")
- gel <- get.edgelist(g, names = FALSE)
- gel <- .dyad_rewire_cpp(gel, niter)
- gel <- as.data.frame(gel)
- # Perhaps consider adding in the vertex and edge attributes here.
- newg <- graph.data.frame(gel, directed = TRUE, vertices = data.frame(name = 1:vcount(g)))
- return(newg)
- }
- rewire_dyads <- cmpfun(rewire_dyads)
Advertisement
Add Comment
Please, Sign In to add comment