Reputation: 641
I want to construct a networkDynamic
object in R from transaction data where each line represents a contribution by a person to a document. Multiple contributions should be represented as an increase in edge weight instead of creating multiple edges.
The following code snippets should be reproducible in RStudio easily to see the problem.
if (!require("pacman")) install.packages("pacman"); library("pacman")
pacman::p_load(network, networkDynamic, ndtv, lubridate)
stTransac <- "
'person', 'document', 'weight', 'instantId'
'A', 'a1', '3', '1'
'A', 'a1', '15', '2'
'A', 'a1', '100', '3'
'B', 'a1', '20', '10'
'C', 'a1', '30', '12'
"
dfTransac <- read.csv(text = stTransac, sep = "," , quote = '\'' , strip.white = TRUE, stringsAsFactors = FALSE)
net <- network.initialize(0, directed = TRUE, bipartite = 3)
add.vertices.networkDynamic(net, 3, vertex.pid = c("A","B","C"))
add.vertices.networkDynamic(net, 1, vertex.pid = "a1")
net %v% "vertex.names" <- c(c("A","B","C"), "a1")
set.network.attribute(net,'vertex.pid','vertex.names')
set.network.attribute(net,'edge.pid','edge.names')
add.edges.networkDynamic(net,
tail = get.vertex.id(net, c("A","B","C")),
head = get.vertex.id(net, "a1"),
edge.pid = paste0(c("A","B","C"), "->a1"))
activate.edges(net,
e = get.edge.id(net, paste0(dfTransac[["person"]], "->a1")),
at = dfTransac$instantId)
Up to now, everything works as expected (if you skip the activate.edge.attribute
block below and jump directly to the last block, you will see in the animation that edges are activated at times 1,2,3,10,12.) However, when using the activate.edge.attribute
function intuitively in the same way as the activate.edges
function, for the first edge the weight attribute is only initialized at 3
with a value of 100
. The earlier two weight values are dropped.
activate.edge.attribute(net,
prefix = "weight",
value = dfTransac$weight,
e = get.edge.id(net, paste0(dfTransac[["person"]], "->a1")),
at = dfTransac$instantId)
I could iterate over the transaction data frame, but I suppose this will not scale too well:
by(dfTransac, 1:nrow(dfTransac), function(row) {
net <<- activate.edge.attribute(net,
prefix = "weight",
value = row[["weight"]],
e = get.edge.id(net, paste0(row[["person"]], "->", row[["document"]])),
at = row[["instantId"]])
})
This last block renders the animation...
reconcile.vertex.activity(net = net, mode = "encompass.edges", edge.active.default = FALSE)
compute.animation(net, slice.par = list(start = 1, end = 13, interval = 1, aggregate.dur = 1, rule = "any"))
render.animation(net)
ani.replay()
What is the correct and efficient way to set the weight attribute at
multiple different timestamps?
Upvotes: 2
Views: 624
Reputation: 1109
Partially for efficiency reasons, the attribute activate code cannot activate multiple spells per vertex/edge. As the documentation says:
... it is possible to use one function call to activate multiple values on multiple vertices with a different activity time on each vertex, but it is not possible to activate multiple values at multiple times on a single vertex with one call.
I would recommend the following syntax for creating your network with the networkDynamic()
constructor function, which has the option to import attributes at the same time.
# re-arrange the data.frame column order to an edge.spell format,
# duplicating the time to use for onset and terminus
input<-dfTransac[,c(4,4,1,2,3)]
# convert the ids to numeric
ids<-unique(c(dfTransac$person,dfTransac$document))
input[,3]<-match(input[,3],ids)
input[,4]<-match(input[,4],ids)
input
instantId instantId.1 person document weight
1 1 1 1 4 3
2 2 2 1 4 15
3 3 3 1 4 100
4 10 10 2 4 20
5 12 12 3 4 30
# initialize a base network with the appropriate characteristics
net<-network.initialize(length(ids),bipartite=3)
# copy in the vertex names
network.vertex.names(net)<-ids
# use the networkDynamic constructor, telling it to create dynamic attributes
netDyn <- networkDynamic(net,edge.spells = input,
+ create.TEAs = TRUE,edge.TEA.names = 'weight')
Activated TEA edge attributes: weightCreated net.obs.period to describe network
Network observation period info:
Number of observation spells: 1
Maximal time range observed: 1 until 12
Temporal mode: continuous
Time unit: unknown
Suggested time increment: NA
# print out the attributes structure for edge 1 to double check
get.edge.attribute(netDyn,'weight.active',unlist=FALSE)[[1]]
[[1]]
[[1]][[1]]
[1] 3
[[1]][[2]]
[1] 15
[[1]][[3]]
[1] 100
[[2]]
[,1] [,2]
[1,] 1 1
[2,] 2 2
[3,] 3 3
So we can see that the first edge now has the expected 3 values for 'weight'. Note the networkDynamic()
has to do similar looping to appropriately attach the dynamic attributes, but at least it is all under the hood.
Upvotes: 4