From 28b7e54157eeb3ed94111026b1b958a5c17b547c Mon Sep 17 00:00:00 2001
From: msrosenberg
Date: Mon, 14 Nov 2022 16:42:24 -0500
Subject: [PATCH 1/2] typo in help file
---
resources/metawin_help.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/resources/metawin_help.html b/resources/metawin_help.html
index aff909e..e2b5385 100644
--- a/resources/metawin_help.html
+++ b/resources/metawin_help.html
@@ -1054,7 +1054,7 @@ Running a Rank Correlation Analysis
Additionally, there is an option to perform the rank correlation using Kendall's τ
(Kendall 1938) or Spearman's ρ (Spearman
1904). Because the distribution of these metrics can be complicated, particularly when the
- number of studies is low, MetaWin autmoatically uses a randomization
+ number of studies is low, MetaWin automatically uses a randomization
procedure to thest their significance, with the number of iterations user-specifiable.
From ba149aced04faa6ed9f203438a028f4ac6f8c7f5 Mon Sep 17 00:00:00 2001
From: msrosenberg
Date: Mon, 14 Nov 2022 17:13:03 -0500
Subject: [PATCH 2/2] progress bar
Added a progress indicator for resampling tests
---
src/MetaWinAnalysis.py | 30 +++--
src/MetaWinAnalysisFunctions.py | 187 ++++++++++++++++++++++++++++----
src/MetaWinLanguage.py | 3 +
src/MetaWinWidgets.py | 14 ++-
4 files changed, 197 insertions(+), 37 deletions(-)
diff --git a/src/MetaWinAnalysis.py b/src/MetaWinAnalysis.py
index f6ab1d7..5eada4c 100644
--- a/src/MetaWinAnalysis.py
+++ b/src/MetaWinAnalysis.py
@@ -1786,7 +1786,7 @@ def add_resampling_options_to_dialog(sender, test_model: bool = False):
def do_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, tree: Optional = None,
- norm_ci: bool = True):
+ norm_ci: bool = True, sender=None):
"""
primary function controlling the execution of an analysis
@@ -1799,42 +1799,48 @@ def do_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05
output_blocks.extend(output)
if options.structure == SIMPLE_MA:
(output, figure, chart_data, analysis_values,
- citations) = MetaWinAnalysisFunctions.simple_meta_analysis(data, options, decimal_places, alpha, norm_ci)
+ citations) = MetaWinAnalysisFunctions.simple_meta_analysis(data, options, decimal_places, alpha, norm_ci,
+ sender=sender)
elif options.structure == GROUPED_MA:
(output, figure, chart_data, analysis_values,
- citations) = MetaWinAnalysisFunctions.grouped_meta_analysis(data, options, decimal_places, alpha, norm_ci)
+ citations) = MetaWinAnalysisFunctions.grouped_meta_analysis(data, options, decimal_places, alpha, norm_ci,
+ sender=sender)
elif options.structure == CUMULATIVE_MA:
- output, figure, chart_data = MetaWinAnalysisFunctions.cumulative_meta_analysis(data, options,
- decimal_places, alpha, norm_ci)
+ output, figure, chart_data = MetaWinAnalysisFunctions.cumulative_meta_analysis(data, options, decimal_places,
+ alpha, norm_ci, sender=sender)
analysis_values = None
citations = []
elif options.structure == REGRESSION_MA:
(output, figure, chart_data, analysis_values,
- citations) = MetaWinAnalysisFunctions.regression_meta_analysis(data, options, decimal_places, alpha, norm_ci)
+ citations) = MetaWinAnalysisFunctions.regression_meta_analysis(data, options, decimal_places, alpha, norm_ci,
+ sender=sender)
elif options.structure == COMPLEX_MA:
output, analysis_values, citations = MetaWinAnalysisFunctions.complex_meta_analysis(data, options,
decimal_places, alpha,
- norm_ci)
+ norm_ci, sender=sender)
figure = None
chart_data = None
elif options.structure == NESTED_MA:
(output, figure, chart_data, analysis_values,
- citations) = MetaWinAnalysisFunctions.nested_meta_analysis(data, options, decimal_places, alpha, norm_ci)
+ citations) = MetaWinAnalysisFunctions.nested_meta_analysis(data, options, decimal_places, alpha, norm_ci,
+ sender=sender)
elif options.structure == TRIM_FILL:
(output, figure, chart_data, analysis_values,
citations) = MetaWinAnalysisFunctions.trim_and_fill_analysis(data, options, decimal_places, alpha, norm_ci)
elif options.structure == JACKKNIFE:
(output, figure, chart_data,
- citations) = MetaWinAnalysisFunctions.jackknife_meta_analysis(data, options, decimal_places, alpha, norm_ci)
+ citations) = MetaWinAnalysisFunctions.jackknife_meta_analysis(data, options, decimal_places, alpha, norm_ci,
+ sender=sender)
analysis_values = None
elif options.structure == PHYLOGENETIC_MA:
output, citations = MetaWinAnalysisFunctions.phylogenetic_meta_analysis(data, options, tree, decimal_places,
- alpha, norm_ci)
+ alpha, norm_ci, sender=sender)
analysis_values = None
figure = None
chart_data = None
elif options.structure == RANKCOR:
- output, citations = MetaWinAnalysisFunctions.rank_correlation_analysis(data, options, decimal_places)
+ output, citations = MetaWinAnalysisFunctions.rank_correlation_analysis(data, options, decimal_places,
+ sender=sender)
figure = None
chart_data = None
analysis_values = None
@@ -1904,7 +1910,7 @@ def meta_analysis(sender, data, last_effect, last_var, decimal_places: int = 4,
if meta_analysis_options.structure is not None:
output, figure, chart_data, _ = do_meta_analysis(data, meta_analysis_options, decimal_places, alpha, tree,
- norm_ci)
+ norm_ci, sender=sender)
sender.last_effect = meta_analysis_options.effect_data
sender.last_var = meta_analysis_options.effect_vars
return output, figure, chart_data
diff --git a/src/MetaWinAnalysisFunctions.py b/src/MetaWinAnalysisFunctions.py
index d7e0c82..ab2eb4d 100644
--- a/src/MetaWinAnalysisFunctions.py
+++ b/src/MetaWinAnalysisFunctions.py
@@ -16,6 +16,7 @@
from MetaWinUtils import create_output_table, inline_float, interval_to_str, get_citation, exponential_label, \
prob_z_score
import MetaWinCharts
+import MetaWinWidgets
from MetaWinLanguage import get_text
@@ -299,13 +300,20 @@ def median_effect(e: numpy.array, w: numpy.array):
return tmp_data[i, 0]
-def bootstrap_means(bootstrap_n, boot_data, obs_mean, pooled_var, random_effects: bool = False, alpha: float = 0.05):
+def bootstrap_means(bootstrap_n, boot_data, obs_mean, pooled_var, random_effects: bool = False, alpha: float = 0.05,
+ progress_bar=None):
"""
conduct a bootstrap test to create confidence intervals around mean effect size
"""
if bootstrap_n is not None:
rng = numpy.random.default_rng()
all_means = [obs_mean]
+
+ # if sender is not None:
+ # progress = MetaWinWidgets.progress_bar(sender, "Resampling Analysis", "Bootstrap Progress", bootstrap_n)
+ # else:
+ # progress = None
+
# f = 0.5 # count the observation as half less than itself
f = 0
for i in range(bootstrap_n):
@@ -332,6 +340,8 @@ def bootstrap_means(bootstrap_n, boot_data, obs_mean, pooled_var, random_effects
# elif tmp_mean_e == mean_e:
# f += 0.5
all_means.append(tmp_mean)
+ if progress_bar is not None:
+ progress_bar.setValue(progress_bar.value() + 1)
all_means.sort()
lower_index = round((bootstrap_n + 1) * alpha / 2)
upper_index = round(bootstrap_n - (bootstrap_n + 1) * alpha / 2)
@@ -558,7 +568,8 @@ def output_filtered_bad(filtered: list, bad_data: list) -> list:
# ---------- basic meta-analysis ----------
-def simple_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def simple_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -616,9 +627,16 @@ def simple_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
else:
lower_ci, upper_ci = scipy.stats.t.interval(alpha=1-alpha, df=df, loc=mean_e, scale=math.sqrt(var_e))
+ if (options.bootstrap_mean is not None) and (sender is not None):
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Bootstrap Analysis"),
+ options.bootstrap_mean)
+ else:
+ progress_bar = None
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, boot_data,
mean_e, pooled_var,
- options.random_effects, alpha)
+ options.random_effects, alpha,
+ progress_bar=progress_bar)
plot_order = 0
mean_data = mean_data_tuple(get_text("Mean"), plot_order, n, mean_e, median_e, var_e, mean_v,
@@ -684,7 +702,8 @@ def check_data_for_group(output_blocks, n, group_cnts, group_label) -> bool:
return all_good
-def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -757,6 +776,19 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
qe = 0
chart_order = 2
group_i2_values = []
+
+ if ((options.bootstrap_mean is not None) or (options.randomize_model is not None)) and (sender is not None):
+ if options.randomize_model is not None:
+ total_steps = options.randomize_model
+ else:
+ total_steps = 0
+ if options.bootstrap_mean is not None:
+ total_steps += (g_cnt + 1)*options.bootstrap_mean
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Bootstrap Analysis"), total_steps)
+ else:
+ progress_bar = None
+
for group in group_names:
group_mask = [g == group for g in group_data]
group_e = e_data[group_mask]
@@ -777,7 +809,7 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
(group_lower_bs, group_upper_bs,
group_lower_bias, group_upper_bias) = bootstrap_means(options.bootstrap_mean, group_boot,
group_mean, pooled_var, options.random_effects,
- alpha)
+ alpha, progress_bar=progress_bar)
group_het_values.append(heterogeneity_test_tuple(group + " (within)", group_qw, group_df, group_p, ""))
group_i2, group_i2_lower, group_i2_upper = calc_i2(group_qw, group_n, alpha)
@@ -795,7 +827,8 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
lower_ci, upper_ci = scipy.stats.t.interval(alpha=1 - alpha, df=n-1, loc=mean_e, scale=math.sqrt(var_e))
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, boot_data,
mean_e, pooled_var,
- options.random_effects, alpha)
+ options.random_effects, alpha,
+ progress_bar=progress_bar)
global_mean_data = mean_data_tuple(get_text("Global"), 0, n, mean_e, median_e, var_e, mean_v, lower_ci,
upper_ci, lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci)
@@ -823,6 +856,8 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
rand_p_dec = max(decimal_places, math.ceil(math.log10(nreps+1)))
cnt = 1
rng = numpy.random.default_rng()
+ if progress_bar is not None:
+ progress_bar.setLabelText(get_text("Conducting Randomization Analysis"))
for rep in range(nreps):
tmp_qe = 0
# rand_group_data = rng.permutation(group_data)
@@ -836,6 +871,8 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
tmp_qmodel = qt - tmp_qe
if tmp_qmodel >= qm:
cnt += 1
+ if progress_bar is not None:
+ progress_bar.setValue(progress_bar.value() + 1)
p_random = cnt / (nreps + 1)
p_random_str = format(p_random, inline_float(rand_p_dec))
else:
@@ -885,7 +922,8 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
# ---------- cumulative meta-analysis ----------
-def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -930,6 +968,13 @@ def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: floa
cumulative_means = []
cumulative_het = []
+ if (options.bootstrap_mean is not None) and (sender is not None):
+ total_steps = (n - 1)*options.bootstrap_mean
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Bootstrap Analysis"), total_steps)
+ else:
+ progress_bar = None
+
chart_order = 0
for ns in range(2, n+1):
ns_label = get_text("{} studies").format(ns)
@@ -952,7 +997,8 @@ def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: floa
lower_ci, upper_ci = scipy.stats.t.interval(alpha=1-alpha, df=df, loc=mean_e, scale=math.sqrt(var_e))
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, tmp_boot,
mean_e, pooled_var,
- options.random_effects, alpha)
+ options.random_effects, alpha,
+ progress_bar=progress_bar)
mean_data = mean_data_tuple(ns_label, chart_order, ns, mean_e, median_e, var_e, mean_v, lower_ci, upper_ci,
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci)
cumulative_means.append(mean_data)
@@ -994,7 +1040,8 @@ def calculate_regression_ma_values(e_data, w_data, x_data, sum_w, sum_we, qt):
return qm, qe, b1_slope, b0_intercept, var_b1, var_b0, sum_wx, sum_wx2
-def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -1057,6 +1104,19 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa
sum_ws = sum_w
sum_wse = sum_we
+ if ((options.bootstrap_mean is not None) or (options.randomize_model is not None)) and (sender is not None):
+ if options.randomize_model is not None:
+ total_steps = options.randomize_model
+ else:
+ total_steps = 0
+ if options.bootstrap_mean is not None:
+ total_steps += options.bootstrap_mean
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Bootstrap Analysis"),
+ total_steps)
+ else:
+ progress_bar = None
+
median_e = median_effect(e_data, ws_data)
mean_v = numpy.sum(v_data) / n
if norm_ci:
@@ -1065,7 +1125,8 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa
lower_ci, upper_ci = scipy.stats.t.interval(alpha=1 - alpha, df=n-1, loc=mean_e, scale=math.sqrt(var_e))
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, boot_data,
mean_e, pooled_var,
- options.random_effects, alpha)
+ options.random_effects, alpha,
+ progress_bar=progress_bar)
mean_data = mean_data_tuple(get_text("Global"), 0, n, mean_e, median_e, var_e, mean_v, lower_ci, upper_ci,
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci)
@@ -1085,6 +1146,8 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa
# randomization test
if options.randomize_model:
+ if progress_bar is not None:
+ progress_bar.setLabelText(get_text("Conducting Randomization Analysis"))
nreps = options.randomize_model
# decimal places to use for randomization-based p-value
rand_p_dec = max(decimal_places, math.ceil(math.log10(nreps+1)))
@@ -1096,6 +1159,8 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa
_, _, _, _) = calculate_regression_ma_values(rand_e_data, ws_data, x_data, sum_ws, sum_wse, qt)
if rand_qm >= qm:
cnt_q += 1
+ if progress_bar is not None:
+ progress_bar.setValue(progress_bar.value() + 1)
p_random = cnt_q / (nreps + 1)
p_random_str = format(p_random, inline_float(rand_p_dec))
else:
@@ -1185,7 +1250,8 @@ def calculate_glm(e: numpy.array, x: numpy.array, w: numpy.array):
return qm, qe, beta, xtwxinv
-def complex_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def complex_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -1306,6 +1372,19 @@ def complex_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
pqe = 1 - scipy.stats.chi2.cdf(qe, df=dfe)
pqm = 1 - scipy.stats.chi2.cdf(qm, df=dfm)
+ if ((options.bootstrap_mean is not None) or (options.randomize_model is not None)) and (sender is not None):
+ if options.randomize_model is not None:
+ total_steps = options.randomize_model
+ else:
+ total_steps = 0
+ if options.bootstrap_mean is not None:
+ total_steps += options.bootstrap_mean
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Bootstrap Analysis"),
+ total_steps)
+ else:
+ progress_bar = None
+
# basic global calcs
mean_e, var_e, _, _, _, _ = mean_effect_var_and_q(e_data, ws_data)
mean_v = numpy.sum(v_data) / n
@@ -1315,7 +1394,8 @@ def complex_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
lower_ci, upper_ci = scipy.stats.t.interval(alpha=1 - alpha, df=df, loc=mean_e, scale=math.sqrt(var_e))
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, boot_data,
mean_e, pooled_var,
- options.random_effects, alpha)
+ options.random_effects, alpha,
+ progress_bar=progress_bar)
mean_data = mean_data_tuple(get_text("Global"), 0, n, mean_e, median_e, var_e, mean_v, lower_ci, upper_ci,
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci)
@@ -1328,6 +1408,8 @@ def complex_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
# randomization test
if options.randomize_model:
+ if progress_bar is not None:
+ progress_bar.setLabelText(get_text("Conducting Randomization Analysis"))
nreps = options.randomize_model
# decimal places to use for randomization-based p-value
rand_p_dec = max(decimal_places, math.ceil(math.log10(nreps+1)))
@@ -1338,6 +1420,8 @@ def complex_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
rand_qm, _, _, _ = calculate_glm(rand_e_data, x_data, w_matrix)
if rand_qm >= qm:
cnt_q += 1
+ if progress_bar is not None:
+ progress_bar.setValue(progress_bar.value() + 1)
p_random = cnt_q / (nreps + 1)
p_random_str = format(p_random, inline_float(rand_p_dec))
else:
@@ -1438,8 +1522,17 @@ def qe(self, index):
sn += cn
return sq, sn
+ def nested_count(self):
+ """
+ count the total number of nested groups, including this one
+ """
+ count = 1
+ for c in self.children:
+ count += c.nested_count()
+ return count
+
def group_calculations(self, e, w, chart_order: int, boot_data, bootstrap_mean, alpha: float = 0.05,
- norm_ci: bool = True):
+ norm_ci: bool = True, progress_bar=None):
chart_order += 1
mean_output = []
het_output = []
@@ -1461,7 +1554,7 @@ def group_calculations(self, e, w, chart_order: int, boot_data, bootstrap_mean,
scale=math.sqrt(group_var))
(group_lower_bs, group_upper_bs,
group_lower_bias, group_upper_bias) = bootstrap_means(bootstrap_mean, group_boot, self.mean,
- 0, False, alpha)
+ 0, False, alpha, progress_bar=progress_bar)
if self.index > 0:
indent = " " + "→ "*self.index
else:
@@ -1472,7 +1565,8 @@ def group_calculations(self, e, w, chart_order: int, boot_data, bootstrap_mean,
group_lower_bias, group_upper_bias))
for child in self.children:
child_het, child_mean, chart_order = child.group_calculations(e, w, chart_order, boot_data,
- bootstrap_mean, alpha)
+ bootstrap_mean, alpha,
+ progress_bar=progress_bar)
het_output.extend(child_het)
mean_output.extend(child_mean)
if len(self.children) > 0:
@@ -1526,7 +1620,8 @@ def find_next_nested_level(index, group_data, parent) -> list:
return group_list
-def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -1566,7 +1661,6 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
boot_data = numpy.array(boot_data)
top_level = find_next_nested_level(0, group_data, None)
-
output_blocks = output_filtered_bad(filtered, bad_data)
n = len(e_data)
@@ -1582,6 +1676,23 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
if check_data_for_nested(output_blocks, n, top_level, [level.label for level in group_levels]):
output_blocks.append([get_text("{} studies will be included in this analysis").format(n)])
+ total_groups = 0
+ for group in top_level:
+ total_groups += group.nested_count()
+
+ if ((options.bootstrap_mean is not None) or (options.randomize_model is not None)) and (sender is not None):
+ if options.randomize_model is not None:
+ total_steps = options.randomize_model
+ else:
+ total_steps = 0
+ if options.bootstrap_mean is not None:
+ total_steps += options.bootstrap_mean * (total_groups + 1)
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Bootstrap Analysis"),
+ total_steps)
+ else:
+ progress_bar = None
+
mean_e, var_e, qt, sum_w, sum_w2, sum_ew = mean_effect_var_and_q(e_data, w_data)
median_e = median_effect(e_data, w_data)
mean_v = numpy.sum(v_data) / n
@@ -1590,7 +1701,8 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
else:
lower_ci, upper_ci = scipy.stats.t.interval(alpha=1 - alpha, df=n-1, loc=mean_e, scale=math.sqrt(var_e))
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, boot_data,
- mean_e, 0, False, alpha)
+ mean_e, 0, False, alpha,
+ progress_bar=progress_bar)
global_mean_data = mean_data_tuple(get_text("Global"), 0, n, mean_e, median_e, var_e, mean_v, lower_ci,
upper_ci, lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci)
df = n-1
@@ -1607,7 +1719,8 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
group_mean_values = []
for group in top_level:
het_out, mean_out, chart_order = group.group_calculations(e_data, w_data, chart_order, boot_data,
- options.bootstrap_mean, alpha)
+ options.bootstrap_mean, alpha,
+ progress_bar=progress_bar)
group_het_values.extend(het_out)
group_mean_values.extend(mean_out)
@@ -1644,6 +1757,8 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
# randomization test
if options.randomize_model:
+ if progress_bar is not None:
+ progress_bar.setLabelText(get_text("Conducting Randomization Analysis"))
nreps = options.randomize_model
# decimal places to use for randomization-based p-value
rand_p_dec = max(decimal_places, math.ceil(math.log10(nreps+1)))
@@ -1662,6 +1777,8 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float =
tmp_qm += tq
if tmp_qm >= model_het_values[i].q:
cnt_list[i] += 1
+ if progress_bar is not None:
+ progress_bar.setValue(progress_bar.value() + 1)
for i in range(len(group_levels)):
p_random = cnt_list[i] / (nreps + 1)
p_random_str = format(p_random, inline_float(rand_p_dec))
@@ -1886,7 +2003,8 @@ def phylogenetic_correlation(tip_names, root):
return p
-def phylogenetic_meta_analysis(data, options, tree, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def phylogenetic_meta_analysis(data, options, tree, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -2124,7 +2242,8 @@ def phylogenetic_meta_analysis(data, options, tree, decimal_places: int = 4, alp
# ---------- jackknife meta-analysis ----------
-def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True):
+def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True,
+ sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -2163,6 +2282,14 @@ def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float
if n > 1:
output_blocks.append([get_text("{} studies will be included in this analysis").format(n)])
+ if (options.bootstrap_mean is not None) and (sender is not None):
+ total_steps = options.bootstrap_mean*(n + 1)
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Bootstrap Analysis"),
+ total_steps)
+ else:
+ progress_bar = None
+
# global value
df = n - 1
mean_e, var_e, qt, sum_w, sum_w2, sum_ew = mean_effect_var_and_q(e_data, w_data)
@@ -2184,7 +2311,8 @@ def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, boot_data,
mean_e, pooled_var,
- options.random_effects, alpha)
+ options.random_effects, alpha,
+ progress_bar=progress_bar)
plot_order = 0
mean_data = mean_data_tuple(get_text("Mean"), plot_order, n, mean_e, median_e, var_e, mean_v, lower_ci,
upper_ci, lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci)
@@ -2226,7 +2354,8 @@ def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float
lower_ci, upper_ci = scipy.stats.t.interval(alpha=1-alpha, df=df, loc=mean_e, scale=math.sqrt(var_e))
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci = bootstrap_means(options.bootstrap_mean, tmp_boot,
mean_e, pooled_var,
- options.random_effects, alpha)
+ options.random_effects, alpha,
+ progress_bar=progress_bar)
mean_data = mean_data_tuple(j_label, plot_order, n-1, mean_e, median_e, var_e, mean_v, lower_ci, upper_ci,
lower_bs_ci, upper_bs_ci, lower_bias_ci, upper_bias_ci)
jackknife_means.append(mean_data)
@@ -2309,7 +2438,7 @@ def kendalls_tau(e_ranks, x_ranks):
return tau
-def rank_correlation_analysis(data, options, decimal_places: int = 4):
+def rank_correlation_analysis(data, options, decimal_places: int = 4, sender=None):
# filter and prepare data for analysis
effect_sizes = options.effect_data
variances = options.effect_vars
@@ -2387,6 +2516,13 @@ def rank_correlation_analysis(data, options, decimal_places: int = 4):
# test with randomization
nreps = options.randomize_model
+
+ if sender is not None:
+ progress_bar = MetaWinWidgets.progress_bar(sender, get_text("Resampling Progress"),
+ get_text("Conducting Randomization Analysis"), nreps)
+ else:
+ progress_bar = None
+
# decimal places to use for randomization-based p-value
rand_p_dec = max(decimal_places, math.ceil(math.log10(nreps+1)))
cnt_r = 1
@@ -2399,6 +2535,9 @@ def rank_correlation_analysis(data, options, decimal_places: int = 4):
rand_r = correlation(e_ranks, x_ranks)
if abs(rand_r) >= abs(r):
cnt_r += 1
+ if progress_bar is not None:
+ progress_bar.setValue(progress_bar.value() + 1)
+
p_random = cnt_r / (nreps + 1)
p_random_str = format(p_random, inline_float(rand_p_dec))
rstr = format(r, inline_float(decimal_places))
diff --git a/src/MetaWinLanguage.py b/src/MetaWinLanguage.py
index 064470f..9b53e39 100644
--- a/src/MetaWinLanguage.py
+++ b/src/MetaWinLanguage.py
@@ -47,6 +47,8 @@
"Complex/GLM": "Complex/GLM",
"Complex/GLM Meta-Analysis": "Complex/GLM Meta-Analysis",
"Compute": "Compute",
+ "Conducting Bootstrap Analysis": "Conducting Bootstrap Analysis",
+ "Conducting Randomization Analysis": "Conducting Randomization Analysis",
"Confidence Intervals": "Confidence Intervals",
"Continuous Variables": "Continuous Variables",
"Continuous Independent Variables(s)": "Continuous Independent Variable(s)",
@@ -299,6 +301,7 @@
"Rank Correlation Analysis": "Rank Correlation Analysis",
"Rank Correlation Method": "Rank Correlation Method",
"Rank Correlation Results": "Rank Correlation Results",
+ "Resampling Progress": "Resampling Progress",
"Correlate Effect Size with": "Correlate Effect Size with",
"References": "References",
"Regression Results": "Regression Results",
diff --git a/src/MetaWinWidgets.py b/src/MetaWinWidgets.py
index a1c7ddb..b1f240a 100644
--- a/src/MetaWinWidgets.py
+++ b/src/MetaWinWidgets.py
@@ -5,7 +5,7 @@
from typing import Optional
from PyQt6.QtWidgets import QPushButton, QVBoxLayout, QHBoxLayout, QListWidget, QAbstractItemView, QListWidgetItem, \
- QLabel, QComboBox, QCheckBox, QGroupBox, QGridLayout, QLineEdit, QColorDialog
+ QLabel, QComboBox, QCheckBox, QGroupBox, QGridLayout, QLineEdit, QColorDialog, QProgressDialog
from PyQt6.QtGui import QIcon, QColor, QDoubleValidator
from PyQt6 import QtCore
@@ -250,3 +250,15 @@ def add_chart_line_edits(color_text, color, width_text, width, style_text, style
width_label, width_box = add_chart_line_width(width_text, width)
style_label, style_box = add_chart_line_style(style_text, style, line_styles)
return color_label, color_button, width_label, width_box, style_label, style_box
+
+
+def progress_bar(sender, title: str, text: str, max_val: int):
+ pb = QProgressDialog(sender)
+ pb.setWindowModality(QtCore.Qt.WindowModality.WindowModal)
+ pb.setWindowTitle(title)
+ pb.setLabelText(text)
+ pb.setMinimum(0)
+ pb.setMaximum(max_val)
+ pb.setCancelButton(None)
+ pb.setValue(0)
+ return pb