Cross join of different data elements to create a Bipartite graph in R

Viewed 80

I want to create a bipartite graph (in r) of Conditions and Treaters based on actual events. I can do this easily if I can get my data converted to the right format as in the table below:

Physio GP Chemist Psych Dentist
BackPain 1 1 1 0 0
Depression 0 1 1 1 0
Flu 0 1 1 0 0
Anxiety 0 1 0 1 0
Toothache 0 0 0 0 1

For further clarification, the leftmost column rows are "Conditions" and the columns are "Treaters" (obviously ficticious), with the intersection = 1, if the Treater was vistied for the specific Condition.

My data is in long format below. It consists of a patient id, the term that contains the Condition/Treater word and a type which denotes if the term is a Condition or Treater type.

df <-
  data.frame(
    id = c(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5),
    term = c("BackPain", "Physio", "GP", "Chemist", "Depression", "GP", "Chemist", "Psych", "Flu", "GP", "Chemist", "Anxiety", "GP", "Psych", "Toothache", "Dentist"),
    type = c("Condition", "Treater", "Treater", "Treater", "Condition", "Treater", "Treater", "Treater", "Condition", "Treater", "Treater", "Condition", "Treater", "Treater", "Condition", "Treater")
  )

I suspect I need a clever pivot_wider type solution OR bypass the above structure altogether to go direct from my source data to the igraph Bipartite format. I searched everywhere and I cannot find similar questions/answers where the data is in a long format.

Any ideas to: 1) convert the long format to the wide format or 2) how to go from the long format directly to an igraph bipartite graph?

Help will be greatly appreciated! Thanks

4 Answers
library(igraph)
library(magrittr)
library(data.table)
data.table::setDT(df)
DT <- df[!type == "Condition", ][df[type == "Condition", ], on = .(id)]
DT.wide <- dcast(DT, term ~ i.term, value.var = "id", fun.aggregate = length)
graph_from_incidence_matrix(as.matrix(DT.wide, rownames = 1)) %>%
  add_layout_(as_bipartite()) %>%
  plot()

enter image description here

A data.table option

table(setDT(df)[, expand.grid(split(term, type)), id][, id := NULL])

gives

            Treater
Condition    Physio GP Chemist Psych Dentist
  BackPain        1  1       1     0       0
  Depression      0  1       1     1       0
  Flu             0  1       1     0       0
  Anxiety         0  1       0     1       0
  Toothache       0  0       0     0       1

If you want to have the plot, you can add two more lines like below

table(setDT(df)[, expand.grid(split(term, type)), id][, id := NULL]) %>%
    graph_from_incidence_matrix() %>%
    plot(layout = layout_as_bipartite)

which gives

enter image description here

To convert your data into a wide format, you can use:

library(tidyr)
library(dplyr)

df %>% 
  filter(type == "Treater") %>% 
  mutate(type = 1 * (type == "Treater")) %>% 
  pivot_wider(names_from = "term", values_from = "type", values_fill = 0) %>% 
  left_join(df %>% filter(type == "Condition"), by = "id") %>% 
  select(Condition = term, Physio, GP, Chemist, Psych, Dentist)

which returns

# A tibble: 5 x 6
  Condition  Physio    GP Chemist Psych Dentist
  <chr>       <dbl> <dbl>   <dbl> <dbl>   <dbl>
1 BackPain        1     1       1     0       0
2 Depression      0     1       1     1       0
3 Flu             0     1       1     0       0
4 Anxiety         0     1       0     1       0
5 Toothache       0     0       0     0       1

Another option going from long edge data.frame directly to bipartite graph without incidence matrix:

library(igraph)
#create long edges data.frame
d <- do.call(rbind, tapply(df$term, df$id, 
    function(x) data.frame(Condition=x[1L], Treater=x[-1L])))

#create graph
g <- graph_from_data_frame(d, directed=FALSE)

#see https://rpubs.com/pjmurphy/317838 by Phil Murphy & Brendan Knapp
V(g)$type <- bipartite_mapping(g)$type
plot(g, layout=layout_as_bipartite)
Related