diff --git a/NAMESPACE b/NAMESPACE index 28bc96ae600..27b3736f0c8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -585,6 +585,7 @@ export(k_shortest_paths) export(kautz_graph) export(keeping_degseq) export(knn) +export(knnk) export(label.propagation.community) export(laplacian_matrix) export(largest.cliques) diff --git a/R/structural-properties.R b/R/structural-properties.R index 9869cdafc74..b89a116afc2 100644 --- a/R/structural-properties.R +++ b/R/structural-properties.R @@ -3605,3 +3605,104 @@ knn <- function( weights = weights ) } + +#' Degree correlation function +#' +#' `r lifecycle::badge("experimental")` +#' +#' Computes the \eqn{k_{nn}(k)} degree correlation function, which gives the mean degree +#' of neighbors of vertices with degree \eqn{k}. +#' +#' The \eqn{k_{nn}(k)} function characterizes degree correlations in networks. +#' It provides the average degree of neighbors as a function of vertex degree. +#' This is one of the primary ways to measure degree assortativity in networks. +#' +#' For directed graphs, this function provides fine-grained control over how +#' in/out degrees are used through the `from.mode` and `to.mode` parameters. +#' +#' Note that for degrees that do not appear in the network, the result is `NaN` +#' (zero divided by zero). +#' +#' The weighted version computes a weighted average as: +#' +#' \deqn{k_{nn}(k) = \frac{\sum_{i: k_i=k} \sum_j w_{ij} k_j}{\sum_{i: k_i=k} \sum_j w_{ij}}}{k_nn(k) = sum_(i: k_i=k) sum_j w_ij k_j / sum_(i: k_i=k) sum_j w_ij} +#' +#' where the first sum runs over vertices of degree \eqn{k}, the second sum runs +#' over their neighbors \eqn{j}, \eqn{w_{ij}} is the edge weight, and \eqn{k_j} is the neighbor's degree. +#' +#' @param graph The input graph. It may be directed. +#' @param weights Optional edge weights. If the graph has a `weight` edge +#' attribute, then this is used by default. If this argument is given, +#' then edge weights are used in the calculation. Set to `NA` to ignore +#' the `weight` edge attribute even if present. +#' @param from.mode How to compute the degree of source vertices in directed graphs? +#' `out` uses out-degree, `in` uses in-degree, and `all` or `total` uses +#' total degree. Ignored for undirected graphs. +#' @param to.mode How to compute the degree of target vertices (neighbors) in +#' directed graphs? `out` uses out-degree, `in` uses in-degree, and `all` +#' or `total` uses total degree. Ignored for undirected graphs. +#' @param directed.neighbors Logical scalar. Whether to consider edges as directed +#' when computing neighbor relationships in directed graphs. If `FALSE`, +#' edges are treated as undirected (i.e., reciprocal). Ignored for undirected graphs. +#' @return A numeric vector. +#' Element \eqn{i} contains the mean degree of neighbors of vertices with degree \eqn{i-1}. +#' Note that degree 0 is included at index 1. +#' The length of the vector is one more than the maximum degree in the graph. +#' @author Gabor Csardi \email{csardi.gabor@@gmail.com} +#' @references +#' R. Pastor-Satorras, A. Vazquez, A. Vespignani: +#' Dynamical and Correlation Properties of the Internet, +#' Phys. Rev. Lett., vol. 87, pp. 258701 (2001). +#' \doi{10.1103/PhysRevLett.87.258701} +#' +#' A. Vazquez, R. Pastor-Satorras, A. Vespignani: +#' Large-scale topological and dynamical properties of the Internet, +#' Phys. Rev. E, vol. 65, pp. 066130 (2002). +#' \doi{10.1103/PhysRevE.65.066130} +#' +#' A. Barrat, M. Barthelemy, R. Pastor-Satorras, and A. Vespignani: +#' The architecture of complex weighted networks, +#' Proc. Natl. Acad. Sci. USA 101, 3747 (2004). +#' \doi{10.1073/pnas.0400087101} +#' +#' A.-L. Barabási, Network Science (2016). Chapter 7, Degree Correlations. +#' \url{https://networksciencebook.com/chapter/7#measuring-degree} +#' @seealso [knn()] for computing average nearest neighbor degree for specific vertices +#' @keywords graphs +#' @examples +#' # Ring graph - all vertices have degree 2 +#' g <- make_ring(10) +#' knnk(g) +#' +#' # Star graph +#' g2 <- make_star(10) +#' knnk(g2) +#' +#' # Scale-free graph - typically shows degree anti-correlation +#' g3 <- sample_pa(1000, m = 5) +#' result <- knnk(g3) +#' plot(result, xlab = "k", ylab = expression(k[nn](k)), type = "l") +#' +#' # Directed graph with different degree modes +#' g4 <- sample_pa(100, directed = TRUE) +#' knnk(g4, from.mode = "out", to.mode = "in") +#' @family structural.properties +#' @export +#' @cdocs igraph_degree_correlation_vector +knnk <- function( + graph, + weights = NULL, + from.mode = c("out", "in", "all", "total"), + to.mode = c("in", "out", "all", "total"), + directed.neighbors = TRUE +) { + from.mode <- match.arg(from.mode) + to.mode <- match.arg(to.mode) + degree_correlation_vector_impl( + graph = graph, + weights = weights, + from.mode = from.mode, + to.mode = to.mode, + directed.neighbors = directed.neighbors + ) +} diff --git a/man/bfs.Rd b/man/bfs.Rd index d72f3f76036..139f3816dbc 100644 --- a/man/bfs.Rd +++ b/man/bfs.Rd @@ -185,6 +185,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/components.Rd b/man/components.Rd index ac7bffb001b..de0dd254f35 100644 --- a/man/components.Rd +++ b/man/components.Rd @@ -117,6 +117,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/constraint.Rd b/man/constraint.Rd index ae337644aa5..d1fa8782b47 100644 --- a/man/constraint.Rd +++ b/man/constraint.Rd @@ -69,6 +69,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/coreness.Rd b/man/coreness.Rd index c77603f3369..65cfde94bc4 100644 --- a/man/coreness.Rd +++ b/man/coreness.Rd @@ -64,6 +64,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/degree.Rd b/man/degree.Rd index 098cf73fea2..1b559f0d12e 100644 --- a/man/degree.Rd +++ b/man/degree.Rd @@ -87,6 +87,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/dfs.Rd b/man/dfs.Rd index 643c7031f03..010190b0719 100644 --- a/man/dfs.Rd +++ b/man/dfs.Rd @@ -176,6 +176,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/distances.Rd b/man/distances.Rd index 96ffa1c2860..f6aa4e3345d 100644 --- a/man/distances.Rd +++ b/man/distances.Rd @@ -306,6 +306,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/edge_density.Rd b/man/edge_density.Rd index 99b4db0eb71..86afee30e47 100644 --- a/man/edge_density.Rd +++ b/man/edge_density.Rd @@ -66,6 +66,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/ego.Rd b/man/ego.Rd index 69c018240fe..381949204a4 100644 --- a/man/ego.Rd +++ b/man/ego.Rd @@ -192,6 +192,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/feedback_arc_set.Rd b/man/feedback_arc_set.Rd index 59a41641f6a..0e87998831e 100644 --- a/man/feedback_arc_set.Rd +++ b/man/feedback_arc_set.Rd @@ -67,6 +67,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/feedback_vertex_set.Rd b/man/feedback_vertex_set.Rd index 06f635f7ee5..3571c0836fc 100644 --- a/man/feedback_vertex_set.Rd +++ b/man/feedback_vertex_set.Rd @@ -53,6 +53,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/girth.Rd b/man/girth.Rd index c95e85f018b..7df5ad73ae9 100644 --- a/man/girth.Rd +++ b/man/girth.Rd @@ -74,6 +74,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/is_acyclic.Rd b/man/is_acyclic.Rd index 21a8b7dc86d..47978332496 100644 --- a/man/is_acyclic.Rd +++ b/man/is_acyclic.Rd @@ -55,6 +55,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/is_dag.Rd b/man/is_dag.Rd index 0c651de7b3e..5bccf741832 100644 --- a/man/is_dag.Rd +++ b/man/is_dag.Rd @@ -55,6 +55,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/k_shortest_paths.Rd b/man/k_shortest_paths.Rd index c136dc00977..c8a5c478372 100644 --- a/man/k_shortest_paths.Rd +++ b/man/k_shortest_paths.Rd @@ -82,6 +82,7 @@ Other structural.properties: \code{\link{is_dag}()}, \code{\link{is_matching}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/knn.Rd b/man/knn.Rd index 13c35422f0c..ed2449f8a30 100644 --- a/man/knn.Rd +++ b/man/knn.Rd @@ -111,6 +111,7 @@ Other structural.properties: \code{\link{is_dag}()}, \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/knnk.Rd b/man/knnk.Rd new file mode 100644 index 00000000000..7d24e05dac7 --- /dev/null +++ b/man/knnk.Rd @@ -0,0 +1,138 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/structural-properties.R +\name{knnk} +\alias{knnk} +\title{Degree correlation function} +\usage{ +knnk( + graph, + weights = NULL, + from.mode = c("out", "in", "all", "total"), + to.mode = c("in", "out", "all", "total"), + directed.neighbors = TRUE +) +} +\arguments{ +\item{graph}{The input graph. It may be directed.} + +\item{weights}{Optional edge weights. If the graph has a \code{weight} edge +attribute, then this is used by default. If this argument is given, +then edge weights are used in the calculation. Set to \code{NA} to ignore +the \code{weight} edge attribute even if present.} + +\item{from.mode}{How to compute the degree of source vertices in directed graphs? +\code{out} uses out-degree, \verb{in} uses in-degree, and \code{all} or \code{total} uses +total degree. Ignored for undirected graphs.} + +\item{to.mode}{How to compute the degree of target vertices (neighbors) in +directed graphs? \code{out} uses out-degree, \verb{in} uses in-degree, and \code{all} +or \code{total} uses total degree. Ignored for undirected graphs.} + +\item{directed.neighbors}{Logical scalar. Whether to consider edges as directed +when computing neighbor relationships in directed graphs. If \code{FALSE}, +edges are treated as undirected (i.e., reciprocal). Ignored for undirected graphs.} +} +\value{ +A numeric vector. +Element \eqn{i} contains the mean degree of neighbors of vertices with degree \eqn{i-1}. +Note that degree 0 is included at index 1. +The length of the vector is one more than the maximum degree in the graph. +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} +} +\details{ +Computes the \eqn{k_{nn}(k)} degree correlation function, which gives the mean degree +of neighbors of vertices with degree \eqn{k}. + +The \eqn{k_{nn}(k)} function characterizes degree correlations in networks. +It provides the average degree of neighbors as a function of vertex degree. +This is one of the primary ways to measure degree assortativity in networks. + +For directed graphs, this function provides fine-grained control over how +in/out degrees are used through the \code{from.mode} and \code{to.mode} parameters. + +Note that for degrees that do not appear in the network, the result is \code{NaN} +(zero divided by zero). + +The weighted version computes a weighted average as: + +\deqn{k_{nn}(k) = \frac{\sum_{i: k_i=k} \sum_j w_{ij} k_j}{\sum_{i: k_i=k} \sum_j w_{ij}}}{k_nn(k) = sum_(i: k_i=k) sum_j w_ij k_j / sum_(i: k_i=k) sum_j w_ij} + +where the first sum runs over vertices of degree \eqn{k}, the second sum runs +over their neighbors \eqn{j}, \eqn{w_{ij}} is the edge weight, and \eqn{k_j} is the neighbor's degree. +} +\examples{ +# Ring graph - all vertices have degree 2 +g <- make_ring(10) +knnk(g) + +# Star graph +g2 <- make_star(10) +knnk(g2) + +# Scale-free graph - typically shows degree anti-correlation +g3 <- sample_pa(1000, m = 5) +result <- knnk(g3) +plot(result, xlab = "k", ylab = expression(k[nn](k)), type = "l") + +# Directed graph with different degree modes +g4 <- sample_pa(100, directed = TRUE) +knnk(g4, from.mode = "out", to.mode = "in") +} +\references{ +R. Pastor-Satorras, A. Vazquez, A. Vespignani: +Dynamical and Correlation Properties of the Internet, +Phys. Rev. Lett., vol. 87, pp. 258701 (2001). +\doi{10.1103/PhysRevLett.87.258701} + +A. Vazquez, R. Pastor-Satorras, A. Vespignani: +Large-scale topological and dynamical properties of the Internet, +Phys. Rev. E, vol. 65, pp. 066130 (2002). +\doi{10.1103/PhysRevE.65.066130} + +A. Barrat, M. Barthelemy, R. Pastor-Satorras, and A. Vespignani: +The architecture of complex weighted networks, +Proc. Natl. Acad. Sci. USA 101, 3747 (2004). +\doi{10.1073/pnas.0400087101} + +A.-L. Barabási, Network Science (2016). Chapter 7, Degree Correlations. +\url{https://networksciencebook.com/chapter/7#measuring-degree} +} +\seealso{ +\code{\link[=knn]{knn()}} for computing average nearest neighbor degree for specific vertices + +Other structural.properties: +\code{\link{bfs}()}, +\code{\link{component_distribution}()}, +\code{\link{connect}()}, +\code{\link{constraint}()}, +\code{\link{coreness}()}, +\code{\link{degree}()}, +\code{\link{dfs}()}, +\code{\link{distance_table}()}, +\code{\link{edge_density}()}, +\code{\link{feedback_arc_set}()}, +\code{\link{feedback_vertex_set}()}, +\code{\link{girth}()}, +\code{\link{is_acyclic}()}, +\code{\link{is_dag}()}, +\code{\link{is_matching}()}, +\code{\link{k_shortest_paths}()}, +\code{\link{knn}()}, +\code{\link{reciprocity}()}, +\code{\link{subcomponent}()}, +\code{\link{subgraph}()}, +\code{\link{topo_sort}()}, +\code{\link{transitivity}()}, +\code{\link{unfold_tree}()}, +\code{\link{which_multiple}()}, +\code{\link{which_mutual}()} +} +\author{ +Gabor Csardi \email{csardi.gabor@gmail.com} +} +\concept{structural.properties} +\keyword{graphs} +\section{Related documentation in the C library}{\href{https://igraph.org/c/html/latest/igraph-Structural.html#igraph_degree_correlation_vector}{\code{degree_correlation_vector()}}.} + diff --git a/man/matching.Rd b/man/matching.Rd index aef4e2ac57a..c22cab1bfb8 100644 --- a/man/matching.Rd +++ b/man/matching.Rd @@ -134,6 +134,7 @@ Other structural.properties: \code{\link{is_dag}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/reciprocity.Rd b/man/reciprocity.Rd index 72b71313216..e8db6a56191 100644 --- a/man/reciprocity.Rd +++ b/man/reciprocity.Rd @@ -60,6 +60,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, \code{\link{topo_sort}()}, diff --git a/man/subcomponent.Rd b/man/subcomponent.Rd index 128a85e74b6..6a67a11d6f7 100644 --- a/man/subcomponent.Rd +++ b/man/subcomponent.Rd @@ -56,6 +56,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subgraph}()}, \code{\link{topo_sort}()}, diff --git a/man/subgraph.Rd b/man/subgraph.Rd index 36705561d56..335207c708c 100644 --- a/man/subgraph.Rd +++ b/man/subgraph.Rd @@ -83,6 +83,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{topo_sort}()}, diff --git a/man/topo_sort.Rd b/man/topo_sort.Rd index 4d6cfb66c5a..296f76cbe52 100644 --- a/man/topo_sort.Rd +++ b/man/topo_sort.Rd @@ -55,6 +55,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/transitivity.Rd b/man/transitivity.Rd index 2c6800eda4b..4257fe53e9f 100644 --- a/man/transitivity.Rd +++ b/man/transitivity.Rd @@ -152,6 +152,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/unfold_tree.Rd b/man/unfold_tree.Rd index 88fef56131b..80c25790521 100644 --- a/man/unfold_tree.Rd +++ b/man/unfold_tree.Rd @@ -67,6 +67,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/which_multiple.Rd b/man/which_multiple.Rd index c7ae8a79065..c98aed93477 100644 --- a/man/which_multiple.Rd +++ b/man/which_multiple.Rd @@ -103,6 +103,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/man/which_mutual.Rd b/man/which_mutual.Rd index 9acbed38d8d..ce3bedaa6f9 100644 --- a/man/which_mutual.Rd +++ b/man/which_mutual.Rd @@ -60,6 +60,7 @@ Other structural.properties: \code{\link{is_matching}()}, \code{\link{k_shortest_paths}()}, \code{\link{knn}()}, +\code{\link{knnk}()}, \code{\link{reciprocity}()}, \code{\link{subcomponent}()}, \code{\link{subgraph}()}, diff --git a/tests/testthat/test-structural-properties.R b/tests/testthat/test-structural-properties.R index f938d495dd0..21c7af98c6f 100644 --- a/tests/testthat/test-structural-properties.R +++ b/tests/testthat/test-structural-properties.R @@ -949,6 +949,71 @@ test_that("knn works", { ) }) +test_that("knnk works", { + withr::local_seed(42) + + ## Ring graph - all vertices have degree 2 + g <- make_ring(10) + result <- knnk(g) + # Index 1 is degree 0 (NaN), index 2 is degree 1 (NaN), index 3 is degree 2 (value 2) + expect_equal(result, c(NaN, NaN, 2)) + + # Compare with knn result (knn's knnk starts at degree 1) + knn_result <- knn(g) + expect_equal(result[2:3], knn_result$knnk) + + ## Star graph + g2 <- make_star(10) + result2 <- knnk(g2) + # Degree 0: NaN, Degree 1: avg neighbor degree is 9 + expect_equal(result2, c(NaN, 9)) + + # Compare with knn + knn_result2 <- knn(g2) + expect_equal(result2[2], knn_result2$knnk[1]) + + ## Directed graph with different modes + g3 <- make_graph(c(1, 2, 1, 3, 2, 3, 3, 4), directed = TRUE) + # Vertex 1 has out-degree 2, points to vertices 2 and 3 with in-degrees 1 and 2 + # Vertex 2 has out-degree 1, points to vertex 3 with in-degree 2 + # Vertex 3 has out-degree 1, points to vertex 4 with in-degree 1 + # Vertex 4 has out-degree 0 + + # from.mode = "out", to.mode = "in" + result3 <- knnk(g3, from.mode = "out", to.mode = "in") + expect_equal(result3[1], NaN) # degree 0 + expect_equal(result3[2], 1.5) # degree 1: vertices 2 and 3 point to vertices with in-degree 2 and 1, avg = 1.5 + expect_equal(result3[3], 1.5) # degree 2: vertex 1 points to vertices with in-degree 1 and 2, avg = 1.5 + + ## Weighted graph - star with 5 vertices + g4 <- make_star(5) + E(g4)$weight <- c(1, 2, 3, 4) + result4 <- knnk(g4) + # Only degree 1 and degree 4 exist in this graph + expect_equal(result4, c(NaN, 4)) + + ## Test directed.neighbors parameter + g5 <- make_graph(c(1, 2, 1, 3), directed = TRUE) + # With directed.neighbors = TRUE (default): edges are directed + result5a <- knnk( + g5, + from.mode = "out", + to.mode = "in", + directed.neighbors = TRUE + ) + # With directed.neighbors = FALSE: edges are treated as reciprocal + result5b <- knnk( + g5, + from.mode = "out", + to.mode = "in", + directed.neighbors = FALSE + ) + # Results should differ when directed.neighbors changes + expect_false(identical(result5a, result5b)) + expect_equal(result5a, c(NaN, NaN, 1)) # With directed: only out-degree 2 vertex exists + expect_equal(result5b, c(NaN, 2, 1)) # With reciprocal: out-degree 0 vertices now also have out-degree 1 +}) + test_that("reciprocity works", { g <- make_graph(c(1, 2, 2, 1, 2, 3, 3, 4, 4, 4), directed = TRUE) expect_equal(reciprocity(g), 0.5)