ChiLiubio / microeco

An R package for data analysis in microbial community ecology
GNU General Public License v3.0
207 stars 59 forks source link

Network modularity accounting for positive and negative interactions #262

Open spencerlong1 opened 1 year ago

spencerlong1 commented 1 year ago

Hi Chi,

I was wondering if there was a way to assign network modules based on clustering of positive interactions alone, and with nodes exhibiting a negative correlation to be less likely to end up in a module together. Am I right in thinking that cluster_fast_greedy doesn't take into account the +/- label assigned to an edge? I would like some repulsion based on negative scores.

Cheers! Spencer

ChiLiubio commented 1 year ago

Hi Spencer,

Yes. cluster_fast_greedy has no parameters to add +/- labels related infomation into the modules. How about assign network modules by only extracting the network with positive edges like this.

library(microeco)
data(dataset)
test1 <- trans_network$new(dataset = dataset, cor_method = "pearson", filter_thres = 0.0005)
test1$cal_network(COR_p_thres = 0.05, COR_cut = 0.4)
# extract positive edges
pos_network <- test1$subset_network(edge = "+")
# clone an object
test2 <- clone(test1)
test2$res_network <- pos_network
# test2 only has positive edges
test2$cal_module()

Best, Chi

spencerlong1 commented 1 year ago

thanks Chi, that works. Is there a way to then add the negative edges back into the network after this so that they can still be visualised on gephi? or alternatively how would I add these module assignments from test2 into test1?

Thanks for all the help

ChiLiubio commented 1 year ago

Hi. Interesting! This is an example to add these module assignments from test2 into test1. The way is to extract modules from test2 and assign back to test1.

# get modules and node information table for both networks
test2$cal_module()
test2$get_node_table(node_roles = FALSE)
test1$cal_module()
test1$get_node_table(node_roles = FALSE)
# use test1 names to extract modules of test2
new_modules <- test2$res_node_table[rownames(test1$res_node_table), "module"]
# assign new modules back to network in test1
test1_network <- test1$res_network
test1_network <- igraph::set_vertex_attr(test1_network, "module", value = new_modules)
test1$res_network <- test1_network

Chi

spencerlong1 commented 1 year ago

Hi Chi, this works really well for some of my data, but for one of my "positive only" networks, as probably expected, some nodes which only have negative associations are not being assigned modules. This is fine in general, but when I then assign the modules with the original network with both + and - there are some blank spaces and I think this is interfering with cal_eigen down the line. I was wondering if there was a simple way to add e.g. MX to any blank/ NA in the "module" column, and then the eigengene analysis can be performed and I can just ignore the results for MX.

For now, it hasn't been a problem as for this particular network I have just calculated modularity and eigengene analysis witth the "positive only" network, and in gephi manually added the module assignments into the original network, which is fine for what I have needed to do. was just wondering if there was a faster way via in R in the future, or for with larger networks where this isnt practical?

Cheers for all the help!

Spencer

ChiLiubio commented 1 year ago

Hi. Yes. You have thought of it. It is feasible to add modules to NA. It is better to assign each unique module name to each NA.

# I generate an example data, in which test2_example is like the pos with fewer nodes.
library(magrittr)
test2_example <- test2$res_node_table[1:2, ]
test1_example <- test1$res_node_table[1:4, ]
# now new_modules have two NA
new_modules <- test2_example[rownames(test1_example), "module"]
# remove factor levels
new_modules %<>% as.character
# Then all we need to do is to change new_modules
# determine the max number to add more modules
start_more <- new_modules %>% gsub("M", "", .) %>% .[!is.na(.)] %>% as.numeric %>% max
# fill NA with larger numbers. You can also paste a specfic character to distingush them from 'M'
new_modules[is.na(new_modules)] <- (start_more + 1):(start_more + length(new_modules[is.na(new_modules)]))
# now new_modules is fine to assign back to network like the above operation with igraph::set_vertex_attr

Chi

spencerlong1 commented 1 year ago

Great, all works perfectly!

Spencer