I get different (unexpected) results when selecting edges by edge attribute using igraph::E()[] and igraph::edge_attr(), why?

Viewed 51

From a graph built with igraph, I'm trying to select edges by edge attribute, using igraph::E()[] and igraph::edge_attr(). Changing my code slightly in the selection condition, I get different results applying the selection. I find this unexpected. I can't explain it. Please let me know why.

library("igraph")

# create dummy graph and selection attribute
set.seed(42)
g <- make_ring(10)
g <- set_edge_attr(g, "group", value = sample(c("A", "B"), 10, replace = TRUE))
edge_attr(g)

# select by
edge_attr_group <- "group"
random_name_of_variable <- "A"
group <- "A"

First, recognize that

# indices to select
which(edge_attr(g, edge_attr_group) == "A")
which(edge_attr(g, edge_attr_group) == random_name_of_variable)
which(edge_attr(g, edge_attr_group) == group)
# all equal and expected

So far so good I guess. But then applying the selection condition

# apply selection
E(g)[which(edge_attr(g, edge_attr_group) == "A")]
E(g)[which(edge_attr(g, edge_attr_group) == random_name_of_variable)]
# both equal and expected, but
E(g)[which(edge_attr(g, edge_attr_group) == group)]
# is not equal and thus unexpected

So, selecting using a static value ("A") or a random name for the condition variable (random_name_of_variable), we get the intended results. But when selecting using the group variable, we don't get the same result. What is the reason for this? I recognize that the variable name is the same as the attribute name, but why would that matter?

For reference, session info and my output:

> sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)

Matrix products: default

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] igraph_1.3.2

loaded via a namespace (and not attached):
[1] compiler_4.1.2  magrittr_2.0.3  tools_4.1.2     pkgconfig_2.0.3

> which(edge_attr(g, edge_attr_group) == "A")
[1] 1 2 3 4 9
> which(edge_attr(g, edge_attr_group) == random_name_of_variable)
[1] 1 2 3 4 9
> which(edge_attr(g, edge_attr_group) == group)
[1] 1 2 3 4 9
> E(g)[which(edge_attr(g, edge_attr_group) == "A")]
+ 5/10 edges from 0e5f311:
[1] 1-- 2 2-- 3 3-- 4 4-- 5 9--10
> E(g)[which(edge_attr(g, edge_attr_group) == random_name_of_variable)]
+ 5/10 edges from 0e5f311:
[1] 1-- 2 2-- 3 3-- 4 4-- 5 9--10
> E(g)[which(edge_attr(g, edge_attr_group) == group)]
+ 10/10 edges from 0e5f311:
 [1] 1-- 2 2-- 3 3-- 4 4-- 5 5-- 6 6-- 7 7-- 8 8-- 9 9--10 1--10
1 Answers

This is because attribute names are interpreted specially when indexing into an edge or vertex sequence. This is described in the documentation here and here:

When indexing edge sequences, edge attributes can be referred to simply by using their names. E.g. if a graph has a weight edge attribute, then E(G)[weight > 1] selects all edges with a larger than one weight. See more examples below.

Thus a simpler way to select edges from group "A" is E(g)[group == "A"].


Update: Since R/igraph 1.3.3, it is possible to disambiguate names using the .data and .env pronouns.

Unqualified names are attempted to be interpreted as attributes. If an attribute of that name does not exist, the name is treated as a variable.

Thus, this selects the 3rd edge:

> g <- make_ring(5)
> foo <- 3
> E(g)[foo]
+ 1/5 edge from febda41:
[1] 3--4

But as soon as we set an attribute named foo, its behaviour changes:

> E(g)$foo <- LETTERS[1:5]
> E(g)[foo]
Error in simple_es_index(x, ii) : Unknown edge selected

To specify that foo is a variable, and avoid any ambiguity, we can refer to it as .env$foo:

> E(g)[.env$foo]
+ 1/5 edge from febda41:
[1] 3--4

For completeness, we can also make it explicit that it refers to an attribute name:

> E(g)[.data$foo == 'D']
+ 1/5 edge from febda41:
[1] 4--5
Related