From 2aedec27ff7cefd38998ef3d331df7df0e0b76e8 Mon Sep 17 00:00:00 2001 From: Veli-Matti Isoviita Date: Wed, 17 Sep 2025 15:28:48 +0300 Subject: [PATCH 1/5] Included a wrapper functionality in __init__.py, which takes the sns.boxplot and replaces it with a function that handles calling the additional annotation functions, when pvalplot is imported after sns --- src/pvalplot/__init__.py | 4 +- src/pvalplot/annotation_wrapper.py | 69 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 src/pvalplot/annotation_wrapper.py diff --git a/src/pvalplot/__init__.py b/src/pvalplot/__init__.py index d6648b6..746ef73 100644 --- a/src/pvalplot/__init__.py +++ b/src/pvalplot/__init__.py @@ -1,2 +1,2 @@ -def hello() -> str: - return "Hello from pvalplot!" +from .annotation_wrapper import wrap_boxplot +wrap_boxplot() \ No newline at end of file diff --git a/src/pvalplot/annotation_wrapper.py b/src/pvalplot/annotation_wrapper.py new file mode 100644 index 0000000..9ced507 --- /dev/null +++ b/src/pvalplot/annotation_wrapper.py @@ -0,0 +1,69 @@ +import seaborn as sns +import matplotlib.pyplot as plt +import matplotlib.patches as mpatches + +from .annotation_sample import add_sample_annot2 +from .annotation_hline import add_horizontal_line_annot +from .annotation_pvalues import add_pbars_annot + +_original_sns_boxplot = sns.boxplot + +def wrapped_boxplot(*args, **kwargs) -> plt.axis: + """ + This wrapper function replaces original sns.boxplot and adds the custom parameters for + the annotations provided in the package + """ + + # Pop out custom variables + sample_size_annotation = kwargs.pop('sample_size_annotation', None) + horizontal_line = kwargs.pop('horizontal_line', None) + pbars = kwargs.pop('pbars', None) + + # Original SNS function call + ax = _original_sns_boxplot(*args, **kwargs) + + # Access original arguments + group_col = kwargs.get("x", args[0] if len(args) > 0 else None) + val_col = kwargs.get("y", args[1] if len(args) > 0 else None) + data = kwargs.get("data", args[2] if len(args) > 2 else None) + + + if pbars is not None: + + font_ylim = pbars.get('font_ylim', None) + yshift_c = pbars.get('yshift_c', None) + font_ndigits = pbars.get('font_ndigits', None) + group = pbars.get('group', None) + hue_col = pbars.get('hue_col', None) + + add_pbars_annot(ax, data, group_col, val_col, font_ylim, yshift_c, font_ndigits, group, hue_col) + if sample_size_annotation: + ax = add_sample_annot2(ax, data, group_col) + + if horizontal_line is not None: + if horizontal_line == True: + horizontal_line = "mean" + + # Get colors of the box plots + palette = [ + patch.get_facecolor() + for patch in ax.patches + if isinstance(patch, mpatches.PathPatch)] + + add_horizontal_line_annot(ax, data, group_col, val_col, palette, horizontal_line) + + return ax #input type and annotation type as well. + +def wrap_boxplot() -> None: + """When called, this function wraps the sns boxplot with a function, that handles the + annotation via sample_size_annotation key. The usage is then: + + from matplotlib import pyplot as plt + import seaborn as sns + import pvalplot # Triggers the wrapping + + df = sns.load_dataset("tips") + sns.boxplot(y="tip", x="day", data=df, sample_size_annotation=True) + plt.show() + """ + sns.boxplot = wrapped_boxplot \ No newline at end of file From 4533f41d494c81ecf25a75d1288d0acd8a129f8c Mon Sep 17 00:00:00 2001 From: Veli-Matti Isoviita Date: Wed, 17 Sep 2025 16:02:37 +0300 Subject: [PATCH 2/5] Changed the default behaviour to be a wrapper function that has to be explicitly called for a sns function --- src/pvalplot/__init__.py | 2 - src/pvalplot/annotation_wrapper.py | 106 ++++++++++++----------------- 2 files changed, 45 insertions(+), 63 deletions(-) diff --git a/src/pvalplot/__init__.py b/src/pvalplot/__init__.py index 746ef73..e69de29 100644 --- a/src/pvalplot/__init__.py +++ b/src/pvalplot/__init__.py @@ -1,2 +0,0 @@ -from .annotation_wrapper import wrap_boxplot -wrap_boxplot() \ No newline at end of file diff --git a/src/pvalplot/annotation_wrapper.py b/src/pvalplot/annotation_wrapper.py index 9ced507..344a5ea 100644 --- a/src/pvalplot/annotation_wrapper.py +++ b/src/pvalplot/annotation_wrapper.py @@ -6,64 +6,48 @@ from .annotation_hline import add_horizontal_line_annot from .annotation_pvalues import add_pbars_annot -_original_sns_boxplot = sns.boxplot - -def wrapped_boxplot(*args, **kwargs) -> plt.axis: - """ - This wrapper function replaces original sns.boxplot and adds the custom parameters for - the annotations provided in the package - """ - - # Pop out custom variables - sample_size_annotation = kwargs.pop('sample_size_annotation', None) - horizontal_line = kwargs.pop('horizontal_line', None) - pbars = kwargs.pop('pbars', None) - - # Original SNS function call - ax = _original_sns_boxplot(*args, **kwargs) - - # Access original arguments - group_col = kwargs.get("x", args[0] if len(args) > 0 else None) - val_col = kwargs.get("y", args[1] if len(args) > 0 else None) - data = kwargs.get("data", args[2] if len(args) > 2 else None) - - - if pbars is not None: - - font_ylim = pbars.get('font_ylim', None) - yshift_c = pbars.get('yshift_c', None) - font_ndigits = pbars.get('font_ndigits', None) - group = pbars.get('group', None) - hue_col = pbars.get('hue_col', None) - - add_pbars_annot(ax, data, group_col, val_col, font_ylim, yshift_c, font_ndigits, group, hue_col) - if sample_size_annotation: - ax = add_sample_annot2(ax, data, group_col) - - if horizontal_line is not None: - if horizontal_line == True: - horizontal_line = "mean" - - # Get colors of the box plots - palette = [ - patch.get_facecolor() - for patch in ax.patches - if isinstance(patch, mpatches.PathPatch)] - - add_horizontal_line_annot(ax, data, group_col, val_col, palette, horizontal_line) - - return ax #input type and annotation type as well. - -def wrap_boxplot() -> None: - """When called, this function wraps the sns boxplot with a function, that handles the - annotation via sample_size_annotation key. The usage is then: - - from matplotlib import pyplot as plt - import seaborn as sns - import pvalplot # Triggers the wrapping - - df = sns.load_dataset("tips") - sns.boxplot(y="tip", x="day", data=df, sample_size_annotation=True) - plt.show() - """ - sns.boxplot = wrapped_boxplot \ No newline at end of file +def wrapped(original_func): + + def wrapper(*args, **kwargs): + + # Pop out custom variables + sample_size_annotation = kwargs.pop('sample_size_annotation', None) + horizontal_line = kwargs.pop('horizontal_line', None) + pbars = kwargs.pop('pbars', None) + + # Original SNS function call + ax = original_func(*args, **kwargs) + + # Access original arguments + group_col = kwargs.get("x", args[0] if len(args) > 0 else None) + val_col = kwargs.get("y", args[1] if len(args) > 0 else None) + data = kwargs.get("data", args[2] if len(args) > 2 else None) + + if pbars is not None: + + font_ylim = pbars.get('font_ylim', None) + yshift_c = pbars.get('yshift_c', None) + font_ndigits = pbars.get('font_ndigits', None) + group = pbars.get('group', None) + hue_col = pbars.get('hue_col', None) + + add_pbars_annot(ax, data, group_col, val_col, font_ylim, yshift_c, font_ndigits, group, hue_col) + if sample_size_annotation: + ax = add_sample_annot2(ax, data, group_col) + + if horizontal_line is not None: + if horizontal_line == True: + horizontal_line = "mean" + + # Get colors of the box plots + palette = [ + patch.get_facecolor() + for patch in ax.patches + if isinstance(patch, mpatches.PathPatch)] + + add_horizontal_line_annot(ax, data, group_col, val_col, palette, horizontal_line) + + return ax #input type and annotation type as well. + return ax + + return wrapper \ No newline at end of file From 3e0d01563fe217a7ee567ae3072a1aecdcabfaf0 Mon Sep 17 00:00:00 2001 From: Veli-Matti Isoviita Date: Wed, 17 Sep 2025 16:09:55 +0300 Subject: [PATCH 3/5] comments --- src/pvalplot/annotation_wrapper.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pvalplot/annotation_wrapper.py b/src/pvalplot/annotation_wrapper.py index 344a5ea..d25c014 100644 --- a/src/pvalplot/annotation_wrapper.py +++ b/src/pvalplot/annotation_wrapper.py @@ -23,6 +23,7 @@ def wrapper(*args, **kwargs): val_col = kwargs.get("y", args[1] if len(args) > 0 else None) data = kwargs.get("data", args[2] if len(args) > 2 else None) + # Add p value brackets if pbars is not None: font_ylim = pbars.get('font_ylim', None) @@ -32,9 +33,12 @@ def wrapper(*args, **kwargs): hue_col = pbars.get('hue_col', None) add_pbars_annot(ax, data, group_col, val_col, font_ylim, yshift_c, font_ndigits, group, hue_col) + + # Add sample size annotations if sample_size_annotation: ax = add_sample_annot2(ax, data, group_col) + # Add horizontal lines if horizontal_line is not None: if horizontal_line == True: horizontal_line = "mean" From 31111623ef25cf7ec206c4d2a89f61183652485d Mon Sep 17 00:00:00 2001 From: Veli-Matti Isoviita Date: Wed, 17 Sep 2025 16:16:10 +0300 Subject: [PATCH 4/5] docstring to wrapper --- src/pvalplot/annotation_wrapper.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/pvalplot/annotation_wrapper.py b/src/pvalplot/annotation_wrapper.py index d25c014..5c745b2 100644 --- a/src/pvalplot/annotation_wrapper.py +++ b/src/pvalplot/annotation_wrapper.py @@ -7,6 +7,30 @@ from .annotation_pvalues import add_pbars_annot def wrapped(original_func): + """Wrapper to add functionalities to default sns plot function calls. Use example: + + from matplotlib import pyplot as plt + import seaborn as sns + from pvalplot.annotation_wrapper import wrapped + + sns.boxplot = wrapped(sns.boxplot) + df = sns.load_dataset("tips") + sns.boxplot( + y="total_bill", + x="day", + data=df, + sample_size_annotation=True, + horizontal_line = "median", + pbars=dict({ + 'font_ylim': None, + 'yshift_c': None, + 'font_ndigits': 2, + 'group': None, + 'hue_col': None + }), + ) + + """ def wrapper(*args, **kwargs): From b9a06dab227e3a20c54bf16c8f23beedc041b813 Mon Sep 17 00:00:00 2001 From: Veli-Matti Isoviita Date: Wed, 17 Sep 2025 16:16:58 +0300 Subject: [PATCH 5/5] revert __init__.py --- src/pvalplot/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pvalplot/__init__.py b/src/pvalplot/__init__.py index e69de29..d838a7b 100644 --- a/src/pvalplot/__init__.py +++ b/src/pvalplot/__init__.py @@ -0,0 +1,2 @@ +def hello() -> str: + return "Hello from pvalplot!" \ No newline at end of file