Turning relationship data into hierarchical list in R
Asked Answered
J

2

10

This is my first question; so please, be gentle.

I have some data that is in the form of :

library('networkD3')
    Relationships<- data.frame(Parent=c("earth","earth","forest","forest","ocean","ocean","ocean","ocean"),
                  Child=c("ocean","forest","tree","sasquatch","fish","seaweed","mantis shrimp","sea monster"))
> Relationships
  Parent         Child
1  earth         ocean
2  earth        forest
3 forest          tree
4 forest     sasquatch
5  ocean          fish
6  ocean       seaweed
7  ocean mantis shrimp
8  ocean   sea monster

Essentially this is a list of edges that can be used to make a network map:

net <- graph_from_data_frame(d = Relationships,
                             directed = T)
plot(net)

enter image description here

I would like to convert it to a form that could be used in the diagonalNetwork function, below.

Hierarchical_list <- list(name = "earth",
                 children = list(list(name = "ocean",
                                      children = list(list(name = "mantis shrimp"),
                                                      list(name = "fish"),
                                                      list(name = "sea monster"),
                                                      list(name = "seaweed")
                                                      )),
                                 list(name = "forest",
                                      children = list(list(name = "sasquatch"),
                                                      list(name = "tree")
                                                      ))
                 ))
diagonalNetwork(Hierarchical_list)

Like this:

enter image description here

When I try to generate the list using this loop:

    List_attempt <- list()

levels<- levels(factor(Relationships$Parent))

for(n in 1:length(levels)){
  Children <- subset(Relationships, Relationships$Parent == levels[n], select = Child)
  for(c in 1:length(Children)){
    sublist <- as.list(Children)
    List_attempt <- list(List_attempt, name = levels[n],children = sublist)
  }
}

diagonalNetwork(List_attempt)

I get this error:

Error in FUN(X[[i]], ...) : 
  'options' must be a fully named list, or have no names (NULL)

1) Is there a better way of creating a list for diagonalNetwork?

2) Failing that; How can I modify my loops to kick out a list of the correct structure?

3) Is there a whole other function/package I should be using?

Thanks for any help you can give, I have been beating my head against this wall for a while. Feedback on better ways to ask questions on SO would also be welcomed.

Clarification:

A similar question is found here, Convert a data frame to a treeNetwork compatible list. However it relies on a data structure where the root is always in the first column, and its children are in subsequent columns, not a list of edges as in this question, which is commonly used in igraph.

Josphinejoss answered 29/3, 2016 at 1:5 Comment(2)
@Procrastinatus Maximus, I edited it to clarify the difference between the two questions. That one has the same end goal but is not converting relationship data, it only works if the root is in the first column and the whole parent-child structure is in the subsequent columns, which is not the case in a list of edges.Josphinejoss
ok, i've reopened the questionStatue
V
1

thanks for pointing out the error @Symbolix

inspired by @MrFlick comment, suggestion to start from root and get child to create list elements recursively :) ... surely can be improved further for robustness against unexpected data inputs

library(igraph)
library('networkD3')
Relationships<- data.frame(Parent=c("earth","earth","forest","forest","ocean","ocean","ocean","ocean"),
    Child=c("ocean","forest","tree","sasquatch","fish","seaweed","mantis shrimp","sea monster"))
net <- graph_from_data_frame(d=Relationships, directed=T)
plot(net)

#net and Relationships as declared in question
#get root
root <- setdiff(Relationships$Parent, Relationships$Child)

#traverse next layer and then recurve
as.list.igraph <- function(thisNode) {
    nm <- vertex_attr(net, "name", thisNode)
    childNodes <- V(net)[which(shortest.paths(net, thisNode, mode="out") == 1)]
    if (length(childNodes)==0) return(list(name=nm))
    list(name=nm, children=unname(lapply(childNodes, as.list.igraph)))
}

#plot D3 network
diagonalNetwork(as.list.igraph(V(net)[root]))

btw, if I am not wrong, there is also a layout.reingold.tilford option in igraph

Violoncello answered 29/3, 2016 at 2:31 Comment(3)
These return different structures to the one posted in the questionWarison
@Symbolix Good catch, the structures are different, but only in ordering. For my goal(the graph) this isn't a concern. I am going to look into the layout.reingold.tilford option.Josphinejoss
This is going to help a lot by allowing me to use some of the features of the networkd3 package, on a few of the things I make igraph plots for now without duplicating effort. Thanks.Josphinejoss
P
11

You could use the data.tree package, which does many conversions from and to hierarchical data out of the box:

library('networkD3')
Relationships<- data.frame(Parent=c("earth","earth","forest","forest","ocean","ocean","ocean","ocean"),
                           Child=c("ocean","forest","tree","sasquatch","fish","seaweed","mantis shrimp","sea monster"))

library('data.tree')
tree <- FromDataFrameNetwork(Relationships)
tree
lol <- ToListExplicit(tree, unname = TRUE)
diagonalNetwork(lol)
Potter answered 29/3, 2016 at 19:46 Comment(0)
V
1

thanks for pointing out the error @Symbolix

inspired by @MrFlick comment, suggestion to start from root and get child to create list elements recursively :) ... surely can be improved further for robustness against unexpected data inputs

library(igraph)
library('networkD3')
Relationships<- data.frame(Parent=c("earth","earth","forest","forest","ocean","ocean","ocean","ocean"),
    Child=c("ocean","forest","tree","sasquatch","fish","seaweed","mantis shrimp","sea monster"))
net <- graph_from_data_frame(d=Relationships, directed=T)
plot(net)

#net and Relationships as declared in question
#get root
root <- setdiff(Relationships$Parent, Relationships$Child)

#traverse next layer and then recurve
as.list.igraph <- function(thisNode) {
    nm <- vertex_attr(net, "name", thisNode)
    childNodes <- V(net)[which(shortest.paths(net, thisNode, mode="out") == 1)]
    if (length(childNodes)==0) return(list(name=nm))
    list(name=nm, children=unname(lapply(childNodes, as.list.igraph)))
}

#plot D3 network
diagonalNetwork(as.list.igraph(V(net)[root]))

btw, if I am not wrong, there is also a layout.reingold.tilford option in igraph

Violoncello answered 29/3, 2016 at 2:31 Comment(3)
These return different structures to the one posted in the questionWarison
@Symbolix Good catch, the structures are different, but only in ordering. For my goal(the graph) this isn't a concern. I am going to look into the layout.reingold.tilford option.Josphinejoss
This is going to help a lot by allowing me to use some of the features of the networkd3 package, on a few of the things I make igraph plots for now without duplicating effort. Thanks.Josphinejoss

© 2022 - 2024 — McMap. All rights reserved.