From cb28e6cd986935a6a0346abfc65978e625528dc2 Mon Sep 17 00:00:00 2001 From: JAnns98 Date: Fri, 12 Apr 2024 18:23:13 +0800 Subject: [PATCH] Added summary bars and edited aesthetic of contrast and swarm bars --- dabest/_effsize_objects.py | 15 ++++++-- dabest/plotter.py | 69 +++++++++++++++++++++++++++-------- nbs/API/effsize_objects.ipynb | 15 ++++++-- nbs/API/plotter.ipynb | 69 +++++++++++++++++++++++++++-------- 4 files changed, 128 insertions(+), 40 deletions(-) diff --git a/dabest/_effsize_objects.py b/dabest/_effsize_objects.py index f159c312..defe3552 100644 --- a/dabest/_effsize_objects.py +++ b/dabest/_effsize_objects.py @@ -1022,6 +1022,8 @@ def plot( swarm_bars=True, contrast_bars_kwargs=None, swarm_bars_kwargs=None, + summary_bars=None, + summary_bars_kwargs=None, ): """ Creates an estimation plot for the effect size of interest. @@ -1171,14 +1173,19 @@ def plot( swarm_bars : boolean, default True Whether or not to display the swarm bars. contrast_bars_kwargs : dict, default None - Pass relevant keyword arguments to the contrast bars. Pass any keyword argumentd accepted by + Pass relevant keyword arguments to the contrast bars. Pass any keyword arguments accepted by matplotlib.patches.Rectangle here, as a string. If None, the following keywords are passed: - {"color": None, "alpha": 0.1} + {"color": None, "alpha": 0.15} swarm_bars_kwargs : dict, default None - Pass relevant keyword arguments to the swarm bars. Pass any keyword argumentd accepted by + Pass relevant keyword arguments to the swarm bars. Pass any keyword arguments accepted by matplotlib.patches.Rectangle here, as a string. If None, the following keywords are passed: - {"color": None, "alpha": 0.15} + {"color": None, "alpha": 0.1} + summary_bars : list, default None + Pass a list of indices of the contrast objects to have summary bars displayed on the plot. + For example, [0,1] will show summary bars for the first two contrast objects. + summary_bars_kwargs: dict, default None + If None, the following keywords are passed: {"color": None, "alpha": 0.15} Returns ------- diff --git a/dabest/plotter.py b/dabest/plotter.py index f5b244d1..8a91bfd2 100644 --- a/dabest/plotter.py +++ b/dabest/plotter.py @@ -53,7 +53,10 @@ def effectsize_df_plotter(effectsize_df, **plot_kwargs): title=None, fontsize_title=16, fontsize_rawxlabel=12, fontsize_rawylabel=12, fontsize_contrastxlabel=12, fontsize_contrastylabel=12, - fontsize_delta2label=12 + fontsize_delta2label=12, + swarm_bars=True, swarm_bars_kwargs=None, + contrast_bars=True, contrast_bars_kwargs=None, + """ from .misc_tools import merge_two_dicts from .plot_tools import ( @@ -1591,7 +1594,7 @@ def effectsize_df_plotter(effectsize_df, **plot_kwargs): ####################################################### END GRIDKEY MAIN CODE WIP - ################################################### Contrast Bars WIP + ################################################### Swarm & Contrast Bars WIP # Swarm Bars WIP swarm_bars = plot_kwargs["swarm_bars"] @@ -1615,14 +1618,11 @@ def effectsize_df_plotter(effectsize_df, **plot_kwargs): swarm_bars_order = pd.unique(plot_data[xvar]) swarm_means = plot_data.groupby(xvar)[yvar].mean().reindex(index=swarm_bars_order) - swarm_bar_colors = [swarm_bars_kwargs.get('color')]*(len(swarm_bars_order)+1) if swarm_bars_kwargs.get('color') is not None else ['black']*(len(swarm_bars_order)+1) if color_col is not None or is_paired else swarm_colors + swarm_bars_colors = [swarm_bars_kwargs.get('color')]*(len(swarm_bars_order)+1) if swarm_bars_kwargs.get('color') is not None else ['black']*(len(swarm_bars_order)+1) if color_col is not None or is_paired else swarm_colors swarm_bars_kwargs.pop('color') - for swarm_bars_x,swarm_bars_y,c in zip(np.arange(0,len(swarm_bars_order)+1,1), swarm_means, swarm_bar_colors): - rawdata_axes.add_patch(mpatches.Rectangle((swarm_bars_x-0.2,0), - 0.5-0.1, swarm_bars_y, zorder=-1,color=c,**swarm_bars_kwargs)) - - else: - pass + for swarm_bars_x,swarm_bars_y,c in zip(np.arange(0,len(swarm_bars_order)+1,1), swarm_means, swarm_bars_colors): + rawdata_axes.add_patch(mpatches.Rectangle((swarm_bars_x-0.25,0), + 0.5, swarm_bars_y, zorder=-1,color=c,**swarm_bars_kwargs)) # Contrast Bars WIP contrast_bars = plot_kwargs["contrast_bars"] @@ -1636,21 +1636,58 @@ def effectsize_df_plotter(effectsize_df, **plot_kwargs): for j, tick in enumerate(ticks_to_plot): contrast_means.append(results.difference[j]) - contrast_bar_colors = [contrast_bars_kwargs.get('color')]*(len(ticks_to_plot)+1) if contrast_bars_kwargs.get('color') is not None else ['black']*(max(ticks_to_plot)+1) if color_col is not None or (proportional and is_paired) or is_paired else swarm_colors + contrast_bars_colors = [contrast_bars_kwargs.get('color')]*(len(ticks_to_plot)+1) if contrast_bars_kwargs.get('color') is not None else ['black']*(max(ticks_to_plot)+1) if color_col is not None or (proportional and is_paired) or is_paired else swarm_colors contrast_bars_kwargs.pop('color') for contrast_bars_x,contrast_bars_y in zip(ticks_to_plot, contrast_means): - contrast_axes.add_patch(mpatches.Rectangle((contrast_bars_x,0),0.25, contrast_bars_y, zorder=-1, color=contrast_bar_colors[contrast_bars_x], **contrast_bars_kwargs)) + contrast_axes.add_patch(mpatches.Rectangle((contrast_bars_x-0.25,0),0.5, contrast_bars_y, zorder=-1, color=contrast_bars_colors[contrast_bars_x], **contrast_bars_kwargs)) if show_mini_meta: - contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2,0),0.25, mini_meta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs)) + contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2-0.25,0),0.5, mini_meta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs)) if show_delta2: - contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2,0),0.25, delta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs)) + contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2-0.25,0),0.5, delta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs)) + ################################################### Swarm & Contrast Bars WIP + + ################################################### Summary Bars WIP + + summary_bars = plot_kwargs["summary_bars"] + default_summary_bars_kwargs = {"color": None, "alpha": 0.15} + if plot_kwargs["summary_bars_kwargs"] is None: + summary_bars_kwargs = default_summary_bars_kwargs else: - pass - - ################################################### Contrast Bars WIP + summary_bars_kwargs = merge_two_dicts(default_summary_bars_kwargs, plot_kwargs["summary_bars_kwargs"]) + + if summary_bars is not None: + if not isinstance(summary_bars, list): + raise TypeError("summary_bars must be a list of indices (ints).") + if not all(isinstance(i, int) for i in summary_bars): + raise TypeError("summary_bars must be a list of indices (ints).") + if any(i >= len(results) for i in summary_bars): + raise ValueError("Index {} chosen is out of range for the contrast objects.".format([i for i in summary_bars if i >= len(results)])) + if float_contrast: + raise ValueError("summary_bars cannot be used with Gardner-Altman plots.") + else: + print('Summary plots WIP') + summary_xmin, summary_xmax = contrast_axes.get_xlim() + summary_bars_colors = [summary_bars_kwargs.get('color')]*(len(summary_bars)+1) if summary_bars_kwargs.get('color') is not None else ['black']*(max(summary_bars)+1) if color_col is not None or (proportional and is_paired) or is_paired else swarm_colors + summary_bars_kwargs.pop('color') + for summary_index in summary_bars: + print('Summary plot for contrast object:', summary_index) + if ci_type == "bca": + summary_ci_low = results.bca_low[summary_index] + summary_ci_high = results.bca_high[summary_index] + else: + summary_ci_low = results.pct_low[summary_index] + summary_ci_high = results.pct_high[summary_index] + + summary_color = summary_bars_colors[ticks_to_plot[summary_index]] + + contrast_axes.add_patch(mpatches.Rectangle((summary_xmin,summary_ci_low),summary_xmax+1, + summary_ci_high-summary_ci_low, zorder=-2, color=summary_color, **summary_bars_kwargs)) + + + ################################################### Summary Bars WIP # Make sure no stray ticks appear! rawdata_axes.xaxis.set_ticks_position("bottom") diff --git a/nbs/API/effsize_objects.ipynb b/nbs/API/effsize_objects.ipynb index ea1374c3..351b939f 100644 --- a/nbs/API/effsize_objects.ipynb +++ b/nbs/API/effsize_objects.ipynb @@ -1183,6 +1183,8 @@ " swarm_bars=True,\n", " contrast_bars_kwargs=None,\n", " swarm_bars_kwargs=None,\n", + " summary_bars=None,\n", + " summary_bars_kwargs=None,\n", " ):\n", " \"\"\"\n", " Creates an estimation plot for the effect size of interest.\n", @@ -1332,14 +1334,19 @@ " swarm_bars : boolean, default True\n", " Whether or not to display the swarm bars.\n", " contrast_bars_kwargs : dict, default None\n", - " Pass relevant keyword arguments to the contrast bars. Pass any keyword argumentd accepted by \n", + " Pass relevant keyword arguments to the contrast bars. Pass any keyword arguments accepted by \n", " matplotlib.patches.Rectangle here, as a string. If None, the following keywords are passed:\n", - " {\"color\": None, \"alpha\": 0.1}\n", + " {\"color\": None, \"alpha\": 0.15}\n", " swarm_bars_kwargs : dict, default None\n", - " Pass relevant keyword arguments to the swarm bars. Pass any keyword argumentd accepted by \n", + " Pass relevant keyword arguments to the swarm bars. Pass any keyword arguments accepted by \n", " matplotlib.patches.Rectangle here, as a string. If None, the following keywords are passed:\n", - " {\"color\": None, \"alpha\": 0.15}\n", + " {\"color\": None, \"alpha\": 0.1}\n", "\n", + " summary_bars : list, default None\n", + " Pass a list of indices of the contrast objects to have summary bars displayed on the plot.\n", + " For example, [0,1] will show summary bars for the first two contrast objects.\n", + " summary_bars_kwargs: dict, default None\n", + " If None, the following keywords are passed: {\"color\": None, \"alpha\": 0.15}\n", "\n", " Returns\n", " -------\n", diff --git a/nbs/API/plotter.ipynb b/nbs/API/plotter.ipynb index 0cd8c25a..88d6f326 100644 --- a/nbs/API/plotter.ipynb +++ b/nbs/API/plotter.ipynb @@ -112,7 +112,10 @@ " title=None, fontsize_title=16,\n", " fontsize_rawxlabel=12, fontsize_rawylabel=12,\n", " fontsize_contrastxlabel=12, fontsize_contrastylabel=12,\n", - " fontsize_delta2label=12\n", + " fontsize_delta2label=12,\n", + " swarm_bars=True, swarm_bars_kwargs=None,\n", + " contrast_bars=True, contrast_bars_kwargs=None,\n", + "\n", " \"\"\"\n", " from .misc_tools import merge_two_dicts\n", " from .plot_tools import (\n", @@ -1650,7 +1653,7 @@ "\n", " ####################################################### END GRIDKEY MAIN CODE WIP\n", " \n", - " ################################################### Contrast Bars WIP\n", + " ################################################### Swarm & Contrast Bars WIP\n", " \n", " # Swarm Bars WIP\n", " swarm_bars = plot_kwargs[\"swarm_bars\"]\n", @@ -1674,14 +1677,11 @@ " swarm_bars_order = pd.unique(plot_data[xvar])\n", "\n", " swarm_means = plot_data.groupby(xvar)[yvar].mean().reindex(index=swarm_bars_order)\n", - " swarm_bar_colors = [swarm_bars_kwargs.get('color')]*(len(swarm_bars_order)+1) if swarm_bars_kwargs.get('color') is not None else ['black']*(len(swarm_bars_order)+1) if color_col is not None or is_paired else swarm_colors\n", + " swarm_bars_colors = [swarm_bars_kwargs.get('color')]*(len(swarm_bars_order)+1) if swarm_bars_kwargs.get('color') is not None else ['black']*(len(swarm_bars_order)+1) if color_col is not None or is_paired else swarm_colors\n", " swarm_bars_kwargs.pop('color')\n", - " for swarm_bars_x,swarm_bars_y,c in zip(np.arange(0,len(swarm_bars_order)+1,1), swarm_means, swarm_bar_colors):\n", - " rawdata_axes.add_patch(mpatches.Rectangle((swarm_bars_x-0.2,0),\n", - " 0.5-0.1, swarm_bars_y, zorder=-1,color=c,**swarm_bars_kwargs))\n", - "\n", - " else:\n", - " pass\n", + " for swarm_bars_x,swarm_bars_y,c in zip(np.arange(0,len(swarm_bars_order)+1,1), swarm_means, swarm_bars_colors):\n", + " rawdata_axes.add_patch(mpatches.Rectangle((swarm_bars_x-0.25,0),\n", + " 0.5, swarm_bars_y, zorder=-1,color=c,**swarm_bars_kwargs))\n", " \n", " # Contrast Bars WIP\n", " contrast_bars = plot_kwargs[\"contrast_bars\"]\n", @@ -1695,21 +1695,58 @@ " for j, tick in enumerate(ticks_to_plot):\n", " contrast_means.append(results.difference[j])\n", "\n", - " contrast_bar_colors = [contrast_bars_kwargs.get('color')]*(len(ticks_to_plot)+1) if contrast_bars_kwargs.get('color') is not None else ['black']*(max(ticks_to_plot)+1) if color_col is not None or (proportional and is_paired) or is_paired else swarm_colors\n", + " contrast_bars_colors = [contrast_bars_kwargs.get('color')]*(len(ticks_to_plot)+1) if contrast_bars_kwargs.get('color') is not None else ['black']*(max(ticks_to_plot)+1) if color_col is not None or (proportional and is_paired) or is_paired else swarm_colors\n", " contrast_bars_kwargs.pop('color')\n", " for contrast_bars_x,contrast_bars_y in zip(ticks_to_plot, contrast_means):\n", - " contrast_axes.add_patch(mpatches.Rectangle((contrast_bars_x,0),0.25, contrast_bars_y, zorder=-1, color=contrast_bar_colors[contrast_bars_x], **contrast_bars_kwargs))\n", + " contrast_axes.add_patch(mpatches.Rectangle((contrast_bars_x-0.25,0),0.5, contrast_bars_y, zorder=-1, color=contrast_bars_colors[contrast_bars_x], **contrast_bars_kwargs))\n", "\n", " if show_mini_meta:\n", - " contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2,0),0.25, mini_meta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs))\n", + " contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2-0.25,0),0.5, mini_meta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs))\n", "\n", " if show_delta2:\n", - " contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2,0),0.25, delta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs))\n", + " contrast_axes.add_patch(mpatches.Rectangle((max(rawdata_axes.get_xticks())+2-0.25,0),0.5, delta_delta.difference, zorder=-1, color='black', **contrast_bars_kwargs))\n", "\n", + " ################################################### Swarm & Contrast Bars WIP\n", + "\n", + " ################################################### Summary Bars WIP\n", + "\n", + " summary_bars = plot_kwargs[\"summary_bars\"]\n", + " default_summary_bars_kwargs = {\"color\": None, \"alpha\": 0.15}\n", + " if plot_kwargs[\"summary_bars_kwargs\"] is None:\n", + " summary_bars_kwargs = default_summary_bars_kwargs\n", " else:\n", - " pass\n", - " \n", - " ################################################### Contrast Bars WIP\n", + " summary_bars_kwargs = merge_two_dicts(default_summary_bars_kwargs, plot_kwargs[\"summary_bars_kwargs\"])\n", + "\n", + " if summary_bars is not None:\n", + " if not isinstance(summary_bars, list):\n", + " raise TypeError(\"summary_bars must be a list of indices (ints).\")\n", + " if not all(isinstance(i, int) for i in summary_bars):\n", + " raise TypeError(\"summary_bars must be a list of indices (ints).\")\n", + " if any(i >= len(results) for i in summary_bars):\n", + " raise ValueError(\"Index {} chosen is out of range for the contrast objects.\".format([i for i in summary_bars if i >= len(results)]))\n", + " if float_contrast:\n", + " raise ValueError(\"summary_bars cannot be used with Gardner-Altman plots.\")\n", + " else:\n", + " print('Summary plots WIP')\n", + " summary_xmin, summary_xmax = contrast_axes.get_xlim()\n", + " summary_bars_colors = [summary_bars_kwargs.get('color')]*(len(summary_bars)+1) if summary_bars_kwargs.get('color') is not None else ['black']*(max(summary_bars)+1) if color_col is not None or (proportional and is_paired) or is_paired else swarm_colors\n", + " summary_bars_kwargs.pop('color')\n", + " for summary_index in summary_bars:\n", + " print('Summary plot for contrast object:', summary_index)\n", + " if ci_type == \"bca\":\n", + " summary_ci_low = results.bca_low[summary_index]\n", + " summary_ci_high = results.bca_high[summary_index]\n", + " else:\n", + " summary_ci_low = results.pct_low[summary_index]\n", + " summary_ci_high = results.pct_high[summary_index]\n", + "\n", + " summary_color = summary_bars_colors[ticks_to_plot[summary_index]]\n", + "\n", + " contrast_axes.add_patch(mpatches.Rectangle((summary_xmin,summary_ci_low),summary_xmax+1, \n", + " summary_ci_high-summary_ci_low, zorder=-2, color=summary_color, **summary_bars_kwargs))\n", + "\n", + "\n", + " ################################################### Summary Bars WIP\n", "\n", " # Make sure no stray ticks appear!\n", " rawdata_axes.xaxis.set_ticks_position(\"bottom\")\n",