Skip to content

Commit

Permalink
Convert edge attributes to list
Browse files Browse the repository at this point in the history
Since igraph version 2.1, when joining networks using
'igraph::disjoint_union', edge attributes of the joining networks
require identical types. As simplifiying networks necessarily converts
types of edge attributes to list when merging edges, attributes now have
to be of type list by default.

Edge attributes that are explicitly considered during simplification
and, therefore, are not converted to lists are excluded from this rule.

This works towards fixing se-sic#271.

Signed-off-by: Maximilian Löffler <[email protected]>
  • Loading branch information
maxloeffler committed Dec 10, 2024
1 parent 87911ad commit 1c35d1f
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 20 deletions.
60 changes: 43 additions & 17 deletions util-networks.R
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,7 @@ NetworkBuilder = R6::R6Class("NetworkBuilder",
attr(net, "range") = private$proj.data$get.range()
}

net = convert.edge.attributes.to.list(net)
return(net)
},

Expand Down Expand Up @@ -1026,6 +1027,7 @@ NetworkBuilder = R6::R6Class("NetworkBuilder",
attr(net, "range") = private$proj.data$get.range()
}

net = convert.edge.attributes.to.list(net)
return(net)
},

Expand Down Expand Up @@ -1068,6 +1070,7 @@ NetworkBuilder = R6::R6Class("NetworkBuilder",
attr(net, "range") = private$proj.data$get.range()
}

net = convert.edge.attributes.to.list(net)
return(net)
},

Expand Down Expand Up @@ -1174,6 +1177,7 @@ NetworkBuilder = R6::R6Class("NetworkBuilder",
attr(network, "range") = private$proj.data$get.range()
}

network = convert.edge.attributes.to.list(network)
return(network)
},

Expand Down Expand Up @@ -1279,22 +1283,6 @@ NetworkBuilder = R6::R6Class("NetworkBuilder",
## 1) merge the existing networks
u = igraph::disjoint_union(authors.net, artifacts.net)

## As there is a bug in 'igraph::disjoint_union' in igraph from its version 1.4.0 on, which is still
## present, at least, until its version 2.0.3 (see https://github.com/igraph/rigraph/issues/761), we need
## to adjust the type of the date attribute of the outcome of 'igraph::disjoint_union'.
## Note: The following temporary fix only considers the 'date' attribute. However, this problem could also
## affect several other attributes, whose classes are not adjusted in our temporary fix.
## The following code block should be redundant as soon as igraph has fixed their bug.
u.actual.edge.attribute.date = igraph::edge_attr(u, "date")
if (!is.null(u.actual.edge.attribute.date)) {
if (is.list(u.actual.edge.attribute.date)) {
u.expected.edge.attribute.date = lapply(u.actual.edge.attribute.date, get.date.from.unix.timestamp)
} else {
u.expected.edge.attribute.date = get.date.from.unix.timestamp(u.actual.edge.attribute.date)
}
u = igraph::set_edge_attr(u, "date", value = u.expected.edge.attribute.date)
}

## 2) add the bipartite edges
u = add.edges.for.bipartite.relation(u, authors.to.artifacts, private$network.conf)

Expand Down Expand Up @@ -1632,6 +1620,7 @@ construct.network.from.edge.list = function(vertices, edge.list, network.conf, d

## initialize edge weights
net = igraph::set_edge_attr(net, "weight", value = 1)
net = convert.edge.attributes.to.list(net)

logging::logdebug("construct.network.from.edge.list: finished.")

Expand Down Expand Up @@ -1792,6 +1781,15 @@ add.edges.for.bipartite.relation = function(net, bipartite.relations, network.co
extra.edge.attributes["type"] = TYPE.EDGES.INTER # add egde type
extra.edge.attributes["relation"] = relation # add relation type

## Convert edge attributes to list similarly to 'convert.edge.attributes.to.list'.
## We cannot use 'convert.edge.attributes.to.list', as we operate on edge
## data directly, instead of a network.
edge.attrs = names(extra.edge.attributes)
which.attrs = !(edge.attrs %in% names(EDGE.ATTR.HANDLING))
for (attr in edge.attrs[which.attrs]) {
extra.edge.attributes[[attr]] = as.list(extra.edge.attributes[[attr]])
}

## add the vertex sequences as edges to the network
net = igraph::add_edges(net, unlist(vertex.sequence.for.edges), attr = extra.edge.attributes)
}
Expand All @@ -1812,7 +1810,7 @@ create.empty.network = function(directed = TRUE, add.attributes = FALSE) {
# set proper attributes if wanted
if (add.attributes) {
mandatory.edge.attributes.classes = list(
date = c("POSIXct", "POSIXt"), artifact.type = "character", weight = "numeric",
date = "list", artifact.type = "list", weight = "numeric",
type = "character", relation = "character"
)
mandatory.vertex.attributes.classes = list(name = "character", kind = "character", type = "character")
Expand Down Expand Up @@ -2146,6 +2144,34 @@ get.data.sources.from.relations = function(network) {
return(data.sources)
}

#' Convert edge attributes to list type.
#'
#' This conversion is necessary to ensure merging networks works in all cases,
#' especially when merging simplified networks with unsimplified networks as
#' simplification may convert edge attributes to list type. Attributes that are
#' explicitly considered during simplification (through EDGE.ATTR.HANDLING)
#' generally do not need to be converted.
#'
#' @param network the network of which the edge attributes are to be converted
#' @param remain.as.is the edge attributes to remain as they are
#' [default: names(EDGE.ATTR.HANDLING)]
#'
#' @return the network with converted edge attributes
convert.edge.attributes.to.list = function(network, remain.as.is = names(EDGE.ATTR.HANDLING)) {

## get edge attributes
edge.attrs = igraph::edge_attr_names(network)
which.attrs = !(edge.attrs %in% remain.as.is)

## convert edge attributes to list type
for (attr in edge.attrs[which.attrs]) {
list.attr = as.list(igraph::edge_attr(network, attr))
network = igraph::set_edge_attr(network, attr, value = list.attr)
}

return(network)
}


## / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
## Sample network ----------------------------------------------------------
Expand Down
8 changes: 5 additions & 3 deletions util-split.R
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,8 @@ split.network.time.based = function(network, time.period = "3 months", bins = NU
number.windows = NULL, sliding.window = FALSE,
remove.isolates = TRUE) {
## extract date attributes from edges
dates = get.date.from.unix.timestamp(igraph::edge_attr(network, "date"))
dates = do.call(base::c, igraph::edge_attr(network, "date"))
dates = get.date.from.unix.timestamp(dates)

## number of windows given (ignoring time period and bins)
if (!is.null(number.windows)) {
Expand Down Expand Up @@ -619,7 +620,7 @@ split.networks.time.based = function(networks, time.period = "3 months", bins =
dates = igraph::E(net)$date
return(dates)
})
dates = unlist(networks.dates, recursive = FALSE)
dates = unlist(networks.dates)
dates = get.date.from.unix.timestamp(dates)

## 2) get bin information
Expand Down Expand Up @@ -708,8 +709,9 @@ split.network.activity.based = function(network, number.edges = 5000, number.win
number.edges, number.windows)

## get dates in a data.frame for splitting purposes
dates = do.call(base::c, igraph::edge_attr(network, "date"))
df = data.frame(
date = get.date.from.unix.timestamp(igraph::edge_attr(network, "date")),
date = get.date.from.unix.timestamp(dates),
my.unique.id = seq_len(edge.count) # as a unique identifier only
)
## sort by date
Expand Down

0 comments on commit 1c35d1f

Please sign in to comment.