Social Network Measures using Matrices
Overview
Provides measures to describe and manipulate one-mode, two-mode, multiplex, and multilevel networks using matrix algebra. Implements functions for network centrality, cohesive subgroups, communities, structural holes, roles and positions, similarity measures, path distances, signed networks, segregation, social influence, and random network generation. Supports ego-centric and whole-network analyses, including dyadic and triadic censuses, structural balance, bipartite projections, measures with overlapping group memberships, Q-analysis, neighbourhood-inclusion dominance, main path analysis of citation networks, and permutation tests for networks. Key references: Bonacich (1972) doi:10.1080/0022250X.1972.9989806, Breiger (1974) doi:10.2307/2576011, Kivela et al. (2014) doi:10.1093/comnet/cnu016, Espinosa-Rada et al. (2024) doi:10.1016/j.socnet.2023.11.008, Schoch and Brandes (2016) doi:10.1017/S0956792516000401, Traag et al. (2019) doi:10.1038/s41598-019-41695-z, Everett and Borgatti (2026) doi:10.1016/j.socnet.2025.12.001.
Changes that affect results
These fixes change the numbers that some functions returned. Code written for
version 1.0-3 runs without errors, but gives different results:
count_geodesics()counted only some of the geodesics between two nodes. A node that had already been reached did not add the geodesics of the other nodes at the same distance, so the counts were too low. The counts now agree withsna::geodist().wall_distances()returned a single path for each node instead of one path for each pair of nodes. It now returnsfromTo[[i]][[j]]andtoFrom[[i]][[j]]for every pair.wlocal_distances()andwall_distances()now use Dijkstra's algorithm, as the documentation said. Before they enumerated every simple path, which did not finish on networks of a dozen nodes. Between paths of the same length, the path returned might differ from the one returned before.
Show all changes in 1.1-0Hide the rest of the changes in 1.1-0
triad_uman(): the covariance between the counts of 201 and 102 was positive, and should have been negative. The term of the two triads that share a dyad usedm + n + 4where the combinatorics givem + n - 4. The covariance matrix now adds up to zero, as it must because the number of triads is fixed.triad_uman()no longer returns the elementz_testby default. It tested the sum of the sixteen counts, which is alwayschoose(g, 3), so it wasNaNfor every network. The new argumentltests a linear combination of the census (Wasserman and Faust, 1994: 583); withztest = TRUEand nol, the function returns the data frame with the columnsZandP. The covariance matrix is now returned symmetric instead of triangular.eb_constraint()failed for egos with a single alter, and for egos whose alters are not tied to each other. It now returns the constraint in both cases, and gives an informative error for isolates.components_id()did not return the components of a directed network. It grouped the nodes that reach the same nodes, so a chain of citations gave as many components as nodes. It now returns the weak components, which for undirected networks are the ones it returned before, and takes the argumentsmode = "strong"andbipartite.edgelist_to_matrix()placed some ties in the wrong direction, depending on the order in which the nodes appeared in the edge list, and dropped the ties of a node with itself. The names are now matched one by one, andloops = TRUEkeeps the diagonal. It also returned a vector instead of a matrix when the result had a single row or column.mix_matrix()counted only the ties of the upper triangle of the matrix, so for a directed network with 15 arcs it returned a table adding up to 5, and it dropped the row or the column of a group that never sent or received a tie, which made the table lose its shape. The mixing matrix is now built from the ties between every pair of groups.ei_index()ignored the attribute unlessmixed = FALSEwas given, and with its default arguments it returned one for any network without loops. The attribute now takes precedence, and the index agrees withnetseg::ei()for directed and undirected networks.trans_matrix()failed with "the condition has length > 1" whenever a node belonged to more than one transitive triple, which is the usual case in an undirected network with a triangle. It now marks the nodes of every transitive triple.triad_uman()failed on a network without ties, because the matrix was stored in a class that cannot be modified.multilevel_degree()returned wrong values for the nodes of the third level whenB2was given withoutB3: the rowsk1, k2, ...of the columnmultilevelheld the degrees of the nodes of the second level. They now count the ties of each node within the third level and with the second level, which is the value of the same nodes inhigh_multilevel, and the documentation describes what every column counts.gen_degree()withweighted = TRUEremoved the loops from the degree but not from the strength whenloops = FALSE, and symmetrized the ties but not the weights whendigraph = FALSE. The weights now follow the ties in both cases.gen_density()removed the diagonal of two-mode matrices, which holds real ties, so with the defaultloops = FALSEthe density of an incidence matrix was too low (its own example gave 0.33 instead of 0.5). For a directed matrix in a list (multilayer = TRUE) it used only the lower triangle, and withdirected = FALSEit kept the ties of the upper triangle instead of the underlying graph. Withloops = TRUEthe diagonal is now counted among the possible ties. The results agree withigraph::edge_density().eb_constraint()chose the maximum constraint by the size of the ego network including ego, so an ego with seven alters got the maximum of the shadow ego network (0.486) instead of the complete one (0.493, Everett and Borgatti, 2020: Table 1), and a normalization that was too high. The maximum is now the larger of the two. An ego with a single alter has a normalization of one, as in Everett and Borgatti (2020: Eq. 5), instead ofNaN.q_analysis()reported only the dimensions q that some simplex had exactly, so the levels at which components merge were missing: two 4-simplices that share a face of dimension 2 were never reported as 2-connected. It now reports every q from the largest dimension down to 0. A network was turned into a complex of its triangles and all its edges, so a clique of four nodes became four triangles; the simplices are now the maximal cliques. A square incidence matrix, which stopped the function, is now accepted. The function returns a list with the simplices, the table of the structure vectors, the components at each q and the eccentricities; the argumentdimensionsis kept but no longer needed. The values agree with Freeman (1980) and with the Python package of Smirnov et al. (2025).simplicial_complexes()built the complex of a network from its triangles and all its edges, so a clique of four nodes became four triangles and six edges, and it stopped with an error on networks without triangles. The simplices are now the maximal cliques, named after their nodes (a-b-c) instead of numbered.zero_simplex = TRUE, now the default, adds the isolated nodes as simplices of dimension 0; before it added columns that did not correspond to the nodes. The rows are still the nodes and the columns the simplices.k_core()of a binary network counted the loops even whenloops = FALSE, which is the default. The loops are now counted only withloops = TRUE. Without loops the values are the same as before.zone_sample()returns adjacency matrices instead of igraph objects. Withcore = TRUE, the indicator of the actors at distance one is the attributecoreof each matrix. The nodes and the ties are the same as before.- An audit of every exported function found further errors, recorded in
dev/audit/audit.mdwith how each was verified: multiplex_census()added counts of the two networks instead of counting the triples of each joint configuration (two empty networks gave 5 of 10 triples). It now returns the classes of Figure 12 of Espinosa-Rada (2021), named by the type of the directed triad and the position of the undirected edges (e.g.021U_102ac);merge = "overlap"merges the classes that give the same overlapped triad.mixed_census(quad = TRUE): the class "201" multiplied two counts instead of adding them.kp_reciprocity()used the number of arcs minus twice the mutual dyads instead of the number of arcs for free choices.posneg_index():select = "all"returned the out-index and"out"the all-index. They now agree withsignnet::pn_index().k_core()withweighted = TRUEormultilevel = TRUEreturned the round in which a node was removed instead of its core value (a path gave 1 0 1).- With
digraph = FALSE,gen_degree(),eb_constraint(),redundancy(),clique_table(),dyad_triad_table(),struc_balance()and the signedeigenvector_centrality()copied the upper triangle of the matrix over the lower one, so the ties present only in the lower triangle were lost and the results depended on the order of the nodes. They now use the underlying graph. edgelist_to_matrix(digraph = FALSE)dropped the edges listed as (b, a);matrix_to_edgelist(valued = TRUE)dropped ties with non-integer values and listed undirected ties twice;matrix_adjlist()dropped ties below 1.adj_to_matrix(type = "adjacency")returned one row per line of the list instead of one per node.meta_matrix()placed the ties between the second and third levels only above the diagonal, and dropped them whenB3was given.extract_component()returned wrong matrices when several components had the same size;positionnow counts the distinct sizes.dist_sim_matrix()computed the Hamming distance of two-mode matrices over the wrong nodes, failed with rows without ties, and now keeps the names of the nodes.fractional_approach()did not compute the networks of Batagelj (2020): its "citation" network was a product of the two incidence matrices, and the fractional co-citation was normalised on one side only. It now takes the citation network between works (A1) and, for the citations between authors, the authorship matrix (A2), with full or fractional counting (fractional); the fractional bibliographic coupling can be made symmetric with the six measures of Batagelj (2020) (symmetric). Calls with two incidence matrices, as in version 1.0-3, now stop.dyad_triad_table()called "Triad201" every pair of neighbours of a node, closed or not, andminandmaxlimited how often a triad was repeated (which selected the closed triads). It now returns thetypeof each triad (201 for the forbidden triad of Granovetter, 300 when closed, 102 for a dyad and 003 for an isolated node) and itsmembers, andminandmaxlimit the number of forbidden triads of which a node is the centre.
Other bug fixes
structural_na()warned whenever the labels had more nodes than the matrix, which is its purpose. It now warns only when a node of the matrix is not in the labels and is dropped.eb_constraint(),redundancy()andego_net()withoutegofailed with "argument is of length zero"; they now ask for the name of ego.trans_coef(method = "mean")always failed withcould not find function "local_trans".short_path()failed when there was no path between the two nodes. It now warns and returnsNULL.percolation_clique()failed when every node belonged to a clique.power_function()reached the limit of nested expressions for large powers, which made its own example fail. It now uses a loop instead of recursion.matrix_to_edgelist()failed on a network without ties. It now returns an empty edge list.percolation_clique()failed when the network had a single clique, and when some nodes did not belong to any clique.redundancy()failed with an obscure message for an isolated ego, andclique_table()stopped without a message when there were no cliques.- Several functions failed or never ended on common inputs:
k_core()looped forever on a matrix withNA, andind_rand_matrix(type = "edges")for undirected networks wheneverlwas larger than the number of nodes.bfs_ugraph(),count_geodesics(),gen_degree(),short_path(),wall_distances(),wlocal_distances(),matrix_to_edgelist(),multiplex_census()andego_net()now treatNAas an absent tie;adj_to_incidence()andedgelist_to_matrix()accept networks without ties;minmax_overlap()accepts a single row;co_occurrence(occurrence = FALSE)no longer fails;ego_net()returns a matrix for an ego with one alter, andredundancy()no longer calls that ego an isolate. wall_distances()andwlocal_distances()accept binary matrices, in which every tie has length one.
Extensions of the existing functions
ind_rand_matrix()takessparse, which draws the ties among the cells that the model allows and returns a sparse matrix of theMatrixpackage, without building the dense one. A network of 10,000 nodes takes 0.2 MB instead of 800 MB. It is available for one-mode and two-mode networks withtrials = 1.edgelist_to_matrix()orders the nodes as inlabel(andlabel2for two-mode networks), with the nodes that are not in the labels after them in alphabetical order. Before, the nodes were always in alphabetical order, so a matrix could not be recovered from its edge list in its own order.components_id()takesmode(weak or strong) andbipartite, which covers the nodes of both modes (issue #4).eigenvector_centrality()takessigned, for networks with negative ties (Bonacich and Lloyd, 2004) (issue #9).trans_coef()takesmethod = "barrat", the weighted transitivity of Barrat et al. (2004) (issue #5).gen_density()computes the density of weighted networks, which is the average strength of the possible ties (issue #12).edgelist_to_matrix()takesvalued, which reads the value of the ties from a third column, andloops(issue #2).dist_sim_matrix()takes a list of matrices, and compares the nodes across all the relations at once (issue #7).q_analysis()returns the second and third structure vectors (Raj et al., 2024), the obstruction vector and the eccentricity of each simplex, with the definition of Atkin (1974) or of Johnson (eccentricity). A network can be analysed through its clique complex or its neighbourhood complex (complex), with open or closed neighbourhoods (closed). It is also about a hundred times faster.simplicial_complexes()takescomplex = "neighbourhood"andclosed, asq_analysis(), which now builds its complex with it, andvaluedfor projections that count the shared nodes and simplices.eb_constraint()takesdigraph = TRUEandweighted = TRUE, which used to stop. A directed network has the constraint of the valued networkA + t(A), and the maximum used in the normalization is that of Everett and Borgatti (2020: Eq. 6 to 9). The results agree with their Tables 1 and 2 and withigraph::constraint()on the ego network.edgelist_to_matrix()takesrulefor undirected networks: a tie listed in either order (weak, default) or only in both orders (strong), assna::symmetrize().gen_degree()andgen_density()no longer warn that a symmetric matrix is undirected whendigraph = TRUE(ordirected = TRUE), as the result is the same.
New functions
supra_adjacency()arranges the layers of a multiplex network in a single matrix of actor-layer pairs, with categorical, ordinal or no coupling between the layers (De Domenico et al., 2013; Kivela et al., 2014), andaggregate_layers()reduces the layers to a single matrix by sum, binary or mean (Battiston et al., 2014).
Centrality:
closeness_centrality(),betweenness_centrality()(Brandes' algorithm),eigenvector_centrality(),katz_centrality(),bonacich_power(),page_rank_centrality()andcentrality_centralization().geo_distances()andgeo_summary()for the distances, the diameter and the average distance.
Positions and dominance:
neigh_inclusion(),set_inclusion(),pareto_dominance(),dominance_pairs(),preserved_order()anddominance_layers().hyperevent_dominance()for the dominance of authors through the chain author, citing paper, cited paper, author (Espinosa-Rada, 2026). With the data of the article, it gives the same dominance matrices as the analysis scripts, the maximal and dominant authors of Section 6 and the values of Table 1. The argumentsstrictandclosure_papersgive the alternatives of the text of Section 3.5 where it differs from the analysis scripts.dir_inclusion()with the nine directed neighbourhood-inclusion criteria of Marmulla and Brandes (2026).- `pos_dominanc
The NEWS section is longer than shown here.
From NEWS.md in the release
Install
Health
- OK13 OK2026-08-05
- NOTE12 OK, 1 NOTE2026-08-01
- OK13 OK2026-06-09
- WARNING12 OK, 1 WARNING2026-06-08
- OK3 OK2026-04-17
Documentation
- Help pages
- 112
- Examples that run
- 99%105 run in a plain check · 1 only when donttest examples are enabled (R CMD check --as-cran)
- Exported functions with examples
- 100%
- Documented parameters
- 99%
- Return-value docs
- 100%
- Help pages citing references
- 82%
Downloads
Repository
- PRs merged (all time)
- 95%
- Median days to close, 50 most recently updated closed issues
- 1,589
- Median days to close, 50 most recently updated closed PRs
- 0
Stars over time
Releases over time
Issues over time
PRs over time
Commits over time
14 development-tooling and community-health practices detected across 7 families in the upstream repository
Checks run against github.com/anespinosa/netmem on 2026-10-21.
The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel
GitHub Pages site last deployed 2026-09-24 · The repository has a site deploy workflow
pkgdown site last built 2026-09-24
Show all practicesHide 3 practices
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2026-04-01Commits: at least 2 crediting it in the message
- Claude Sonnet 4.62 commits · Apr 2026
Dependencies
Installing netmem adds no packages beyond those that come with R.
Code & Tests
Datasets
People & History
Pick two releases to compare their code metrics.
- 1.1-0Latest2026-09-24 · current release · diff ↗ ·
added 54 exports
aggregate_layers, alter_composition, alter_heterogeneity, alter_homophily, betweenness_centrality, block_density, bonacich_power, brokerage_roles, centrality_centralization, citation_decay, clique_max, closeness_centrality, community_betweenness, community_greedy, community_label, concor, core_periphery, cug_test, dag_check, dag_sort, dir_inclusion, dominance_layers, dominance_pairs, dominance_ranks, eigenvector_centrality, geo_distances, geo_summary, hyperevent_dominance, indirect_rel, katz_centrality and 24 more - RR 4.6.0 released · 2026-04-24
- 1.0-32026-04-16
- RR 4.5.0 released · 2025-04-11
Package metadata
- First published
- 2026-04-16
- Total releases
- 2 / 1 yrs
- License
- GPL-3 OSI
- Minimum R
- ≥ 4.0.0
- Bundled data
- 5.1 KB / 6 files
- Download size
- 551 KB
Cite
Cite this package
Run in R for the authors' preferred citation:
citation("netmem")This package has no citation file, so citation() returns this entry built from DESCRIPTION.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-24, which the citation names so these numbers can be found later. More on citing and the projects behind them.