Skip to content

Commit 07218b3

Browse files
committed
Fix logic and improve printer for degree_adoption_diagnostic
1 parent 6bec689 commit 07218b3

1 file changed

Lines changed: 136 additions & 80 deletions

File tree

R/degree_adoption_diagnostic.R

Lines changed: 136 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,14 @@
7878
#'
7979
#' # Different degree aggregation strategies
8080
#' result_first <- degree_adoption_diagnostic(kfamilyDiffNet, degree_strategy = "first")
81-
#' result_last <- degree_adoption_diagnostic(kfamilyDiffNet, degree_strategy = "last")
81+
#' result_last <- degree_adoption_diagnostic(kfamilyDiffNet, degree_strategy = "last")
8282
#'
8383
#' # Multi-diffusion (toy) ----------------------------------------------------
8484
#' set.seed(999)
85-
#' n <- 40; t <- 5; q <- 2
86-
#' garr <- rgraph_ws(n, t, p=.3)
85+
#' n <- 40
86+
#' t <- 5
87+
#' q <- 2
88+
#' garr <- rgraph_ws(n, t, p = .3)
8789
#' diffnet_multi <- rdiffnet(seed.graph = garr, t = t, seed.p.adopt = rep(list(0.1), q))
8890
#'
8991
#' # pooled (one combined analysis)
@@ -96,20 +98,19 @@
9698
#' @family statistics
9799
#' @export
98100
degree_adoption_diagnostic <- function(
99-
graph,
100-
degree_strategy = c("mean", "first", "last"),
101-
bootstrap = TRUE,
102-
R = 1000,
103-
conf.level = 0.95,
104-
toa = NULL,
105-
t0 = NULL, t1 = NULL,
106-
name = NULL,
107-
behavior = NULL,
108-
combine = c("none", "pooled", "average", "earliest"),
109-
min_adopters = 3,
110-
valued = getOption("diffnet.valued", FALSE),
111-
...
112-
) {
101+
graph,
102+
degree_strategy = c("mean", "first", "last"),
103+
bootstrap = TRUE,
104+
R = 1000,
105+
conf.level = 0.95,
106+
toa = NULL,
107+
t0 = NULL, t1 = NULL,
108+
name = NULL,
109+
behavior = NULL,
110+
combine = c("none", "pooled", "average", "earliest"),
111+
min_adopters = 3,
112+
valued = getOption("diffnet.valued", FALSE),
113+
...) {
113114
# Check that bootstrap is a logical scalar
114115
if (!is.logical(bootstrap) || length(bootstrap) != 1 || is.na(bootstrap)) {
115116
stop("'bootstrap' must be a logical scalar")
@@ -154,8 +155,10 @@ degree_adoption_diagnostic <- function(
154155
}
155156
behavior_indices <- match(behavior, colnames(toa))
156157
if (any(is.na(behavior_indices))) {
157-
stop("Some behavior names not found in colnames(toa): ",
158-
paste(behavior[is.na(behavior_indices)], collapse = ", "))
158+
stop(
159+
"Some behavior names not found in colnames(toa): ",
160+
paste(behavior[is.na(behavior_indices)], collapse = ", ")
161+
)
159162
}
160163
} else if (is.numeric(behavior)) {
161164
behavior_indices <- behavior
@@ -186,8 +189,10 @@ degree_adoption_diagnostic <- function(
186189
combined_data <- prepare_combined_data(degrees, toa, combine, min_adopters, Q)
187190

188191
if (nrow(combined_data) < min_adopters) {
189-
stop("Insufficient adopters for correlation analysis. (n=", nrow(combined_data),
190-
", minimum = ", min_adopters, ").")
192+
stop(
193+
"Insufficient adopters for correlation analysis. (n=", nrow(combined_data),
194+
", minimum = ", min_adopters, ")."
195+
)
191196
}
192197

193198
# Compute correlations
@@ -232,8 +237,12 @@ process_graph_input <- function(graph, toa, t0, t1, name, ...) {
232237
# If graph is a list, ensure all elements are dgCMatrix
233238
if (is.list(graph)) {
234239
graph <- lapply(graph, function(g) {
235-
if (inherits(g, "dgCMatrix")) return(g)
236-
if (is.matrix(g)) return(as(Matrix::Matrix(g, sparse = TRUE), "dgCMatrix"))
240+
if (inherits(g, "dgCMatrix")) {
241+
return(g)
242+
}
243+
if (is.matrix(g)) {
244+
return(as(Matrix::Matrix(g, sparse = TRUE), "dgCMatrix"))
245+
}
237246
stop("All elements of the graph list must be matrices or dgCMatrix.")
238247
})
239248
}
@@ -328,8 +337,8 @@ analyze_multi_behaviors_separately <- function(degrees, toa, min_adopters, boots
328337
toa = toa_q[adopters_q]
329338
)
330339

331-
correlations_matrix[1, q] <- cor_safe(data_q$indegree, data_q$toa )
332-
correlations_matrix[2, q] <- cor_safe(data_q$outdegree, data_q$toa )
340+
correlations_matrix[1, q] <- cor_safe(data_q$indegree, data_q$toa)
341+
correlations_matrix[2, q] <- cor_safe(data_q$outdegree, data_q$toa)
333342
sample_sizes[q] <- nrow(data_q)
334343

335344
if (bootstrap) {
@@ -391,7 +400,9 @@ prepare_combined_data <- function(degrees, toa, combine, min_adopters, Q) {
391400
} else if (combine == "earliest") {
392401
# Earliest TOA across behaviors per actor
393402
toa_min <- apply(toa, 1, function(row) {
394-
if (all(is.na(row))) return(NA_real_)
403+
if (all(is.na(row))) {
404+
return(NA_real_)
405+
}
395406
min(row, na.rm = TRUE)
396407
})
397408
toa_min[is.infinite(toa_min)] <- NA
@@ -414,12 +425,12 @@ compute_correlations <- function(data) {
414425

415426
compute_bootstrap_results <- function(combined_data, R, conf.level) {
416427
# Compute baseline correlations
417-
base_corr <- compute_correlations(combined_data)
418-
indeg_corr <- base_corr[["indegree_toa"]]
428+
base_corr <- compute_correlations(combined_data)
429+
indeg_corr <- base_corr[["indegree_toa"]]
419430
outdeg_corr <- base_corr[["outdegree_toa"]]
420431

421432
indeg_boot_list <- NULL
422-
out_boot_list <- NULL
433+
out_boot_list <- NULL
423434

424435
# Out-degree
425436
if (!is.na(outdeg_corr)) {
@@ -430,13 +441,16 @@ compute_bootstrap_results <- function(combined_data, R, conf.level) {
430441
}
431442
boot_obj_out <- boot::boot(combined_data, statistic = safe_bootstrap_out, R = R)
432443
bias_out <- mean(boot_obj_out$t, na.rm = TRUE) - outdeg_corr
433-
se_out <- stats::sd(boot_obj_out$t, na.rm = TRUE)
434-
435-
ci_out <- tryCatch({
436-
bci <- boot::boot.ci(boot_obj_out, conf = conf.level, type = "perc")
437-
# Percentile CI vector (low, high)
438-
if (!is.null(bci$percent)) bci$percent[4:5] else NULL
439-
}, error = function(e) NULL)
444+
se_out <- stats::sd(boot_obj_out$t, na.rm = TRUE)
445+
446+
ci_out <- tryCatch(
447+
{
448+
bci <- boot::boot.ci(boot_obj_out, conf = conf.level, type = "perc")
449+
# Percentile CI vector (low, high)
450+
if (!is.null(bci$percent)) bci$percent[4:5] else NULL
451+
},
452+
error = function(e) NULL
453+
)
440454

441455
out_boot_list <- list(
442456
correlation = outdeg_corr,
@@ -462,12 +476,15 @@ compute_bootstrap_results <- function(combined_data, R, conf.level) {
462476
}
463477
boot_obj_in <- boot::boot(combined_data, statistic = safe_bootstrap_in, R = R)
464478
bias_in <- mean(boot_obj_in$t, na.rm = TRUE) - indeg_corr
465-
se_in <- stats::sd(boot_obj_in$t, na.rm = TRUE)
466-
467-
ci_in <- tryCatch({
468-
bci <- boot::boot.ci(boot_obj_in, conf = conf.level, type = "perc")
469-
if (!is.null(bci$percent)) bci$percent[4:5] else NULL
470-
}, error = function(e) NULL)
479+
se_in <- stats::sd(boot_obj_in$t, na.rm = TRUE)
480+
481+
ci_in <- tryCatch(
482+
{
483+
bci <- boot::boot.ci(boot_obj_in, conf = conf.level, type = "perc")
484+
if (!is.null(bci$percent)) bci$percent[4:5] else NULL
485+
},
486+
error = function(e) NULL
487+
)
471488

472489
indeg_boot_list <- list(
473490
correlation = indeg_corr,
@@ -508,7 +525,7 @@ check_undirected_graph <- function(graph) {
508525
return(all(sapply(graph, function(g) isSymmetric(as.matrix(g)))))
509526
}
510527
if (is.array(graph) && length(dim(graph)) == 3) {
511-
return(all(sapply(seq_len(dim(graph)[3]), function(t) isSymmetric(as.matrix(graph[,,t])))))
528+
return(all(sapply(seq_len(dim(graph)[3]), function(t) isSymmetric(as.matrix(graph[, , t])))))
512529
}
513530
if (is.matrix(graph)) {
514531
return(isSymmetric(as.matrix(graph)))
@@ -568,7 +585,7 @@ print_single_behavior_results <- function(x, undirected) {
568585
# Print correlations
569586
cat("Correlations:\n")
570587
if (undirected) {
571-
deg_r <- indeg_r # For undirected graphs, in-degree = out-degree = degree
588+
deg_r <- indeg_r # For undirected graphs, in-degree = out-degree = degree
572589
cat(sprintf(" Degree - Time of Adoption: %.3f\n", deg_r))
573590
} else {
574591
cat(sprintf(" In-degree - Time of Adoption: %.3f\n", indeg_r))
@@ -582,16 +599,24 @@ print_single_behavior_results <- function(x, undirected) {
582599
bootstrap_data <- x$bootstrap
583600
deg_ci <- if (undirected && !is.null(bootstrap_data$indegree$conf_int)) {
584601
bootstrap_data$indegree$conf_int
585-
} else NULL
602+
} else {
603+
NULL
604+
}
586605
indeg_ci <- if (!is.null(bootstrap_data$indegree$conf_int)) {
587606
bootstrap_data$indegree$conf_int
588-
} else NULL
607+
} else {
608+
NULL
609+
}
589610
outdeg_ci <- if (!is.null(bootstrap_data$outdegree$conf_int)) {
590611
bootstrap_data$outdegree$conf_int
591-
} else NULL
612+
} else {
613+
NULL
614+
}
592615
lvl <- if (!is.null(bootstrap_data$indegree$conf_level)) {
593616
bootstrap_data$indegree$conf_level * 100
594-
} else NA_real_
617+
} else {
618+
NA_real_
619+
}
595620

596621
if (undirected) {
597622
explain_degree_correlation("Degree", deg_r, deg_ci, lvl_arg = lvl)
@@ -648,16 +673,24 @@ print_multi_behavior_results <- function(x, undirected) {
648673
bootstrap_data <- if (!is.null(x$bootstrap)) x$bootstrap[[j]] else NULL
649674
deg_ci <- if (undirected && !is.null(bootstrap_data) && !is.null(bootstrap_data$indegree$conf_int)) {
650675
bootstrap_data$indegree$conf_int
651-
} else NULL
676+
} else {
677+
NULL
678+
}
652679
indeg_ci <- if (!is.null(bootstrap_data) && !is.null(bootstrap_data$indegree$conf_int)) {
653680
bootstrap_data$indegree$conf_int
654-
} else NULL
681+
} else {
682+
NULL
683+
}
655684
outdeg_ci <- if (!is.null(bootstrap_data) && !is.null(bootstrap_data$outdegree$conf_int)) {
656685
bootstrap_data$outdegree$conf_int
657-
} else NULL
686+
} else {
687+
NULL
688+
}
658689
lvl <- if (!is.null(bootstrap_data) && !is.null(bootstrap_data$indegree$conf_level)) {
659690
bootstrap_data$indegree$conf_level * 100
660-
} else NA_real_
691+
} else {
692+
NA_real_
693+
}
661694

662695
cat(sprintf(" [%s]\n", bname))
663696
if (undirected) {
@@ -696,49 +729,72 @@ explain_degree_correlation <- function(label, r, ci, lvl_arg = NA_real_, thr = 0
696729

697730
format_interpretation_no_ci <- function(label, r, abs_big, degree_term, thr) {
698731
if (!abs_big) {
699-
cat(sprintf(" %s: Weak relationship between %s and adoption timing:\n |r| \u2264 %.1f; no CI.\n",
700-
label, degree_term, thr))
701-
} else if (r > 0) {
702-
cat(sprintf(" %s: Central actors (high %s) tended to adopt early (supporters):\n |r| > %.1f; no CI.\n",
703-
label, degree_term, thr))
732+
cat(sprintf(
733+
" %s: Weak relationship between %s and adoption timing:\n |r| \u2264 %.1f; no CI.\n",
734+
label, degree_term, thr
735+
))
736+
} else if (r < 0) {
737+
cat(sprintf(
738+
" %s: Central actors (high %s) tended to adopt early (supporters):\n |r| > %.1f; no CI.\n",
739+
label, degree_term, thr
740+
))
704741
} else {
705-
cat(sprintf(" %s: Central actors (high %s) tended to adopt late (opposers):\n |r| > %.1f; no CI.\n",
706-
label, degree_term, thr))
742+
cat(sprintf(
743+
" %s: Central actors (high %s) tended to adopt late (opposers):\n |r| > %.1f; no CI.\n",
744+
label, degree_term, thr
745+
))
707746
}
708747
}
709748

710749
format_interpretation_with_ci <- function(label, r, ci, abs_big, degree_term, thr, lvl_arg) {
711750
lvl_local <- if (!is.na(lvl_arg)) lvl_arg else 95
712751
ci_includes_zero <- (length(ci) >= 2) && is.finite(ci[1]) && is.finite(ci[2]) && (ci[1] <= 0 && ci[2] >= 0)
752+
753+
ci_low <- if (length(ci) >= 1) ci[1] else NA_real_
754+
ci_high <- if (length(ci) >= 2) ci[2] else NA_real_
713755

714756
if (!abs_big) {
715-
cat(sprintf(" %s: Weak relationship between %s and adoption timing; %s statistically supported:\n |r| \u2264 %.1f; CI (%.1f%%) %s 0.\n",
716-
label, degree_term,
717-
if (ci_includes_zero) "NOT" else "",
718-
thr, lvl_local,
719-
if (ci_includes_zero) "includes" else "excludes"))
720-
} else if (r > 0) {
721-
cat(sprintf(" %s: Central actors (high %s) tended to adopt early (supporters); %s statistically supported:\n |r| > %.1f; CI (%.1f%%) %s 0.\n",
722-
label, degree_term,
723-
if (ci_includes_zero) "NOT" else "",
724-
thr, lvl_local,
725-
if (ci_includes_zero) "includes" else "excludes"))
757+
cat(sprintf(
758+
" %s: Weak relationship between %s and adoption timing; %s statistically supported:\n |r| \u2264 %.1f; CI (%.1f%%) = [%.3f, %.3f]\n",
759+
label, degree_term,
760+
if (ci_includes_zero) "NOT" else "",
761+
thr, lvl_local,
762+
ci_low, ci_high
763+
))
764+
} else if (r < 0) {
765+
cat(sprintf(
766+
" %s: Central actors (high %s) tended to adopt early (supporters); %s statistically supported:\n |r| > %.1f; CI (%.1f%%) = [%.3f, %.3f]\n",
767+
label, degree_term,
768+
if (ci_includes_zero) "NOT" else "",
769+
thr, lvl_local,
770+
ci_low, ci_high
771+
))
726772
} else {
727-
cat(sprintf(" %s: Central actors (high %s) tended to adopt late (opposers); %s statistically supported:\n |r| > %.1f; CI (%.1f%%) %s 0.\n",
728-
label, degree_term,
729-
if (ci_includes_zero) "NOT" else "",
730-
thr, lvl_local,
731-
if (ci_includes_zero) "includes" else "excludes"))
773+
cat(sprintf(
774+
" %s: Central actors (high %s) tended to adopt late (opposers); %s statistically supported:\n |r| > %.1f; CI (%.1f%%) = [%.3f, %.3f]\n",
775+
label, degree_term,
776+
if (ci_includes_zero) "NOT" else "",
777+
thr, lvl_local,
778+
ci_low, ci_high
779+
))
732780
}
733781
}
734782

735783
# Safe correlation: returns NA (no warnings) if zero-variance or too few pairs
736784
cor_safe <- function(x, y) {
737-
x <- as.numeric(x); y <- as.numeric(y)
785+
x <- as.numeric(x)
786+
y <- as.numeric(y)
738787
ok <- is.finite(x) & is.finite(y)
739-
if (!any(ok)) return(NA_real_)
740-
x <- x[ok]; y <- y[ok]
741-
if (length(x) < 2L) return(NA_real_)
742-
if (sd(x) == 0 || sd(y) == 0) return(NA_real_)
788+
if (!any(ok)) {
789+
return(NA_real_)
790+
}
791+
x <- x[ok]
792+
y <- y[ok]
793+
if (length(x) < 2L) {
794+
return(NA_real_)
795+
}
796+
if (sd(x) == 0 || sd(y) == 0) {
797+
return(NA_real_)
798+
}
743799
stats::cor(x, y)
744800
}

0 commit comments

Comments
 (0)