From a5d548720058b1ca9297b76647113dc375508b24 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Tue, 6 Sep 2022 12:59:19 -0400 Subject: [PATCH 1/6] typo in normal quantile caption --- resources/metawin_help.html | 2 +- src/MetaWinLanguage.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/metawin_help.html b/resources/metawin_help.html index 70e4eb7..a10fb80 100644 --- a/resources/metawin_help.html +++ b/resources/metawin_help.html @@ -2487,7 +2487,7 @@

Output Example

Normal Quantile plot following Wang and Bushman (1998). The standardized effect size is the - efffect size divided by the square-root of its variance. The solid line represents the regression + effect size divided by the square-root of its variance. The solid line represents the regression and the dashed lines the 95% prediction envelope.

diff --git a/src/MetaWinLanguage.py b/src/MetaWinLanguage.py index 2f5f335..26c2ba5 100644 --- a/src/MetaWinLanguage.py +++ b/src/MetaWinLanguage.py @@ -225,7 +225,7 @@ "Normal Quantile": "Normal Quantile", "Normal Quantile Plot": "Normal Quantile Plot", "normal_quantile_caption": "Normal Quantile plot following {}. The " - "standardized effect size is the efffect size divided by the " + "standardized effect size is the effect size divided by the " "square-root of its variance. The solid line represents the " "regression and the dashed lines the 95% prediction envelope.", "note_funnel_plot": "Note: A funnel plot is just a scatter plot of a metric (such as a
" From a81d2cc4661df3ea712750d0ff2eaf65c4f4192f Mon Sep 17 00:00:00 2001 From: msrosenberg Date: Wed, 7 Sep 2022 11:44:00 -0400 Subject: [PATCH 2/6] added icon for analysis options menu --- MetaWin_mac.spec | 1 + MetaWin_windows.spec | 1 + resources/images/sum-gear-filled@256px.png | Bin 0 -> 20803 bytes src/MetaWinConstants.py | 1 + 4 files changed, 3 insertions(+) create mode 100644 resources/images/sum-gear-filled@256px.png diff --git a/MetaWin_mac.spec b/MetaWin_mac.spec index b19c3d0..35f775e 100644 --- a/MetaWin_mac.spec +++ b/MetaWin_mac.spec @@ -43,6 +43,7 @@ added_files = [("resources/images/exit@256px.png", "resources/images"), ("resources/images/flag-united-states@256px.png", "resources/images"), ("resources/images/flag-spain@256px.png", "resources/images"), ("resources/images/letter-t@256px.png", "resources/images"), + ("resources/images/sum-gear-filled@256px.png", "resources/images"), ("resources/images/metawin3icon.png", "resources/images"), ("resources/images/draw_forest.png", "resources/images"), diff --git a/MetaWin_windows.spec b/MetaWin_windows.spec index e212679..cc15f5b 100644 --- a/MetaWin_windows.spec +++ b/MetaWin_windows.spec @@ -43,6 +43,7 @@ added_files = [("resources/images/exit@256px.png", "resources/images"), ("resources/images/flag-united-states@256px.png", "resources/images"), ("resources/images/flag-spain@256px.png", "resources/images"), ("resources/images/letter-t@256px.png", "resources/images"), + ("resources/images/sum-gear-filled@256px.png", "resources/images"), ("resources/images/metawin3icon.png", "resources/images"), ("resources/images/draw_forest.png", "resources/images"), diff --git a/resources/images/sum-gear-filled@256px.png b/resources/images/sum-gear-filled@256px.png new file mode 100644 index 0000000000000000000000000000000000000000..53b8cdabb2fa0e9afa64f7fef7fa1a0bf65eb30e GIT binary patch literal 20803 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 literal 0 HcmV?d00001 diff --git a/src/MetaWinConstants.py b/src/MetaWinConstants.py index bf4efea..865e9c4 100644 --- a/src/MetaWinConstants.py +++ b/src/MetaWinConstants.py @@ -79,6 +79,7 @@ def resource_path(relative_path: str, inc_file: bool = False) -> str: save_graph_icon = resource_path(icon_path + "save-filled-picture-filled@256px.png") edit_graph_icon = resource_path(icon_path + "picture-edit-filled@256px.png") export_graph_data_icon = resource_path(icon_path + "data-export@256px.png") +analysis_options_icon = resource_path(icon_path + "sum-gear-filled@256px.png") language_icon = resource_path(icon_path + "translation@256px.png") alpha_icon = resource_path(icon_path + "letter-alpha@256px.png") show_toolbar_icon = resource_path(icon_path + "toolbar-position-left-add-filled@256px.png") From bc46859a2e4c1390df74a4c15a35b844a0367be8 Mon Sep 17 00:00:00 2001 From: msrosenberg Date: Wed, 7 Sep 2022 11:45:49 -0400 Subject: [PATCH 3/6] forgot to enable new icon --- src/MetaWinMain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MetaWinMain.py b/src/MetaWinMain.py index 2270a55..136d774 100644 --- a/src/MetaWinMain.py +++ b/src/MetaWinMain.py @@ -171,7 +171,7 @@ def init_ui(self): options_menu.addMenu(data_options_menu) # analysis options submenu analysis_options_menu = QMenu(get_text("Analysis Options"), self) - # analysis_options_menu.setIcon(QIcon(MetaWinConstants.output_icon)) + analysis_options_menu.setIcon(QIcon(MetaWinConstants.analysis_options_icon)) output_alpha_action = QAction(QIcon(MetaWinConstants.alpha_icon), get_text("Significance Level"), self) output_alpha_action.triggered.connect(self.set_alpha_significance) analysis_options_menu.addAction(output_alpha_action) From 71b170aa55396b6c89bc8c79cbef39a38233cb50 Mon Sep 17 00:00:00 2001 From: msrosenberg Date: Wed, 7 Sep 2022 19:38:23 -0400 Subject: [PATCH 4/6] refactoring of caption generation changed the way and place are captions are generated, which not only simplifies the code in some ways but allows for regeneration of captions and opens up the possibility of caption customization based on user style edits --- src/MetaWinAnalysis.py | 37 ++-- src/MetaWinAnalysisFunctions.py | 148 ++++++++-------- src/MetaWinCharts.py | 303 +++++++++++++++++++++++++++----- src/MetaWinDraw.py | 94 ++++------ src/MetaWinMain.py | 43 ++--- tests/test_metawin.py | 149 ++++++++-------- 6 files changed, 483 insertions(+), 291 deletions(-) diff --git a/src/MetaWinAnalysis.py b/src/MetaWinAnalysis.py index cbb4295..f6ab1d7 100644 --- a/src/MetaWinAnalysis.py +++ b/src/MetaWinAnalysis.py @@ -1798,62 +1798,56 @@ def do_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05 output, all_citations = options.report_choices() output_blocks.extend(output) if options.structure == SIMPLE_MA: - (output, figure, fig_caption, chart_data, analysis_values, + (output, figure, chart_data, analysis_values, citations) = MetaWinAnalysisFunctions.simple_meta_analysis(data, options, decimal_places, alpha, norm_ci) elif options.structure == GROUPED_MA: - (output, figure, fig_caption, chart_data, analysis_values, + (output, figure, chart_data, analysis_values, citations) = MetaWinAnalysisFunctions.grouped_meta_analysis(data, options, decimal_places, alpha, norm_ci) elif options.structure == CUMULATIVE_MA: - output, figure, fig_caption, 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) analysis_values = None citations = [] elif options.structure == REGRESSION_MA: - (output, figure, fig_caption, chart_data, analysis_values, + (output, figure, chart_data, analysis_values, citations) = MetaWinAnalysisFunctions.regression_meta_analysis(data, options, decimal_places, alpha, norm_ci) elif options.structure == COMPLEX_MA: output, analysis_values, citations = MetaWinAnalysisFunctions.complex_meta_analysis(data, options, decimal_places, alpha, norm_ci) figure = None - fig_caption = None chart_data = None elif options.structure == NESTED_MA: - (output, figure, fig_caption, chart_data, analysis_values, + (output, figure, chart_data, analysis_values, citations) = MetaWinAnalysisFunctions.nested_meta_analysis(data, options, decimal_places, alpha, norm_ci) elif options.structure == TRIM_FILL: - (output, figure, fig_caption, chart_data, analysis_values, + (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, fig_caption, - chart_data, citations) = MetaWinAnalysisFunctions.jackknife_meta_analysis(data, options, decimal_places, - alpha, norm_ci) + (output, figure, chart_data, + citations) = MetaWinAnalysisFunctions.jackknife_meta_analysis(data, options, decimal_places, alpha, norm_ci) analysis_values = None elif options.structure == PHYLOGENETIC_MA: output, citations = MetaWinAnalysisFunctions.phylogenetic_meta_analysis(data, options, tree, decimal_places, alpha, norm_ci) analysis_values = None figure = None - fig_caption = None chart_data = None elif options.structure == RANKCOR: output, citations = MetaWinAnalysisFunctions.rank_correlation_analysis(data, options, decimal_places) figure = None - fig_caption = None chart_data = None analysis_values = None else: output = [] analysis_values = None figure = None - fig_caption = None chart_data = None citations = [] all_citations.extend(citations) output_blocks.extend(output) output_blocks.extend(create_reference_list(all_citations)) - return output_blocks, figure, fig_caption, chart_data, analysis_values + return output_blocks, figure, chart_data, analysis_values def meta_analysis(sender, data, last_effect, last_var, decimal_places: int = 4, alpha: float = 0.05, @@ -1909,12 +1903,9 @@ def meta_analysis(sender, data, last_effect, last_var, decimal_places: int = 4, meta_analysis_options.structure = None if meta_analysis_options.structure is not None: - output, figure, fig_caption, chart_data, _ = do_meta_analysis(data, meta_analysis_options, decimal_places, - alpha, tree, norm_ci) + output, figure, chart_data, _ = do_meta_analysis(data, meta_analysis_options, decimal_places, alpha, tree, + norm_ci) sender.last_effect = meta_analysis_options.effect_data sender.last_var = meta_analysis_options.effect_vars - return output, figure, fig_caption, chart_data - else: - return None, None, None, None - else: - return None, None, None, None + return output, figure, chart_data + return None, None, None diff --git a/src/MetaWinAnalysisFunctions.py b/src/MetaWinAnalysisFunctions.py index 741c4d7..5982f53 100644 --- a/src/MetaWinAnalysisFunctions.py +++ b/src/MetaWinAnalysisFunctions.py @@ -13,8 +13,8 @@ import MetaWinConstants from MetaWinConstants import mean_data_tuple -from MetaWinUtils import create_output_table, inline_float, interval_to_str, create_reference_list, get_citation, \ - exponential_label, prob_z_score +from MetaWinUtils import create_output_table, inline_float, interval_to_str, get_citation, exponential_label, \ + prob_z_score import MetaWinCharts from MetaWinLanguage import get_text @@ -525,12 +525,12 @@ def output_filtered_bad(filtered: list, bad_data: list) -> list: return output_blocks -def caption_bootstrap_text(bs_n: int): - """ - extra figure caption info for forest plots with bootstrap confidence intervals - """ - citation = "Adams_et_1997" - return get_text("bootstrap_caption").format(bs_n, get_citation(citation)), [citation] +# def caption_bootstrap_text(bs_n: int): +# """ +# extra figure caption info for forest plots with bootstrap confidence intervals +# """ +# citation = "Adams_et_1997" +# return get_text("bootstrap_caption").format(bs_n, get_citation(citation)), [citation] # ---- I'm not convinced this is correct, which is why I'm removing it for now --- # def calc_aic(q: float, n: int, p: int) -> float: @@ -577,7 +577,7 @@ def simple_meta_analysis(data, options, decimal_places: int = 4, alpha: float = output_blocks = output_filtered_bad(filtered, bad_data) figure = None - fig_caption = None + # fig_caption = None chart_data = None n = len(e_data) citations = [] @@ -639,21 +639,22 @@ def simple_meta_analysis(data, options, decimal_places: int = 4, alpha: float = citations.extend(new_cites) if options.create_graph: - figure, chart_data = MetaWinCharts.chart_forest_plot(effect_sizes.label, forest_data, alpha, - options.bootstrap_mean) - fig_caption = get_text("Forest plot of individual effect sizes for each study, as well as the overall " - "mean.") + MetaWinCharts.caption_forest_plot_text(effect_sizes.label, alpha) - if options.bootstrap_mean is not None: - new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - fig_caption += new_text + create_reference_list(new_cites, True) + figure, chart_data = MetaWinCharts.chart_forest_plot("basic analysis", effect_sizes.label, forest_data, + alpha, options.bootstrap_mean) + # fig_caption = get_text("Forest plot of individual effect sizes for each study, as well as the overall " + # "mean.") + MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) + # if options.bootstrap_mean is not None: + # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) + # # fig_caption += new_text + create_reference_list(new_cites, True) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) qt, df, p, pooled_var, i2 = None, None, None, None, None mean_data = None - return (output_blocks, figure, fig_caption, chart_data, simple_ma_values(mean_data, pooled_var, qt, df, p, i2), - citations) + # return (output_blocks, figure, fig_caption, chart_data, simple_ma_values(mean_data, pooled_var, qt, df, p, i2), + # citations) + return output_blocks, figure, chart_data, simple_ma_values(mean_data, pooled_var, qt, df, p, i2), citations # ---------- grouped meta-analysis ---------- @@ -716,7 +717,7 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = output_blocks = output_filtered_bad(filtered, bad_data) figure = None - fig_caption = None + # fig_caption = None chart_data = None n = len(e_data) g_cnt = len(group_names) @@ -863,13 +864,13 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = citations.extend(new_cites) if options.create_graph: - figure, chart_data = MetaWinCharts.chart_forest_plot(effect_sizes.label, forest_data, alpha, - options.bootstrap_mean) - fig_caption = get_text("group_forest_plot").format(groups.label) + \ - MetaWinCharts.caption_forest_plot_text(effect_sizes.label, alpha) - if options.bootstrap_mean is not None: - new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - fig_caption += new_text + create_reference_list(new_cites, True) + figure, chart_data = MetaWinCharts.chart_forest_plot("grouped analysis", effect_sizes.label, forest_data, + alpha, options.bootstrap_mean, groups.label) + # fig_caption = get_text("group_forest_plot").format(groups.label) + \ + # MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) + # if options.bootstrap_mean is not None: + # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) + # fig_caption += new_text + create_reference_list(new_cites, True) global_values = simple_ma_values(global_mean_data, pooled_var, qt, df, pqt, i2) else: @@ -879,8 +880,10 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = model_het = None error_het = None - return (output_blocks, figure, fig_caption, chart_data, + return (output_blocks, figure, chart_data, group_ma_values(global_values, group_mean_values, group_het_values, model_het, error_het), citations) + # return (output_blocks, figure, fig_caption, chart_data, + # group_ma_values(global_values, group_mean_values, group_het_values, model_het, error_het), citations) # ---------- cumulative meta-analysis ---------- @@ -907,7 +910,7 @@ def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: floa output_blocks = output_filtered_bad(filtered, bad_data) figure = None - fig_caption = None + # fig_caption = None chart_data = None n = len(tmp_data) if n > 1: @@ -970,17 +973,19 @@ def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: floa decimal_places, alpha, options.log_transformed)) if options.create_graph: - figure, chart_data = MetaWinCharts.chart_forest_plot(effect_sizes.label, cumulative_means, alpha, - options.bootstrap_mean) - fig_caption = get_text("cumulative_forest_plot").format(order.label) + \ - MetaWinCharts.caption_forest_plot_text(effect_sizes.label, alpha) - if options.bootstrap_mean is not None: - new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - fig_caption += new_text + create_reference_list(new_cites, True) + figure, chart_data = MetaWinCharts.chart_forest_plot("cumulative analysis", effect_sizes.label, + cumulative_means, alpha, options.bootstrap_mean, + order.label) + # fig_caption = get_text("cumulative_forest_plot").format(order.label) + \ + # MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) + # if options.bootstrap_mean is not None: + # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) + # fig_caption += new_text + create_reference_list(new_cites, True) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) - return output_blocks, figure, fig_caption, chart_data + return output_blocks, figure, chart_data + # return output_blocks, figure, fig_caption, chart_data # ---------- simple regression meta-analysis ---------- @@ -1033,7 +1038,7 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa output_blocks = output_filtered_bad(filtered, bad_data) figure = None - fig_caption = None + # fig_caption = None chart_data = None model_het = None error_het = None @@ -1132,8 +1137,6 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa citations.extend(new_cites) if options.create_graph: - figure, chart_data = MetaWinCharts.chart_regression(options.independent_variable.label, effect_sizes.label, - x_data, e_data, b1_slope, b0_intercept) if options.random_effects: ref_list = "{}, {}, and {}".format(get_citation("Hedges_Olkin_1985"), get_citation("Greenland_1987"), @@ -1146,16 +1149,19 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa get_citation("Greenland_1987")) model = get_text("fixed effects") fig_citations = ["Hedges_Olkin_1985", "Greenland_1987"] - - fig_caption = get_text("regression_caption").format(effect_sizes.label, options.independent_variable.label, - model, ref_list) + \ - create_reference_list(fig_citations, True) + figure, chart_data = MetaWinCharts.chart_regression(options.independent_variable.label, effect_sizes.label, + x_data, e_data, b1_slope, b0_intercept, model, + ref_list, fig_citations) + # fig_caption = get_text("regression_caption").format(effect_sizes.label, options.independent_variable.label, + # model, ref_list) + \ + # create_reference_list(fig_citations, True) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) - return (output_blocks, figure, fig_caption, chart_data, - reg_ma_values(global_values, model_het, error_het, predictors), citations) + return output_blocks, figure, chart_data, reg_ma_values(global_values, model_het, error_het, predictors), citations + # return (output_blocks, figure, fig_caption, chart_data, + # reg_ma_values(global_values, model_het, error_het, predictors), citations) # ---------- complex (glm) meta-analysis ---------- @@ -1580,7 +1586,7 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float = n = len(e_data) figure = None - fig_caption = None + # fig_caption = None chart_data = None group_het_values = None group_mean_values = None @@ -1702,16 +1708,18 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float = citations.extend(new_cites) if options.create_graph: - figure, chart_data = MetaWinCharts.chart_forest_plot(effect_sizes.label, forest_data, alpha, - options.bootstrap_mean) - fig_caption = get_text("nest_caption") + MetaWinCharts.caption_forest_plot_text(effect_sizes.label, alpha) - if options.bootstrap_mean is not None: - new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - fig_caption += new_text + create_reference_list(new_cites, True) + figure, chart_data = MetaWinCharts.chart_forest_plot("nested analysis", effect_sizes.label, forest_data, + alpha, options.bootstrap_mean) + # fig_caption = get_text("nest_caption") + MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) + # if options.bootstrap_mean is not None: + # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) + # fig_caption += new_text + create_reference_list(new_cites, True) - return (output_blocks, figure, fig_caption, chart_data, - group_ma_values(global_values, group_mean_values, group_het_values, model_het_values, error_het_values), - citations) + return (output_blocks, figure, chart_data, group_ma_values(global_values, group_mean_values, group_het_values, + model_het_values, error_het_values), citations) + # return (output_blocks, figure, fig_caption, chart_data, + # group_ma_values(global_values, group_mean_values, group_het_values, model_het_values, error_het_values), + # citations) # ---------- trim-and-fill analysis ---------- @@ -1745,7 +1753,7 @@ def trim_and_fill_analysis(data, options, decimal_places: int = 4, alpha: float output_blocks = output_filtered_bad(filtered, bad_data) figure = None - fig_caption = None + # fig_caption = None chart_data = None n = len(e_data) citations = [] @@ -1867,16 +1875,16 @@ def trim_and_fill_analysis(data, options, decimal_places: int = 4, alpha: float decimal_places, alpha, options.log_transformed)) if options.create_graph: - figure, chart_data = MetaWinCharts.chart_trim_fill_plot(effect_sizes.label, tmp_data, n, original_mean, + figure, chart_data = MetaWinCharts.chart_trim_fill_plot(effect_sizes.label, tmp_data, n, original_mean, mean_e) - fig_caption = get_text("trim_fill_caption").format(effect_sizes.label, "Duval and Tweedie 2000a, b") - new_cites = ["Duval_Tweedie_2000a", "Duval_Tweedie_2000b"] - fig_caption += create_reference_list(new_cites, True) + # fig_caption = get_text("trim_fill_caption").format(effect_sizes.label, "Duval and Tweedie 2000a, b") + # new_cites = ["Duval_Tweedie_2000a", "Duval_Tweedie_2000b"] + # fig_caption += create_reference_list(new_cites, True) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) - return output_blocks, figure, fig_caption, chart_data, None, citations + return output_blocks, figure, chart_data, None, citations # ---------- phylogenetic meta-analysis ---------- @@ -2191,7 +2199,7 @@ def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float output_blocks = output_filtered_bad(filtered, bad_data) figure = None - fig_caption = None + # fig_caption = None chart_data = None n = len(e_data) citations = [] @@ -2286,17 +2294,17 @@ def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float citations.extend(new_cites) if options.create_graph: - figure, chart_data = MetaWinCharts.chart_forest_plot(effect_sizes.label, forest_data, alpha, - options.bootstrap_mean) - fig_caption = get_text("jackknife_forest_plot") + \ - MetaWinCharts.caption_forest_plot_text(effect_sizes.label, alpha) - if options.bootstrap_mean is not None: - new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - fig_caption += new_text + create_reference_list(new_cites, True) + figure, chart_data = MetaWinCharts.chart_forest_plot("jackknife analysis", effect_sizes.label, forest_data, + alpha, options.bootstrap_mean) + # fig_caption = get_text("jackknife_forest_plot") + \ + # MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) + # if options.bootstrap_mean is not None: + # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) + # fig_caption += new_text + create_reference_list(new_cites, True) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) - return output_blocks, figure, fig_caption, chart_data, citations + return output_blocks, figure, chart_data, citations # ---------- rank correlation analysis ---------- diff --git a/src/MetaWinCharts.py b/src/MetaWinCharts.py index 10c51ad..d95c02a 100644 --- a/src/MetaWinCharts.py +++ b/src/MetaWinCharts.py @@ -11,11 +11,16 @@ import numpy import scipy.stats -from MetaWinUtils import exponential_label +from MetaWinUtils import exponential_label, get_citation, create_reference_list from MetaWinLanguage import get_text import MetaWinWidgets +# weighting options for the histograms +WEIGHT_NONE = 0 +WEIGHT_INVVAR = 1 +WEIGHT_N = 2 + LINE_STYLES = ("solid", "dashed", "dotted", "dashdot") # MARKER_STYLES = {"point": ".", "circle": "o", "downward triangle": "v", "upward triangle": "^", # "left triangle": "<", "right triangle": ">", "downard tri": "1", "upward tri": "2", "left tri": "3", @@ -35,6 +40,7 @@ "centered right caret": 9} +# ---------- Chart Data Classes ---------- # class BaseChartData: def __init__(self): self.name = "" @@ -383,19 +389,200 @@ def create_edit_panel(self): return self.edit_panel +# ---------- Chart Caption Classes ---------- # +class NormalQuantileCaption: + def __init__(self): + self.upper_limit = None + self.lower_limit = None + self.horizontal_mean = None + self.vertical_mean = None + self.regression = None + self.regression_scatter = None + + def __str__(self): + "Normal Quantile plot following {}. The " + "standardized effect size is the effect size divided by the " + "square-root of its variance. The solid line represents the " + "regression and the dashed lines the 95% prediction envelope." + + return get_text("normal_quantile_caption").format(get_citation("Wang_and_Bushman_1998")) + \ + create_reference_list(["Wang_and_Bushman_1998"], True) + + +class ScatterCaption: + def __init__(self): + self.x_label = "" + self.y_label = "" + + def __str__(self): + return get_text("Scatter plot of {} vs. {}.").format(self.y_label, self.x_label) + + +class HistogramCaption: + def __init__(self): + self.e_label = "" + self.weight_type = WEIGHT_NONE + + def __str__(self): + caption = get_text("Histogram of {} from individual studies.").format(self.e_label) + if self.weight_type == WEIGHT_INVVAR: + caption += get_text(" Counts were weighted by the inverse of the variance of each effect size.") + elif self.weight_type == WEIGHT_N: + caption += get_text(" Counts were weighted by a sample size associated with each effect size.") + return caption + + +class RadialCaption: + def __init__(self): + self.e_label = "" + + def __str__(self): + return get_text("Radial_chart_caption").format(self.e_label) + + +class ForestPlotBaseCaption: + def __init__(self): + self.e_label = "" + self.alpha = 0.05 + self.bootstrap_n = None + + +class ForestPlotCaption(ForestPlotBaseCaption): + def __str__(self): + return get_text("Forest plot of individual effect sizes for each study.") + \ + common_forest_plot_caption(self.e_label, self.alpha, inc_median=False) + + +class RegressionCaption: + def __init__(self): + self.e_label = "" + self.i_label = "" + self.model = "" + self.ref_list = "" + self.citations = [] + + def __str__(self): + return get_text("regression_caption").format(self.e_label, self.i_label, self.model, self.ref_list) + \ + create_reference_list(self.citations, True) + + +class TrimAndFillCaption: + def __init__(self): + self.e_label = "" + self.original_scatter = None + self.inferred_scatter = None + self.original_mean = None + self.inferred_mean = None + + def __str__(self): + "Funnel plot of {} vs. precision, showing the results of a Trim and " + "Fill Analysis ({}). Solid black circles represent the original data; " + "open red circles represent inferred \"missing\" data. The dashed line " + "represents the mean effect size of the original data, the dotted line " + "the mean effect size including the inferred data." + new_cites = ["Duval_Tweedie_2000a", "Duval_Tweedie_2000b"] + return get_text("trim_fill_caption").format(self.e_label, "Duval and Tweedie 2000a, b") + \ + create_reference_list(new_cites, True) + + +class BasicAnalysisCaption(ForestPlotBaseCaption): + def __str__(self): + return get_text("Forest plot of individual effect sizes for each study, as well as the overall mean.") + \ + common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + + +class GroupedAnalysisCaption(ForestPlotBaseCaption): + def __init__(self): + super().__init__() + self.group_label = "" + + def __str__(self): + return get_text("group_forest_plot").format(self.group_label) + \ + common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + + +class NestedAnalysisCaption(ForestPlotBaseCaption): + def __str__(self): + return get_text("nest_caption") + common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + + +class CumulativeAnalysisCaption(ForestPlotBaseCaption): + def __init__(self): + super().__init__() + self.order_label = "" + + def __str__(self): + return get_text("cumulative_forest_plot").format(self.order_label) + \ + common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + + +class JackknifeAnalysisCaption(ForestPlotBaseCaption): + def __str__(self): + return get_text("jackknife_forest_plot") + common_forest_plot_caption(self.e_label, self.alpha, + self.bootstrap_n) + + +def common_forest_plot_caption(effect_name: str, alpha: float = 0.05, bootstrap_n: Optional[int] = None, + inc_median: bool = True) -> str: + # " Effect size measured as {}. The dotted vertical line " + # "represents no effect, or a mean of zero. Circles represent " + # "mean effect size, with the corresponding line " + # "the {:0.0%} confidence interval." + # + text = get_text("forest_plot_common_caption").format(effect_name, 1 - alpha) + if inc_median: + text += get_text("forest_plot_median_caption") + if bootstrap_n is not None: + citation = "Adams_et_1997" + text += get_text("bootstrap_caption").format(bootstrap_n, get_citation(citation)) + \ + create_reference_list([citation], True) + return text + + +# ---------- Main Chart Data Class ---------- # class ChartData: """ an object to contain all data that appears on charts this will allow chart data exporting, as well as open up the possibility of figure editing """ - def __init__(self): + def __init__(self, caption_type): self.x_label = "" self.y_label = "" self.data = [] # special adjustments self.suppress_y = False self.rescale_x = None + # caption + if caption_type == "normal quantile": + self.caption = NormalQuantileCaption() + elif caption_type == "scatter plot": + self.caption = ScatterCaption() + elif caption_type == "histogram": + self.caption = HistogramCaption() + elif caption_type == "radial": + self.caption = RadialCaption() + elif caption_type == "regression": + self.caption = RegressionCaption() + elif caption_type == "trim and fill": + self.caption = TrimAndFillCaption() + elif caption_type == "basic analysis": + self.caption = BasicAnalysisCaption() + elif caption_type == "grouped analysis": + self.caption = GroupedAnalysisCaption() + elif caption_type == "nested analysis": + self.caption = NestedAnalysisCaption() + elif caption_type == "cumulative analysis": + self.caption = CumulativeAnalysisCaption() + elif caption_type == "jackknife analysis": + self.caption = JackknifeAnalysisCaption() + elif caption_type == "forest plot": + self.caption = ForestPlotCaption() + else: + self.caption = "" + + def caption_text(self): + return str(self.caption) def add_scatter(self, name: str, x_data, y_data, marker: Union[str, int] = "o", label="", zorder=0, color="#1f77b4", edgecolors="#1f77b4", size=36, linewidths=1.5, linestyle="solid"): @@ -412,6 +599,7 @@ def add_scatter(self, name: str, x_data, y_data, marker: Union[str, int] = "o", new_scatter.linewidths = linewidths new_scatter.linestyle = linestyle self.data.append(new_scatter) + return new_scatter def add_line(self, name: str, x_min, y_min, x_max, y_max, linestyle="solid", color="silver", zorder=0, linewidth=1.5): @@ -424,6 +612,7 @@ def add_line(self, name: str, x_min, y_min, x_max, y_max, linestyle="solid", col new_line.zorder = zorder new_line.linewidth = linewidth self.data.append(new_line) + return new_line def add_histogram(self, name, cnts, bins, linewidth=1, edgecolor="black", color="#1f77b4", linestyle="solid"): new_hist = HistogramData() @@ -435,6 +624,7 @@ def add_histogram(self, name, cnts, bins, linewidth=1, edgecolor="black", color= new_hist.edgecolor = edgecolor new_hist.color = color self.data.append(new_hist) + return new_hist def add_arc(self, name, xc, yc, height, width, start_angle, end_angle, zorder=0, edgecolor="silver", linestyle="solid", linewidth=1.5): @@ -451,6 +641,7 @@ def add_arc(self, name, xc, yc, height, width, start_angle, end_angle, zorder=0, new_arc.linestyle = linestyle new_arc.linewidth = linewidth self.data.append(new_arc) + return new_arc def add_multi_line(self, name, x_values, y_values, linestyle="solid", color="silver", zorder=0, linewidth=1.5): new_ml = LineData() @@ -462,6 +653,7 @@ def add_multi_line(self, name, x_values, y_values, linestyle="solid", color="sil new_ml.zorder = zorder new_ml.linewidth = linewidth self.data.append(new_ml) + return new_ml def add_ci(self, name, min_x, max_x, y, zorder=0, color="#1f77b4", linestyle="solid", linewidth=1.5): new_ci = ForestCIData() @@ -474,6 +666,7 @@ def add_ci(self, name, min_x, max_x, y, zorder=0, color="#1f77b4", linestyle="so new_ci.linestyle = linestyle new_ci.linewidth = linewidth self.data.append(new_ci) + return new_ci def add_labels(self, name, labels, y_data): new_labels = LabelData() @@ -481,6 +674,7 @@ def add_labels(self, name, labels, y_data): new_labels.labels = labels new_labels.y_pos = y_data self.data.append(new_labels) + return new_labels def add_annotations(self, name, annotations, x_data, y_data): new_annotation = AnnotationData() @@ -489,6 +683,7 @@ def add_annotations(self, name, annotations, x_data, y_data): new_annotation.y = y_data new_annotation.annotations = annotations self.data.append(new_annotation) + return new_annotation def export_to_list(self): outlist = ["X-axis label\t{}\n".format(self.x_label), @@ -557,9 +752,18 @@ def create_figure(chart_data): return figure_canvas -def chart_forest_plot(effect_name, forest_data, alpha: float = 0.05, - bootstrap_n: Optional[int] = None) -> Tuple[FigureCanvasQTAgg, ChartData]: - chart_data = ChartData() +def chart_forest_plot(analysis_type: str, effect_name, forest_data, alpha: float = 0.05, + bootstrap_n: Optional[int] = None, + extra_name: Optional[str] = None) -> Tuple[FigureCanvasQTAgg, ChartData]: + chart_data = ChartData(analysis_type) + chart_data.caption.e_label = effect_name + chart_data.caption.alpha = alpha + chart_data.caption.bootstrap_n = bootstrap_n + if analysis_type == "grouped analysis": + chart_data.caption.group_label = extra_name + elif analysis_type == "cumulative analysis": + chart_data.caption.order_label = extra_name + chart_data.x_label = effect_name chart_data.suppress_y = True @@ -619,13 +823,6 @@ def chart_forest_plot(effect_name, forest_data, alpha: float = 0.05, return figure_canvas, chart_data -def caption_forest_plot_text(effect_name: str, alpha: float = 0.05, inc_median: bool = True) -> str: - text = get_text("forest_plot_common_caption").format(effect_name, 1 - alpha) - if inc_median: - text += get_text("forest_plot_median_caption") - return text - - def add_regression_to_chart(x_name: str, y_name: str, x_data, y_data, slope: float, intercept: float, x_min: float, x_max: float, chart_data) -> None: y_at_min = slope*x_min + intercept @@ -633,14 +830,22 @@ def add_regression_to_chart(x_name: str, y_name: str, x_data, y_data, slope: flo chart_data.x_label = x_name chart_data.y_label = y_name - chart_data.add_scatter(get_text("Point Data"), x_data, y_data, zorder=10) - chart_data.add_line(get_text("Regression Line"), x_min, y_at_min, x_max, y_at_max, zorder=8, color="silver") + chart_data.caption.regression_scatter = chart_data.add_scatter(get_text("Point Data"), x_data, y_data, zorder=10) + chart_data.caption.regression = chart_data.add_line(get_text("Regression Line"), x_min, y_at_min, x_max, y_at_max, + zorder=8, color="silver") -def chart_regression(x_name, y_name, x_data, y_data, slope, intercept) -> Tuple[FigureCanvasQTAgg, ChartData]: +def chart_regression(x_name, y_name, x_data, y_data, slope, intercept, model, ref_list, + citations) -> Tuple[FigureCanvasQTAgg, ChartData]: x_min = numpy.min(x_data) x_max = numpy.max(x_data) - chart_data = ChartData() + chart_data = ChartData("regression") + chart_data.caption.e_label = y_name + chart_data.caption.i_label = x_name + chart_data.caption.model = model + chart_data.caption.ref_list = ref_list + chart_data.caption.citations = citations + add_regression_to_chart(x_name, y_name, x_data, y_data, slope, intercept, x_min, x_max, chart_data) figure_canvas = create_figure(chart_data) @@ -668,21 +873,22 @@ def add_quantile_axes_to_chart(x_data, y_data, slope: float, intercept: float, c y_lower = [y_pos[i] - t_score*math.sqrt(mse*(1 + 1/n + ((x_pos[i] - x_mean)**2)/ss_x)) for i in range(nsteps)] y_upper = [y_pos[i] + t_score*math.sqrt(mse*(1 + 1/n + ((x_pos[i] - x_mean)**2)/ss_x)) for i in range(nsteps)] - chart_data.add_multi_line(get_text("Lower Prediction Limit"), x_pos, y_lower, linestyle="dashed", color="silver", - zorder=3) - chart_data.add_multi_line(get_text("Upper Prediction Limit"), x_pos, y_upper, linestyle="dashed", color="silver", - zorder=3) + chart_data.caption.lower_limit = chart_data.add_multi_line(get_text("Lower Prediction Limit"), x_pos, y_lower, + linestyle="dashed", color="silver", zorder=3) + chart_data.caption.upper_limit = chart_data.add_multi_line(get_text("Upper Prediction Limit"), x_pos, y_upper, + linestyle="dashed", color="silver", zorder=3) # draw center lines - chart_data.add_line(get_text("Horizontal Axis Mean Line"), 0, min(y_min, min(y_lower)), 0, max(y_max, max(y_upper)), - linestyle="dotted", color="silver") - chart_data.add_line(get_text("Vertical Axis Mean Line"), x_min, y_mean, x_max, y_mean, linestyle="dotted", - color="silver") + chart_data.caption.horizontal_mean = chart_data.add_line(get_text("Horizontal Axis Mean Line"), 0, + min(y_min, min(y_lower)), 0, max(y_max, max(y_upper)), + linestyle="dotted", color="silver") + chart_data.caption.vertical_mean = chart_data.add_line(get_text("Vertical Axis Mean Line"), x_min, y_mean, x_max, + y_mean, linestyle="dotted", color="silver") def chart_normal_quantile(x_name, y_name, x_data, y_data, slope, intercept, alpha: float = 0.05) -> Tuple[FigureCanvasQTAgg, ChartData]: - chart_data = ChartData() + chart_data = ChartData("normal quantile") add_quantile_axes_to_chart(x_data, y_data, slope, intercept, chart_data, alpha) x_min = numpy.min(x_data) x_max = numpy.max(x_data) @@ -769,7 +975,8 @@ def add_radial_curve_to_chart(effect_label: str, r: float, min_e: float, max_e: def chart_radial(e_name, x_data, y_data, slope, min_e, max_e, is_log: bool = False) -> Tuple[FigureCanvasQTAgg, ChartData]: - chart_data = ChartData() + chart_data = ChartData("radial") + chart_data.caption.e_label = e_name max_d = numpy.max(numpy.sqrt(numpy.square(x_data) + numpy.square(y_data)))+1 x_min = 0 x_max = (max_d + 1)*math.cos(math.atan(slope)) @@ -782,15 +989,25 @@ def chart_radial(e_name, x_data, y_data, slope, min_e, max_e, is_log: bool = Fal return figure_canvas, chart_data -def chart_histogram(e_data, w_data, n_bins, e_label, weighted: bool = False) -> Tuple[FigureCanvasQTAgg, ChartData]: - if weighted: - cnts, bins = numpy.histogram(e_data, n_bins, weights=w_data) - y_label = get_text("Weighted Count") - else: +def chart_histogram(e_data, w_data, n_bins, e_label, + weighted: int = WEIGHT_NONE) -> Tuple[FigureCanvasQTAgg, ChartData]: + if weighted == WEIGHT_NONE: cnts, bins = numpy.histogram(e_data, n_bins) y_label = get_text("Count") + else: + cnts, bins = numpy.histogram(e_data, n_bins, weights=w_data) + y_label = get_text("Weighted Count") + # if weighted: + # cnts, bins = numpy.histogram(e_data, n_bins, weights=w_data) + # y_label = get_text("Weighted Count") + # else: + # cnts, bins = numpy.histogram(e_data, n_bins) + # y_label = get_text("Count") + + chart_data = ChartData("histogram") + chart_data.caption.e_label = e_label + chart_data.caption.weight_type = weighted - chart_data = ChartData() chart_data.x_label = e_label chart_data.y_label = y_label chart_data.add_histogram("Bin Counts", cnts, bins, edgecolor="black", linewidth=1) @@ -893,7 +1110,9 @@ def chart_phylogeny(root) -> FigureCanvasQTAgg: def chart_scatter(x_data, y_data, x_label: str = "x", y_label: str = "y") -> Tuple[FigureCanvasQTAgg, ChartData]: - chart_data = ChartData() + chart_data = ChartData("scatter plot") + chart_data.caption.x_label = x_label + chart_data.caption.y_label = y_label chart_data.x_label = x_label chart_data.y_label = y_label chart_data.add_scatter(get_text("Point Data"), x_data, y_data) @@ -903,27 +1122,31 @@ def chart_scatter(x_data, y_data, x_label: str = "x", y_label: str = "y") -> Tup def chart_trim_fill_plot(effect_label, data, n, original_mean, new_mean) -> Tuple[FigureCanvasQTAgg, ChartData]: - chart_data = ChartData() + chart_data = ChartData("trim and fill") + chart_data.caption.e_label = effect_label chart_data.x_label = effect_label chart_data.y_label = "{} (1/SE)".format(get_text("Precision")) # plot original points x_data = data[:n, 0] y_data = numpy.reciprocal(numpy.sqrt(data[:n, 2])) - chart_data.add_scatter(get_text("Original Data"), x_data, y_data, color="black", edgecolors="black") + chart_data.caption.original_scatter = chart_data.add_scatter(get_text("Original Data"), x_data, y_data, + color="black", edgecolors="black") y_min = numpy.min(y_data) y_max = numpy.max(y_data) # plot inferred points x_data = data[n:, 0] y_data = numpy.reciprocal(numpy.sqrt(data[n:, 2])) - chart_data.add_scatter(get_text("Inferred Data"), x_data, y_data, edgecolors="red", color="none") + chart_data.caption.inferred_scatter = chart_data.add_scatter(get_text("Inferred Data"), x_data, y_data, + edgecolors="red", color="none") # draw original and new mean - chart_data.add_line(get_text("Original Mean"), original_mean, y_min, original_mean, y_max, color="silver", - linestyle="dashed", zorder=1) - chart_data.add_line(get_text("Inferred Mean"), new_mean, y_min, new_mean, y_max, color="red", linestyle="dashed", - zorder=1) + chart_data.caption.original_mean = chart_data.add_line(get_text("Original Mean"), original_mean, y_min, + original_mean, y_max, color="silver", linestyle="dashed", + zorder=1) + chart_data.caption.inferred_mean = chart_data.add_line(get_text("Inferred Mean"), new_mean, y_min, new_mean, y_max, + color="red", linestyle="dashed", zorder=1) figure_canvas = create_figure(chart_data) return figure_canvas, chart_data diff --git a/src/MetaWinDraw.py b/src/MetaWinDraw.py index 8e45445..bc53f10 100644 --- a/src/MetaWinDraw.py +++ b/src/MetaWinDraw.py @@ -22,17 +22,10 @@ import MetaWinConstants from MetaWinConstants import mean_data_tuple from MetaWinWidgets import add_ok_cancel_help_button_layout, add_effect_choice_to_dialog -from MetaWinUtils import create_reference_list, get_citation import MetaWinCharts from MetaWinLanguage import get_text -# weighting options for the histograms -WEIGHT_NONE = 0 -WEIGHT_INVVAR = 1 -WEIGHT_N = 2 - - class MetaAnalysisDrawScatterDialog(QDialog): def __init__(self, data: MetaWinData): super().__init__() @@ -378,9 +371,9 @@ def draw_scatter_plot(data, x_data_col, y_data_col): x_data = numpy.array(x_data) y_data = numpy.array(y_data) figure, chart_data = MetaWinCharts.chart_scatter(x_data, y_data, x_data_col.label, y_data_col.label) - fig_caption = get_text("Scatter plot of {} vs. {}.").format(y_data_col.label, x_data_col.label) - return figure, fig_caption, chart_data - return None, None, None + # fig_caption = get_text("Scatter plot of {} vs. {}.").format(y_data_col.label, x_data_col.label) + return figure, chart_data + return None, None def draw_scatter_dialog(sender, data): @@ -388,12 +381,12 @@ def draw_scatter_dialog(sender, data): if sender.draw_dialog.exec(): x_data_col = sender.draw_dialog.columns[sender.draw_dialog.x_box.currentIndex()] y_data_col = sender.draw_dialog.columns[sender.draw_dialog.y_box.currentIndex()] - figure, fig_caption, chart_data = draw_scatter_plot(data, x_data_col, y_data_col) + figure, chart_data = draw_scatter_plot(data, x_data_col, y_data_col) if figure is not None: - return figure, fig_caption, chart_data + return figure, chart_data else: MetaWinMessages.report_critical(sender, "Error", "No valid data found for given options.") - return None, None, None + return None, None def calculate_regression(x: numpy.array, y: numpy.array) -> Tuple[float, float]: @@ -439,11 +432,11 @@ def draw_normal_quantile_plot(data, e_data_col, v_data_col, alpha: float = 0.05) figure, chart_data = MetaWinCharts.chart_normal_quantile(get_text("Normal Quantile"), get_text("Standardized Effect Size"), x_data, y_data, slope, intercept, alpha) - fig_caption = get_text("normal_quantile_caption").format(get_citation("Wang_and_Bushman_1998")) + \ - create_reference_list(["Wang_and_Bushman_1998"], True) + # fig_caption = get_text("normal_quantile_caption").format(get_citation("Wang_and_Bushman_1998")) + \ + # create_reference_list(["Wang_and_Bushman_1998"], True) - return figure, fig_caption, chart_data - return None, None, None + return figure, chart_data + return None, None def draw_normal_quantile_dialog(sender, data, last_effect, last_var, alpha: float = 0.05): @@ -451,12 +444,12 @@ def draw_normal_quantile_dialog(sender, data, last_effect, last_var, alpha: floa if sender.draw_dialog.exec(): e_data_col = sender.draw_dialog.columns[sender.draw_dialog.effect_size_box.currentIndex()] v_data_col = sender.draw_dialog.columns[sender.draw_dialog.variance_box.currentIndex()] - figure, fig_caption, chart_data = draw_normal_quantile_plot(data, e_data_col, v_data_col, alpha) + figure, chart_data = draw_normal_quantile_plot(data, e_data_col, v_data_col, alpha) if figure is not None: - return figure, fig_caption, chart_data + return figure, chart_data else: MetaWinMessages.report_critical(sender, "Error", "No valid data found for given options.") - return None, None, None + return None, None def draw_radial_plot(data, e_data_col, v_data_col, is_log: bool = False): @@ -489,10 +482,10 @@ def draw_radial_plot(data, e_data_col, v_data_col, is_log: bool = False): figure, chart_data = MetaWinCharts.chart_radial(e_data_col.label, x_data, y_data, slope_through_origin, min_e, max_e, is_log) - fig_caption = get_text("Radial_chart_caption").format(e_data_col.label) - fig_caption += create_reference_list(["Galbraith_1988", "Galbraith_1994"], True) - return figure, fig_caption, chart_data - return None, None, None + # fig_caption = get_text("Radial_chart_caption").format(e_data_col.label) + # fig_caption += create_reference_list(["Galbraith_1988", "Galbraith_1994"], True) + return figure, chart_data + return None, None def draw_radial_dialog(sender, data, last_effect, last_var): @@ -501,12 +494,12 @@ def draw_radial_dialog(sender, data, last_effect, last_var): e_data_col = sender.draw_dialog.columns[sender.draw_dialog.effect_size_box.currentIndex()] v_data_col = sender.draw_dialog.columns[sender.draw_dialog.variance_box.currentIndex()] is_log = sender.draw_dialog.log_transform_box.isChecked() - figure, fig_caption, chart_data = draw_radial_plot(data, e_data_col, v_data_col, is_log) + figure, chart_data = draw_radial_plot(data, e_data_col, v_data_col, is_log) if figure is not None: - return figure, fig_caption, chart_data + return figure, chart_data else: MetaWinMessages.report_critical(sender, "Error", "No valid data found for given options.") - return None, None, None + return None, None def draw_histogram_plot(data, e_data_col, w_data_col, weight_type, n_bins: int): @@ -517,10 +510,10 @@ def draw_histogram_plot(data, e_data_col, w_data_col, weight_type, n_bins: int): for r, row in enumerate(data.rows): if row.not_filtered(): e = data.check_value(r, e_data_col.position(), value_type=MetaWinConstants.VALUE_NUMBER) - if weight_type != WEIGHT_NONE: + if weight_type != MetaWinCharts.WEIGHT_NONE: w = data.check_value(r, w_data_col.position(), value_type=MetaWinConstants.VALUE_NUMBER) if (e is not None) and (w is not None): - if weight_type == WEIGHT_INVVAR: + if weight_type == MetaWinCharts.WEIGHT_INVVAR: if w > 0: w_data.append(1/w) e_data.append(e) @@ -540,20 +533,9 @@ def draw_histogram_plot(data, e_data_col, w_data_col, weight_type, n_bins: int): if len(e_data) > 0: e_data = numpy.array(e_data) w_data = numpy.array(w_data) - if weight_type == WEIGHT_NONE: - weighted = False - else: - weighted = True - - figure, chart_data = MetaWinCharts.chart_histogram(e_data, w_data, n_bins, e_data_col.label, weighted) - fig_caption = get_text("Histogram of {} from individual studies.").format(e_data_col.label) - if weight_type == WEIGHT_INVVAR: - fig_caption += get_text(" Counts were weighted by the inverse of the variance of each effect size.") - elif weight_type == WEIGHT_N: - fig_caption += get_text(" Counts were weighted by a sample size associated with each effect size.") - return figure, fig_caption, chart_data - else: - return None, None, None + figure, chart_data = MetaWinCharts.chart_histogram(e_data, w_data, n_bins, e_data_col.label, weight_type) + return figure, chart_data + return None, None def draw_histogram_dialog(sender, data, last_effect, last_var): @@ -562,18 +544,18 @@ def draw_histogram_dialog(sender, data, last_effect, last_var): e_data_col = sender.draw_dialog.columns[sender.draw_dialog.effect_size_box.currentIndex()] w_data_col = sender.draw_dialog.columns[sender.draw_dialog.weight_box.currentIndex()] if sender.draw_dialog.weight_var.isChecked(): - weight_type = WEIGHT_INVVAR + weight_type = MetaWinCharts.WEIGHT_INVVAR elif sender.draw_dialog.weight_n.isChecked(): - weight_type = WEIGHT_N + weight_type = MetaWinCharts.WEIGHT_N else: - weight_type = WEIGHT_NONE + weight_type = MetaWinCharts.WEIGHT_NONE n_bins = int(sender.draw_dialog.bin_edit.text()) - figure, fig_caption, chart_data = draw_histogram_plot(data, e_data_col, w_data_col, weight_type, n_bins) + figure, chart_data = draw_histogram_plot(data, e_data_col, w_data_col, weight_type, n_bins) if figure is not None: - return figure, fig_caption, chart_data + return figure, chart_data else: MetaWinMessages.report_critical(sender, "Error", "No valid data found for given options.") - return None, None, None + return None, None def draw_forest_plot(data, e_data_col, v_data_col, alpha: float = 0.05): @@ -594,11 +576,11 @@ def draw_forest_plot(data, e_data_col, v_data_col, alpha: float = 0.05): else: filtered.append(row.label) - figure, chart_data = MetaWinCharts.chart_forest_plot(e_data_col.label, data_list, alpha, None) + figure, chart_data = MetaWinCharts.chart_forest_plot("forest plot", e_data_col.label, data_list, alpha, None) - fig_caption = get_text("Forest plot of individual effect sizes for each study.") + \ - MetaWinCharts.caption_forest_plot_text(e_data_col.label, alpha, inc_median=False) - return figure, fig_caption, chart_data + # fig_caption = get_text("Forest plot of individual effect sizes for each study.") + \ + # MetaWinCharts.common_forest_plot_caption(e_data_col.label, alpha, inc_median=False) + return figure, chart_data def draw_forest_dialog(sender, data, last_effect, last_var, alpha: float = 0.05): @@ -606,12 +588,12 @@ def draw_forest_dialog(sender, data, last_effect, last_var, alpha: float = 0.05) if sender.draw_dialog.exec(): e_data_col = sender.draw_dialog.columns[sender.draw_dialog.effect_size_box.currentIndex()] v_data_col = sender.draw_dialog.columns[sender.draw_dialog.variance_box.currentIndex()] - figure, fig_caption, chart_data = draw_forest_plot(data, e_data_col, v_data_col, alpha) + figure, chart_data = draw_forest_plot(data, e_data_col, v_data_col, alpha) if figure is not None: - return figure, fig_caption, chart_data + return figure, chart_data else: MetaWinMessages.report_critical(sender, "Error", "No valid data found for given options.") - return None, None, None + return None, None def edit_figure(sender, chart_data): diff --git a/src/MetaWinMain.py b/src/MetaWinMain.py index 136d774..2fde591 100644 --- a/src/MetaWinMain.py +++ b/src/MetaWinMain.py @@ -77,7 +77,6 @@ def __init__(self, config: dict): self.empty_col_num = 10 self.empty_row_num = 15 self.chart_data = None - self.chart_caption = "" self.init_ui() def init_ui(self): @@ -696,18 +695,18 @@ def meta_analysis(self) -> None: norm_ci = False else: norm_ci = True - output, figure, fig_caption, chart_data = MetaWinAnalysis.meta_analysis(self, self.data, self.last_effect, - self.last_var, self.output_decimals, - self.alpha, self.phylogeny, norm_ci) + output, figure, chart_data = MetaWinAnalysis.meta_analysis(self, self.data, self.last_effect, + self.last_var, self.output_decimals, + self.alpha, self.phylogeny, norm_ci) if output is not None: self.write_multi_output_blocks(output) self.main_area.setCurrentIndex(1) if figure is not None: - self.show_figure(figure, fig_caption, chart_data) + self.show_figure(figure, chart_data) else: MetaWinMessages.report_warning(self, get_text("Warning"), get_text("No data has been loaded.")) - def show_figure(self, figure, fig_caption: str, chart_data) -> None: + def show_figure(self, figure, chart_data) -> None: """ Replace the current figure in the graphics tab with a new figure, toolbar, and caption """ @@ -720,8 +719,7 @@ def show_figure(self, figure, fig_caption: str, chart_data) -> None: toolbar = NavigationToolbar2QT(figure, None) self.save_graph_action.triggered.connect(toolbar.save_figure) caption_box = QTextEdit() - caption_box.setText(fig_caption) - self.chart_caption = fig_caption + caption_box.setText(chart_data.caption_text()) self.graph_layout.addWidget(figure, stretch=8) self.graph_layout.addWidget(caption_box, stretch=1) self.chart_data = chart_data @@ -744,41 +742,39 @@ def refresh_tree_panel(self) -> None: def draw_scatter_plot(self) -> None: if self.data is not None: - figure, fig_caption, chart_data = MetaWinDraw.draw_scatter_dialog(self, self.data) + figure, chart_data = MetaWinDraw.draw_scatter_dialog(self, self.data) if figure is not None: - self.show_figure(figure, fig_caption, chart_data) + self.show_figure(figure, chart_data) self.main_area.setCurrentIndex(2) def draw_histogram(self) -> None: if self.data is not None: - figure, fig_caption, chart_data = MetaWinDraw.draw_histogram_dialog(self, self.data, self.last_effect, - self.last_var) + figure, chart_data = MetaWinDraw.draw_histogram_dialog(self, self.data, self.last_effect, self.last_var) if figure is not None: - self.show_figure(figure, fig_caption, chart_data) + self.show_figure(figure, chart_data) self.main_area.setCurrentIndex(2) def draw_normal_quantile_plot(self) -> None: if self.data is not None: - figure, fig_caption, chart_data = MetaWinDraw.draw_normal_quantile_dialog(self, self.data, self.last_effect, - self.last_var) + figure, chart_data = MetaWinDraw.draw_normal_quantile_dialog(self, self.data, self.last_effect, + self.last_var) if figure is not None: - self.show_figure(figure, fig_caption, chart_data) + self.show_figure(figure, chart_data) self.main_area.setCurrentIndex(2) def draw_radial_plot(self) -> None: if self.data is not None: - figure, fig_caption, chart_data = MetaWinDraw.draw_radial_dialog(self, self.data, self.last_effect, - self.last_var) + figure, chart_data = MetaWinDraw.draw_radial_dialog(self, self.data, self.last_effect, self.last_var) if figure is not None: - self.show_figure(figure, fig_caption, chart_data) + self.show_figure(figure, chart_data) self.main_area.setCurrentIndex(2) def draw_forest_plot(self) -> None: if self.data is not None: - figure, fig_caption, chart_data = MetaWinDraw.draw_forest_dialog(self, self.data, self.last_effect, - self.last_var, self.alpha) + figure, chart_data = MetaWinDraw.draw_forest_dialog(self, self.data, self.last_effect, self.last_var, + self.alpha) if figure is not None: - self.show_figure(figure, fig_caption, chart_data) + self.show_figure(figure, chart_data) self.main_area.setCurrentIndex(2) def clear_filters(self) -> None: @@ -851,8 +847,7 @@ def edit_graph(self): if self.chart_data is not None: figure = MetaWinDraw.edit_figure(self, self.chart_data) if figure is not None: - caption = self.chart_caption - self.show_figure(figure, caption, self.chart_data) + self.show_figure(figure, self.chart_data) def change_conf_int_distribution(self): if self.confidence_interval_dist == "Normal": diff --git a/tests/test_metawin.py b/tests/test_metawin.py index b47364a..237f6e4 100644 --- a/tests/test_metawin.py +++ b/tests/test_metawin.py @@ -28,7 +28,7 @@ import MetaWinDraw -TEST_FIGURES = False +TEST_FIGURES = True class TestFigureDialog(QDialog): @@ -291,11 +291,11 @@ def test_simple_meta_analysis(): options.rosenberg_failsafe = 0.05 options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() mean_values = analysis_values.mean_data @@ -328,11 +328,11 @@ def test_simple_meta_analysis_lep(): options.effect_vars = data.cols[5] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() mean_values = analysis_values.mean_data @@ -365,11 +365,11 @@ def test_simple_meta_analysis_lep_randeff(): options.effect_vars = data.cols[5] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() mean_values = analysis_values.mean_data @@ -405,7 +405,7 @@ def test_simple_meta_analysis_random_effects(): options.rosenthal_failsafe = 0.05 options.rosenberg_failsafe = 0.05 - output, _, _, _, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, _, _, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) mean_values = analysis_values.mean_data @@ -443,10 +443,10 @@ def test_simple_meta_analysis_bootstrap(): options.bootstrap_mean = 9999 options.create_graph = True - output, figure, fig_caption, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -483,11 +483,11 @@ def test_group_meta_analysis_lep_suborders(): options.groups = data.cols[1] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() global_values = analysis_values.global_values @@ -537,11 +537,11 @@ def test_group_meta_analysis_lep_suborders_rand_eff(): options.random_effects = True options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() global_values = analysis_values.global_values @@ -618,11 +618,11 @@ def test_group_meta_analysis_lep_families(): options.create_graph = True data.cols[2].group_filter = ["Yponomeutidae", "Lycaenidae", "Hesperiidae"] - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() global_values = analysis_values.global_values @@ -661,10 +661,10 @@ def test_group_meta_analysis_bootstrap(): options.bootstrap_mean = 9999 options.create_graph = True - output, figure, fig_caption, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -681,10 +681,10 @@ def test_group_meta_analysis_lrr(): options.create_graph = True - output, figure, fig_caption, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -713,10 +713,10 @@ def test_cumulative_meta_analysis(): options.create_graph = True - output, figure, fig_caption, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -732,10 +732,10 @@ def test_cumulative_meta_analysis_bootstrap(): options.create_graph = True - output, figure, fig_caption, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, _ = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -761,11 +761,11 @@ def test_regression_meta_analysis_lep(): options.independent_variable = data.cols[3] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() global_values = analysis_values.global_values @@ -807,11 +807,11 @@ def test_regression_meta_analysis_lep_randeff(): options.random_effects = True options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() global_values = analysis_values.global_values @@ -872,7 +872,7 @@ def test_complex_meta_analysis_lep_simple_regression(): options.continuous_vars = [data.cols[3]] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, _, _, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) global_values = analysis_values.global_values @@ -918,7 +918,7 @@ def test_complex_meta_analysis_lep_group(): options.continuous_vars = [] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, _, _, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) global_values = analysis_values.global_values @@ -966,7 +966,7 @@ def test_complex_meta_analysis_lep(): options.continuous_vars = [data.cols[3]] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, _, _, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) global_values = analysis_values.global_values @@ -1025,7 +1025,7 @@ def test_complex_meta_analysis_lep_randomeff(): options.random_effects = True options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, _, _, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) global_values = analysis_values.global_values @@ -1088,11 +1088,11 @@ def test_nested_meta_analysis_lep(): options.create_graph = True data.cols[2].group_filter = ["Yponomeutidae", "Lycaenidae", "Hesperiidae"] - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() global_values = analysis_values.global_values @@ -1200,8 +1200,8 @@ def test_scatter_plot(): x_col = data.cols[11] y_col = data.cols[10] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_scatter_plot(data, x_col, y_col) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_scatter_plot(data, x_col, y_col) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1210,8 +1210,8 @@ def test_normal_quantile_plot(): e_col = data.cols[10] v_col = data.cols[11] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_normal_quantile_plot(data, e_col, v_col) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_normal_quantile_plot(data, e_col, v_col) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1220,8 +1220,8 @@ def test_radial_plot_d(): e_col = data.cols[10] v_col = data.cols[11] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_radial_plot(data, e_col, v_col, False) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_radial_plot(data, e_col, v_col, False) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1230,8 +1230,8 @@ def test_radial_plot_lnrr(): e_col = data.cols[10] v_col = data.cols[11] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_radial_plot(data, e_col, v_col, True) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_radial_plot(data, e_col, v_col, True) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1240,8 +1240,8 @@ def test_histogram_d_unweighted(): e_col = data.cols[10] v_col = data.cols[11] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_histogram_plot(data, e_col, v_col, 0, 10) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_histogram_plot(data, e_col, v_col, 0, 10) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1250,8 +1250,8 @@ def test_histogram_d_weighted_invvar(): e_col = data.cols[10] v_col = data.cols[11] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_histogram_plot(data, e_col, v_col, 1, 10) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_histogram_plot(data, e_col, v_col, 1, 10) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1260,8 +1260,8 @@ def test_histogram_d_weighted_sample_size(): e_col = data.cols[10] v_col = data.cols[2] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_histogram_plot(data, e_col, v_col, 2, 15) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_histogram_plot(data, e_col, v_col, 2, 15) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1270,8 +1270,8 @@ def test_forest_plot(): e_col = data.cols[10] v_col = data.cols[11] if TEST_FIGURES: - figure, fig_caption, _ = MetaWinDraw.draw_forest_plot(data, e_col, v_col) - test_win = TestFigureDialog(figure, fig_caption) + figure, chart_data = MetaWinDraw.draw_forest_plot(data, e_col, v_col) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1296,11 +1296,11 @@ def test_trim_and_fill_analysis(): options.create_graph = True options.k_estimator = "L" - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1325,11 +1325,11 @@ def test_trim_and_fill_analysis_negative_mean(): options.create_graph = True options.k_estimator = "R" - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1354,11 +1354,11 @@ def test_jackknife(): options.effect_vars = data.cols[5] options.create_graph = True - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) if TEST_FIGURES: - test_win = TestFigureDialog(figure, fig_caption) + test_win = TestFigureDialog(figure, chart_data.caption_text()) test_win.exec() @@ -1377,22 +1377,21 @@ def test_phylogenetic_simple_test(): options.tip_names = data.cols[0] print("UNWEIGHTED") options.structure = MetaWinAnalysis.SIMPLE_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) print() print() print("WEIGHTED") options.effect_vars = data.cols[2] - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) print() print() print("PHYLOGENETIC") options.structure = MetaWinAnalysis.PHYLOGENETIC_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, - tree=tree) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) @@ -1409,12 +1408,11 @@ def test_phylogenetic_glm_simple(): options.random_effects = True options.structure = MetaWinAnalysis.SIMPLE_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) options.structure = MetaWinAnalysis.PHYLOGENETIC_MA print(data.column_labels()[1]) - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, - tree=tree) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) print() @@ -1426,11 +1424,10 @@ def test_phylogenetic_glm_simple(): options.effect_vars = data.cols[4] options.structure = MetaWinAnalysis.SIMPLE_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) options.structure = MetaWinAnalysis.PHYLOGENETIC_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, - tree=tree) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) print() @@ -1441,11 +1438,10 @@ def test_phylogenetic_glm_simple(): options.effect_data = data.cols[5] options.effect_vars = data.cols[6] options.structure = MetaWinAnalysis.SIMPLE_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) options.structure = MetaWinAnalysis.PHYLOGENETIC_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, - tree=tree) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) print() @@ -1456,11 +1452,10 @@ def test_phylogenetic_glm_simple(): options.effect_data = data.cols[7] options.effect_vars = data.cols[8] options.structure = MetaWinAnalysis.SIMPLE_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) options.structure = MetaWinAnalysis.PHYLOGENETIC_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, - tree=tree) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) print() @@ -1471,11 +1466,10 @@ def test_phylogenetic_glm_simple(): options.effect_data = data.cols[9] options.effect_vars = data.cols[10] options.structure = MetaWinAnalysis.SIMPLE_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) options.structure = MetaWinAnalysis.PHYLOGENETIC_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, - tree=tree) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) print() @@ -1486,9 +1480,8 @@ def test_phylogenetic_glm_simple(): options.effect_data = data.cols[11] options.effect_vars = data.cols[12] options.structure = MetaWinAnalysis.SIMPLE_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4) print_test_output(output) options.structure = MetaWinAnalysis.PHYLOGENETIC_MA - output, figure, fig_caption, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, - tree=tree) + output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) From 5c4f7ae8d8f5dfa5414e11945a7dce34918cd18c Mon Sep 17 00:00:00 2001 From: msrosenberg Date: Fri, 9 Sep 2022 14:36:06 -0400 Subject: [PATCH 5/6] captions now include custom figure styles --- resources/metawin_help.html | 20 +++ src/MetaWinAnalysisFunctions.py | 67 ++-------- src/MetaWinCharts.py | 209 ++++++++++++++++++++++---------- src/MetaWinLanguage.py | 45 +++++-- tests/test_metawin.py | 39 ++++++ 5 files changed, 240 insertions(+), 140 deletions(-) diff --git a/resources/metawin_help.html b/resources/metawin_help.html index a10fb80..aff909e 100644 --- a/resources/metawin_help.html +++ b/resources/metawin_help.html @@ -55,6 +55,7 @@

Help Table of Contents

  • Graph Tab
  • Phylogeny Tab
  • @@ -424,6 +425,25 @@

    Markers

    the chosen marker is a filled marker. Unfilled markers will turn invisible if the no fill option is checked.

    +

    A Note on Captions

    +

    + MetaWin will autogenerate captions for each figure it creates, including + references in some cases. As style elements of figures can be user edited, this creates a bit of a + challenge for captioning, particularly as it relates to specifying color. Computers can + generate over 16.7 million unique colors, most of which obviously do not have unique names. + Although exact, specifying colors by RGB or hex number in captions would not be particularly + useful. Therefore, whenever a caption includes a reference to a color, a color name is chosen by + matching the specific color to the closest named color from a set of approximately 1,000 + named colors based on a survey done at + XKCD. This set was chosen primarily because of it's size; other standard color names, + e.g., CSS4, only include names for fewer than 150 colors. With that many crowd-coursed color + names, many me be a bit odd, so simply be warned if strange color labels appear. +

    +

    + Currently only line and markers description appear in captions. Line descriptions include only color + and style (solid, dahsed, etc.), while markers include color and shape (two colors if the primary + and border colors are different). Properties such as size and thickness are not included in captions. +

    Phylogeny Tab

    diff --git a/src/MetaWinAnalysisFunctions.py b/src/MetaWinAnalysisFunctions.py index 5982f53..fe0579d 100644 --- a/src/MetaWinAnalysisFunctions.py +++ b/src/MetaWinAnalysisFunctions.py @@ -525,13 +525,6 @@ def output_filtered_bad(filtered: list, bad_data: list) -> list: return output_blocks -# def caption_bootstrap_text(bs_n: int): -# """ -# extra figure caption info for forest plots with bootstrap confidence intervals -# """ -# citation = "Adams_et_1997" -# return get_text("bootstrap_caption").format(bs_n, get_citation(citation)), [citation] - # ---- I'm not convinced this is correct, which is why I'm removing it for now --- # def calc_aic(q: float, n: int, p: int) -> float: # """ @@ -577,7 +570,6 @@ def simple_meta_analysis(data, options, decimal_places: int = 4, alpha: float = output_blocks = output_filtered_bad(filtered, bad_data) figure = None - # fig_caption = None chart_data = None n = len(e_data) citations = [] @@ -640,20 +632,13 @@ def simple_meta_analysis(data, options, decimal_places: int = 4, alpha: float = if options.create_graph: figure, chart_data = MetaWinCharts.chart_forest_plot("basic analysis", effect_sizes.label, forest_data, - alpha, options.bootstrap_mean) - # fig_caption = get_text("Forest plot of individual effect sizes for each study, as well as the overall " - # "mean.") + MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) - # if options.bootstrap_mean is not None: - # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - # # fig_caption += new_text + create_reference_list(new_cites, True) + alpha, options.bootstrap_mean, normal_ci=norm_ci) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) qt, df, p, pooled_var, i2 = None, None, None, None, None mean_data = None - # return (output_blocks, figure, fig_caption, chart_data, simple_ma_values(mean_data, pooled_var, qt, df, p, i2), - # citations) return output_blocks, figure, chart_data, simple_ma_values(mean_data, pooled_var, qt, df, p, i2), citations @@ -676,7 +661,6 @@ def check_data_for_group(output_blocks, n, group_cnts, group_label) -> bool: output.append(get_text("Please filter problematic data to continue")) output_blocks.append(output) return all_good - # return True def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = 0.05, norm_ci: bool = True): @@ -717,7 +701,6 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = output_blocks = output_filtered_bad(filtered, bad_data) figure = None - # fig_caption = None chart_data = None n = len(e_data) g_cnt = len(group_names) @@ -865,12 +848,8 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = if options.create_graph: figure, chart_data = MetaWinCharts.chart_forest_plot("grouped analysis", effect_sizes.label, forest_data, - alpha, options.bootstrap_mean, groups.label) - # fig_caption = get_text("group_forest_plot").format(groups.label) + \ - # MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) - # if options.bootstrap_mean is not None: - # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - # fig_caption += new_text + create_reference_list(new_cites, True) + alpha, options.bootstrap_mean, groups.label, + normal_ci=norm_ci) global_values = simple_ma_values(global_mean_data, pooled_var, qt, df, pqt, i2) else: @@ -882,8 +861,6 @@ def grouped_meta_analysis(data, options, decimal_places: int = 4, alpha: float = return (output_blocks, figure, chart_data, group_ma_values(global_values, group_mean_values, group_het_values, model_het, error_het), citations) - # return (output_blocks, figure, fig_caption, chart_data, - # group_ma_values(global_values, group_mean_values, group_het_values, model_het, error_het), citations) # ---------- cumulative meta-analysis ---------- @@ -910,7 +887,6 @@ def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: floa output_blocks = output_filtered_bad(filtered, bad_data) figure = None - # fig_caption = None chart_data = None n = len(tmp_data) if n > 1: @@ -975,17 +951,12 @@ def cumulative_meta_analysis(data, options, decimal_places: int = 4, alpha: floa if options.create_graph: figure, chart_data = MetaWinCharts.chart_forest_plot("cumulative analysis", effect_sizes.label, cumulative_means, alpha, options.bootstrap_mean, - order.label) - # fig_caption = get_text("cumulative_forest_plot").format(order.label) + \ - # MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) - # if options.bootstrap_mean is not None: - # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - # fig_caption += new_text + create_reference_list(new_cites, True) + order.label, normal_ci=norm_ci) + else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) return output_blocks, figure, chart_data - # return output_blocks, figure, fig_caption, chart_data # ---------- simple regression meta-analysis ---------- @@ -1038,7 +1009,6 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa output_blocks = output_filtered_bad(filtered, bad_data) figure = None - # fig_caption = None chart_data = None model_het = None error_het = None @@ -1152,16 +1122,11 @@ def regression_meta_analysis(data, options, decimal_places: int = 4, alpha: floa figure, chart_data = MetaWinCharts.chart_regression(options.independent_variable.label, effect_sizes.label, x_data, e_data, b1_slope, b0_intercept, model, ref_list, fig_citations) - # fig_caption = get_text("regression_caption").format(effect_sizes.label, options.independent_variable.label, - # model, ref_list) + \ - # create_reference_list(fig_citations, True) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) return output_blocks, figure, chart_data, reg_ma_values(global_values, model_het, error_het, predictors), citations - # return (output_blocks, figure, fig_caption, chart_data, - # reg_ma_values(global_values, model_het, error_het, predictors), citations) # ---------- complex (glm) meta-analysis ---------- @@ -1586,7 +1551,6 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float = n = len(e_data) figure = None - # fig_caption = None chart_data = None group_het_values = None group_mean_values = None @@ -1709,17 +1673,10 @@ def nested_meta_analysis(data, options, decimal_places: int = 4, alpha: float = if options.create_graph: figure, chart_data = MetaWinCharts.chart_forest_plot("nested analysis", effect_sizes.label, forest_data, - alpha, options.bootstrap_mean) - # fig_caption = get_text("nest_caption") + MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) - # if options.bootstrap_mean is not None: - # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - # fig_caption += new_text + create_reference_list(new_cites, True) + alpha, options.bootstrap_mean, normal_ci=norm_ci) return (output_blocks, figure, chart_data, group_ma_values(global_values, group_mean_values, group_het_values, model_het_values, error_het_values), citations) - # return (output_blocks, figure, fig_caption, chart_data, - # group_ma_values(global_values, group_mean_values, group_het_values, model_het_values, error_het_values), - # citations) # ---------- trim-and-fill analysis ---------- @@ -1753,7 +1710,6 @@ def trim_and_fill_analysis(data, options, decimal_places: int = 4, alpha: float output_blocks = output_filtered_bad(filtered, bad_data) figure = None - # fig_caption = None chart_data = None n = len(e_data) citations = [] @@ -1877,9 +1833,6 @@ def trim_and_fill_analysis(data, options, decimal_places: int = 4, alpha: float if options.create_graph: figure, chart_data = MetaWinCharts.chart_trim_fill_plot(effect_sizes.label, tmp_data, n, original_mean, mean_e) - # fig_caption = get_text("trim_fill_caption").format(effect_sizes.label, "Duval and Tweedie 2000a, b") - # new_cites = ["Duval_Tweedie_2000a", "Duval_Tweedie_2000b"] - # fig_caption += create_reference_list(new_cites, True) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) @@ -2199,7 +2152,6 @@ def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float output_blocks = output_filtered_bad(filtered, bad_data) figure = None - # fig_caption = None chart_data = None n = len(e_data) citations = [] @@ -2295,12 +2247,7 @@ def jackknife_meta_analysis(data, options, decimal_places: int = 4, alpha: float if options.create_graph: figure, chart_data = MetaWinCharts.chart_forest_plot("jackknife analysis", effect_sizes.label, forest_data, - alpha, options.bootstrap_mean) - # fig_caption = get_text("jackknife_forest_plot") + \ - # MetaWinCharts.common_forest_plot_caption(effect_sizes.label, alpha) - # if options.bootstrap_mean is not None: - # new_text, new_cites = caption_bootstrap_text(options.bootstrap_mean) - # fig_caption += new_text + create_reference_list(new_cites, True) + alpha, options.bootstrap_mean, normal_ci=norm_ci) else: output_blocks.append([get_text("Fewer than two studies were valid for analysis")]) diff --git a/src/MetaWinCharts.py b/src/MetaWinCharts.py index d95c02a..77aa5b3 100644 --- a/src/MetaWinCharts.py +++ b/src/MetaWinCharts.py @@ -8,6 +8,7 @@ from matplotlib import patches from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg from matplotlib.figure import Figure +from matplotlib.colors import XKCD_COLORS, hex2color import numpy import scipy.stats @@ -39,6 +40,10 @@ "centered upward caret": 10, "centered downward caret": 11, "centered left caret": 8, "centered right caret": 9} +UNFILLED_MARKERS = {"point", "plus", "X", "vertical line", "horizontal line", "tick left", "tick right", "tick up", + "tick down", "upward caret", "downward caret", "left caret", "right caret", + "centered upward caret", "centered downward caret", "centered left caret", "centered right caret"} + # ---------- Chart Data Classes ---------- # class BaseChartData: @@ -133,6 +138,27 @@ def update_style(self): self.size = float(self.size_box.text()) self.marker = MARKER_STYLES[self.marker_box.currentText()] + def style_text(self) -> str: + marker_name = list(MARKER_STYLES.keys())[list(MARKER_STYLES.values()).index(self.marker)] + if marker_name.endswith("s"): + s = "ses" + else: + s = "s" + if marker_name in UNFILLED_MARKERS: + if self.color == "none": + return get_text("nothing (marker is invisible)") + else: + return find_color_name(self.color) + " " + marker_name + s + else: + if self.color == self.edgecolors: + return find_color_name(self.color) + " " + marker_name + s + else: + if self.color == "none": + return get_text("marker_style_open_text").format(marker_name, s, find_color_name(self.edgecolors)) + else: + return get_text("marker_style_text").format(find_color_name(self.color), marker_name, s, + find_color_name(self.edgecolors)) + class HistogramData(BaseChartData): """ @@ -312,6 +338,9 @@ def update_style(self): self.linewidth = float(self.linewidth_box.text()) self.color = self.color_button.color + def style_text(self) -> str: + return self.linestyle + " " + find_color_name(self.color) + " line" + class ArcData(BaseChartData): """ @@ -400,12 +429,14 @@ def __init__(self): self.regression_scatter = None def __str__(self): - "Normal Quantile plot following {}. The " - "standardized effect size is the effect size divided by the " - "square-root of its variance. The solid line represents the " - "regression and the dashed lines the 95% prediction envelope." - - return get_text("normal_quantile_caption").format(get_citation("Wang_and_Bushman_1998")) + \ + regression_text = self.regression.style_text() + upper_text = self.upper_limit.style_text() + lower_text = self.lower_limit.style_text() + if upper_text == lower_text: + style_text = get_text("normal_quantile_style1").format(regression_text, upper_text) + else: + style_text = get_text("normal_quantile_style2").format(regression_text, upper_text, lower_text) + return get_text("normal_quantile_caption").format(get_citation("Wang_and_Bushman_1998")) + style_text + \ create_reference_list(["Wang_and_Bushman_1998"], True) @@ -440,19 +471,6 @@ def __str__(self): return get_text("Radial_chart_caption").format(self.e_label) -class ForestPlotBaseCaption: - def __init__(self): - self.e_label = "" - self.alpha = 0.05 - self.bootstrap_n = None - - -class ForestPlotCaption(ForestPlotBaseCaption): - def __str__(self): - return get_text("Forest plot of individual effect sizes for each study.") + \ - common_forest_plot_caption(self.e_label, self.alpha, inc_median=False) - - class RegressionCaption: def __init__(self): self.e_label = "" @@ -475,20 +493,76 @@ def __init__(self): self.inferred_mean = None def __str__(self): - "Funnel plot of {} vs. precision, showing the results of a Trim and " - "Fill Analysis ({}). Solid black circles represent the original data; " - "open red circles represent inferred \"missing\" data. The dashed line " - "represents the mean effect size of the original data, the dotted line " - "the mean effect size including the inferred data." + original_mean_text = self.original_mean.style_text() + inferred_mean_text = self.inferred_mean.style_text() + original_marker_text = self.original_scatter.style_text() + inferred_marker_text = self.inferred_scatter.style_text() new_cites = ["Duval_Tweedie_2000a", "Duval_Tweedie_2000b"] - return get_text("trim_fill_caption").format(self.e_label, "Duval and Tweedie 2000a, b") + \ - create_reference_list(new_cites, True) + return get_text("trim_fill_caption").format(self.e_label, "Duval and Tweedie 2000a, b", original_marker_text, + inferred_marker_text, original_mean_text, + inferred_mean_text) + create_reference_list(new_cites, True) + + +class ForestPlotBaseCaption: + def __init__(self): + self.e_label = "" + self.alpha = 0.05 + self.bootstrap_n = None + self.normal_ci = True + self.no_effect = None + self.means = None + self.medians = None + self.boot = None + self.bias = None + + def base_forest_plot_caption(self) -> str: + """ + Basic forest plot description + """ + return get_text("forest_plot_common_caption1").format(self.e_label, self.no_effect.style_text()) + + def mid_forest_plot_caption(self) -> str: + """ + Middle part of forest plot captions, when no study specific intervals are included + """ + if self.normal_ci: + dist_text = get_text("normal_ci_dist") + else: + dist_text = get_text("t_ci_dist") + return get_text("mid_forest_plot_caption").format(self.means.style_text(), 1-self.alpha, dist_text) + + def extra_forest_plot_caption(self, inc_median: bool = True) -> str: + """ + Final part of forest plot captions, to indicate median and bootstrap style markers + """ + text = "" + if inc_median: + text += get_text("forest_plot_median_caption").format(self.medians.style_text()) + if self.bootstrap_n is not None: + citation = "Adams_et_1997" + text += get_text("bootstrap_caption").format(self.bootstrap_n, get_citation(citation), + self.boot.style_text(), self.bias.style_text()) + \ + create_reference_list([citation], True) + return text + + +class ForestPlotCaption(ForestPlotBaseCaption): + def __str__(self): + return get_text("Forest plot of individual effect sizes for each study.") + \ + self.base_forest_plot_caption() + \ + get_text("study_forest_plot_extra").format(self.means.style_text(), 1-self.alpha) class BasicAnalysisCaption(ForestPlotBaseCaption): def __str__(self): + if self.normal_ci: + dist_text = get_text("normal_ci_dist") + else: + dist_text = get_text("mixed_ci_dist") return get_text("Forest plot of individual effect sizes for each study, as well as the overall mean.") + \ - common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + self.base_forest_plot_caption() + \ + get_text("basic_analysis_forest_plot_extra").format(self.means.style_text(), 1-self.alpha, dist_text) + \ + self.extra_forest_plot_caption() class GroupedAnalysisCaption(ForestPlotBaseCaption): @@ -498,12 +572,13 @@ def __init__(self): def __str__(self): return get_text("group_forest_plot").format(self.group_label) + \ - common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + self.base_forest_plot_caption() + self.mid_forest_plot_caption() + self.extra_forest_plot_caption() class NestedAnalysisCaption(ForestPlotBaseCaption): def __str__(self): - return get_text("nest_caption") + common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + return get_text("nest_caption") + self.base_forest_plot_caption() + self.mid_forest_plot_caption() + \ + self.extra_forest_plot_caption() class CumulativeAnalysisCaption(ForestPlotBaseCaption): @@ -513,30 +588,13 @@ def __init__(self): def __str__(self): return get_text("cumulative_forest_plot").format(self.order_label) + \ - common_forest_plot_caption(self.e_label, self.alpha, self.bootstrap_n) + self.base_forest_plot_caption() + self.mid_forest_plot_caption() + self.extra_forest_plot_caption() class JackknifeAnalysisCaption(ForestPlotBaseCaption): def __str__(self): - return get_text("jackknife_forest_plot") + common_forest_plot_caption(self.e_label, self.alpha, - self.bootstrap_n) - - -def common_forest_plot_caption(effect_name: str, alpha: float = 0.05, bootstrap_n: Optional[int] = None, - inc_median: bool = True) -> str: - # " Effect size measured as {}. The dotted vertical line " - # "represents no effect, or a mean of zero. Circles represent " - # "mean effect size, with the corresponding line " - # "the {:0.0%} confidence interval." - # - text = get_text("forest_plot_common_caption").format(effect_name, 1 - alpha) - if inc_median: - text += get_text("forest_plot_median_caption") - if bootstrap_n is not None: - citation = "Adams_et_1997" - text += get_text("bootstrap_caption").format(bootstrap_n, get_citation(citation)) + \ - create_reference_list([citation], True) - return text + return get_text("jackknife_forest_plot") + self.base_forest_plot_caption() + self.mid_forest_plot_caption() + \ + self.extra_forest_plot_caption() # ---------- Main Chart Data Class ---------- # @@ -753,12 +811,13 @@ def create_figure(chart_data): def chart_forest_plot(analysis_type: str, effect_name, forest_data, alpha: float = 0.05, - bootstrap_n: Optional[int] = None, - extra_name: Optional[str] = None) -> Tuple[FigureCanvasQTAgg, ChartData]: + bootstrap_n: Optional[int] = None, extra_name: Optional[str] = None, + normal_ci: bool = True) -> Tuple[FigureCanvasQTAgg, ChartData]: chart_data = ChartData(analysis_type) chart_data.caption.e_label = effect_name chart_data.caption.alpha = alpha chart_data.caption.bootstrap_n = bootstrap_n + chart_data.caption.normal_ci = normal_ci if analysis_type == "grouped analysis": chart_data.caption.group_label = extra_name elif analysis_type == "cumulative analysis": @@ -802,22 +861,24 @@ def chart_forest_plot(analysis_type: str, effect_name, forest_data, alpha: float bs_cis.extend([d.lower_bs_ci, d.upper_bs_ci]) bias_cis.extend([d.lower_bias_ci, d.upper_bias_ci]) - chart_data.add_line(get_text("Line of No Effect"), 0, 0, 0, -(n_effects+1), color="silver", linestyle="dotted", - zorder=1) + chart_data.caption.no_effect = chart_data.add_line(get_text("Line of No Effect"), 0, 0, 0, -(n_effects+1), + color="silver", linestyle="dotted", zorder=1) chart_data.add_ci(get_text("Confidence Intervals"), min_cis, max_cis, y_data, zorder=3) - chart_data.add_scatter(get_text("Means"), mean_data, y_data, marker="o", zorder=5, - label="mean and {:0.0%} CI (t-dist)".format(1-alpha)) + chart_data.caption.means = chart_data.add_scatter(get_text("Means"), mean_data, y_data, marker="o", zorder=5, + label="mean and {:0.0%} CI (t-dist)".format(1-alpha)) if median_data is not None: - chart_data.add_scatter(get_text("Medians"), median_data, y_data, marker="x", label="median", zorder=5, - color="#ff7f0e") + chart_data.caption.medians = chart_data.add_scatter(get_text("Medians"), median_data, y_data, marker="x", + label="median", zorder=5, color="#ff7f0e") chart_data.add_labels(get_text("Vertical Axis Tick Labels"), labels, y_data) if bootstrap: - chart_data.add_scatter(get_text("Bootstrap Confidence Limits"), bs_cis, ci_y_data, marker=6, zorder=4, - color="#2ca02c", label="{:0.0%} CI (bootstrap)".format(1-alpha)) + chart_data.caption.boot = chart_data.add_scatter(get_text("Bootstrap Confidence Limits"), bs_cis, ci_y_data, + marker=6, zorder=4, color="#2ca02c", + label="{:0.0%} CI (bootstrap)".format(1-alpha)) - chart_data.add_scatter(get_text("Bias-corrected Bootstrap Confidence Limits"), bias_cis, ci_y_data, marker=7, - zorder=4, color="#d62728", label="{:0.0%} CI (bias-corrected bootstrap)".format(1-alpha)) + chart_data.caption.bias = chart_data.add_scatter(get_text("Bias-corrected Bootstrap Confidence Limits"), + bias_cis, ci_y_data, marker=7, zorder=4, color="#d62728", + label="{:0.0%} CI (bias-corrected bootstrap)".format(1-alpha)) figure_canvas = create_figure(chart_data) return figure_canvas, chart_data @@ -997,12 +1058,6 @@ def chart_histogram(e_data, w_data, n_bins, e_label, else: cnts, bins = numpy.histogram(e_data, n_bins, weights=w_data) y_label = get_text("Weighted Count") - # if weighted: - # cnts, bins = numpy.histogram(e_data, n_bins, weights=w_data) - # y_label = get_text("Weighted Count") - # else: - # cnts, bins = numpy.histogram(e_data, n_bins) - # y_label = get_text("Count") chart_data = ChartData("histogram") chart_data.caption.e_label = e_label @@ -1150,3 +1205,23 @@ def chart_trim_fill_plot(effect_label, data, n, original_mean, new_mean) -> Tupl figure_canvas = create_figure(chart_data) return figure_canvas, chart_data + + +def find_color_name(color: str) -> str: + """ + Given a color as a hex string, e.g., #0123A5, find the closest named color from the CSS 4 color name list + and return that name + """ + names = list(XKCD_COLORS) + dist = 10000 + match = "None" + r, g, b = hex2color(color) + for n in names: + rx, gx, bx = hex2color(XKCD_COLORS[n]) + # Squared Euclidean distance in RGB space should be good enough + # Squared is more computationally efficient than non-squared, as we skip calculating the square-root + d = (rx-r)**2 + (gx-g)**2 + (bx-b)**2 + if d < dist: + match = n + dist = d + return match[5:] diff --git a/src/MetaWinLanguage.py b/src/MetaWinLanguage.py index 26c2ba5..064470f 100644 --- a/src/MetaWinLanguage.py +++ b/src/MetaWinLanguage.py @@ -22,9 +22,9 @@ "Basic Meta-Analysis": "Basic Meta-Analysis", "Between": "Between", "Bootstrap Mean Effect Size(s)": "Bootstrap Mean Effect Size(s)", - "bootstrap_caption": " Upward-pointing triangles mark the confidence interval from a " - "bootstrap ({:,} iterations) procedure, following {}; downward-pointing " - "triangles mark the bias-corrected bootstrap interval.", + "bootstrap_caption": " Confidence intervals from a boostrap ({:,} iterations) procedure, " + "following {}, are indicated by {}; the bias-corrected bootstrap interval " + "is indicated by {}.", "Bootstrap Confidence Limits": "Bootstrap Confidence Limits", "Bias-corrected Bootstrap Confidence Limits": "Bias-corrected Bootstrap Confidence Limits", "Calculate Effect Sizes": "Calculate Effect Sizes", @@ -132,11 +132,24 @@ "Forest plot of individual effect sizes for each study.", "Forest plot of individual effect sizes for each study, as well as the overall mean.": "Forest plot of individual effect sizes for each study, as well as the overall mean.", - "forest_plot_common_caption": " Effect size measured as {}. The dotted vertical line " - "represents no effect, or a mean of zero. Circles represent " - "mean effect size, with the corresponding line " - "the {:0.0%} confidence interval.", - "forest_plot_median_caption": " X's represent the median.", + "study_forest_plot_extra": " Study effect sizes are indicated by {}, with the corresponding line " + "the {:0.0%} confidence interval based on a Normal distribution.", + "basic_analysis_forest_plot_extra": " Study and mean effect sizes are indicated by {}, with the " + "corresponding line the {:0.0%} confidence interval based on " + "{}.", + "mid_forest_plot_caption": " Mean effect sizes are indicated by {}, with the corresponding " + "line the {:0.0%} confidence interval based on {}.", + "normal_ci_dist": "the Normal distribution", + "t_ci_dist": "Student's t distribution", + "mixed_ci_dist": "Student's t distribution for the mean and the Normal distribution for the " + "individual studies", + "forest_plot_common_caption1": " Effect size measured as {}. The vertical {} represents no " + "effect.", + "forest_plot_common_caption2": " Effect size measured as {}. The vertical {} " + "represents no effect. The mean effect size is " + "indicated by {}, with the corresponding line the {:0.0%} " + "confidence interval.", + "forest_plot_median_caption": " Medians are represented by {}.", "group_forest_plot": "Forest plot of effect sizes for the mean of all studies, as well as subgroups of studies " "designated by {}.", @@ -196,6 +209,8 @@ "Log Transformed Measure": "Log Transformed Measure", "Lower Prediction Limit": "Lower Prediction Limit", "Markdown": "Markdown", + "marker_style_text": "{} {}{} with a {} border", + "marker_style_open_text": "open {}{} with a {} border", "Mean": "Mean", "Means": "Means", "Means and Standard Deviations": "Means and Standard Deviations", @@ -226,10 +241,14 @@ "Normal Quantile Plot": "Normal Quantile Plot", "normal_quantile_caption": "Normal Quantile plot following {}. The " "standardized effect size is the effect size divided by the " - "square-root of its variance. The solid line represents the " - "regression and the dashed lines the 95% prediction envelope.", + "square-root of its variance. ", + "normal_quantile_style1": "The {} represents the regression and the {}s the 95% prediction " + "envelope.", + "normal_quantile_style2": "The {} represents the regression and the {} and {} the upper and " + "lower limits of the 95% prediction envelope, respectively.", "note_funnel_plot": "Note: A funnel plot is just a scatter plot of a metric (such as a
    " "mean or effect size) vs. it\'s variance or sample size.", + "nothing (marker is invisible)": "nothing (marker is invisible)", "Number of Bins": "Number of Bins", "Number of decimal places": "Number of decimal places", "Number of Decimal Places to Display": "Number of Decimal Places to Display", @@ -338,9 +357,9 @@ "Trim and Fill Analysis estimated {} missing studies.": "Trim and Fill Analysis estimated {} missing studies.", "trim_fill_caption": "Funnel plot of {} vs. precision, showing the results of a Trim and " - "Fill Analysis ({}). Solid black circles represent the original data; " - "open red circles represent inferred \"missing\" data. The dashed line " - "represents the mean effect size of the original data, the dotted line " + "Fill Analysis ({}). Original data are represented by {}, inferred " + "\"missing\" data by {}. The {} " + "represents the mean effect size of the original data, the {} " "the mean effect size including the inferred data.", "Two x Two Contingency Table": "Two x Two Contingency Table", "Unable to write to ": "Unable to write to ", diff --git a/tests/test_metawin.py b/tests/test_metawin.py index 237f6e4..404521d 100644 --- a/tests/test_metawin.py +++ b/tests/test_metawin.py @@ -15,6 +15,7 @@ from typing import Tuple from PyQt6.QtWidgets import QDialog, QVBoxLayout, QFrame, QPushButton, QTextEdit +import matplotlib.colors as mcolors # note these may be marked by the IDE as unknown modules, but pytest.ini will resolve the errors when tests # are actually executed @@ -1485,3 +1486,41 @@ def test_phylogenetic_glm_simple(): options.structure = MetaWinAnalysis.PHYLOGENETIC_MA output, figure, chart_data, analysis_values = MetaWinAnalysis.do_meta_analysis(data, options, 4, tree=tree) print_test_output(output) + + +def test_colors(): + colors = mcolors.CSS4_COLORS + names = list(colors) + for n in names: + print(n, colors[n]) + + +def test_match_color_to_name(): + colors = mcolors.CSS4_COLORS + xcolors = mcolors.XKCD_COLORS + c = "#ff7f0e" + r, g, b = mcolors.hex2color(c) + print(r, g, b) + cnames = list(colors) + cdist = 10000 + cmatch = "None" + for n in cnames: + rc, gc, bc = mcolors.hex2color(colors[n]) + d = (rc-r)**2 + (gc-g)**2 + (bc-b)**2 + if d < cdist: + cmatch = n + cdist = d + xnames = list(xcolors) + xdist = 10000 + xmatch = "None" + for n in xnames: + print(n) + rx, gx, bx = mcolors.hex2color(xcolors[n]) + d = (rx-r)**2 + (gx-g)**2 + (bx-b)**2 + if d < xdist: + xmatch = n + xdist = d + print(cmatch, math.sqrt(cdist)) + print(xmatch, math.sqrt(xdist)) + print(len(cnames), len(xnames)) + From db9b62914a2d60a126a46525e4a893587c604e3c Mon Sep 17 00:00:00 2001 From: msrosenberg Date: Fri, 16 Sep 2022 10:55:55 -0400 Subject: [PATCH 6/6] preparing for release 3.0.7 beta --- src/MetaWinConstants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MetaWinConstants.py b/src/MetaWinConstants.py index 865e9c4..5eeaba1 100644 --- a/src/MetaWinConstants.py +++ b/src/MetaWinConstants.py @@ -13,7 +13,7 @@ MAJOR_VERSION = 3 MINOR_VERSION = 0 -PATCH_VERSION = 6 +PATCH_VERSION = 7 # validity check when fetching value from data matrix VALUE_NUMBER = 0