problem with igraph degree( ) function - igraph

I have an N x 2 table of integers called games[ , ]. The table of nodes/edges is converted to a graph:
net <- graph.data.frame(as.data.frame(games), directed=FALSE)
deg.net <- degree(net, mode='total', loops=FALSE)
(I realize that not all options are necessary.)
The problem I am having is that the degree distribution seems to be for in-degree only. For example, the games file has the lines:
103 86
24 103
103 2
92 103
87 103
103 101
103 44
and yet igraph indicates that the degree for node 103 is '3' when it should be '7'.
Any insight in what I am missing would be appreciated.

One thing that you should keep in mind is that most igraph functions refer to the vertices by their IDs, which are simply integers from 0 to N-1 where N is the number of vertices in the graph. If you have an N x 2 table of integers (containing zero-based vertex indices) and you want igraph to use the integers as the vertex IDs, you can simply use the graph constructor after having flattened the matrix into a vector by rows. When you use graph.data.frame, the first two columns of the data frame are assumed to contain symbolic vertex names (i.e. there is no requirement that they must be integers); these will be assigned to the name vertex attribute, and igraph will simply make up the IDs from 0 to N-1.
So, let's assume that you have an N x 2 matrix, one row per each edge:
> edges <- matrix(c(103, 86, 24, 103, 103, 2, 92, 103, 87, 103, 103, 101, 103, 44), ncol=2, byrow=T)
First we create a graph out of it after flattening the matrix by rows:
> g <- graph(as.vector(t(edges)))
This gives you a directed graph with 7 edges and the out/in-degrees of vertex 103 will be as expected:
> ecount(g)
7
> degree(g, 103, mode="out")
4
> degree(g, 103, mode="in")
3
> degree(g, 103, mode="all")
7
If you use graph.data.frame with the above matrix, igraph will construct a graph where the numbers in the matrix are stored in the name vertex attribute:
> g <- graph.data.frame(as.data.frame(edges))
> V(g)$name
[1] "103" "24" "92" "87" "86" "2" "101" "44"
This shows you that the vertex with the name 103 actually became vertex zero in the graph:
> degree(g, 0, mode="out")
4
> degree(g, 0, mode="in")
3
> degree(g, 0, mode="all")
7
As far as I know, degree is also able to work with the vertex names directly if there is a vertex attribute called name in the graph, so you can also do this:
> degree(g, "103", mode="in")
3
Hope this helps.

You created a undirected graph. There is no in and out degree in such a graph. From the igraph documentation link you can get the general idea. Your deg.net will return a vector with all the degrees(per node) on your graph, similar to this:
[1] 2 1 1 1 1 1 1
If you want to get the degree of a specific node(in our example it's 103) you have to specify the node(appearence_order-1). In your example you are looking for the (1st_node-1), it's node 0. So you must type:
degree(net,0, mode='total', loops=FALSE)
Which will return the degree of node 0, "7".

Related

NetLogo - using BehaviorSpace get all turtles locations as the result of each repetition

I am using BehaviorSpace to run the model hundreds of times with different parameters. But I need to know the locations of all turtles as a result instead of only the number of turtles. How can I achieve it with BehaviorSpace?
Currently, I output the results in a csv file by this code:
to-report get-locations
report (list xcor ycor)
end
to generate-output
file-open "model_r_1.0_locations.csv"
file-print csv:to-row get-locations
file-close
end
but all results are popped into same csv file, so I can't tell the condition of each running.
Seth's suggestion of incorporating behaviorspace-run-number in the filename of your csv output is one alternative. It would allow you to associate that file with the summary data in your main BehaviorSpace output file.
Another option is to include list reporters as "measures" in your behavior space experiment definition. For example, in your case:
map [ t -> [ xcor ] of t ] sort turtles
map [ t -> [ ycor ] of t ] sort turtles
You can then parse the resulting list "manually" in your favourite data analysis language. I've used the following function for this before, in Julia:
parselist(strlist, T = Float64) = parse.(T, split(strlist[2:end-1]))
I'm sure you can easily write some equivalent code in Python or R or whatever language you're using.
In the example above, I've outputted separate lists for the xcor and the ycor of turtles. You could also output a single "list of lists", but the parsing would be trickier.
Edit: How to do this using the csv extension and R
Coincidentally, I had to do something similar today for a different project, and I realized that a combination of the csv extension and R can make this very easy.
The general idea is the following:
In NetLogo, use csv:to-string to encode list data into a string and then write that string directly in the BehaviorSpace output.
In R, use purrr::map and readr::read_csv, followed by tidyr::unnest, to unpack everything in a neat "one observation per row" dataframe.
In other words: we like CSV, so we put CSV in our CSV so we can parse while we parse.
Here is a full-fledged example. Let's say we have the following NetLogo model:
extensions [ csv ]
to setup
clear-all
create-turtles 2 [ move-to one-of patches ]
reset-ticks
end
to go
ask turtles [ forward 1 ]
tick
end
to-report positions
let coords [ (list who xcor ycor) ] of turtles
report csv:to-string fput ["who" "x" "y"] coords
end
We then define the following tiny BehaviorSpace experiment, with only two repetitions and a time limit of two, using our positions reporter as an output:
The R code to process this is pleasantly straightforward:
library(tidyverse)
df <- read_csv("experiment-table.csv", skip = 6) %>%
mutate(positions = map(positions, read_csv)) %>%
unnest()
Which results in the following dataframe, all neat and tidy:
> df
# A tibble: 12 x 5
`[run number]` `[step]` who x y
<int> <int> <int> <dbl> <dbl>
1 1 0 0 16 10
2 1 0 1 10 -2
3 1 1 1 9.03 -2.24
4 1 1 0 -16.0 10.1
5 1 2 1 8.06 -2.48
6 1 2 0 -15.0 10.3
7 2 0 1 -14 1
8 2 0 0 13 15
9 2 1 0 14.0 15.1
10 2 1 1 -13.7 0.0489
11 2 2 0 15.0 15.1
12 2 2 1 -13.4 -0.902
The same thing in Julia:
using CSV, DataFrames
df = CSV.read("experiment-table.csv", header = 7)
cols = filter(col -> col != :positions, names(df))
df = by(df -> CSV.read(IOBuffer(df[:positions][1])), df, cols)

rjson::fromJSON returns only the first item

I have a sqlite database file with several columns. One of the columns has a JSON dictionary (with two keys) embedded in it. I want to extract the JSON column to a data frame in R that shows each key in a separate column.
I tried rjson::fromJSON, but it reads only the first item. Is there a trick that I'm missing?
Here's an example that mimics my problem:
> eg <- as.vector(c("{\"3x\": 20, \"6y\": 23}", "{\"3x\": 60, \"6y\": 50}"))
> fromJSON(eg)
$3x
[1] 20
$6y
[1] 23
The desired output is something like:
# a data frame for both variables
3x 6y
1 20 23
2 60 50
or,
# a data frame for each variable
3x
1 20
2 60
6y
1 23
2 50
What you are looking for is actually a combination of lapply and some application of rbind or related.
I'll extend your data a little, just to have more than 2 elements.
eg <- c("{\"3x\": 20, \"6y\": 23}",
"{\"3x\": 60, \"6y\": 50}",
"{\"3x\": 99, \"6y\": 72}")
library(jsonlite)
Using base R, we can do
do.call(rbind.data.frame, lapply(eg, fromJSON))
# X3x X6y
# 1 20 23
# 2 60 50
# 3 99 72
You might be tempted to do something like Reduce(rbind, lapply(eg, fromJSON)), but the notable difference is that in the Reduce model, rbind is called "N-1" times, where "N" is the number of elements in eg; this results in a LOT of copying of data, and though it might work alright with small "N", it scales horribly. With the do.call option, rbind is called exactly once.
Notice that the column labels have been R-ized, since data.frame column names should not start with numbers. (It is possible, but generally discouraged.)
If you're confident that all substrings will have exactly the same elements, then you may be good here. If there's a chance that there will be a difference at some point, perhaps
eg <- c(eg, "{\"3x\": 99}")
then you'll notice that the base R solution no longer works by default.
do.call(rbind.data.frame, lapply(eg, fromJSON))
# Error in (function (..., deparse.level = 1, make.row.names = TRUE, stringsAsFactors = default.stringsAsFactors()) :
# numbers of columns of arguments do not match
There may be techniques to try to normalize the elements such that you can be assured of matches. However, if you're not averse to a tidyverse package:
library(dplyr)
eg2 <- bind_rows(lapply(eg, fromJSON))
eg2
# # A tibble: 4 × 2
# `3x` `6y`
# <int> <int>
# 1 20 23
# 2 60 50
# 3 99 72
# 4 99 NA
though you cannot call it as directly with the dollar-method, you can still use [[ or backticks.
eg2$3x
# Error: unexpected numeric constant in "eg2$3"
eg2[["3x"]]
# [1] 20 60 99 99
eg2$`3x`
# [1] 20 60 99 99

Error in FUN(X[[1L]], ...) : as.edgelist.sna input must be an adjacency matrix/array, edgelist matrix, network, or sparse matrix, or list thereof

I am trying to learn few basic functions in Igraph- But, I am having problems computing the degrees from a gragph: see example below (I copied the following example from this site):
Example of data set:
edges <- matrix(c(103, 86, 24, 103, 103, 2, 92, 103, 87, 103, 103, 101, 103, 44), ncol=2, byrow=T)
Create graph
g <- graph(as.vector(t(edges)))
I can compute the degrees from the matrix edges:
degree(edges)
[1] 378 254 210 390 380 408 294 1230 1084
But I cannot compute the degrees from the graph g:
degree(g)
I am getting the following error:
Error in FUN(X[[1L]], ...) :
as.edgelist.sna input must be an adjacency matrix/array, edgelist matrix, network, or sparse matrix, or list thereof.
Anyone knows why I am getting this error?
So what happened here is igraph::degree is masked by sna::degree.
Just use:
igraph::degree
and it should work
I ran into same issue.
This worked for me:
net <- make_ring(10)
deg <- centralization.degree(net)$res

How does multi-texture OBJ->JSON converted files keeps track of face-texture mapping?

I'm trying to manually (no libs such as Three.js) load a JSON 3D model into my webGL code just for fun but I'm having a hard time when my models have more than 1 texture.
In a OBJ->JSON converted file, how do I know which texture is the "active" for the faces that follow? OBJ files use 'usemtl' tag to identify the texture/material in use but I can't seem to find that kind of pointer when working with JSONs.
In time, I'm using the OBJ->JSON converter written by alteredq
Thanks a bunch,
Rod
Take a look at this file: three.js / src / extras / loaders / JSONLoader.js.
The first element of each face in the faces array of the JSON file is a bit field. The first bit says if that face have three o four indices. And the second bit says if that face has a material assigned. Material index, if any, appears after indices.
Example: faces: [2, 46, 44, 42, 0, 1, 45, 46, 48, 3, ...
First face (triangle with material):
Type: 2 (00000010b)
Indices: 46, 44, 42
Material index: 0
Second face (quad without material):
Type: 1 (00000001b)
Indices: 45, 46, 48
Third face (quad with material):
Type: 3 (00000011b)
Indices: ...
Check source code for full meaning of that bit field.
In the OBJ->JSON converter I have written for the KickJS game engine, each material has its own range of indices.
This means a simple OBJ model such as
mtllib plane.mtl
o Plane
v 1.000000 0.000000 -1.000000
v 1.000000 0.000000 1.000000
v -1.000000 0.000000 1.000000
v -1.000000 0.000000 -1.000000
usemtl Material
s 1
f 2 3 4
usemtl Material.001
f 1 2 4
Would be translated into this (With two indices; one for each material):
[
{
"vertex": [1,0,1,-1,0,1,-1,0,-1,1,0,-1],
"name": "Plane mesh",
"normal": [0,-1,0,0,-1,0,0,-1,0,0,0,0],
"indices0": [0,1,2],
"indices1": [3,0,2]
}
]
Use the online model viewer for the convertion:
http://www.kickjs.org/example/model_viewer/model_viewer.html

Fill a region on a graph with no outline?

I'd like to fill a region on a graph plotted with octave, without any outline:
The fill command accepts a color argument that it respects for the filled area, but it doesn't seem to accept the 'LineColor' property to change the color of the line it draws around the filled area...
e.g.
fill([1 2 3 3 2 1], [1 0.5 1 -1 -1 -1], [0.9,0.9,0.9]); # line is black
fill([1 2 3 3 2 1], [1 0.5 1 -1 -1 -1], [0.9,0.9,0.9], 'LineColor', 'r') # hangs
I'm using octave-3.4.0 on OS X.
The patch command should do the job
verts = [0.2 0.4; ...
0.2 0.8; ...
0.8 0.8; ...
0.8 0.4];
faces = [1 2 3 4];
p = patch('Faces',faces,'Vertices',verts,'FaceColor','b','EdgeColor','none');
Of course you could also place it in one line ... ;-)