library(idopNetwork)
<- options()
backup_options # load pre-computered results
= idopNetwork:::test_result test_result
idopNetwork is packed as a cartographic tool that performs power curve fitting, classification, variable selection, microbial abundance decomposition, and network visualization based on microbial 16S rRNA gene sequencing metadata.
For complete details on the use and execution of this protocol, please refer to Chen et al and Cao et al.
Before running idopNetwork, user need to provide datasets, and they should be cleaned and merged to exactly the same format of the example data.
Microbe Operational taxonomic unit dataset must have first column as IDs for microbes.
data("gut_microbe")
View(gut_microbe)
name | GQ_UC1 | GQ_UC3 | GQ_UC4 | GQ_UC5 |
---|---|---|---|---|
Escherichia_coli | 176 | 1701 | 28506 | 177 |
Arthrobacter_oxydans | 3 | 215 | 1578 | 1 |
Ruminococcus_gnavus | 744 | 0 | 4 | 733 |
Bacteroides_plebeius | 271 | 4401 | 7 | 8394 |
Bacteroides_pyogenes | 0 | 15 | 0 | 0 |
Clostridium_tertium | 305 | 1 | 1 | 270 |
Bacteroides_stercoris | 24 | 0 | 0 | 11 |
Flavisolibacter_ginsengisoli | 0 | 123 | 831 | 0 |
Bacteroides_massiliensis | 0 | 6098 | 6 | 0 |
Prevotella_heparinolytica | 0 | 6 | 0 | 0 |
data("mustard_microbe")
View(mustard_microbe)
Taxon | Leaf_RelAbund_Jam | Leaf_RelAbund_Mah | Leaf_RelAbund_Mil | Leaf_RelAbund_Par | |
---|---|---|---|---|---|
2 | OTU_1006 | 0.0002184 | 0.0003188 | 0.0017428 | 0.0003321 |
3 | OTU_10073 | 0.0003568 | 0.0006330 | 0.0003985 | 0.0002410 |
4 | OTU_10088 | 0.0002668 | 0.0000756 | 0.0003282 | 0.0003647 |
5 | OTU_10094 | 0.0000717 | 0.0000364 | 0.0004385 | 0.0002214 |
6 | OTU_1009 | 0.0000667 | 0.0000905 | 0.0000855 | 0.0000261 |
7 | OTU_10131 | 0.0002768 | 0.0000180 | 0.0000400 | 0.0001758 |
8 | OTU_101 | 0.0005052 | 0.0001986 | 0.0008342 | 0.0009899 |
9 | OTU_10202 | 0.0002601 | 0.0001445 | 0.0000662 | 0.0004298 |
10 | OTU_10204 | 0.0002101 | 0.0000322 | 0.0000248 | 0.0001237 |
12 | OTU_1023 | 0.0016624 | 0.0000046 | 0.0001999 | 0.0010746 |
Leaf_RelAbund_Sil | Root_RelAbund_Jam.1 | Root_RelAbund_Mah.1 | Root_RelAbund_Mil.1 | Root_RelAbund_Par.1 | |
---|---|---|---|---|---|
2 | 0.0000262 | 0.0002587 | 0.0004471 | 0.0012986 | 0.0003003 |
3 | 0.0002968 | 0.0006510 | 0.0003989 | 0.0013566 | 0.0005467 |
4 | 0.0000213 | 0.0001202 | 0.0001587 | 0.0001683 | 0.0001111 |
5 | 0.0000405 | 0.0000401 | 0.0003653 | 0.0005675 | 0.0001981 |
6 | 0.0000126 | 0.0000385 | 0.0000353 | 0.0000229 | 0.0000234 |
7 | 0.0000108 | 0.0002373 | 0.0000591 | 0.0000544 | 0.0003422 |
8 | 0.0000248 | 0.0152178 | 0.0018196 | 0.0032232 | 0.0066187 |
9 | 0.0012265 | 0.0000499 | 0.0002222 | 0.0001714 | 0.0001192 |
10 | 0.0002244 | 0.0004985 | 0.0000801 | 0.0000844 | 0.0008406 |
12 | 0.0000028 | 0.0002308 | 0.0000032 | 0.0000402 | 0.0002110 |
The first major step in our idopNetwork reconstruction is to fit allometric growth curves to the data using the power function. This is easily done by using the related function power_fit. This function needs cleaned dataset as input and will return fitted OTUs for given dataset. Then the fitted output with original dataset can be transfer into function power_equation_plot for quick visualization
= data_cleaning(gut_microbe)
df = power_equation_fit(df)
result1 power_equation_plot(result1)
In this step we implement the power equation into functional clustering to detect different microbial modules. If after clustering there are still too many microbes within a certain module for network reconstruction, we can rerun functional clustering to further classify this module into distinct submodules.
we fit mean vector of each cluster center by power equation(assume k=3)
matplot(t(power_equation(x = 1:30, matrix(c(2, 1, 3, 0.2, 0.5, -0.5), nrow = 3, ncol = 2))),
type = "l", xlab = "time", ylab = "population")
legend("topright", c("cluster 1", "cluster 2", "cluster 3"), lty = c(1, 2, 3), col = c(1,
2, 3), box.lwd = 0)
we fit covariance matrix of multivariate normal distribution with SAD1, it can be showed as
get_SAD1_covmatrix(c(2, 0.5), n = 5)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.25 0.50 1.00 2.00 4.00
#> [2,] 0.50 1.25 2.50 5.00 10.00
#> [3,] 1.00 2.50 5.25 10.50 21.00
#> [4,] 2.00 5.00 10.50 21.25 42.50
#> [5,] 4.00 10.00 21.00 42.50 85.25
we can check initial parameters (k=4)
get_par_int(X = log10(df + 1), k = 4, times = as.numeric(log10(colSums(df) + 1)))
#> $initial_cov_params
#> [1] 0.100000 1.098059
#>
#> $initial_mu_params
#> a b
#> [1,] 7.473984e-23 32.129751
#> [2,] 1.065011e+03 -3.515885
#> [3,] 1.239458e+15 -21.841287
#> [4,] 7.449189e-06 7.924409
#>
#> $initial_probibality
#>
#> 1 2 3 4
#> 0.37500000 0.04166667 0.47916667 0.10416667
# use kmeans to get initial centers
= kmeans(log10(df + 1), 4)$centers
tmp = power_equation_fit(tmp, trans = NULL)
tmp2 power_equation_plot(tmp2, label = NULL, n = 4)
idopNetowrk already wrapped the mean curve modelling, covariance
matrix modelling and likelihood ratio calculation into a function
fun_clu()
.
options(max.print = 10)
fun_clu(result1$original_data, k = 3, iter.max = 5)
#> initial value 1257.084808
#> iter 10 value 1164.839738
#> final value 1164.839666
#> converged
#>
#> iter = 1
#> Log-Likelihood = 1164.84
#> initial value 1160.462757
#> final value 1158.030989
#> converged
#>
#> iter = 2
#> Log-Likelihood = 1158.031
#> initial value 1156.642054
#> final value 1156.099251
#> converged
#>
#> iter = 3
#> Log-Likelihood = 1156.099
#> initial value 1155.818025
#> final value 1155.731122
#> converged
#>
#> iter = 4
#> Log-Likelihood = 1155.731
#> initial value 1155.668324
#> final value 1155.648281
#> converged
#>
#> iter = 5
#> Log-Likelihood = 1155.648
#> initial value 1155.631659
#> final value 1155.626175
#> converged
#>
#> iter = 6
#> Log-Likelihood = 1155.626
#> $cluster_number
#> [1] 3
#>
#> $Log_likelihodd
#> [1] 1155.626
#>
#> $AIC
#> [1] 2331.252
#>
#> $BIC
#> [1] 2349.964
#>
#> $cov_par
#> [1] 0.1801803 0.7749108
#>
#> $mu_par
#> [,1] [,2]
#> [1,] 4.804981e+01 -1.687026
#> [2,] 1.596032e+13 -19.051299
#> [3,] 4.141375e-26 36.850027
#>
#> $probibality
#> [1] 0.08323678 0.50221752 0.41454570
#>
#> $omega
#> [,1] [,2] [,3]
#> [1,] 2.210646e-13 9.999994e-01 6.039520e-07
#> [2,] 6.941373e-14 9.998855e-01 1.144993e-04
#> [3,] 5.497180e-24 9.905076e-01 9.492427e-03
#> [ reached getOption("max.print") -- omitted 45 rows ]
#>
#> $cluster
#> GQ_UC1 GQ_UC3 GQ_UC4 GQ_UC5 MC_UC1 MC_UC2 MC_UC3 MC_UC4 HC_UC2 HC_UC4
#> HJC_UC1 HJC_UC2 HJC_UC3 HJC_UC4 JJC_UC2 YZJC_UC3 ZC_UC1 ZC_UC3 ZC_UC4
#> ZC_UC5 apply.omega..1..which.max.
#> [ reached 'max' / getOption("max.print") -- omitted 48 rows ]
#>
#> $cluster2
#> GQ_UC1 GQ_UC3 GQ_UC4 GQ_UC5 MC_UC1 MC_UC2 MC_UC3 MC_UC4 HC_UC2 HC_UC4
#> HJC_UC1 HJC_UC2 HJC_UC3 HJC_UC4 JJC_UC2 YZJC_UC3 ZC_UC1 ZC_UC3 ZC_UC4
#> ZC_UC5 apply.omega..1..which.max.
#> [ reached 'max' / getOption("max.print") -- omitted 48 rows ]
#>
#> [ reached getOption("max.print") -- omitted 2 entries ]
Usually we use multithread to calcuation k = 2-n and then to decide best k , fun_clu_BIC use BIC to select best cluster number by default
= fun_clu_parallel(result1$original_data, start = 2, end = 5) result2
= which.min(sapply(result2, "[[", "BIC")) + 1 #skipped k = 1
best.k
best.k#> [1] 7
fun_clu_BIC(result = result2)
# we can direct give other k value
fun_clu_plot(result = result2, best.k = best.k)
data("mustard_microbe")
= data_cleaning(mustard_microbe, x = 1) df2
= power_equation_fit(df2[, 1:5], trans = NULL)
res_l = power_equation_fit(df2[, 6:10], trans = NULL)
res_r = data_match(result1 = res_l, result2 = res_r) res1
= bifun_clu_parallel(data1 = res1$dataset1$original_data, data2 = res1$dataset2$original_data,
res2 Time1 = res1$dataset1$Time, Time2 = res1$dataset2$Time, trans = NULL, start = 2,
end = 10, iter.max = 10)
fun_clu_BIC(result = res2)
# we can set best.k directly
bifun_clu_plot(result = res2, best.k = 5, label = NULL, n1 = 5, n2 = 5)
Sometimes a module is still too large for network reconstruction, which is determined by Dunbar’s number, we can further cluster it into sub-modules.
= bifun_clu_convert(res2, best.k = 6, n1 = 5, n2 = 5)
res3 = order(sapply(res3$a$Module.all, nrow))[5]
large.module
= fun_clu_select(result_fit = res1$dataset1, result_funclu = res3$a, i = large.module)
res_suba = fun_clu_select(result_fit = res1$dataset2, result_funclu = res3$b, i = large.module)
res_subb = power_equation_fit(res_suba$original_data, trans = NULL)
dfsuba_l = power_equation_fit(res_subb$original_data, trans = NULL)
dfsubb_r = data_match(result1 = dfsuba_l, result2 = dfsubb_r)
ressub1 = bifun_clu_parallel(data1 = ressub1$dataset1$original_data, data2 = ressub1$dataset2$original_data,
ressub2 Time1 = ressub1$dataset1$Time, Time2 = ressub1$dataset2$Time, trans = NULL, start = 2,
end = 5, iter.max = 1)
= test_result$d2_subcluster ressub2
fun_clu_BIC(result = ressub2)
bifun_clu_plot(result = ressub2, best.k = 3, label = NULL, n1 = 5, n2 = 5)
idopNetwork implements a LASSO-based procedure to choose a small set
of the most significant microbes/module that links with a given
microbes/modules. get_interaction()
return a compound list
contain the target microbe/module name, the most relevant
Modules/microbes names and the coefficients.
= fun_clu_convert(result2, best.k = best.k)
result3 = result3$original_data
df.module get_interaction(df.module, 1)
#> $ind.name
#> [1] "M1"
#>
#> $dep.name
#> [1] "M5"
#>
#> $coefficient
#> [1] 5.982483
# we can the microbial relationship in Module1
= result3$Module.all$`1`
df.M1 get_interaction(df.M1, 1)
#> Warning: Option grouped=FALSE enforced in cv.glmnet, since < 3 observations per
#> fold
#> Warning: Option grouped=FALSE enforced in cv.glmnet, since < 3 observations per
#> fold
#> Warning: Option grouped=FALSE enforced in cv.glmnet, since < 3 observations per
#> fold
#> $ind.name
#> [1] "Arthrobacter_oxydans"
#>
#> $dep.name
#> [1] "Fusobacterium_necrophorum"
#>
#> $coefficient
#> [1] 0.2479058
qdODE system is build based on variable selection results, it has unique ability to decompose the observed module/microbe abundance level into its independent component and dependent component, which can be used for inferring idopNetwork.
options(max.print = 10)
# first we test solving a qdODE
= lapply(1:best.k, function(c) get_interaction(df.module, c))
module.relationship = qdODE_all(result = result3, relationship = module.relationship, 1, maxit = 100)
ode.test #> initial value 161.858385
#> iter 10 value 43.270181
#> iter 20 value 0.970116
#> iter 30 value 0.111549
#> iter 40 value 0.077045
#> iter 50 value 0.054422
#> iter 60 value 0.039414
#> iter 70 value 0.029324
#> iter 80 value 0.022068
#> iter 90 value 0.016649
#> iter 100 value 0.012526
#> final value 0.012526
#> stopped after 100 iterations
# we can view the result
qdODE_plot_base(ode.test)
# then we solve all qdODEs
= qdODE_parallel(result3) ode.module
qdODE_plot_all(ode.module)
= fun_clu_select(result_fit = result1, result_funclu = result3, i = 1)
result_m1 = qdODE_parallel(result_m1) ode.M1
qdODE_plot_all(ode.M1)
The final step of this guide is to visualization the multilayer network, and our package provide network_plot function to easily draw our idopNetwork. We can simply plug previous qdODE results into network_conversion function, and it will convert qdODE result for network visualization
= lapply(ode.module$ode_result, network_conversion)
net_module network_plot(net_module, title = "Module Network")
= lapply(ode.M1$ode_result, network_conversion)
net_m1 network_plot(net_m1, title = "M1 Network")
= qdODE_parallel(res3$a)
mustard_module_a = qdODE_parallel(res3$b)
mustard_module_b
= fun_clu_select(result_fit = res1$dataset1, result_funclu = res3$a, i = 3)
res_m1a = fun_clu_select(result_fit = res1$dataset2, result_funclu = res3$b, i = 3)
res_m1b = qdODE_parallel(res_m1a)
mustard_M1a = qdODE_parallel(res_m1b) mustard_M1b
<- lapply(mustard_module_a$ode_result, network_conversion)
mustard_m_a <- lapply(mustard_module_b$ode_result, network_conversion)
mustard_m_b
# set seed to make same random layout
layout(matrix(c(1, 2), 1, 2, byrow = TRUE))
set.seed(1)
network_plot(mustard_m_a, title = "Module Network a")
set.seed(1)
network_plot(mustard_m_b, title = "Module Network b")
object “LL.next” not found
This happens when parameters optimization failure, try rerun
cluster.
plot failure when using fun_clu_plot()
or
bifun_clu_plot()
This often happens when bad initial parameters is given and some cluster
is lost, try rerun cluster or use a smaller k.
sessionInfo()
#> R version 4.2.1 (2022-06-23 ucrt)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 22000)
#>
#> Matrix products: default
#>
#> locale:
#> [1] LC_COLLATE=C
#> [2] LC_CTYPE=English_United States.utf8
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C
#> [5] LC_TIME=English_United States.utf8
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] idopNetwork_0.1.0
#>
#> loaded via a namespace (and not attached):
#> [1] deSolve_1.33 shape_1.4.6 tidyselect_1.1.2
#> [4] xfun_0.33 bslib_0.4.0 reshape2_1.4.4
#> [7] purrr_0.3.4 splines_4.2.1 lattice_0.20-45
#> [10] colorspace_2.0-3 vctrs_0.4.2 generics_0.1.3
#> [13] htmltools_0.5.3 yaml_2.3.5 utf8_1.2.2
#> [16] survival_3.3-1 rlang_1.0.6 orthopolynom_1.0-6.1
#> [19] jquerylib_0.1.4 pillar_1.8.1 glue_1.6.2
#> [22] plyr_1.8.7 foreach_1.5.2 lifecycle_1.0.3
#> [25] stringr_1.4.1 munsell_0.5.0 gtable_0.3.1
#> [28] mvtnorm_1.1-3 codetools_0.2-18 evaluate_0.16
#> [31] labeling_0.4.2 knitr_1.40 fastmap_1.1.0
#> [34] parallel_4.2.1 fansi_1.0.3 highr_0.9
#> [37] Rcpp_1.0.9 polynom_1.4-1 scales_1.2.1
#> [40] formatR_1.12 cachem_1.0.6 jsonlite_1.8.2
#> [43] farver_2.1.1 ggplot2_3.3.6 digest_0.6.29
#> [46] stringi_1.7.8 dplyr_1.0.10 grid_4.2.1
#> [49] cli_3.4.1 tools_4.2.1 magrittr_2.0.3
#> [52] sass_0.4.2 glmnet_4.1-4 patchwork_1.1.2
#> [55] tibble_3.1.8 pkgconfig_2.0.3 Matrix_1.5-1
#> [58] rmarkdown_2.16 rstudioapi_0.14 iterators_1.0.14
#> [61] R6_2.5.1 igraph_1.3.5 compiler_4.2.1