{"id":44106817,"url":"https://github.com/joshloyal/dynetlsm","last_synced_at":"2026-02-08T15:35:01.794Z","repository":{"id":44598633,"uuid":"254146758","full_name":"joshloyal/dynetlsm","owner":"joshloyal","description":"DynetLSM: latent space models for dynamic networks","archived":false,"fork":false,"pushed_at":"2022-12-08T16:40:09.000Z","size":10034,"stargazers_count":23,"open_issues_count":1,"forks_count":5,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-27T01:41:38.609Z","etag":null,"topics":["bayesian-inference","dynamic-networks","machine-learning","network-analysis","network-embedding"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/joshloyal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-08T16:48:33.000Z","updated_at":"2024-10-28T03:13:39.000Z","dependencies_parsed_at":"2023-01-25T14:46:33.267Z","dependency_job_id":null,"html_url":"https://github.com/joshloyal/dynetlsm","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/joshloyal/dynetlsm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshloyal%2Fdynetlsm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshloyal%2Fdynetlsm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshloyal%2Fdynetlsm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshloyal%2Fdynetlsm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joshloyal","download_url":"https://codeload.github.com/joshloyal/dynetlsm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshloyal%2Fdynetlsm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29235498,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-08T14:18:14.570Z","status":"ssl_error","status_checked_at":"2026-02-08T14:18:14.071Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["bayesian-inference","dynamic-networks","machine-learning","network-analysis","network-embedding"],"created_at":"2026-02-08T15:35:01.112Z","updated_at":"2026-02-08T15:35:01.786Z","avatar_url":"https://github.com/joshloyal.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/joshloyal/dynetlsm/blob/master/LICENSE)\n[![Travis](https://travis-ci.com/joshloyal/dynetlsm.svg?token=gTKqq3zSsip89mhYVQPZ\u0026branch=master)](https://travis-ci.com/joshloyal/dynetlsm)\n[![AppVeyor](https://ci.appveyor.com/api/projects/status/github/joshloyal/dynetlsm)](https://ci.appveyor.com/project/joshloyal/dynetlsm/history)\n[![PyPI Latest Release](https://img.shields.io/pypi/v/dynetlsm)](https://pypi.org/project/dynetlsm/)\n\n# DynetLSM: latent space models for dynamic networks\n\n*Author: [Joshua D. Loyal](https://joshloyal.github.io/)*\n\nThis package provides an interface for learning and inference in latent\nspace models for dynamic networks. Inference is performed using\nblocked Metropolis-Hastings within Gibbs sampling.\n\nThe primary method implemented in this package is the hierarchical Dirichlet\nprocess latent position cluster model (HDP-LPCM) described in\n\"A Bayesian nonparametric latent space approach to modeling evolving communities in\ndynamic networks\".\n\nDependencies\n------------\nDynetLSM requires:\n\n- Python (\u003e= 3.7)\n\nand the requirements highlighted in [requirements.txt](requirements.txt).\n\nInstallation\n------------\nYou need a working installation of numpy and scipy to install DynetLSM. If you have a working installation of numpy and scipy, the easiest way to install DynetLSM is using ``pip``:\n\n```\npip install -U dynetlsm\n```\n\nIf you prefer, you can clone the repository and run the setup.py file. Use the following commands to get the copy from GitHub and install all the dependencies:\n\n```\ngit clone https://github.com/joshloyal/dynetlsm.git\ncd dynetlsm\npip install .\n```\n\nOr install using pip and GitHub:\n\n```\npip install -U git+https://github.com/joshloyal/dynetlsm.git\n```\n\nBackground\n----------\n\n### Latent Space Models\n\n#### Static Networks\nLatent space models (LSMs) are a powerful approach to modeling network data. One is often interested in inferring properties of nodes in a network based on their connectivity patterns. Originally proposed by Hoff et al. (2002)\u003csup\u003e[[1]](#References)\u003c/sup\u003e, LSMs learn a latent embedding for each node that captures the similarity between them. This package focuses on embeddings within a Euclidean space so that the log-odds of forming an edge between two nodes is inversely proportional to the distance between their latent positions. In other words, nodes that are close together in the latent space are more likely to form a connection in the observed network. The generative model is as follows:\n\n1. For each node, sample a node's latent position from a Gaussian distribution:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/static_lsm_prior.png\" alt=\"latent positions prior\" width=\"200\"\u003e\n\u003c/p\u003e\n\n2. For each edge, sample a connection from a Bernoulli distribution:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/static_lsm.png\" alt=\"static lsm\" width=\"400\"\u003e\n\u003c/p\u003e\n\n#### Dynamic Networks\nFor dynamic (time-varying) networks, one is also interested in determining how properties of the nodes change over time. LSMs can also accomplish this task. Sarkar and Moore (2005)\u003csup\u003e[[2]](#References)\u003c/sup\u003e and Sewell and Chen (2015)\u003csup\u003e[[3]](#References)\u003c/sup\u003e proposed propagating the latent positions through time with a Gaussian random-walk Markovian process. Based on these latent positions, the edges in the network form in the same way as the static case. The generative process is as follows:\n\n1. For `t = 1`, sample a node's initial latent position from a Gaussian distribution:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/dynamic_lsm_initial.png\" alt=\"lsm initial position prior\" width=\"200\"\u003e\n\u003c/p\u003e\n\n2. For `t = 2, ..., T`, a node's latent position follows a Gaussian random walk:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/dynamic_lsm_rw.png\" alt=\"lsm dynamic random walk\" width=\"200\"\u003e\n\n\n3. For each edge, sample a connection from a Bernoulli distribution:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/dynamic_lsm.png\" alt=\"dynamic lsm\" width=\"400\"\u003e\n\u003c/p\u003e\n\n\n### Latent Position Cluster Models\n\n\n#### Static Networks\nDetermining the high-level community structure of a network is another important task in network analysis. Community structure was incorporated into LSMs by Handcock et al. (2007)\u003csup\u003e[[4]](#References)\u003c/sup\u003e with their latent position cluster model (LPCM). Intuitively, the LPCM posits that communities are the result of clustering within the latent space. This clustering is incorporated in the LSM framework by assuming the latent positions are drawn from a Gaussian mixture model, i.e,\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/lpcm.png\" alt=\"latent position cluster model\" width=\"225\"\u003e\n\u003c/p\u003e\n\nThe LPCM relates the latent positions to the probability of forming an edge in the same way as the original LSM. In practice, one interprets nodes that share the same mixture component as belonging to the same community.\n\n#### Dynamic Networks\nInferring a network's community structure is especially difficult for dynamic networks because the number of communities may change over time. If one assumes that the number of communities is fixed, then the model of Sewell and Chen (2017)\u003csup\u003e[[5]](#References)\u003c/sup\u003e is able to infer a dynamic network's community structure by propagating each node's mixture assignment through time with a autoregressive hidden Markov model (AR-HMM). However, the assumption of a static number of communities is at odds with many real-world dynamic networks. It is often the case that the number of communities evolves over time.\n\nTo solve the problem of inferring evolving community structures in dynamic networks, Loyal and Chen (2020)\u003csup\u003e[[6]](#References)\u003c/sup\u003e proposed using a sticky hierarchical Dirichlet process hidden Markov model (HDP-HMM) with time-inhomogeneous transition probabilities in conjunction with the LPCM . For this reason, the model is called the hierarchical Dirichlet process latent position cluster model (HDP-LPCM). Under the HDP-LPCM, a node's latent community label propagate through time according to iid HDP-HMMs. Unlike previous models, this allows the HDP-LPCM to create and delete communities over-time as well as infer the number of the communities from the data. The generative model is as follows:\n\n1. Draw the time-varying transition probabilities from a sticky-HDP:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/hdp.png\" alt=\"sticky-hdp prior\" width=\"500\"\u003e\n\u003c/p\u003e\n\n2. For `t = 1, ..., T`, propagate a node's latent community label through time according to an HMM:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/dynamic_label.png\" alt=\"latent label hmm\" width=\"150\"\u003e\n\u003c/p\u003e\n\n3. For `t = 1`, sample a node's initial latent position from its assigned Gaussian mixture component:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/dynamic_lpcm_initial.png\" alt=\"hdp-lpcm initial positions\" width=\"225\"\u003e\n\u003c/p\u003e\n\n4. For `t = 2, ..., T`, sample a node's latent position as a mixture between its previous position and its assigned Gaussian mixture component:\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/dynamic_lpcm_rw.png\" alt=\"hdp-lpcm mixture random walk\" width=\"400\"\u003e\n\u003c/p\u003e\n\n5. For each edge, sample a connection from a Bernoulli distribution\n:\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/dynamic_lsm.png\" alt=\"hdp-lpcm\" width=\"400\"\u003e\n\u003c/p\u003e\n\n\nExample\n-------\nDynetLSM exposes two classes for working with latent space models for dynamic networks:\n\n* `DynamicNetworkLSM`:  Interface for learning the LSM in Sewell and Chen (2015)\u003csup\u003e[[3]](#References)\u003c/sup\u003e,\n* `DynamicNetworkHDPLPCM`: Interface for learning the HDP-LPCM in Loyal and Chen (2020)\u003csup\u003e[[6]](#References)\u003c/sup\u003e.\n\nTo understand the merits of both approaches, we provide an example using a synthetic dynamic network which contains two communities at `t = 1` and four communities at `t = 2`. We can generate the data as follows:\n```python\nfrom dynetlsm.datasets import simple_splitting_dynamic_network\n\n# Y : ndarray, shape (2, 50, 50)\n#   The adjacency matrices at each time point\n# labels : ndarray, shape  (2, 50)\n#   The true community labels of the nodes at each time point.\nY, labels = simple_splitting_dynamic_network(n_nodes=50, n_time_steps=2)\n```\n\nTo fit a dynamic LSM with a 2-dimensional latent space, we initialize the sampler and call `fit`:\n```python\nfrom dynetlsm import DynamicNetworkLSM\n\nlsm = DynamicNetworkLSM(n_iter=5000, burn=2500, tune=2500,\n                        n_features=2, random_state=42)\nlsm.fit(Y)\n```\n\nTo assess the convergence of the algorithm, we visualize the traces:\n```python\nfrom dynetlsm.plots import plot_traces\n\nplot_traces(lsm)\n```\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/lsm_traces.png\" alt=\"Traces of the LSM model\" width=\"400\"\u003e\n\u003c/p\u003e\n\nWe can then visualize the latent space embeddings:\n```python\nimport matplotlib.pyplot as plt\n\nfrom dynetlsm.plots import plot_latent_space\n\n\naxes = plt.subplots(ncols=2, nrows=1, figsize=(10, 6))\nfor t, ax in enumerate(axes.flat):\n    plot_latent_space(lsm, t=t, connectionstyle=None, number_nodes=False,\n                      linewidth=0.1, node_size=200, border=0.2, ax=ax)\n```\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/lsm_latent_space.png\" alt=\"Latent Space of the LSM model\" width=\"400\"\u003e\n\u003c/p\u003e\n\nAlthough the LSM's embedding places nodes that share many connections close together, the true community structure of the network is not apparent. This is easily remedied by applying the HDP-LPCM. As before, we initialize the model and call `fit`:\n```python\n\nfrom dynetlsm import DynamicNetworkHDPLPCM\n\nlpcm = DynamicNetworkHDPLPCM(n_iter=5000, burn=2500, tune=2500,\n                             n_features=2, n_components=10, random_state=42)\nlpcm.fit(Y)\n```\n\nOnce again, we assess the convergence of the algorithm by visualizing the traces:\n```python\nfrom dynetlsm.plots import plot_traces\n\nplot_traces(lpcm)\n```\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/hdp_lpcm_traces.png\" alt=\"Traces of the HDP-LPCM\" width=\"400\"\u003e\n\u003c/p\u003e\n\nWe can then visualize the latent space embeddings as well as the components of the inferred Gaussian mixture:\n```python\nimport matplotlib.pyplot as plt\n\nfrom dynetlsm.plots import plot_latent_space\n\n\nfig, axes = plt.subplots(ncols=2, nrows=1, figsize=(10, 6))\nfor t, ax in enumerate(axes.flat):\n    plot_latent_space(lpcm, t=t, connectionstyle=None,\n                      number_nodes=False, border=1.2, linewidth=0.2,\n                      center_size=100, node_size=100, ax=ax)\n```\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/hdp_lpcm_latent_space.png\" alt=\"Latent Space of the HDP-LPCM\" width=\"500\"\u003e\n\u003c/p\u003e\nThe HDP-LPCM infers an embedding that makes the community structure of the network apparent. Furthermore, the HDP-LPCM correctly infers that the two communities split off into four communities at the second time point. To better visualize this behavior, one can display an alluvial diagram of the label assignments over time:\n\n```python\nfrom dynetlsm.plots import alluvial_plot\n\nalluvial_plot(lpcm.z_)\n```\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"/images/alluvial_diagram.png\" alt=\"Alluvial Diagram of the HDP-LPCM\" width=\"300\"\u003e\n\u003c/p\u003e\n\nFrom this diagram, one can see that group 1 primarily splits off into group 3, while group 2 primarily splits off into group 4.\n\nSimulation Studies and Real-Data Applications\n---------------------------------------------\nThis package includes the simulation studies and real-data applications found in Loyal and Chen (2021):\n\n* A synthetic dynamic network with a time-homogeneous community structure (HDP-LPCM + LPCM): ([here](/examples/homogeneous_simulation.py)).\n* A synthetic dynamic network with a time-homogeneous community structure (SBM): ([here](/examples/homogeneous_sbm.R)).\n* A synthetic dynamic network with a time-homogeneous community structure (DynSBM): ([here](/examples/homogeneous_dynsbm.R)).\n* A synthetic dynamic network with a time-inhomogeneous community structure (HDP-LPCM): ([here](/examples/inhomogeneous_simulation.py)).\n* A synthetic dynamic network with a time-inhomogeneous community structure (SBM): ([here](/examples/inhomogeneous_sbm.R)).\n* Quantify the HDP-LPCM's sensitivity to group seperation and stability: ([here](/examples/detection_limit.py)).\n* A synthetic dynamic network with a slowly merging group structure: ([here](/examples/merging_communities.py))\n* Sampson's monastery network: ([here](/examples/sampson_monks.py)).\n* A dynamic network constructed from international military alliances during the first three decades of the Cold War (1950 - 1979): ([here](/examples/military_alliances.py)).\n* A dynamic network constructed from character interactions in the first four seasons of the Game of Thrones television series: ([here](/examples/GoT.py)).\n\nWe also provide a few [jupyter notebooks](/notebooks) that demonstrate the use of this package.\n\nReferences\n----------\n\n[1]: Hoff, P. D., Raftery, A. E., and Handcock, M. S. (2002). Latent space approaches to social network analysis. *Journal of the American Statistical Association*, 97(460):1090-1098.\n\n[2]: Sarkar, P. and Moore, A. W. (2006). Dynamic social network analysis using latent space models. pages 1145-1152.\n\n[3]: Sewell, D. K. and Chen, Y. (2015). Latent space models for dynamic networks. *Journal of the American Statistical Association*, 110(512):1646-1657.\n\n[4]: Handcock, M. S., Raftery, A. E., and Tantrum, J. M. (2007). Model-based clustering of social networks. *Journal of the Royal Statistical Society A*, 170(2):301-354.\n\n[5]: Sewell, D. K. and Chen, Y. (2017). Latent space approaches to community detection in dynamic networks. *Bayesian Analysis*, 12(2):351-377.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshloyal%2Fdynetlsm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoshloyal%2Fdynetlsm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshloyal%2Fdynetlsm/lists"}