Nothing
# ---- Hypergraph -> pairwise network projection (HON-7) -------------------
# Standard clique expansion: for each hyperedge, every pair of members
# contributes a unit (or weighted) tally to the resulting pairwise network.
# Closes the I/O cycle with build_hypergraph() / bipartite_groups().
#' Clique expansion of a hypergraph
#'
#' Projects a [net_hypergraph][build_hypergraph] to a standard pairwise
#' [netobject][build_network] (the *clique expansion* - also called the
#' "downgrade" of a hypergraph to a dyadic graph). Each hyperedge of size
#' k contributes 1 (or its weight) to every pair of its members. The
#' resulting edge weight `W[i, j]` equals the number of hyperedges
#' containing both `i` and `j` (binary incidence) or the sum of incidence
#' products (weighted incidence).
#'
#' @param hg A `net_hypergraph` object as returned by [build_hypergraph()]
#' or [bipartite_groups()].
#' @param weighted Logical. If `TRUE` (default), use the hypergraph's
#' incidence values directly (so weighted hypergraphs from
#' [bipartite_groups()] produce weighted projections). If `FALSE`,
#' binarise the incidence first so `W[i, j]` is just the count of
#' shared hyperedges.
#'
#' @return A `netobject` (also `cograph_network`) with `method =
#' "clique_expansion"`, undirected, with weighted symmetric adjacency
#' `W = incidence %*% t(incidence)` and zero diagonal.
#'
#' @details
#' The clique expansion is the standard "loss-y but lossless-on-pairwise"
#' projection: it preserves *which pairs co-occurred* and *how often* but
#' discards the higher-order grouping. Comparing `clique_expansion(hg)` to
#' a directly-estimated pairwise network (e.g. via [cooccurrence()] on
#' the same data) quantifies how much information was carried by the
#' hyperedge structure.
#'
#' Computed in one BLAS call via `tcrossprod(incidence)`; runs in
#' `O(n_nodes^2 * n_hyperedges)` time, fast for typical sizes.
#'
#' Closes the I/O cycle: event data -> [bipartite_groups()] ->
#' `clique_expansion()` -> any function that accepts a `netobject`
#' (centrality, bootstrap, clustering, plotting via cograph).
#'
#' @seealso [build_hypergraph()], [bipartite_groups()], [build_network()].
#'
#' @examples
#' df <- data.frame(
#' player = c("A", "B", "C", "A", "B", "D", "C", "D", "E"),
#' session = c("S1", "S1", "S1", "S2", "S2", "S3", "S3", "S3", "S3")
#' )
#' hg <- bipartite_groups(df, player = "player", group = "session")
#' net <- clique_expansion(hg)
#' net$weights
#'
#' @references
#' Tian, Y., & Zafarani, R. (2024). Higher-order network analysis methods.
#' \emph{SIGKDD Explorations} 26(1), Section 5.1.5.
#'
#' @note (experimental) Validated against `tcrossprod(incidence)` with zero
#' diagonal. No external R package exposes clique expansion as a primitive;
#' the implementation is a direct one-line restatement of the definition.
#'
#' @export
clique_expansion <- function(hg, weighted = TRUE) {
stopifnot(
inherits(hg, "net_hypergraph"),
is.logical(weighted), length(weighted) == 1L
)
if (hg$n_hyperedges == 0L) {
weights <- matrix(0, hg$n_nodes, hg$n_nodes,
dimnames = list(hg$nodes, hg$nodes))
} else {
inc <- hg$incidence
if (!weighted) {
inc <- (inc > 0) * 1L
}
storage.mode(inc) <- "double"
weights <- tcrossprod(inc)
diag(weights) <- 0
rownames(weights) <- colnames(weights) <- hg$nodes
}
net <- .wrap_netobject(weights,
method = "clique_expansion",
directed = FALSE)
net$params <- list(
source = "clique_expansion",
weighted = weighted,
n_hyperedges = hg$n_hyperedges,
hypergraph_size_distribution = hg$size_distribution
)
net
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.