|
a |
|
b/R/7.stability.R |
|
|
1 |
# ========7.stability=========== |
|
|
2 |
|
|
|
3 |
#' Evaluate the stability of a network |
|
|
4 |
#' |
|
|
5 |
#' @param go_ls an igraph object or igraph list. |
|
|
6 |
#' @param mode "robust_test", "vulnerability", "robustness" |
|
|
7 |
#' @param partial how much percent vertexes be removed in total (default: 0.5, only for robust_test) |
|
|
8 |
#' @param step how many nodes be removed each time? (default: 10, only for robust_test) |
|
|
9 |
#' @param reps simulation number (default: 9) |
|
|
10 |
#' @param threads threads |
|
|
11 |
#' @param verbose verbose |
|
|
12 |
#' @param keystone remove 70%% keystones instead of remove 50%% nodes (default: False, only for robustness) |
|
|
13 |
#' |
|
|
14 |
#' @return a data.frame |
|
|
15 |
#' @export |
|
|
16 |
#' |
|
|
17 |
#' @examples |
|
|
18 |
#' \donttest{ |
|
|
19 |
#' data("c_net") |
|
|
20 |
#' if (requireNamespace("ggpmisc")) { |
|
|
21 |
#' c_net_stability(co_net, mode = "robust_test", step = 20, reps = 9) -> robust_res |
|
|
22 |
#' plot(robust_res, index = "Average_degree", mode = 2) |
|
|
23 |
#' } |
|
|
24 |
#' |
|
|
25 |
#' c_net_stability(co_net, mode = "vulnerability") -> vulnerability_res |
|
|
26 |
#' plot(vulnerability_res) |
|
|
27 |
#' |
|
|
28 |
#' robustness(co_net) -> robustness_res |
|
|
29 |
#' plot(robustness_res) |
|
|
30 |
#' |
|
|
31 |
#' module_detect(co_net) -> co_net_modu |
|
|
32 |
#' zp_analyse(co_net_modu, mode = 2) -> co_net_modu |
|
|
33 |
#' |
|
|
34 |
#' c_net_stability(co_net_modu, mode = "robustness", keystone = TRUE) -> robustness_res |
|
|
35 |
#' plot(robustness_res) |
|
|
36 |
#' } |
|
|
37 |
c_net_stability <- function(go_ls, mode = "robust_test", partial = 0.5, step = 10, reps = 9, |
|
|
38 |
threads = 1, verbose = TRUE, keystone = FALSE) { |
|
|
39 |
mode <- match.arg(mode, c("robust_test", "vulnerability", "robustness")) |
|
|
40 |
if (mode == "robust_test") { |
|
|
41 |
res <- robust_test(go_ls, partial = partial, step = step, reps = reps, threads = threads, verbose = verbose) |
|
|
42 |
} else if (mode == "vulnerability") { |
|
|
43 |
res <- vulnerability(go_ls, threads = threads, verbose = verbose) |
|
|
44 |
} else if (mode == "robustness") { |
|
|
45 |
res <- robustness(go_ls, keystone = keystone, reps = reps, threads = threads, verbose = verbose) |
|
|
46 |
} |
|
|
47 |
return(res) |
|
|
48 |
} |
|
|
49 |
|
|
|
50 |
#' Robust_test for a network |
|
|
51 |
#' @rdname c_net_stability |
|
|
52 |
#' |
|
|
53 |
#' @return data.frame (robustness class) |
|
|
54 |
#' @export |
|
|
55 |
#' |
|
|
56 |
robust_test <- function(go_ls, partial = 0.5, step = 10, reps = 9, threads = 1, verbose = TRUE) { |
|
|
57 |
if ("igraph" %in% class(go_ls)) { |
|
|
58 |
robustness_res <- robust_test_in(go_ls, partial = partial, step = step, reps = reps, threads = threads, verbose = verbose) |
|
|
59 |
robustness_res <- data.frame(robustness_res, group = "Network") |
|
|
60 |
} else { |
|
|
61 |
if (!is.igraph(go_ls[[1]])) stop("No igraph or igraph-list.") |
|
|
62 |
robustness_res <- lapply(names(go_ls), \(i){ |
|
|
63 |
tmp <- robust_test_in(go_ls[[i]], partial = partial, step = step, reps = reps, threads = threads, verbose = verbose) |
|
|
64 |
data.frame(tmp, group = i) |
|
|
65 |
}) |
|
|
66 |
robustness_res <- do.call(rbind, robustness_res) |
|
|
67 |
robustness_res$group <- factor(robustness_res$group, levels = names(go_ls)) |
|
|
68 |
} |
|
|
69 |
class(robustness_res) <- c("robust", "data.frame") |
|
|
70 |
return(robustness_res) |
|
|
71 |
} |
|
|
72 |
|
|
|
73 |
robust_test_in <- function(go, partial = 0.5, step = 10, reps = 9, threads = 1, verbose = TRUE) { |
|
|
74 |
i <- NULL |
|
|
75 |
|
|
|
76 |
cal_del <- \(go, partial, step, rep){ |
|
|
77 |
nodes <- length(igraph::V(go)) |
|
|
78 |
floor(nodes * partial) -> del_i |
|
|
79 |
del_i_indexs <- data.frame() |
|
|
80 |
sequ <- seq(0, del_i, step) |
|
|
81 |
if (sequ[length(sequ)] < del_i) sequ <- c(sequ, del_i) |
|
|
82 |
res <- lapply(sequ, \(i){ |
|
|
83 |
# remove i nodes in the network |
|
|
84 |
remove_node <- sample(1:nodes, i) |
|
|
85 |
dp <- igraph::delete.vertices(go, remove_node) |
|
|
86 |
dp <- igraph::delete.vertices(dp, igraph::V(dp)[igraph::degree(dp) == 0]) |
|
|
87 |
|
|
|
88 |
# calculate network parameters |
|
|
89 |
tmp_ind <- (MetaNet::net_par(dp, mode = "n")$n_index) |
|
|
90 |
data.frame(tmp_ind, i = i) |
|
|
91 |
}) |
|
|
92 |
del_i_indexs <- do.call(rbind, res) |
|
|
93 |
|
|
|
94 |
# for (i in sequ) { |
|
|
95 |
# # remove i nodes in the network |
|
|
96 |
# remove_node <- sample(1:nodes, i) |
|
|
97 |
# dp <- igraph::delete.vertices(go, remove_node) |
|
|
98 |
# dp=igraph::delete.vertices(dp,igraph::V(dp)[igraph::degree(dp)==0]) |
|
|
99 |
# |
|
|
100 |
# # calculate network parameters |
|
|
101 |
# tmp_ind=(MetaNet::net_par(dp,mode = "n")$n_index) |
|
|
102 |
# del_i_indexs <- rbind( |
|
|
103 |
# del_i_indexs, |
|
|
104 |
# data.frame(tmp_ind, i = i) |
|
|
105 |
# ) |
|
|
106 |
# } |
|
|
107 |
|
|
|
108 |
return(data.frame(del_i_indexs, "rep" = rep)) |
|
|
109 |
} |
|
|
110 |
|
|
|
111 |
# parallel |
|
|
112 |
# main function |
|
|
113 |
loop <- function(i) { |
|
|
114 |
cal_del(go, partial, step, i) |
|
|
115 |
} |
|
|
116 |
{ |
|
|
117 |
if (threads > 1) { |
|
|
118 |
pcutils::lib_ps("foreach", "doSNOW", "snow", library = FALSE) |
|
|
119 |
pb <- utils::txtProgressBar(max = reps, style = 3) |
|
|
120 |
if (verbose) { |
|
|
121 |
opts <- list(progress = function(n) utils::setTxtProgressBar(pb, n)) |
|
|
122 |
cl <- snow::makeCluster(threads) |
|
|
123 |
} else { |
|
|
124 |
opts <- NULL |
|
|
125 |
} |
|
|
126 |
doSNOW::registerDoSNOW(cl) |
|
|
127 |
res <- foreach::`%dopar%`( |
|
|
128 |
foreach::foreach(i = 1:reps, .options.snow = opts), |
|
|
129 |
loop(i) |
|
|
130 |
) |
|
|
131 |
snow::stopCluster(cl) |
|
|
132 |
gc() |
|
|
133 |
} else { |
|
|
134 |
res <- lapply(1:reps, loop) |
|
|
135 |
} |
|
|
136 |
} |
|
|
137 |
# simplify method |
|
|
138 |
del_i_indexs <- do.call(rbind, res) |
|
|
139 |
|
|
|
140 |
del_i_indexs$i_ratio <- del_i_indexs$i / length(V(go)) %>% round(., 3) |
|
|
141 |
class(del_i_indexs) <- c("robust", class(del_i_indexs)) |
|
|
142 |
return(del_i_indexs) |
|
|
143 |
} |
|
|
144 |
|
|
|
145 |
#' Plot robust |
|
|
146 |
#' |
|
|
147 |
#' @param x `robust_test()` result (robust object) |
|
|
148 |
#' @param mode plot mode, 1~3 |
|
|
149 |
#' @param indexes indexes selected to show |
|
|
150 |
#' @param use_ratio use the delete nodes ratio rather than nodes number |
|
|
151 |
#' @param ... additional arguments for \code{\link[pcutils]{group_box}} |
|
|
152 |
#' |
|
|
153 |
#' @return a ggplot |
|
|
154 |
#' @method plot robust |
|
|
155 |
#' @exportS3Method |
|
|
156 |
#' @rdname robust_test |
|
|
157 |
plot.robust <- function(x, indexes = c("Natural_connectivity", "Average_degree"), use_ratio = FALSE, mode = 1, ...) { |
|
|
158 |
i <- group <- variable <- value <- se <- eq.label <- adj.rr.label <- Natural_connectivity <- lm <- NULL |
|
|
159 |
robust_res <- x |
|
|
160 |
xlab <- "Removed_nodes" |
|
|
161 |
if (use_ratio) { |
|
|
162 |
robust_res$i <- robust_res$i_ratio |
|
|
163 |
xlab <- "Removed_nodes_ratio" |
|
|
164 |
} |
|
|
165 |
robust_res %>% |
|
|
166 |
dplyr::select(i, group, !!indexes) %>% |
|
|
167 |
reshape2::melt(id.var = c("i", "group")) -> pdat |
|
|
168 |
|
|
|
169 |
pdat %>% |
|
|
170 |
dplyr::group_by(i, variable, group) %>% |
|
|
171 |
dplyr::summarise(mean = mean(value), sd = sd(value), se = sd / sqrt(length(value))) -> sdd |
|
|
172 |
|
|
|
173 |
if (mode == 1) { |
|
|
174 |
p <- ggplot(sdd, aes(x = i, y = mean, col = group)) + |
|
|
175 |
geom_line() + |
|
|
176 |
geom_errorbar(data = sdd, aes(ymax = mean + se, ymin = mean - se)) + |
|
|
177 |
# geom_smooth(se = FALSE,method = "loess",formula = 'y ~ x') + |
|
|
178 |
facet_wrap(. ~ variable, scales = "free") + |
|
|
179 |
labs(x = xlab, y = NULL) + |
|
|
180 |
theme_bw() |
|
|
181 |
} |
|
|
182 |
if (mode == 2) { |
|
|
183 |
lib_ps("ggpmisc", library = FALSE) |
|
|
184 |
p <- ggplot(sdd, aes(x = i, y = mean, col = group)) + |
|
|
185 |
geom_point(size = 0.2, alpha = 0.4) + |
|
|
186 |
geom_smooth(se = FALSE, method = "loess", formula = "y ~ x") + |
|
|
187 |
ggpmisc::stat_poly_eq( |
|
|
188 |
aes(label = paste(after_stat(eq.label), after_stat(adj.rr.label), sep = "~~~~~")), |
|
|
189 |
formula = y ~ x, parse = TRUE, label.x = "right", |
|
|
190 |
size = 3, |
|
|
191 |
) + |
|
|
192 |
facet_wrap(. ~ variable, scales = "free") + |
|
|
193 |
labs(x = xlab, y = NULL) + |
|
|
194 |
theme_bw() |
|
|
195 |
} |
|
|
196 |
if (mode == 3) { |
|
|
197 |
robust_res %>% dplyr::select(i, rep, group, `Natural_connectivity`) -> pdat |
|
|
198 |
|
|
|
199 |
pdat %>% |
|
|
200 |
# filter(i<0.4)%>% |
|
|
201 |
dplyr::group_by(rep, group) %>% |
|
|
202 |
dplyr::summarise(slope = coefficients(lm(`Natural_connectivity` ~ i))[2]) -> slope |
|
|
203 |
|
|
|
204 |
p <- pcutils::group_box(slope["slope"], group = "group", metadata = slope, alpha = TRUE, ...) + theme_bw() |
|
|
205 |
} |
|
|
206 |
return(p) |
|
|
207 |
} |
|
|
208 |
|
|
|
209 |
#' Vulnerability calculation |
|
|
210 |
#' |
|
|
211 |
#' @rdname c_net_stability |
|
|
212 |
#' |
|
|
213 |
#' @return a vector |
|
|
214 |
#' @export |
|
|
215 |
#' @description |
|
|
216 |
#' \deqn{Vi=\frac{E-Ei}{E}} |
|
|
217 |
#' E is the global efficiency and Ei is the global efficiency after the removal of the node i and its entire links. |
|
|
218 |
#' |
|
|
219 |
vulnerability <- function(go_ls, threads = 1, verbose = TRUE) { |
|
|
220 |
if ("igraph" %in% class(go_ls)) { |
|
|
221 |
vulnerability_res <- vul_max(go_ls, threads = threads, verbose = verbose) |
|
|
222 |
} else { |
|
|
223 |
if (!"igraph" %in% class(go_ls[[1]])) stop("No igraph or igraph-list.") |
|
|
224 |
vulnerability_res <- lapply(names(go_ls), \(i){ |
|
|
225 |
vul_max(go_ls[[i]], threads = threads, verbose = verbose) |
|
|
226 |
}) |
|
|
227 |
names(vulnerability_res) <- names(go_ls) |
|
|
228 |
} |
|
|
229 |
class(vulnerability_res) <- c("vulnerability", class(vulnerability_res)) |
|
|
230 |
return(vulnerability_res) |
|
|
231 |
} |
|
|
232 |
|
|
|
233 |
vul_max <- function(go, threads = 1, verbose = TRUE) { |
|
|
234 |
i <- NULL |
|
|
235 |
stopifnot(is.igraph(go)) |
|
|
236 |
if (is.null(V(go)$name)) V(go)$name <- V(go) |
|
|
237 |
|
|
|
238 |
# parallel |
|
|
239 |
# main function |
|
|
240 |
loop <- function(i) igraph::global_efficiency(igraph::delete_vertices(go, i)) |
|
|
241 |
{ |
|
|
242 |
if (threads > 1) { |
|
|
243 |
pcutils::lib_ps("foreach", "doSNOW", "snow", library = FALSE) |
|
|
244 |
if (verbose) { |
|
|
245 |
pb <- utils::txtProgressBar(max = length(V(go)$name), style = 3) |
|
|
246 |
opts <- list(progress = function(n) utils::setTxtProgressBar(pb, n)) |
|
|
247 |
} else { |
|
|
248 |
opts <- NULL |
|
|
249 |
} |
|
|
250 |
cl <- snow::makeCluster(threads) |
|
|
251 |
doSNOW::registerDoSNOW(cl) |
|
|
252 |
res <- foreach::`%dopar%`( |
|
|
253 |
foreach::foreach(i = V(go)$name, .options.snow = opts), |
|
|
254 |
loop(i) |
|
|
255 |
) |
|
|
256 |
snow::stopCluster(cl) |
|
|
257 |
gc() |
|
|
258 |
} else { |
|
|
259 |
res <- lapply(V(go)$name, loop) |
|
|
260 |
} |
|
|
261 |
} |
|
|
262 |
# simplify method |
|
|
263 |
tmpv <- do.call(c, res) |
|
|
264 |
|
|
|
265 |
(igraph::global_efficiency(go) - tmpv) / igraph::global_efficiency(go) |
|
|
266 |
} |
|
|
267 |
|
|
|
268 |
#' Plot vulnerability |
|
|
269 |
#' |
|
|
270 |
#' @param x `vulnerability()` result (vulnerability object) |
|
|
271 |
#' @param ... add |
|
|
272 |
#' |
|
|
273 |
#' @return a ggplot |
|
|
274 |
#' @method plot vulnerability |
|
|
275 |
#' @exportS3Method |
|
|
276 |
#' @rdname vulnerability |
|
|
277 |
plot.vulnerability <- function(x, ...) { |
|
|
278 |
group <- NULL |
|
|
279 |
vulnerability_res <- x |
|
|
280 |
if (!is.list(vulnerability_res)) { |
|
|
281 |
vulnerability_res <- list(Network = vulnerability_res) |
|
|
282 |
} |
|
|
283 |
pdat <- data.frame( |
|
|
284 |
group = factor(names(vulnerability_res), levels = names(vulnerability_res)), |
|
|
285 |
vulnerability = vapply(vulnerability_res, max, numeric(1)) |
|
|
286 |
) |
|
|
287 |
ggplot(pdat, aes(group, vulnerability)) + |
|
|
288 |
geom_col(aes(fill = group), ...) + |
|
|
289 |
geom_text(aes(group, vulnerability * 1.05, label = round(vulnerability, 3))) + |
|
|
290 |
theme_bw() |
|
|
291 |
} |
|
|
292 |
|
|
|
293 |
|
|
|
294 |
#' Robustness after remove 50%% nodes or some hubs, need the metanet contains "cor" attribute. |
|
|
295 |
#' |
|
|
296 |
#' @rdname c_net_stability |
|
|
297 |
#' |
|
|
298 |
#' @export |
|
|
299 |
robustness <- function(go_ls, keystone = FALSE, reps = 9, threads = 1, verbose = TRUE) { |
|
|
300 |
if ("igraph" %in% class(go_ls)) { |
|
|
301 |
robustness_res <- robustness_in(go_ls, keystone = keystone, reps = reps, threads = threads, verbose = verbose) |
|
|
302 |
} else { |
|
|
303 |
if (!"igraph" %in% class(go_ls[[1]])) stop("No igraph or igraph-list.") |
|
|
304 |
robustness_res <- lapply(names(go_ls), \(i){ |
|
|
305 |
tmp <- robustness_in(go_ls[[i]], keystone = keystone, reps = reps, threads = threads, verbose = verbose) |
|
|
306 |
data.frame(tmp, group = i) |
|
|
307 |
}) |
|
|
308 |
robustness_res <- do.call(rbind, robustness_res) |
|
|
309 |
robustness_res$group <- factor(robustness_res$group, levels = names(go_ls)) |
|
|
310 |
} |
|
|
311 |
class(robustness_res) <- c("robustness", class(robustness_res)) |
|
|
312 |
return(robustness_res) |
|
|
313 |
} |
|
|
314 |
|
|
|
315 |
robustness_in <- function(go, keystone = FALSE, reps = 9, threads = 1, verbose = TRUE) { |
|
|
316 |
roles <- name <- from <- to <- i <- NULL |
|
|
317 |
|
|
|
318 |
nodes <- length(V(go)) |
|
|
319 |
floor(nodes * 0.5) -> del_i |
|
|
320 |
if (keystone) { |
|
|
321 |
get_v(go) -> tmp_v |
|
|
322 |
if (!"module" %in% colnames(tmp_v)) stop("no modules, please `module_detect()` first") |
|
|
323 |
if (!"roles" %in% colnames(tmp_v)) stop("no roles, please `zp_analyse()` first") |
|
|
324 |
tmp_v %>% |
|
|
325 |
dplyr::filter(roles == "Module hubs") %>% |
|
|
326 |
dplyr::pull(name) -> hubs |
|
|
327 |
floor(length(hubs) * 0.7) -> del_i |
|
|
328 |
} |
|
|
329 |
|
|
|
330 |
# parallel |
|
|
331 |
# main function |
|
|
332 |
loop <- \(i){ |
|
|
333 |
if (!keystone) remove_node <- sample(1:nodes, del_i) |
|
|
334 |
if (keystone) remove_node <- sample(hubs, del_i) |
|
|
335 |
|
|
|
336 |
dp <- igraph::delete.vertices(go, remove_node) |
|
|
337 |
dead_s <- "init" |
|
|
338 |
# calculated the abundance-weighted mean interaction strength (wMIS) of nodes,<=0 will dead |
|
|
339 |
# repeat until all >0 |
|
|
340 |
while (length(dead_s) > 0) { |
|
|
341 |
get_e(dp) -> edge_list |
|
|
342 |
edge_list %>% |
|
|
343 |
dplyr::select(from, cor) %>% |
|
|
344 |
rbind(., dplyr::select(edge_list, to, cor) %>% dplyr::rename(from = to)) %>% |
|
|
345 |
dplyr::group_by(from) %>% |
|
|
346 |
dplyr::summarise(w_degree = sum(cor)) -> w_degree |
|
|
347 |
w_degree %>% |
|
|
348 |
dplyr::filter(w_degree <= 0) %>% |
|
|
349 |
dplyr::pull(from) -> dead_s |
|
|
350 |
dp <- igraph::delete.vertices(dp, dead_s) |
|
|
351 |
} |
|
|
352 |
tmp_ind <- (MetaNet::net_par(dp, mode = "n")$n_index) |
|
|
353 |
data.frame(tmp_ind, reps = i) |
|
|
354 |
} |
|
|
355 |
{ |
|
|
356 |
if (threads > 1) { |
|
|
357 |
pcutils::lib_ps("foreach", "doSNOW", "snow", library = FALSE) |
|
|
358 |
if (verbose) { |
|
|
359 |
pb <- utils::txtProgressBar(max = reps, style = 3) |
|
|
360 |
opts <- list(progress = function(n) utils::setTxtProgressBar(pb, n)) |
|
|
361 |
} else { |
|
|
362 |
opts <- NULL |
|
|
363 |
} |
|
|
364 |
cl <- snow::makeCluster(threads) |
|
|
365 |
doSNOW::registerDoSNOW(cl) |
|
|
366 |
res <- foreach::`%dopar%`( |
|
|
367 |
foreach::foreach(i = 1:reps, .options.snow = opts), |
|
|
368 |
loop(i) |
|
|
369 |
) |
|
|
370 |
snow::stopCluster(cl) |
|
|
371 |
gc() |
|
|
372 |
} else { |
|
|
373 |
res <- lapply(1:reps, loop) |
|
|
374 |
} |
|
|
375 |
} |
|
|
376 |
# simplify method |
|
|
377 |
del_i_indexs <- do.call(rbind, res) |
|
|
378 |
return(del_i_indexs) |
|
|
379 |
} |
|
|
380 |
|
|
|
381 |
#' Plot robustness |
|
|
382 |
#' |
|
|
383 |
#' @param x `robustness()` result (robustness object) |
|
|
384 |
#' @param indexes indexes selected to show |
|
|
385 |
#' @param ... additional arguments for \code{\link[pcutils]{group_box}} |
|
|
386 |
#' |
|
|
387 |
#' @return a ggplot |
|
|
388 |
#' @method plot robustness |
|
|
389 |
#' @exportS3Method |
|
|
390 |
#' @rdname robustness |
|
|
391 |
plot.robustness <- function(x, indexes = "Node_number", ...) { |
|
|
392 |
robustness_res <- x |
|
|
393 |
if (!"group" %in% colnames(robustness_res)) robustness_res$group <- "Network" |
|
|
394 |
pcutils::group_box(robustness_res[, indexes, drop = FALSE], |
|
|
395 |
group = "group", |
|
|
396 |
metadata = robustness_res, ..., facet = FALSE |
|
|
397 |
) + theme_bw() |
|
|
398 |
} |
|
|
399 |
|
|
|
400 |
|
|
|
401 |
#' Cohesion calculation |
|
|
402 |
#' |
|
|
403 |
#' @param otutab otutab |
|
|
404 |
#' @param reps iteration time |
|
|
405 |
#' @param threads threads |
|
|
406 |
#' @param mycor a correlation matrix you want to use, skip the null model build when mycor is not NULL, default: NULL |
|
|
407 |
#' @param verbose verbose |
|
|
408 |
#' |
|
|
409 |
#' @return Cohesion object: a list with two dataframe |
|
|
410 |
#' @export |
|
|
411 |
#' @references |
|
|
412 |
#' Herren, C. M. & McMahon, K. (2017) Cohesion: a method for quantifying the connectivity of microbial communities. doi:10.1038/ismej.2017.91. |
|
|
413 |
#' @examples |
|
|
414 |
#' \donttest{ |
|
|
415 |
#' data("otutab", package = "pcutils") |
|
|
416 |
#' # set reps at least 99 when you run. |
|
|
417 |
#' Cohesion(otutab[1:50, ], reps = 19) -> cohesion_res |
|
|
418 |
#' if (requireNamespace("ggpubr")) { |
|
|
419 |
#' plot(cohesion_res, group = "Group", metadata = metadata, mode = 1) |
|
|
420 |
#' plot(cohesion_res, group = "Group", metadata = metadata, mode = 2) |
|
|
421 |
#' } |
|
|
422 |
#' } |
|
|
423 |
Cohesion <- function(otutab, reps = 200, threads = 1, mycor = NULL, verbose = TRUE) { |
|
|
424 |
i <- NULL |
|
|
425 |
d <- t(otutab) |
|
|
426 |
rel.d <- d / rowSums(d) |
|
|
427 |
|
|
|
428 |
if (is.null(mycor)) { |
|
|
429 |
# Create observed correlation matrix |
|
|
430 |
cor.mat.true <- cor(rel.d) |
|
|
431 |
nc <- ncol(rel.d) |
|
|
432 |
|
|
|
433 |
loop <- \(which.taxon){ |
|
|
434 |
# create vector to hold correlations from every permutation for each single otu |
|
|
435 |
## perm.cor.vec.mat stands for permuted correlations vector matrix |
|
|
436 |
perm.cor.vec.mat <- vector() |
|
|
437 |
|
|
|
438 |
for (i in 1:reps) { |
|
|
439 |
# Create empty matrix of same dimension as rel.d |
|
|
440 |
perm.rel.d <- matrix(numeric(0), dim(rel.d)[1], dim(rel.d)[2]) |
|
|
441 |
rownames(perm.rel.d) <- rownames(rel.d) |
|
|
442 |
colnames(perm.rel.d) <- colnames(rel.d) |
|
|
443 |
|
|
|
444 |
# For each otu |
|
|
445 |
for (j in seq_len(dim(rel.d)[2])) { |
|
|
446 |
# Replace the original taxon vector with a permuted taxon vector |
|
|
447 |
perm.rel.d[, j] <- sample(rel.d[, j]) |
|
|
448 |
} |
|
|
449 |
|
|
|
450 |
# Do not randomize focal column |
|
|
451 |
perm.rel.d[, which.taxon] <- rel.d[, which.taxon] |
|
|
452 |
|
|
|
453 |
# Calculate correlation matrix of permuted matrix |
|
|
454 |
cor.mat.null <- cor(perm.rel.d, perm.rel.d[, which.taxon]) |
|
|
455 |
|
|
|
456 |
# For each iteration, save the vector of null matrix correlations between focal taxon and other taxa |
|
|
457 |
perm.cor.vec.mat <- cbind(perm.cor.vec.mat, cor.mat.null) |
|
|
458 |
} |
|
|
459 |
|
|
|
460 |
# Save the median correlations between the focal taxon and all other taxa |
|
|
461 |
apply(perm.cor.vec.mat, 1, median) |
|
|
462 |
} |
|
|
463 |
{ |
|
|
464 |
if (threads > 1) { |
|
|
465 |
pcutils::lib_ps("foreach", "doSNOW", "snow", library = FALSE) |
|
|
466 |
pb <- utils::txtProgressBar(max = reps, style = 3) |
|
|
467 |
if (verbose) { |
|
|
468 |
opts <- list(progress = function(n) utils::setTxtProgressBar(pb, n)) |
|
|
469 |
cl <- snow::makeCluster(threads) |
|
|
470 |
} else { |
|
|
471 |
opts <- NULL |
|
|
472 |
} |
|
|
473 |
doSNOW::registerDoSNOW(cl) |
|
|
474 |
res <- foreach::`%dopar%`( |
|
|
475 |
foreach::foreach(i = 1:nc, .options.snow = opts), |
|
|
476 |
loop(i) |
|
|
477 |
) |
|
|
478 |
snow::stopCluster(cl) |
|
|
479 |
gc() |
|
|
480 |
} else { |
|
|
481 |
res <- lapply(1:nc, loop) |
|
|
482 |
} |
|
|
483 |
} |
|
|
484 |
|
|
|
485 |
simplify2array(res) -> med.tax.cors |
|
|
486 |
|
|
|
487 |
obs.exp.cors.mat <- cor.mat.true - med.tax.cors |
|
|
488 |
} else { |
|
|
489 |
obs.exp.cors.mat <- mycor |
|
|
490 |
} |
|
|
491 |
|
|
|
492 |
diag(obs.exp.cors.mat) <- 0 |
|
|
493 |
|
|
|
494 |
# Calculate connectedness by averaging positive and negative observed - expected correlations |
|
|
495 |
pn.mean <- \(vector, mode = "p"){ |
|
|
496 |
if (mode == "p") { |
|
|
497 |
pos.vals <- vector[which(vector > 0)] |
|
|
498 |
} else if (mode == "n") pos.vals <- vector[which(vector < 0)] |
|
|
499 |
p.mean <- mean(pos.vals) |
|
|
500 |
if (length(pos.vals) == 0) p.mean <- 0 |
|
|
501 |
return(p.mean) |
|
|
502 |
} |
|
|
503 |
connectedness.pos <- apply(obs.exp.cors.mat, 2, pn.mean, mode = "p") |
|
|
504 |
connectedness.neg <- apply(obs.exp.cors.mat, 2, pn.mean, mode = "n") |
|
|
505 |
|
|
|
506 |
# Calculate cohesion by multiplying the relative abundance dataset by associated connectedness |
|
|
507 |
cohesion.pos <- rel.d %*% connectedness.pos |
|
|
508 |
cohesion.neg <- rel.d %*% connectedness.neg |
|
|
509 |
|
|
|
510 |
#### Combine vectors into one list and print |
|
|
511 |
output <- list( |
|
|
512 |
data.frame(neg = connectedness.neg, pos = connectedness.pos), |
|
|
513 |
data.frame(neg = cohesion.neg, pos = cohesion.pos) |
|
|
514 |
) |
|
|
515 |
|
|
|
516 |
names(output) <- c("Connectedness", "Cohesion") |
|
|
517 |
|
|
|
518 |
class(output) <- c("cohesion", class(output)) |
|
|
519 |
return(output) |
|
|
520 |
} |
|
|
521 |
|
|
|
522 |
|
|
|
523 |
#' Plot cohesion |
|
|
524 |
#' |
|
|
525 |
#' @param x `Cohesion()` result (cohesion object) |
|
|
526 |
#' @param mode plot mode, 1~2 |
|
|
527 |
#' @param group group name in colnames(metadata) |
|
|
528 |
#' @param metadata metadata |
|
|
529 |
#' @param ... additional arguments for \code{\link[pcutils]{group_box}} (mode=1) or \code{\link[pcutils]{group_box}} (mode=2) |
|
|
530 |
#' |
|
|
531 |
#' @return a ggplot |
|
|
532 |
#' @method plot cohesion |
|
|
533 |
#' @exportS3Method |
|
|
534 |
#' @rdname Cohesion |
|
|
535 |
plot.cohesion <- function(x, group, metadata, mode = 1, ...) { |
|
|
536 |
neg <- pos <- NULL |
|
|
537 |
cohesion_res <- x |
|
|
538 |
if (mode == 1) p <- pcutils::stackplot(abs(t(cohesion_res$Cohesion)), group = group, metadata = metadata, ...) |
|
|
539 |
if (mode == 2) { |
|
|
540 |
co <- cohesion_res$Cohesion %>% dplyr::transmute(`neg:pos` = neg / pos) |
|
|
541 |
p <- pcutils::group_box(co, group = group, metadata = metadata, p_value2 = TRUE, ...) + |
|
|
542 |
ylab("neg:pos cohesion") + theme_bw() |
|
|
543 |
} |
|
|
544 |
p |
|
|
545 |
} |