From 641c4edf1d92536b7452820432df50e34d53a6f2 Mon Sep 17 00:00:00 2001 From: JAnns98 Date: Thu, 29 Aug 2024 17:33:49 +0800 Subject: [PATCH] Fixed coloring of summary bars and added delta text (WIP) --- dabest/_effsize_objects.py | 10 ++++++ dabest/plot_tools.py | 6 ++-- dabest/plotter.py | 57 +++++++++++++++++++++++++++++++- nbs/API/effsize_objects.ipynb | 16 ++++++--- nbs/API/plot_tools.ipynb | 6 ++-- nbs/API/plotter.ipynb | 61 ++++++++++++++++++++++++++++++++--- 6 files changed, 139 insertions(+), 17 deletions(-) diff --git a/dabest/_effsize_objects.py b/dabest/_effsize_objects.py index defe3552..c49a696c 100644 --- a/dabest/_effsize_objects.py +++ b/dabest/_effsize_objects.py @@ -1024,6 +1024,8 @@ def plot( swarm_bars_kwargs=None, summary_bars=None, summary_bars_kwargs=None, + delta_text=True, + delta_text_kwargs=None, ): """ Creates an estimation plot for the effect size of interest. @@ -1186,6 +1188,14 @@ def 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} + delta_text : boolean, default True + Whether or not to display the text deltas. + delta_text_kwargs : dict, default None + Pass relevant keyword arguments to the delta text. Pass any keyword arguments accepted by + matplotlib.text.Text here, as a string. If None, the following keywords are passed: + {"color": None, "alpha": 1, "fontsize": 10, "ha": 'center', "va": 'center', "rotation": 0, + "x_location": 'right', "x_coordinates": None, "y_coordinates": None} + Use "x_coordinates" and "y_coordinates" if you would like to specify the text locations manually. Returns ------- diff --git a/dabest/plot_tools.py b/dabest/plot_tools.py index d4981469..15e31a8c 100644 --- a/dabest/plot_tools.py +++ b/dabest/plot_tools.py @@ -824,7 +824,7 @@ def summary_bars_plotter(summary_bars: list, results: object, ax_to_plot: object # End checks else: summary_xmin, summary_xmax = ax_to_plot.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_colors = [summary_bars_kwargs.get('color')]*(max(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: if ci_type == "bca": @@ -880,7 +880,7 @@ def contrast_bars_plotter(results: object, ax_to_plot: object, swarm_plot_ax: o for j, tick in enumerate(ticks_to_plot): contrast_means.append(results.difference[j]) - 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_colors = [contrast_bars_kwargs.get('color')]*(max(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): ax_to_plot.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)) @@ -929,7 +929,7 @@ def swarm_bars_plotter(plot_data: object, xvar: str, yvar: str, ax: object, swarm_bars_order = pd.unique(plot_data[xvar]) swarm_means = plot_data.groupby(xvar)[yvar].mean().reindex(index=swarm_bars_order) - 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_colors = [swarm_bars_kwargs.get('color')]*(max(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_bars_colors): ax.add_patch(mpatches.Rectangle((swarm_bars_x-0.25,0), diff --git a/dabest/plotter.py b/dabest/plotter.py index 7f0cf0e5..d5d8fe6f 100644 --- a/dabest/plotter.py +++ b/dabest/plotter.py @@ -56,7 +56,7 @@ def effectsize_df_plotter(effectsize_df, **plot_kwargs): fontsize_delta2label=12, swarm_bars=True, swarm_bars_kwargs=None, contrast_bars=True, contrast_bars_kwargs=None, - + delta_text=True, delta_text_kwargs=None, """ from .misc_tools import merge_two_dicts from .plot_tools import ( @@ -1637,6 +1637,61 @@ def effectsize_df_plotter(effectsize_df, **plot_kwargs): ################################################### Swarm & Contrast & Summary Bars WIP END + ################################################### Delta numbers WIP START + delta_text = plot_kwargs["delta_text"] + if delta_text: + default_delta_text_kwargs = {"color": None, "alpha": 1, "fontsize": 10, "ha": 'center', "va": 'center', "rotation": 0, "x_location": 'right', "x_coordinates": None, "y_coordinates": None} + if plot_kwargs["delta_text_kwargs"] is None: + delta_text_kwargs = default_delta_text_kwargs + else: + delta_text_kwargs = merge_two_dicts(default_delta_text_kwargs, plot_kwargs["delta_text_kwargs"]) + + delta_text_x_location = delta_text_kwargs.get('x_location') + if delta_text_x_location != 'right' and delta_text_x_location != 'left': + raise ValueError("delta_text_kwargs['x_location'] must be either 'right' or 'left'.") + delta_text_kwargs.pop('x_location') + + delta_text_colors = [delta_text_kwargs.get('color')]*(max(ticks_to_plot)+1) if delta_text_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 + delta_text_kwargs.pop('color') + + Delta_Values = [] + for j, tick in enumerate(ticks_to_plot): + Delta_Values.append(results.difference[j]) + + delta_text_x_coordinates = delta_text_kwargs.get('x_coordinates') + delta_text_y_coordinates = delta_text_kwargs.get('y_coordinates') + + if delta_text_x_coordinates is not None: + if not isinstance(delta_text_x_coordinates, list): + raise TypeError("delta_text_kwargs['x_coordinates'] must be a list of x-coordinates.") + if len(delta_text_x_coordinates) != len(ticks_to_plot): + raise ValueError("delta_text_kwargs['x_coordinates'] must have the same length as the number of ticks to plot.") + delta_text_x_coordinates_default = False + delta_text_kwargs.pop('x_coordinates') + else: + delta_text_x_coordinates = ticks_to_plot + delta_text_x_coordinates_default = True + delta_text_kwargs.pop('x_coordinates') + + + if delta_text_y_coordinates is not None: + if not isinstance(delta_text_y_coordinates, list): + raise TypeError("delta_text_kwargs['y_coordinates'] must be a list of y-coordinates.") + if len(delta_text_y_coordinates) != len(ticks_to_plot): + raise ValueError("delta_text_kwargs['y_coordinates'] must have the same length as the number of ticks to plot.") + delta_text_kwargs.pop('y_coordinates') + else: + delta_text_y_coordinates = Delta_Values + delta_text_kwargs.pop('y_coordinates') + + for x,y,t,tick in zip(delta_text_x_coordinates, delta_text_y_coordinates,Delta_Values,ticks_to_plot): + Delta_Text = '+'+'{0:.2f}'.format(t) if t > 0 else '{0:.2f}'.format(t) + X_Adjust = 0 if not delta_text_x_coordinates_default else 0.53 if delta_text_x_location == 'right' else -0.35 + contrast_axes.text(x+X_Adjust, y, Delta_Text, color=delta_text_colors[tick],**delta_text_kwargs) + + ################################################### Delta numbers WIP END + + # Make sure no stray ticks appear! rawdata_axes.xaxis.set_ticks_position("bottom") rawdata_axes.yaxis.set_ticks_position("left") diff --git a/nbs/API/effsize_objects.ipynb b/nbs/API/effsize_objects.ipynb index 351b939f..bde78b53 100644 --- a/nbs/API/effsize_objects.ipynb +++ b/nbs/API/effsize_objects.ipynb @@ -1185,6 +1185,8 @@ " swarm_bars_kwargs=None,\n", " summary_bars=None,\n", " summary_bars_kwargs=None,\n", + " delta_text=True,\n", + " delta_text_kwargs=None,\n", " ):\n", " \"\"\"\n", " Creates an estimation plot for the effect size of interest.\n", @@ -1347,6 +1349,14 @@ " 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", + " delta_text : boolean, default True\n", + " Whether or not to display the text deltas.\n", + " delta_text_kwargs : dict, default None\n", + " Pass relevant keyword arguments to the delta text. Pass any keyword arguments accepted by\n", + " matplotlib.text.Text here, as a string. If None, the following keywords are passed:\n", + " {\"color\": None, \"alpha\": 1, \"fontsize\": 10, \"ha\": 'center', \"va\": 'center', \"rotation\": 0, \n", + " \"x_location\": 'right', \"x_coordinates\": None, \"y_coordinates\": None}\n", + " Use \"x_coordinates\" and \"y_coordinates\" if you would like to specify the text locations manually.\n", "\n", " Returns\n", " -------\n", @@ -1949,13 +1959,9 @@ ], "metadata": { "kernelspec": { - "display_name": "DABESTDEV", + "display_name": "python3", "language": "python", "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.11.8" } }, "nbformat": 4, diff --git a/nbs/API/plot_tools.ipynb b/nbs/API/plot_tools.ipynb index 20c70e92..2150d604 100644 --- a/nbs/API/plot_tools.ipynb +++ b/nbs/API/plot_tools.ipynb @@ -878,7 +878,7 @@ "# End checks\n", " else:\n", " summary_xmin, summary_xmax = ax_to_plot.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_colors = [summary_bars_kwargs.get('color')]*(max(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", " if ci_type == \"bca\":\n", @@ -934,7 +934,7 @@ " for j, tick in enumerate(ticks_to_plot):\n", " contrast_means.append(results.difference[j])\n", "\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_colors = [contrast_bars_kwargs.get('color')]*(max(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", " ax_to_plot.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", @@ -983,7 +983,7 @@ " 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_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_colors = [swarm_bars_kwargs.get('color')]*(max(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_bars_colors):\n", " ax.add_patch(mpatches.Rectangle((swarm_bars_x-0.25,0),\n", diff --git a/nbs/API/plotter.ipynb b/nbs/API/plotter.ipynb index fd79bcb0..ac55e9e5 100644 --- a/nbs/API/plotter.ipynb +++ b/nbs/API/plotter.ipynb @@ -115,7 +115,7 @@ " fontsize_delta2label=12,\n", " swarm_bars=True, swarm_bars_kwargs=None,\n", " contrast_bars=True, contrast_bars_kwargs=None,\n", - "\n", + " delta_text=True, delta_text_kwargs=None,\n", " \"\"\"\n", " from .misc_tools import merge_two_dicts\n", " from .plot_tools import (\n", @@ -1696,6 +1696,61 @@ " \n", " ################################################### Swarm & Contrast & Summary Bars WIP END\n", "\n", + " ################################################### Delta numbers WIP START\n", + " delta_text = plot_kwargs[\"delta_text\"]\n", + " if delta_text:\n", + " default_delta_text_kwargs = {\"color\": None, \"alpha\": 1, \"fontsize\": 10, \"ha\": 'center', \"va\": 'center', \"rotation\": 0, \"x_location\": 'right', \"x_coordinates\": None, \"y_coordinates\": None}\n", + " if plot_kwargs[\"delta_text_kwargs\"] is None:\n", + " delta_text_kwargs = default_delta_text_kwargs\n", + " else:\n", + " delta_text_kwargs = merge_two_dicts(default_delta_text_kwargs, plot_kwargs[\"delta_text_kwargs\"])\n", + "\n", + " delta_text_x_location = delta_text_kwargs.get('x_location')\n", + " if delta_text_x_location != 'right' and delta_text_x_location != 'left':\n", + " raise ValueError(\"delta_text_kwargs['x_location'] must be either 'right' or 'left'.\")\n", + " delta_text_kwargs.pop('x_location')\n", + "\n", + " delta_text_colors = [delta_text_kwargs.get('color')]*(max(ticks_to_plot)+1) if delta_text_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", + " delta_text_kwargs.pop('color')\n", + "\n", + " Delta_Values = []\n", + " for j, tick in enumerate(ticks_to_plot):\n", + " Delta_Values.append(results.difference[j])\n", + "\n", + " delta_text_x_coordinates = delta_text_kwargs.get('x_coordinates')\n", + " delta_text_y_coordinates = delta_text_kwargs.get('y_coordinates')\n", + "\n", + " if delta_text_x_coordinates is not None:\n", + " if not isinstance(delta_text_x_coordinates, list):\n", + " raise TypeError(\"delta_text_kwargs['x_coordinates'] must be a list of x-coordinates.\")\n", + " if len(delta_text_x_coordinates) != len(ticks_to_plot):\n", + " raise ValueError(\"delta_text_kwargs['x_coordinates'] must have the same length as the number of ticks to plot.\")\n", + " delta_text_x_coordinates_default = False\n", + " delta_text_kwargs.pop('x_coordinates')\n", + " else:\n", + " delta_text_x_coordinates = ticks_to_plot\n", + " delta_text_x_coordinates_default = True\n", + " delta_text_kwargs.pop('x_coordinates')\n", + "\n", + "\n", + " if delta_text_y_coordinates is not None:\n", + " if not isinstance(delta_text_y_coordinates, list):\n", + " raise TypeError(\"delta_text_kwargs['y_coordinates'] must be a list of y-coordinates.\")\n", + " if len(delta_text_y_coordinates) != len(ticks_to_plot):\n", + " raise ValueError(\"delta_text_kwargs['y_coordinates'] must have the same length as the number of ticks to plot.\")\n", + " delta_text_kwargs.pop('y_coordinates')\n", + " else:\n", + " delta_text_y_coordinates = Delta_Values\n", + " delta_text_kwargs.pop('y_coordinates')\n", + "\n", + " for x,y,t,tick in zip(delta_text_x_coordinates, delta_text_y_coordinates,Delta_Values,ticks_to_plot):\n", + " Delta_Text = '+'+'{0:.2f}'.format(t) if t > 0 else '{0:.2f}'.format(t)\n", + " X_Adjust = 0 if not delta_text_x_coordinates_default else 0.53 if delta_text_x_location == 'right' else -0.35\n", + " contrast_axes.text(x+X_Adjust, y, Delta_Text, color=delta_text_colors[tick],**delta_text_kwargs)\n", + "\n", + " ################################################### Delta numbers WIP END\n", + "\n", + "\n", " # Make sure no stray ticks appear!\n", " rawdata_axes.xaxis.set_ticks_position(\"bottom\")\n", " rawdata_axes.yaxis.set_ticks_position(\"left\")\n", @@ -1717,10 +1772,6 @@ "display_name": "python3", "language": "python", "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.11.8" } }, "nbformat": 4,