{"id":31041755,"url":"https://github.com/stocnet/monan","last_synced_at":"2025-09-14T10:40:56.475Z","repository":{"id":178323778,"uuid":"614786717","full_name":"stocnet/MoNAn","owner":"stocnet","description":"R package for the analysis of mobility networks","archived":false,"fork":false,"pushed_at":"2025-05-27T12:43:38.000Z","size":13160,"stargazers_count":13,"open_issues_count":4,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-27T13:51:33.118Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stocnet.png","metadata":{"files":{"readme":"README.Rmd","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-16T10:15:47.000Z","updated_at":"2025-05-15T10:01:02.000Z","dependencies_parsed_at":"2023-10-11T11:59:26.157Z","dependency_job_id":"d5e11296-cd66-498e-82c8-b78c61a9d0e5","html_url":"https://github.com/stocnet/MoNAn","commit_stats":null,"previous_names":["perblock/monan","stocnet/monan"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/stocnet/MoNAn","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stocnet%2FMoNAn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stocnet%2FMoNAn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stocnet%2FMoNAn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stocnet%2FMoNAn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stocnet","download_url":"https://codeload.github.com/stocnet/MoNAn/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stocnet%2FMoNAn/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275094398,"owners_count":25404446,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-14T02:00:10.474Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-14T10:40:43.737Z","updated_at":"2025-09-14T10:40:56.453Z","avatar_url":"https://github.com/stocnet.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nknitr::opts_chunk$set(\n  collapse = TRUE,\n  comment = \"#\u003e\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n```\n\n\n# MoNAn \u003cimg src=\"https://raw.githubusercontent.com/stocnet/MoNAn/main/MoNAn_logo.png\" align=\"right\" width=\"150\"/\u003e\n\n\u003c!-- badges: start --\u003e\n\u003c!-- badges: end --\u003e\n\nMoNAn is the software implementation of the statistical model outlined in:\n\nBlock, P., Stadtfeld, C., \u0026 Robins, G. (2022). A statistical model for the analysis of mobility \ntables as weighted networks with an application to faculty hiring networks. Social Networks, 68, 264-278.\n\n[Link to the study in Social Networks](https://doi.org/10.1016/j.socnet.2021.08.003).\n\n[Pre-print with minor differences to the journal version](https://osf.io/preprints/socarxiv/n86rx/). \n\nThe model allows for the analysis of emergent structures (known from network analysis) in mobility tables, alongside exogenous predictors. It combines features from classical log-linear models for mobility tables and Exponential Random Graph Models (ERGMs). In the absence of emergent structures, the models reduces to the former, when ignoring characteristics of individuals, it is an ERGM for weighted networks.\n\nAnnouncements about workshops etc. can be found [here](https://www.suz.uzh.ch/de/institut/professuren/block/monan_software.html).\n\nThe **MoNAn Manual** is available here on github in the manual folder or on [SocArXiv](https://osf.io/preprints/socarxiv/8q2xu/)\n\n## Note from the developers\n\nThis is version 1.1.0 of MoNAn (Sep 2024). The update to version 1.0.0 (March 2024) included many changes, including some that mean code written for older versions will not work anymore!\n\nCode use for version 1.0.0 has been simplified; the most important changes are:\n\n- the cache is now hidden from the user and does not need to be create or specified in functions anymore\n\n- there is a new way to specify the model with createEffects and addEffect\n\n- in gofMobilityNetwork there is no need to specify \"simulations\" anymore\n\n- new way to generate the process state using monanDataCreate\n\nAs of 30 Aug 2023, we have a CRAN release of the package.\nNevertheless, we cannot guarantee that the package and the documentation is \nfree of bugs or errors; further, you might\nnot be able to do what you want. In that case, or if you are unsure \nplease write the package\nmaintainer under his institutional email address.\n\nWe are currently (Sep 2024) on version 1.1.0 on github. \nVersion 1.1.0 will be released to CRAN in Sep 2024.\n\n# Installation\n\nYou can install MoNAn either from CRAN or from GitHub.\n\nThe advantage of installing from github is that you will always stay up-to-date\nwith the latest developments, in particular, new functionality and effects.\nYou can install the current version of MoNAn from GitHub using:\n\n``` r\n# install.packages(\"remotes\")\nremotes::install_github(\"stocnet/MoNAn\")\n```\n\nHowever, you can also get the (often slightly older) CRAN version with:\n\n``` r\ninstall.packages(\"MoNAn\")\n```\n\n# Example\n\n\nIn this section, we outline a simple example with synthetic data stored in the MoNAn package.\n\n```{r}\nlibrary(MoNAn)\n```\n\n## The Data\n\nThe example case uses synthetic data that represents mobility of 742 individuals between 17 organisations. The artificial data includes an edgelist, i.e., a list of origins (column 1) and destinations (column 2) of all individuals sorted by origin.\n\n```{r}\nmobilityEdgelist[1:10,]\n```\n\nThe data further includes (artificial) characteristics of individuals, which we will call sex. However, using continuous covariates is also possible, although for some effects this would not be meaningful.\n\n```{r}\nindSex[1:10]\n```\n\nCharacteristics of the organisations are the region in which they are located (binary,  northern/southern island) and the organisations' size that represents a composite measure of the number of workers, assets, and revenue.\n\n```{r}\norgRegion\norgSize\n```\n\n\n## Working with the package\n\n### Preparing the data\n\nFirst, we create MoNAn data objects from the introduced data files. A necessary part is that we define and name the nodesets, i.e., we name who (\"people\") is mobile between what (\"organisations\").\n\n```{r}\n# extract number of individuals and organisations from the mobility data\n\nN_ind \u003c- nrow(mobilityEdgelist)\nN_org \u003c- length(unique(as.numeric(mobilityEdgelist)))\n\n# create monan objects\npeople \u003c- monanEdges(N_ind)\norganisations \u003c- monanNodes(N_org)\ntransfers \u003c- monanDependent(mobilityEdgelist, \n                            nodes = \"organisations\", \n                            edges = \"people\")\n\nsameRegion \u003c- outer(orgRegion, orgRegion, \"==\") * 1\nsameRegion \u003c- dyadicCovar(sameRegion, nodeSet = c(\"organisations\", \"organisations\"))\n\nregion \u003c- monadicCovar(orgRegion, nodeSet = \"organisations\")\nsize \u003c- monadicCovar(orgSize, nodeSet = \"organisations\")\nsex \u003c- monadicCovar(indSex, nodeSet = \"people\", addSame = F, addSim = F)\n```\n\nWe combine the data objects into the process state, i.e., a MoNAn object that stores all information about the data that will be used in the estimation later. We must include one outcome variable (transfers) and two nodeSets (people, organisations).\n\n```{r}\nmyState \u003c- monanDataCreate(transfers,\n                           people,\n                           organisations,\n                           sameRegion,\n                           region,\n                           size,\n                           sex)\n\n# inspect the created object\nmyState\n```\n\n\n### Specifying the model\n\nThe predictors in the model are called \"Effects\". To specify a model, first an empty effects object\nis created that only uses the state, and then effects are added one-by-one. Adding\nan effect requires the effects name and any additional parameters it needs.\n\n```{r}\n# create an effects object\nmyEffects \u003c- createEffects(myState)\nmyEffects \u003c- addEffect(myEffects, loops)\nmyEffects \u003c- addEffect(myEffects, concentration_AC, alpha = 4)\nmyEffects \u003c- addEffect(myEffects, reciprocity_AC, alpha = 4)\nmyEffects \u003c- addEffect(myEffects, dyadic_covariate, attribute.index = \"sameRegion\")\nmyEffects \u003c- addEffect(myEffects, alter_covariate, attribute.index = \"size\")\nmyEffects \u003c- addEffect(myEffects, resource_covar_to_node_covar,\n                       attribute.index = \"region\",\n                       resource.attribute.index = \"sex\")\nmyEffects \u003c- addEffect(myEffects, loops_resource_covar, resource.attribute.index = \"sex\")\n\n# There is also a simpler way using pipes (|\u003e) and using the more intuitive\n# node.attribute \u0026 edge.attribute instead of the older \n# attribute.index \u0026 resource.attribute.index:\nmyEffects \u003c- createEffects(myState) |\u003e\n  addEffect(loops) |\u003e\n  addEffect(concentration_AC, alpha = 4) |\u003e\n  addEffect(reciprocity_AC, alpha = 4) |\u003e\n  addEffect(dyadic_covariate, node.attribute = \"sameRegion\") |\u003e\n  addEffect(alter_covariate, node.attribute = \"size\") |\u003e\n  addEffect(resource_covar_to_node_covar,\n            node.attribute = \"region\",\n            edge.attribute = \"sex\") |\u003e\n  addEffect(loops_resource_covar, edge.attribute = \"sex\")\n\n# inspect the created object\nmyEffects\n```\n\n\n### Optional: Pre-estimation\n\nWe can run a pseudo-likelihood estimation that gives a (biased) guess of the model results. We use this to get improved initial estimates, which increases the chances of model convergence in the first run of the estimation considerably. To get pseudo-likelihood estimates, we need to use functions from other libraries to estimate a multinomial logit model (e.g., \"dfidx\" and \"mlogit\")\n\n```{r}\n# create multinomial statistics object pseudo-likelihood estimation\nmyStatisticsFrame \u003c- getMultinomialStatistics(myState, myEffects)\n\n### additional script to get pseudo-likelihood estimates, requires the dfidx and mlogit package\n# library(dfidx)\n# library(mlogit)\n# my.mlogit.dataframe \u003c- dfidx(myStatisticsFrame,\n#                           shape = \"long\", \n#                           choice = \"choice\")\n# \n# colnames(my.mlogit.dataframe) \u003c- gsub(\" \", \"_\", colnames(my.mlogit.dataframe))\n# \n# IVs \u003c- (colnames(my.mlogit.dataframe)[2:(ncol(myStatisticsFrame)-2)])\n# \n# form \u003c- as.formula(paste(\"choice ~ 1 + \", paste(IVs, collapse = \" + \"), \"| 0\"))\n# \n# my.mlogit.results \u003c- mlogit(formula = eval(form), data = my.mlogit.dataframe, \n#                             heterosc = FALSE)\n# \n# summary(my.mlogit.results)\n#\n# initEst \u003c- my.mlogit.results$coefficients[1:length(IVs)]\n```\n\n\n### Specifying the estimation algorithm\n\nThe estimation algorithm requires multiple parameters that guide the simulation of the chain. Most values are generated by default from the state and the effects object.\n\n```{r}\nmyAlg \u003c- monanAlgorithmCreate(myState, myEffects, nsubN2 = 3, \n                              multinomialProposal = FALSE)\n```\n\n### Estimation\n\nNow we can estimate the model. The first two lines indicate the  data (state), effects, and algorithm. The third line specifies the initial estimates, where the previously obtained pseudo-likelihood estimates can be used. The remaining lines define whether parallel computing should be used, how much console output is desired during the estimation, and whether the simulations from phase 3 should be stored.\n\nRunning the model takes a while (up to 10 minutes for this data with parallel computing).\n\n```{r, eval=FALSE}\nmyResDN \u003c- monanEstimate(\n  myState, myEffects, \n  myAlg,\n  initialParameters = NULL,\n  parallel = TRUE, cpus = 4,\n  verbose = TRUE,\n  returnDeps = TRUE,\n  fish = FALSE\n)\n```\n\n\nIn case pseudo-likelihood estimates have been obtained previously, this can be specified by\n\n```{r, eval=FALSE}\nmyResDN \u003c- monanEstimate(\n  myState, myEffects, myAlg,\n  initialParameters = initEst,\n  parallel = TRUE, cpus = 4,\n  verbose = TRUE,\n  returnDeps = TRUE,\n  fish = FALSE\n)\n```\n\n\nCheck convergence to see whether the results are reliable. In case the maximum convergence ratio is above 0.1 (or 0.2 for less precise estimates), another run is necessary.\n\n```{r}\nmax(abs(myResDN$convergenceStatistics))\n```\n\nIf convergence is too high, update algorithm, re-run estimation with previous results as starting values and check convergence:\n\n```{r, eval=FALSE}\n# estimate mobility network model again based on previous results to improve convergence\n# with an adjusted algorithm\nmyAlg \u003c- monanAlgorithmCreate(myState, myEffects, multinomialProposal = TRUE, \n                              initialIterationsN2 = 200, nsubN2 = 1, initGain = 0.02, iterationsN3 = 1000)\n\n# for users of other stocnet packages, we can also use monan07 to run an estimation \n# (it is an alias for estimateMobilityNetwork)\nmyResDN \u003c- monan07(\n  myState, myEffects, myAlg,\n  prevAns = myResDN,\n  parallel = TRUE, cpus = 4,\n  verbose = TRUE,\n  returnDeps = TRUE,\n  fish = FALSE\n)\n```\n\n\n```{r}\n# check convergence\nmax(abs(myResDN$convergenceStatistics))\n```\n\nIn case convergence is still poor, updating the algorithm might be necessary. Otherwise, we can view the results. The first column is the estimate, the second column the standard error, and the third column the convergence ratio. All values in the final column should be below 0.1 (see above).\n\n```{r}\nmyResDN\n```\n\n## Diagnostics of the estimated model\n\nThe following two diagnostics indicate the extent to which the chain mixes (i.e., whether the thinning was chosen appropriately). The autoCorrelationTest indicates the degree to which the values of the dependent variable of consecutive draws from the chain in phase 3 are correlated. Here lower values are better. Values above 0.5 are very problematic and indicate that a higher thinning is needed.\n\n```{r}\nautoCorrelationTest(myResDN)\n```\n\nThe output of extractTraces indicates the correlation of statistics between subsequent draws from the chain in phase 3. The plot should show data points randomly scattered around the target line, as shown below. If patterns in the traces are discernible, a higher thinning is needed.\n\n```{r}\ntraces \u003c- extractTraces(myResDN, myEffects)\n\npar(mfrow = c(1,2))\nplot(traces)\n```\n\n\n## Score-tests to check model specification\n\nBased on an estimated model, a score-type test is available that shows whether statistics representing non-included effects are well represented. If this is not the case, it is likely that including them will result in significant estimates.\n\n```{r}\nmyEffects2 \u003c- createEffects(myState) |\u003e\n  addEffect(transitivity_min)\n\ntest_ME.2 \u003c- scoreTest(myResDN, myEffects2)\ntest_ME.2\n```\n\nThe interpretation is that there appears to be some transitive clustering in the data that the model does not account for in its current form.\n\n\n## GOF testing\n\nAkin to ERGMs, goodness of fit testing is available to see whether auxiliary statistics are well captured by the model.\n\n```{r}\nmyGofIndegree \u003c- monanGOF(ans = myResDN, gofFunction = getIndegree, lvls = 1:70)\nplot(myGofIndegree, lvls = 20:70)\n\nmyGofTieWeight \u003c- monanGOF(ans = myResDN, gofFunction = getTieWeights, lvls = 1:20)\nplot(myGofTieWeight, lvls = 1:20)\n```\n\n\n## Simulation\n\nThe package also provides the possibility to exemplarily simulate mobility networks based on the data and the specified effects and parameters.\n\n```{r, eval=FALSE}\nmySimDN \u003c- monanSimulate(\n  myState,\n  myEffects,\n  parameters = c(2, 1, 1.5, 0.5, 0.1, -1, -0.5),\n  allowLoops = TRUE,\n  burnin = 45000,\n  thinning = 15000,\n  nSimulations = 10\n)\n\nmySimDN[[1]]\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstocnet%2Fmonan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstocnet%2Fmonan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstocnet%2Fmonan/lists"}