6

Consider this simple example

library(tidygraph)

mynodes <- tibble(id = c(1,2,3,4,5))

myedges <- tibble(from = c(1,1,1,5),
                  to = c(1,2,3,4),
                  power = c(10,10,10,3))

tbl_graph(nodes = mynodes, edges = myedges)
# A tbl_graph: 5 nodes and 4 edges
#
# A directed multigraph with 2 components
#
# Node Data: 5 x 1 (active)
     id
  <dbl>
1     1
2     2
3     3
4     4
5     5
#
# Edge Data: 4 x 3
   from    to power
  <int> <int> <dbl>
1     1     1    10
2     1     2    10
3     1     3    10
# ? with 1 more row

I know I can use filter to easily filter the nodes or the edges.

My problem is how to filter the nodes based on a condition on the edges.

For instance running:

mygraph %>% activate(edges) %>% filter(power == 3)

will still returns all the nodes, which is annoying when plotting (these nodes will have no edges!).

How can I keep all nodes that are connected with my filtered set of edges?

# A tbl_graph: 5 nodes and 1 edges
#
# A rooted forest with 4 trees
#
# Edge Data: 1 x 3 (active)
   from    to power
  <int> <int> <dbl>
1     5     4     3
#
# Node Data: 5 x 1
     id
  <dbl>
1     1
2     2
3     3
# ? with 2 more rows
ℕʘʘḆḽḘ
  • 18,566
  • 34
  • 128
  • 235

3 Answers3

4

One possible solution is to transform your data into subgraph and filter it by id:

Create a subgraph and filter by id:

sub_mygraph <- to_subgraph(mygraph, id %in% c(4, 5), subset_by = "nodes")$subgraph 

sub_mygraph
# A tbl_graph: 2 nodes and 1 edges
#
# A rooted tree
#
# Node Data: 2 x 1 (active)
     id
  <dbl>
1     4
2     5
#
# Edge Data: 1 x 3
   from    to power
  <int> <int> <dbl>
1     2     1     3

Or you can transform your data into data.frame and filter it by power, and then convert to tbl_graph back.

Convert to data.frame:

library(tidygraph)

mygraph %>% igraph::as_data_frame() %>% 
  filter(power == 3) %>% 
  as_tbl_graph()

# A tbl_graph: 2 nodes and 1 edges
#
# A rooted tree
#
# Node Data: 2 x 1 (active)
  name 
  <chr>
1 5    
2 4    
#
# Edge Data: 1 x 3
   from    to power
  <int> <int> <dbl>
1     1     2     3

Or you can filter by id, but this is not the most straightforward approach because you first have to identify the id that're included in power 3:

mygraph %>% filter(id %in% c(5, 4))

# A tbl_graph: 2 nodes and 1 edges
#
# A rooted tree
#
# Node Data: 2 x 1 (active)
     id
  <dbl>
1     4
2     5
#
# Edge Data: 1 x 3
   from    to power
  <int> <int> <dbl>
1     2     1     3

And now you can plot it without the annoying nodes:

library(ggraph)

mygraph %>% igraph::as_data_frame() %>% 
  filter(power == 3) %>% 
  as_tbl_graph () %>% 
  rename(id = name) %>% 
  ggraph() +
  geom_edge_fan() +
  geom_node_point(aes(color = id)) +
  theme_graph()

enter image description here

Note

Note that convert to data.frame will change your id variable name, so you can change it again with rename.

patL
  • 2,259
  • 1
  • 17
  • 38
2

there is actually a very simple solution

activate(nodes) %>% 
  filter(!node_is_isolated())
ℕʘʘḆḽḘ
  • 18,566
  • 34
  • 128
  • 235
  • 3
    So, it'd be: `mygraph %>% activate(edges) %>% filter(power == 3) %>% activate(nodes) %>% filter(!node_is_isolated())` – patL Jun 20 '19 at 06:06
  • @NOOBIE, thanks. can I just graph immediate neighbours of non-isolated nodes using the above code? – Alex Feb 24 '21 at 22:52
  • Weird! Thank you for the comment, tidygraph is very particular about the order....first, filter edges, then activate node and filter(!node_is_isolated())....did not work in reverse – James Crumpler Mar 19 '21 at 13:37
1

You can use the .E() & .N() fcns to access nodes or edges while the other is activated in tidygraph

This is mentioned in the intoduction here: https://www.data-imaginist.com/2017/introducing-tidygraph/

So to filter to nodes based on edge attributes, you can use the .E() function while nodes are activated:

mygraph %>%
  activate("nodes") %>%
  filter(.E()$power[id] == 3)
Kmcd39
  • 87
  • 5