If you have already tried out the igraph version of this practicum, then you can skip ahead considerably. If not, then the purpose of this exercise is to further acquaint you with entering data, importing large amounts of data, and manipulating it into a usable form.

One of the most fundamental (and critical) skills in network analysis, is that of gathering data and actually getting it into the programs that you will be using to analyze them. Although this practicum does not cover every aspect of data collection and formatting, it is designed to provide some fairly universal skills in extracting, entering, and analyzing data.

Practicum 2 introduced some of the fundamentals of getting network data loaded into R. Though, that particular exercise involved a small - no more than ten nodes - network, it still gave you all a chance to see some of the many challenges that can arise when loading network data into R. Now that you have a little practical experience, we’ll use some of your new skills and involve a bit more of a challenge. We will be loading a much larger network and running a few diagnostics to give us a summary description and visualization.

Because we are still in the beginning stages of working with network data in R, you will see a lot of repetition between this practicum and practicum 2. That is intentional. Practicum 2 was more of a confidence builder, and most people found it easiest to copy and paste the scripts directly into R. This time, we’ll use an example network to illustrate your objective and then provide instructions on where to find the data you will be using to complete this practicum.

You are welcome to help one another with this. But I expect everyone to do their own work. If you do not do your own work on at least some level, then these lessons will be much more difficult for you to use later.



Getting Started with the Example Data

This part is important. Please be sure to do this part again:

Create a folder labeled “Practicum 4” someplace on your computer, such as your Desktop or wherever you will be able to easily find it again. Then, set your working directory to that folder in RStudio:

  • Session
    • Set Working Directory
    • Choose Directory…



Although it is not required, it can be helpful to follow along with this tutorial using the example data that we are using. Brendan Knapp has converted a classic network (Zachary’s “Karate Club” network) into an edgelist, and I have added some changes to give us a chance to demonstrate some simple data cleaning and manipulation.

We will use the modified version of Zachary’s Karate Club network to walk though the process you will be using to load and clean the edgelist data. The changes that I made will give us something to fix once the data are loaded.

The edgelist can be found at goo.gl/dXmeMK. If you wish to use the Karate Club network to follow along with the tutorial, download it as a CSV file and save it to the folder that you hopefully just created when you followed the directions, above.


Loading the CSV File

Go ahead and load the statnet package to get started.

library(statnet)

With statnet loaded, go ahead and read in the CSV.

Again, just to be clear, we are using Zachary’s “Karate Club” network as an example. The output you see from here on will be for that network. The network you are using for this practicum will look considerably different.

NetworkEL <- read.csv(file.choose(), header=TRUE, na.strings="")
        # na.strings="" tells R that blank cells are missing values.

Troubleshooting: Tools for Working with Edgelists

Sorting and Inspecting

The head() function used here prints the top portion of NetworkEL to the console. Using head() instead of directly calling the variable is a good habit to practice to avoid printing large objects to the console. tail() is also available to print the bottom portion of NetworkEL.

With that, you should inspect your data. Start by looking at how many rows and columns are in the data.

dim(NetworkEL)
## [1] 82  3

The resulting output is listed as: [object number] row column. In this case, we can see that there are 82 rows and 3 columns in the Karate Club data. That could be a problem, since we need only two columns for an edgelist.

The best way to see what is going on in the data is to take an actual look at what you just imported. You have to be careful, though. For really large data sets like the one that you will be working with later, the data can quickly fill up the screen. To keep things a little more under control, try using the head() and tail() functions. They allow you to view just the first six and last six observations, respectively.

head(NetworkEL)
##   V1 V2  X
## 1  1  2 NA
## 2  1  3 NA
## 3  1  4 NA
## 4  1  5 NA
## 5  1  6 NA
## 6  1  7 NA

The output indicate that we have three columns: “V1”, “V2”, and “X”. There is also a column of row numbers. The “X” column was probably caused by a stray space or other hidden character in the spreadsheet and may cause us some trouble if we try to convert this to a network as it is now. Issues like these are common and easy to fix.

If you see more than two columns, try this:

NetworkEL <- NetworkEL[ , c(1,2)]   # Re-save the edgelist using only columns 1 and 2

The data are presently arranged in order of the first column, representing the sender (ego). To re-sort the data alphabetically (or, in this case, numerically) by the second column (a.k.a, the receiver, or alter) for a different perspective.

NetworkEL <- NetworkEL[order(NetworkEL[2]),]

head(NetworkEL)
##    V1 V2
## 1   1  2
## 2   1  3
## 17  2  3
## 18 NA  3
## 3   1  4
## 19  2  4

The output above is the edgelist that we’ll be converting to a network. In the first column, the row numbers show that the rows are now reordered. Each row represents the edge that exists between the sender (ego) and the receiver (alter). But, before we can convert the edgelist to a network, it is a good idea to be sure to check for errors one more time.

When we resorted the data above, it became apparent that there is a missing value in row 18. Missing values are also an issue that should be resolved before converting the data to a network.

Missing Data

When you convert an edgelist to a network in statnet, each ego should have an alter, and visa versa. In other words, for each node sending a tie, there must be one receiving it, and the other way around. That means that we cannot have any missing data in our edgelist.

To check for missings, use the is.na() function. That will return a “TRUE” or “FALSE” value for each time a missing value is present in the data. To make this a little easier on you, just sum up all the times that “TRUE” appears. R treats “TRUE” as 1, and “FALSE” as 0, so is it just a matter of summing up all the NAs.

sum(is.na(NetworkEL))
## [1] 4

This indicates that we have four missing values in the data set.

The quickest way to address this situation is to simply delete all rows with missing values. In this example, I added the missing values for demonstration purposes. In other circumstances, you would probably want to go back and check to see why certain values are missing and replace them so that you do not lose information.

But, this is an example and we don’t have any good reason to keep the rows with missing values. So let’s delete them!

NetworkEL <- na.omit(NetworkEL)
    # Then check for missings again.
sum(is.na(NetworkEL))
## [1] 0

There. Now we have the correct number of columns and there are no longer missing values in the edgelist. It is time to convert the data into a network.

Conversion from Edgelist to Network in statnet

An edgelist is one of the more common ways that a graph can be represented, and statnet allows us to construct a graph object directly from an edgelist by using the graph_from_edgelist() function.

With unfamiliar functions, your first step should always be to read the documentation. Take a look at it by running ?graph_from_edgelist, which searches for the documentation in RStudio’s help tab.

Think of what you see as a guide to how to use the function.

  • The Description section gives you a summary and basic details of the function’s intended use.
  • The Usage section details how the function is actually defined and the arguments that it expects.
  • The Arguments section explains what each of the arguments are referencing.

In graph_from_edgelist’s Description, we are told that it expects the edgelist to be a two-column matrix. In R, a matrix is a specific type of object. We can check to see if our NetworkEL is a matrix by running:

class(NetworkEL)
## [1] "data.frame"

We see that NetworkEL is actually a data.frame. While similar in concept, matrices and data frames are treated differently. This is because data frames can contain different types of columns/variables (character, numeric, etc.) while matrices only contain data of the same type.

In order for us to pass our edgelist to graph_from_edgelist(), we need to convert it into a matrix. Let’s create a new variable for the new, converted object. This is accomplished by running:

Network_Matrix <- as.matrix(NetworkEL)

We can then inspect Network_Matrix’s class to see if it works.

class(Network_Matrix)
## [1] "matrix"
head(Network_Matrix)
    # You get the idea...

Returning to the documentation, we see in Usage how arguments are expected. Notice the directed = TRUE portion of the function’s arguments. This tells us that the directed argument is assigned a default in the event that we don’t pass our own argument. What this means is that unless we specify otherwise, graph_from_edgelist() defaults to making a directed graph.

Considering that our data do not represent one party being the source of an edge, and therefore that the edges have a direction, we know that our network is undirected.

Refresher on statnet Objects

Now, let’s create our network:

  • g is the variable to which we are assigning the network
  • Network_Matrix is the object we created by converting NetworkEL
  • we are explicitly setting directed = to FALSE:
net <- as.network(Network_Matrix, directed=FALSE)
# Or, if it should be a directed network:
net_dir <- as.network(Network_Matrix, directed=TRUE)  # Keep in mind that this network 
                                                          # was not actually undirected. We 
                                                          # are just pretending here for the 
                                                          # sake of having a directed example.

We can then look at our new statnet object by simply calling its variable:

net
##  Network attributes:
##   vertices = 34 
##   directed = FALSE 
##   hyper = FALSE 
##   loops = FALSE 
##   multiple = FALSE 
##   bipartite = FALSE 
##   total edges= 78 
##     missing edges= 0 
##     non-missing edges= 78 
## 
##  Vertex attribute names: 
##     vertex.names 
## 
## No edge attributes
net_dir
##  Network attributes:
##   vertices = 34 
##   directed = TRUE 
##   hyper = FALSE 
##   loops = FALSE 
##   multiple = FALSE 
##   bipartite = FALSE 
##   total edges= 78 
##     missing edges= 0 
##     non-missing edges= 78 
## 
##  Vertex attribute names: 
##     vertex.names 
## 
## No edge attributes

In comparison with igraph, the information contained in an statnet object is more or less spelled-out.



Analysis

Use the following code to produce the measures discussed in chapter 4 of Understanding Dark Networks.

Local Measures in statnet

There are many, many available centrality measures that have been developed for network analysis. At present, there is no program that is so comprehensive that it includes all of the measures. We will, therefore, limit this discussion to a subset of the measures that are included in statnet. These include the “big four” measures (degree, betweenness, closeness, and eigenvector) and a few useful others. In what follows, we introduce:

  • Degree Centrality
    • In-degree
    • Out-degree
  • Eigenvector Centrality
  • Hubs & Authorities
  • Closeness Centrality
  • Reach Centrality
  • Betweenness Centrality


Degree Centrality

There are variations in the way that each of these measures will treat the network you are analyzing. So it is always a good idea to check the default settings with ?. For instance, ?degree will produce the help page for the degree centrality measure. Under the word “usage” you will find:

degree(dat, g=1, nodes=NULL, gmode="digraph", 
        diag=FALSE, tmaxdev=FALSE, 
        cmode="freeman", rescale=FALSE, 
        ignore.eval=FALSE)

This is saying that the default settings in statnet, among other things, ignore loops when calculating degree (diag=FALSE). Note, this is the opposite of igraph, which employs loops when calculating degree.

We can also see that the resulting output will be raw counts for degree, rather than rescaled scores (rescale=FALSE). These are defaults for the program. So if you simply calculate density using degree(net), then R will return raw counts. To produce rescaled output, add rescale=TRUE to your script: degree(net, rescale=TRUE).

The gmode argument is intended for differentiating between directed and undirected networks. By default, statnet will treat the network as though it is directed. Correspondingly, the cmode argument allows you to select the type of output to calculate. By default, this is Freeman’s definition of degree centrality (the number of edges that are incident upon a node). If you are analyzing a directed network, you should select either “indegree” or “outdegree”.

Try this out with the two networks (net, and net_dir) that we created above.

Degree <- degree(net)
Indegree.Undirected <- degree(net, cmode="indegree")
Outdegree.Undirected <- degree(net, cmode="outdegree")

Degree.Directed <- degree(net_dir)
Indegree <- degree(net_dir, cmode="indegree")
Outdegree <- degree(net_dir, cmode="outdegree")

To see what the output for each of these looks like, use the head() function. That will show you the first six observations.

A method that many may find easier, or possibly more intuitive is to group all of these measures together into a data frame and look at them all at once. To do this, use the cbind command to combine the measures for comparison.

CompareDegree <- cbind(Degree, Indegree.Undirected, Outdegree.Undirected, Degree.Directed, Indegree, Outdegree)
# Then look at just the first few observations, to save space.
head(CompareDegree)
##      Degree Indegree.Undirected Outdegree.Undirected Degree.Directed
## [1,]     32                  16                   16              16
## [2,]     18                   9                    9               9
## [3,]     20                  10                   10              10
## [4,]     12                   6                    6               6
## [5,]      6                   3                    3               3
## [6,]      8                   4                    4               4
##      Indegree Outdegree
## [1,]        0        16
## [2,]        1         8
## [3,]        2         8
## [4,]        3         3
## [5,]        1         2
## [6,]        1         3

This is a little bit of a manufactured comparison, since the network data that we are using were really designed as an undirected network. Normally, you will not see the indegree and outdegree from the directed network summing to the same total as the undirected version of the network. But, hopefully, you get the idea.


That was a fairly lengthy treatment of degree centrality. The following centralities will be fairly minimal in explanation and interpretation. The degree example should demonstrate how to modify the centrality measures to work with dstatnets.


Eigenvector Centrality

Eigenvector centrality calculates a node’s cumulative indirect access to power.

Eig <- evcent(net)

Closeness

Closeness <- closeness(net)

Betweenness

Betweenness <- betweenness(net)

Hubs & Authorities and Reach

Neither of these measures is available in statnet. Though, to be fair, reach centrality is not available in either suite.

Douglas Luke’s A User’s Guide to Network Analysis in R provided the following table to explain what each suite offers in terms of node-level calculations.

Measure statnet igraph
Degree degree() degree()
Closeness closeness() closeness()
Betweenness betweenness() betweenness()
Eigenvector evcent() evcent()
Bonacich power bonpow() bonpow()
Flow betweenness loadcent()
Load loadcent()
Information infocent()
Stress stresscent()
Harary graph graphcent()
Bonacich alpha alpha_centrality()
Kleinberg authority auth_score()
Kleinberg hub_score()
PageRank page_rank()



Comparing Centrality Scores

To keep all your work in one place, put it all together in one data frame. You can then export the data frame to a spreadsheet, or sort it in R (see the “Some More Advanced Versions of Doing the Above” section of this site for that).

centralities <- cbind(Degree, Eig, Closeness, Betweenness)

# Save it to your computer as a spreadsheet
write.csv(centralities, file="centralities.csv")

Comparing the Various Measures Statistically

R is primarily a statistical program. So you can take advantage of that by seeing how correlated the various centralities are.

round(cor(centralities), 2)
##             Degree  Eig Closeness Betweenness
## Degree        1.00 0.92      0.77        0.91
## Eig           0.92 1.00      0.90        0.80
## Closeness     0.77 0.90      1.00        0.72
## Betweenness   0.91 0.80      0.72        1.00

The cor() function produces a correlation matrix. In this case, we can see the correlation values for each pair of centralities. We can see, for example, that degree is correlated with eigenvector, hubs, and authorities at a 0.91 level.

The round( , ) function sets the number of digits that are displayed after the decimal. If you would like to see what you would have gotten if you had not rounded to the first two places after the decimal, try: cor(centralities). The round function takes two arguments: (1) the object, number, or vector that you are rounding, and (2) the number of digits that should appear after the decimal in the output. In this case, we have set the output to just include two digits after the decimal.

Please keep in mind that this is just a “quick and dirty” way of comparing the centralities. In most cases, when you are comparing numeric attributes of vertices it is best to use statistical tests that are designed with network data in mind. Tools such as conditional uniform graphs (CUG), quadratic assignment procedure (QAP), stochastic actor oriented modeling (SAOM) exponential random graph modeling (ERGM / P*), and are explicitly designed to handle the interdependencies present within the network.

We covered those in a different section.







The World Treaty Index’s WTI Bilateral Agreement Dataset

To retrieve the data you will be using for this practicum, go to the World Treaty Index and download their database of bilateral agreements.

If you are typing this into your browser, go to the World Treaty Index: http://worldtreatyindex.com/, go to “download”, and select the “WTI Bilateral Agreement Dataset”.

Now, drag the csv file into the folder you created on your desktop for this purpose. That folder is where you will be doing all of your work.

These data are being used for practice purposes. To find out more about them, spend a little time on the website looking through what they have to say about the data and its proper analysis. There are essentially 18 variables and 61,346 treaties in this spreadsheet. But we are interested in the network created by all of these bilateral agreements. So all you will really need for this is the list of countries participating in each agreement.

Look through the headers in the dataset for “party1” and “party2”. These are the only two columns you will need. You may either copy both columns into a new spreadsheet, or delete all columns that are not “party1” or “party2”.

Important Note:
Although this is a dataset of bilateral agreements, there are still some unilateral “agreements” included. This means that there are missings. You will, therefore, have to decide what to do about those missing values. You could copy the sender’s name into the receiver’s column, and visa versa, to make a recursive tie (a.k.a, a “loop”). Alternatively, you can check the comments section of the dataset to decide who the implicit recipient of the tie should be. Or, you could, of course, just choose to delete the row with the missing value altogether.

The decision about what to do about the missing values in this network is yours. But be ready to explain what you did and why.



Deliverable Instructions

Submit a Google document with the following to satisfy the terms of this practicum:

  1. Describe the data - as though you were preparing this for someone other than me - and respond to the following:
    • Select some global measures that you think are appropriate for this network and interpret them. (See Practicum 3)
    • What the empty cells signify and what you decided to do with them
  2. List five countries that you suspect are the most active in their participation in treaties. (Just guess or list the first five you can think of if you have no idea.), Then provide the list and the name of the measure you used for each of the following:
    • List the top 5 or 10 countries (with their corresponding values of the measure you used) that have entered into the largest number of treaties overall.
    • List the top 5 or 10 countries (with their corresponding values of the measure you used) that you consider to be brokers in this network.
    • List the top 5 or 10 countries (with their corresponding values of the measure you used) that have the largest neighborhood within two steps in this network.
    • List the top 5 or 10 countries (with their corresponding values of the measure you used) that you feel occupy the most critical junctures in this network. Then tell why you select that particular measure for this network.
  3. Include the best visualization you can manage, as well as a caption that tells what layout you used and what the visualization demonstrates.

Include your name, and the title of the practicum.







Some More Advanced Options for Doing the Above

Make the centrality scores into vertex attributes

In each of the following examples, you are adding a vertex attribute to the network data. The attribute in net %v% "attribute" should be replaced with the name of the attribute being added to the network data. The function to the right of the assignment arrow (<-) is calculating the value of the attribute for each node in the network. Once you have finished these, check your work using net. You should see these four centralities listed among the vertex attributes.

Word to the wise, if you want to be able to access these another day, be sure to save your network (save(net, file="Zachary_CentalityNetwork.Rda")). If you forget to save, you can always re-run this script. But, that is more work, isn’t it?

net %v% "degree"      <- degree(net)       # Degree centrality
net %v% "eig"         <- evcent(net)       # Eigenvector centrality
net %v% "closeness"   <- closeness(net)    # Closeness centrality
net %v% "betweenness" <- betweenness(net)  # Vertex betweenness centrality

Alternate way to Create a Data Frame

centrality <- data.frame(row.names   = net %v% "vertex.names",
                         degree      = net %v% "degree",     
                         closeness   = net %v% "eig",        
                         betweenness = net %v% "closeness",  
                         eigenvector = net %v% "betweenness")

centrality <- centrality[order(row.names(centrality)),]

head(centrality)
##    degree  closeness betweenness eigenvector
## 1      32 0.35549144   0.5689655 462.1428571
## 10      4 0.10267425   0.4342105   0.8952381
## 11      6 0.07596882   0.3793103   0.6666667
## 12      2 0.05285570   0.3666667   0.0000000
## 13      4 0.08425463   0.3707865   0.0000000
## 14     10 0.22647272   0.5156250  48.4317460

Save the data frame as a .csv file

Many will choose this method just to make life easier for themselves. Once you have saved this in CSV format, you can sort the output in a spreadsheet program such as Excel, Numbers, Google Sheets, etc.

write.csv(centrality, file = "Centrality.csv")

If you prefer to sort your data in R…

If we use the data frame that we created and named “centrality”, earlier, we can sort it multiple ways. For this example, we’ll only sort on the betweenness measure.

Lowest tie value
# Minimum value
min(centrality$betweenness)
## [1] 0.2844828
# Maximum value
max(centrality$betweenness)
## [1] 0.5689655
# Bottom six 

head(centrality[order(-centrality$betweenness),])
##    degree closeness betweenness eigenvector
## 1      32 0.3554914   0.5689655   462.14286
## 3      20 0.3171925   0.5593220   151.70159
## 34     34 0.3733635   0.5500000   321.10317
## 32     12 0.1910338   0.5409836   146.01905
## 14     10 0.2264727   0.5156250    48.43175
## 33     24 0.3086442   0.5156250   153.38095
# Bottom five
head(centrality[order(-centrality$betweenness),], n=5)
##    degree closeness betweenness eigenvector
## 1      32 0.3554914   0.5689655   462.14286
## 3      20 0.3171925   0.5593220   151.70159
## 34     34 0.3733635   0.5500000   321.10317
## 32     12 0.1910338   0.5409836   146.01905
## 14     10 0.2264727   0.5156250    48.43175
# Bottom 10
head(centrality[order(-centrality$betweenness),], n=10)
##    degree closeness betweenness eigenvector
## 1      32 0.3554914   0.5689655   462.14286
## 3      20 0.3171925   0.5593220   151.70159
## 34     34 0.3733635   0.5500000   321.10317
## 32     12 0.1910338   0.5409836   146.01905
## 14     10 0.2264727   0.5156250    48.43175
## 33     24 0.3086442   0.5156250   153.38095
## 9      10 0.2274039   0.5156250    59.05873
## 20      6 0.1479125   0.5000000    34.29365
## 2      18 0.2659599   0.4852941    56.95714
## 4      12 0.2111797   0.4647887    12.57619
Highest tie value
max(degree(net))
## [1] 34
# Top six
head(centrality[order(centrality$betweenness),])
##    degree  closeness betweenness eigenvector
## 17      4 0.02363563   0.2844828           0
## 27      4 0.07557941   0.3626374           0
## 12      2 0.05285570   0.3666667           0
## 13      4 0.08425463   0.3707865           0
## 15      4 0.10140326   0.3707865           0
## 16      4 0.10140326   0.3707865           0
# Top five
head(centrality[order(centrality$betweenness),], n=5)
##    degree  closeness betweenness eigenvector
## 17      4 0.02363563   0.2844828           0
## 27      4 0.07557941   0.3626374           0
## 12      2 0.05285570   0.3666667           0
## 13      4 0.08425463   0.3707865           0
## 15      4 0.10140326   0.3707865           0
# Top ten
head(centrality[order(centrality$betweenness),], n=10)
##    degree  closeness betweenness eigenvector
## 17      4 0.02363563   0.2844828           0
## 27      4 0.07557941   0.3626374           0
## 12      2 0.05285570   0.3666667           0
## 13      4 0.08425463   0.3707865           0
## 15      4 0.10140326   0.3707865           0
## 16      4 0.10140326   0.3707865           0
## 19      4 0.10140326   0.3707865           0
## 21      4 0.10140326   0.3707865           0
## 23      4 0.10140326   0.3707865           0
## 18      4 0.09239954   0.3750000           0

Visualization Options

Normally, statnet will calculate new coordinates every time you visualize a network. This can be annoying because it changes the orientation of the network every time. For those times when you prefer to show the network in one static position, to show the differences in node size or color, you can save the coordinates from a particular layout and reuse them multiple times. To do so, just save the coordinates from the first time you run the visualization, and reuse it as you like.

Here, lay is a variable to which you’re assigning the coordinates that statnet calculates based off an algorithm of your choosing. Here’s g using a force-directed algorithm referred to as Kamada-Kawai, the names of its creators.

lay <- gplot(net, mode="kamadakawai")

gplot(net, coord=lay, 
     usearrows=FALSE)

This is a complicated network for which Kamada-Kawai doesn’t yield a very helpful plot. You can find mode layouts to try in the statnet documentation by searching in the Help pane or running layout_. Simply assign the coordinates to the lay variable, and use your new lay as your layout argument when use the plot() function.

Here’s another example using another force-directed algorithm referred to as Fruchterman-Reingold, the names of its creators.

lay <- gplot(net, mode="fruchtermanreingold")

gplot(net, coord=lay, 
     usearrows=FALSE)

Try some of the options from Practicum 2 to see if you can render a better visualization with some other layout.

Here is what you can do with the same layout:

Size vertices by centrality measure!

Note: Some centrality measures result in small values. To make the relative differences between them visible, it is important to rescale the centrality vector. We have noted the places where the centrality output was rescaled. There is no real rule to this. In this case, we tried out a few scaling values until the visualization took on the visual aspect we hoped to display.

It is important to rescale an entire vector of output at once in the same manner. Do not change only a few values, as that will change the relative differences between them.

lay <- gplot(net, mode="fruchtermanreingold")

gplot(net, coord=lay, 
     usearrows=FALSE)

gplot(net, coord=lay, 
     usearrows=FALSE,
     vertex.cex=degree(net)/10,        # Rescaled by dividing by 10
     main="Degree")

gplot(net_dir,  coord=lay, 
     usearrows=FALSE,
     vertex.cex=degree(net_dir, cmode="indegree")/2,# Rescaled by multiplying by 2 
     main="Indegree")

gplot(net_dir,  coord=lay, 
     usearrows=FALSE,
     vertex.cex=degree(net_dir, cmode="outdegree")/2,# Rescaled by multiplying by 2 
     main="Outdegree")

gplot(net,  coord=lay, 
     usearrows=FALSE,
     vertex.cex=evcent(net)*10,             # Rescaled by multiplying by 10
     main="Eigenvector")

gplot(net_dir,  coord=lay, 
     usearrows=FALSE,
     vertex.cex=closeness(net)*5,     # Rescaled by multiplying by 5
     main="Closeness (X 1000)")

gplot(net,  coord=lay, 
     usearrows=FALSE,
     vertex.cex=betweenness(net)/75,  # Rescaled by dividing by 75
     main="Betweenness")