3

SNA folks: I am trying to create a two-mode network from a data frame in R. I have a list of organizations which are connected via common membership in a parent organization. I have membership in that organization coded in a binary variable. I have successfully created a sociomatrix and subsequent network object based on those data via the following code (from Create adjacency matrix based on binary variable):

library(statnet)
org <- c("A","B","C","D","E","F","G","H","I","J")
link <- c(1,0,0,0,1,1,0,0,1,1)
person <- c("Mary","Michael","Mary","Jane","Jimmy",
            "Johnny","Becky","Bobby","Becky","Becky")

df <- data.frame(org,link,person)

socmat1 <- tcrossprod(df$link)
rownames(socmat1) <- df$org
colnames(socmat1) <- df$org
diag(socmat1) <- 0
socmat1
#>   A B C D E F G H I J
#> A 0 0 0 0 1 1 0 0 1 1
#> B 0 0 0 0 0 0 0 0 0 0
#> C 0 0 0 0 0 0 0 0 0 0
#> D 0 0 0 0 0 0 0 0 0 0
#> E 1 0 0 0 0 1 0 0 1 1
#> F 1 0 0 0 1 0 0 0 1 1
#> G 0 0 0 0 0 0 0 0 0 0
#> H 0 0 0 0 0 0 0 0 0 0
#> I 1 0 0 0 1 1 0 0 0 1
#> J 1 0 0 0 1 1 0 0 1 0

testnet <- as.network(x = socmat1,
                  directed = FALSE,
                  loops = FALSE,
                  matrix.type = "adjacency"
)
testnet
#>  Network attributes:
#>   vertices = 10 
#>   directed = FALSE 
#>   hyper = FALSE 
#>   loops = FALSE 
#>   multiple = FALSE 
#>   bipartite = FALSE 
#>   total edges= 10 
#>     missing edges= 0 
#>     non-missing edges= 10 
#> 
#>  Vertex attribute names: 
#>     vertex.names 
#> 
#> No edge attributes

Created on 2020-10-24 by the reprex package (v0.3.0)

However, I obviously can't use tcrossprod() similarly to achieve the same result with individuals connected by organizations or vice versa, as shown by the following code:

socmat2 <- tcrossprod(df$org)
#> Error in df$org: object of type 'closure' is not subsettable
rownames(socmat2) <- df$person
#> Error in df$person: object of type 'closure' is not subsettable
colnames(socmat2) <- df$person
#> Error in df$person: object of type 'closure' is not subsettable
diag(socmat2) <- 0
#> Error in diag(socmat2) <- 0: object 'socmat2' not found
socmat2
#> Error in eval(expr, envir, enclos): object 'socmat2' not found

How can I create a two-mode network with the first set of edges being an organization's membership in the larger organization (denoted by the link variable) and the second being an individual's leadership position in an organization?

Thanks, all.

Created on 2020-10-24 by the reprex package (v0.3.0)

Abe
  • 393
  • 2
  • 13
  • Do you have data on individual leadership positions? Linking individuals to orgs seems feasible, though the data you provided doesn't have any info on leadership positions, so the question is unanswerable. – Werner Hertzog Oct 25 '20 at 03:38
  • This is fake data based on the real data I have. The ```person``` variable is the "leader" for each organization in the ```org``` variable. Essentially I want to see the ties between organizations that are running through the person nodes (so first run as a two-mode network, which is then collapsable to a one-mode network based on the intermediary ties between any two organizations that are the people who are leaders in both organizations). – Abe Oct 25 '20 at 04:58
  • So Mary is the leader for A and C. She's a member (link) of A but not a member of C. Correct? – Werner Hertzog Oct 25 '20 at 10:23
  • 1
    She is a leader of both organizations. In the full data I have a "primary contact" and a "secondary contact" variable. The primary contact variable is complete for all organizations; the secondary is only partially complete. The ```person``` variable here is the equivalent of the primary contact in the full data. – Abe Oct 25 '20 at 15:40
  • Oh, OK. I think I get it now. – Werner Hertzog Oct 25 '20 at 16:13

1 Answers1

3

There are many different ways to do what you're trying to do. I don't know of any function that magically creates a two-mode network based on the data you have, so the solution below involves a bit of data manipulation. We first create a data frame with the nodes, then another one with the edges. Then use nodes and edges as inputs to create a network object. The code is self-explanatory:

library(tidyverse)
library(network)

# Let's create a 'nodes' data frame
my_nodes <- as.data.frame(rbind(
  cbind(nodename = org, type = "Organization"),
  cbind(unique(person), "People"),
  cbind("Parent", "Parent org")))

# Let's add an ID column to the nodes data frame
my_nodes <- rowid_to_column(my_nodes, "ID")

# Let's create a data frame with al possible edges 
# (i.e., connecting organizations to people and organizations to the parent organization)
my_edges <- data.frame(rbind(
  cbind(ColA = org, ColB = person, type = "Set 1"),
  cbind(org, link, "Set 2")))

my_edges <- subset(my_edges, ColB != 0) 
my_edges$ColB[my_edges$ColB == 1] <- "Parent"

# Let's set up the network object using edges and nodes
my_network <- network(my_edges,
                      vertex.attr = my_nodes,
                      matrix.type = "edgelist",
                      ignore.eval = FALSE)

Notice that we created a column type to categorize both nodes and edges. We can use type to change node/edge color, size, shape, etc. when visualizing the network.

Here is an example using package igraph. First, we convert the network object into an igraph object.

library(igraph)
library(intergraph)

my_netgraph <- asIgraph(my_network)

The attributes of your nodes can be assessed using V(my_netgraph)$attribute_name. For instance, let's look at the type of nodes in your network that we defined earlier:

> V(my_netgraph)$type
[1] "Organization" "Organization" "Organization" "Organization" "Organization" "Organization"
[7] "Organization" "Organization" "Organization" "Organization" "People"       "People"      
[13] "People"       "People"       "People"       "People"       "People"       "Parent org"

Now let's color those nodes according to type. To do that, we will create a new attribute, $color. Each $color should correspond to a different $type:

V(my_netgraph)[V(my_netgraph)$type == "People"]$color <- "green"
V(my_netgraph)[V(my_netgraph)$type == "Organization"]$color <- "red"
V(my_netgraph)[V(my_netgraph)$type == "Parent org"]$color <- "yellow"

plot(my_netgraph)

This is what the network looks like now:

enter image description here

Now let's change the $shape of the nodes according to attribute $type:

V(my_netgraph)[V(my_netgraph)$type == "People"]$shape <- "circle"
V(my_netgraph)[V(my_netgraph)$type == "Organization"]$shape <- "square"
V(my_netgraph)[V(my_netgraph)$type == "Parent org"]$shape <- "rectangle" 

plot(my_netgraph)

enter image description here

We can change other attributes of our igraph object using the functions below:

E(my_netgraph) # changes he edges of the "net" object
V(my_netgraph) # changes the vertices of the "net" object
E(my_netgraph)$type # changes edge attribute "type"
V(my_netgraph)$media # changes the vertex attribute "media"

You can find more details on this iGraph manual (pages 10-11).

Werner Hertzog
  • 2,002
  • 3
  • 24
  • 36
  • Thanks Werner! This worked. The one issue I'm dealing with now is modifying the color, size, shape as you describe. I've wound up with the dreaded network "snowball"/Deathstar. Any chance you can demonstrate how you make these modifications? Additionally, I'm wondering if there's a similar solution using ```igraph``` since it has such appealing visualizations (to my understanding). – Abe Oct 28 '20 at 21:33
  • Glad it worked! It's hard for me to give an informed opinion on the shape of the network since I only have a tiny sample of your data. I'll edit the answer and add an example of how to use those attributes to change the color of the nodes with igraph. – Werner Hertzog Oct 29 '20 at 02:30
  • 1
    This question might be useful regarding the shape of your network: https://stackoverflow.com/questions/22453273/how-to-visualize-a-large-network-in-r – Werner Hertzog Oct 29 '20 at 03:25
  • 1
    Thanks again. This is super helpful. I had found the ```intergraph``` package just before you posted this so managed the conversion, but still have my "eyeball" thing going on. I'd also like to have node size be based on centrality. I'll do some digging though. Thanks for the detailed responses. If not for COVID and this being the internet, I'd buy you a beer. – Abe Oct 29 '20 at 04:07