From 1a9fae1d87aa3a0b78f09006215fb931cfc5aade Mon Sep 17 00:00:00 2001 From: Nikhil Bhavikatti Date: Tue, 16 Apr 2024 16:29:52 +0200 Subject: [PATCH 1/5] Add box plot feature --- example.py | 30 +++++++++++++++++++++++- uadapy/plotting/boxplot.py | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 uadapy/plotting/boxplot.py diff --git a/example.py b/example.py index cf681ca..4defdba 100644 --- a/example.py +++ b/example.py @@ -2,6 +2,7 @@ import uadapy.data as data import uadapy.dr.uamds as uamds import uadapy.plotting.plots2D as plots2D +import uadapy.plotting.boxplot as boxplot import numpy as np @@ -16,8 +17,35 @@ def example_kde(): distr = ua.distribution.distribution(samples) plots2D.plot_contour(distr) +def example_uamds_scatter(): + distribs_hi = data.load_iris_normal() + print(distribs_hi) + distribs_lo = uamds.uamds(distribs_hi, dims=2) + plots2D.plot_samples(distribs_lo, 10) + +def example_kde_scatter(): + samples = np.random.randn(1000,2) + distr = ua.distribution.distribution(samples) + plots2D.plot_samples(distr, 10) + +def example_uamds_boxplot(): + distribs_hi = data.load_iris_normal() + distribs_lo = uamds.uamds(distribs_hi, dims=2) + labels = ['setosa','versicolor','virginica'] + titles = ['sepal length','sepal width','petal length','petal width'] + boxplot.plot_boxplot(distribs_lo, 100, labels, titles) + +def example_kde_boxplot(): + samples = np.random.randn(1000,2) + distr = ua.distribution.distribution(samples) + boxplot.plot_boxplot(distr, 100) + if __name__ == '__main__': - example_uamds() + # example_uamds() # example_kde() + #example_uamds_scatter() + #example_kde_scatter() + example_uamds_boxplot() + #example_kde_boxplot() diff --git a/uadapy/plotting/boxplot.py b/uadapy/plotting/boxplot.py new file mode 100644 index 0000000..179bc9b --- /dev/null +++ b/uadapy/plotting/boxplot.py @@ -0,0 +1,48 @@ +import matplotlib.pyplot as plt +import uadapy.distribution as dist +from math import ceil, sqrt + +def plot_boxplot(distribution, num_samples, labels=None, titles=None, **kwargs): + """ + Plot box plots for samples drawn from given distributions. + :param distributions: Distributions to plot + :param num_samples: Number of samples per distribution + :param labels: Labels for each distribution + :param titles: Titles for each subplot + :param kwargs: Additional optional plotting arguments + """ + samples = [] + + if isinstance(distribution, dist.distribution): + distribution = [distribution] + + # Calculate the layout of subplots + if (titles == None): + num_rows = num_cols = 2 + num_plots = 1 + else: + num_plots = len(titles) + num_rows = ceil(sqrt(num_plots)) + num_cols = ceil(num_plots / num_rows) + + fig, axs = plt.subplots(num_rows, num_cols, figsize=(10, 10)) + + for d in distribution: + samples.append(d.sample(num_samples)) + + for i, ax_row in enumerate(axs): + for j, ax in enumerate(ax_row): + index = i * num_cols + j + if index < num_plots: + for k, sample in enumerate(samples): + ax.boxplot(sample[index], positions=[k], patch_artist=True) + if labels: + ax.set_xticks(range(len(labels))) + ax.set_xticklabels(labels) + if titles: + ax.set_title(titles[index] if titles and index < len(titles) else 'Comparison') + else: + ax.set_visible(False) # Hide unused subplots + + fig.tight_layout() + plt.show() \ No newline at end of file From f2f880e3ebd6f00850146df394ecfbc73b5ed453 Mon Sep 17 00:00:00 2001 From: Nikhil Bhavikatti Date: Fri, 26 Apr 2024 17:21:55 +0200 Subject: [PATCH 2/5] feature updates for box plot --- example.py | 32 +++-------- uadapy/plotting/boxplot.py | 108 +++++++++++++++++++++++++++++-------- 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/example.py b/example.py index 4defdba..2bea550 100644 --- a/example.py +++ b/example.py @@ -4,6 +4,7 @@ import uadapy.plotting.plots2D as plots2D import uadapy.plotting.boxplot as boxplot import numpy as np +import matplotlib.pyplot as plt @@ -17,35 +18,18 @@ def example_kde(): distr = ua.distribution.distribution(samples) plots2D.plot_contour(distr) -def example_uamds_scatter(): - distribs_hi = data.load_iris_normal() - print(distribs_hi) - distribs_lo = uamds.uamds(distribs_hi, dims=2) - plots2D.plot_samples(distribs_lo, 10) - -def example_kde_scatter(): - samples = np.random.randn(1000,2) - distr = ua.distribution.distribution(samples) - plots2D.plot_samples(distr, 10) - def example_uamds_boxplot(): distribs_hi = data.load_iris_normal() - distribs_lo = uamds.uamds(distribs_hi, dims=2) + distribs_lo = uamds.uamds(distribs_hi, dims=4) labels = ['setosa','versicolor','virginica'] titles = ['sepal length','sepal width','petal length','petal width'] - boxplot.plot_boxplot(distribs_lo, 100, labels, titles) - -def example_kde_boxplot(): - samples = np.random.randn(1000,2) - distr = ua.distribution.distribution(samples) - boxplot.plot_boxplot(distr, 100) - + fig, axs = plt.subplots(2, 1) + fig, axs = boxplot.plot_boxplot(distribs_lo, 10000, fig, axs, labels, titles, vert=True, colorblind_safe=False) + fig.tight_layout() + plt.show() if __name__ == '__main__': - # example_uamds() + example_uamds() # example_kde() - #example_uamds_scatter() - #example_kde_scatter() - example_uamds_boxplot() - #example_kde_boxplot() + # example_uamds_boxplot() diff --git a/uadapy/plotting/boxplot.py b/uadapy/plotting/boxplot.py index 179bc9b..9526e81 100644 --- a/uadapy/plotting/boxplot.py +++ b/uadapy/plotting/boxplot.py @@ -1,48 +1,112 @@ import matplotlib.pyplot as plt import uadapy.distribution as dist from math import ceil, sqrt +import numpy as np +import glasbey as gb -def plot_boxplot(distribution, num_samples, labels=None, titles=None, **kwargs): +def plot_boxplot(distributions, num_samples, fig=None, axs=None, labels=None, titles=None, **kwargs): """ Plot box plots for samples drawn from given distributions. - :param distributions: Distributions to plot - :param num_samples: Number of samples per distribution - :param labels: Labels for each distribution - :param titles: Titles for each subplot - :param kwargs: Additional optional plotting arguments + + Parameters + ---------- + distributions : list + List of distributions to plot. + num_samples : int + Number of samples per distribution. + fig : matplotlib.figure.Figure or None, optional + Figure object to use for plotting. If None, a new figure will be created. + axs : matplotlib.axes.Axes or array of Axes or None, optional + Axes object(s) to use for plotting. If None, new axes will be created. + labels : list or None, optional + Labels for each distribution. + titles : list or None, optional + Titles for each subplot. + **kwargs : additional keyword arguments + Additional optional plotting arguments. + - vert : bool, optional + If True, boxes will be drawn vertically. If False, boxes will be drawn horizontally. + Default is True. + - colorblind_safe : bool, optional + If True, the plot will use colors suitable for colorblind individuals. + Default is False. + + Returns + ------- + matplotlib.figure.Figure + The figure object containing the plot. + list + List of Axes objects used for plotting. """ samples = [] + vertical = True + colorblind_safe = False - if isinstance(distribution, dist.distribution): - distribution = [distribution] + if isinstance(distributions, dist.distribution): + distributions = [distributions] + + if kwargs: + for key, value in kwargs.items(): + if key == 'vert': + vertical = value + if key == 'colorblind_safe': + colorblind_safe = value # Calculate the layout of subplots - if (titles == None): - num_rows = num_cols = 2 - num_plots = 1 - else: - num_plots = len(titles) + if axs is None: + num_plots = distributions[0].dim num_rows = ceil(sqrt(num_plots)) num_cols = ceil(num_plots / num_rows) + fig, axs = plt.subplots(num_rows, num_cols) + else: + # Case 1: axs is a 2D array (multiple rows and columns) + if isinstance(axs, np.ndarray): + dim = axs.shape + if (len(axs.shape) == 1): + num_rows, num_cols = 1, dim[0] + else: + num_rows, num_cols = dim + # Case 2: axs is not an array (single subplot) + else: + num_rows, num_cols = 1, 1 + num_plots = num_rows + num_cols - fig, axs = plt.subplots(num_rows, num_cols, figsize=(10, 10)) + # Ensure axs is a 2D array even if there's only one row or column + if num_rows == 1: + axs = [axs] + if num_cols == 1: + axs = [[ax] for ax in axs] - for d in distribution: + for d in distributions: samples.append(d.sample(num_samples)) + # Generate Glasbey colors + palette = gb.create_palette(palette_size=len(samples), colorblind_safe=colorblind_safe) + for i, ax_row in enumerate(axs): for j, ax in enumerate(ax_row): index = i * num_cols + j if index < num_plots: for k, sample in enumerate(samples): - ax.boxplot(sample[index], positions=[k], patch_artist=True) + boxprops = dict(facecolor=palette[k % len(palette)], edgecolor='black') + whiskerprops = dict(color='black', linestyle='--') + capprops = dict(color='black') + ax.boxplot(sample[index], positions=[k], patch_artist=True, boxprops=boxprops, + showfliers=False, whiskerprops=whiskerprops, capprops=capprops, + showmeans=True, meanline=True, meanprops=dict(color="black", linestyle='-'), + medianprops=dict(linewidth=0), vert=vertical) if labels: - ax.set_xticks(range(len(labels))) - ax.set_xticklabels(labels) + if vertical: + ax.set_xticks(range(len(labels))) + ax.set_xticklabels(labels, rotation=45, ha='right') + else: + ax.set_yticks(range(len(labels))) + ax.set_yticklabels(labels, rotation=45, ha='right') if titles: - ax.set_title(titles[index] if titles and index < len(titles) else 'Comparison') + ax.set_title(titles[index] if titles and index < len(titles) else 'Distribution ' + str(index + 1)) + ax.yaxis.set_ticks_position('none') + ax.grid(True, linestyle=':', linewidth='0.5', color='gray') else: ax.set_visible(False) # Hide unused subplots - - fig.tight_layout() - plt.show() \ No newline at end of file + + return fig, axs \ No newline at end of file From 112b18a6dbff9d0396e202c83ce04aa15d05060d Mon Sep 17 00:00:00 2001 From: Nikhil Bhavikatti Date: Wed, 1 May 2024 14:55:16 +0200 Subject: [PATCH 3/5] Add input parameter colors to boxplot --- example.py | 5 +++-- uadapy/plotting/boxplot.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/example.py b/example.py index 2bea550..8d6e897 100644 --- a/example.py +++ b/example.py @@ -23,8 +23,9 @@ def example_uamds_boxplot(): distribs_lo = uamds.uamds(distribs_hi, dims=4) labels = ['setosa','versicolor','virginica'] titles = ['sepal length','sepal width','petal length','petal width'] - fig, axs = plt.subplots(2, 1) - fig, axs = boxplot.plot_boxplot(distribs_lo, 10000, fig, axs, labels, titles, vert=True, colorblind_safe=False) + colors = ['red','green', 'blue'] + fig, axs = plt.subplots(2, 2) + fig, axs = boxplot.plot_boxplot(distribs_lo, 10000, fig, axs, labels, titles, colors, vert=True, colorblind_safe=False) fig.tight_layout() plt.show() diff --git a/uadapy/plotting/boxplot.py b/uadapy/plotting/boxplot.py index 9526e81..167acc1 100644 --- a/uadapy/plotting/boxplot.py +++ b/uadapy/plotting/boxplot.py @@ -4,7 +4,7 @@ import numpy as np import glasbey as gb -def plot_boxplot(distributions, num_samples, fig=None, axs=None, labels=None, titles=None, **kwargs): +def plot_boxplot(distributions, num_samples, fig=None, axs=None, labels=None, titles=None, colors=None, **kwargs): """ Plot box plots for samples drawn from given distributions. @@ -22,6 +22,8 @@ def plot_boxplot(distributions, num_samples, fig=None, axs=None, labels=None, ti Labels for each distribution. titles : list or None, optional Titles for each subplot. + colors : list or None, optional + List of colors to use for each distribution. If None, Glasbey colors will be used. **kwargs : additional keyword arguments Additional optional plotting arguments. - vert : bool, optional @@ -81,7 +83,14 @@ def plot_boxplot(distributions, num_samples, fig=None, axs=None, labels=None, ti samples.append(d.sample(num_samples)) # Generate Glasbey colors - palette = gb.create_palette(palette_size=len(samples), colorblind_safe=colorblind_safe) + if colors is None: + palette = gb.create_palette(palette_size=len(samples), colorblind_safe=colorblind_safe) + else: + # If colors are provided but fewer than the number of samples, add more colors from Glasbey palette + if len(colors) < len(samples): + additional_colors = gb.create_palette(palette_size=len(samples) - len(colors), colorblind_safe=colorblind_safe) + colors.extend(additional_colors) + palette = colors for i, ax_row in enumerate(axs): for j, ax in enumerate(ax_row): From 7f8782c1f156cba69dcd6e0d56bc8599c61c13c3 Mon Sep 17 00:00:00 2001 From: Nikhil Bhavikatti Date: Sun, 5 May 2024 23:05:44 +0200 Subject: [PATCH 4/5] Bug fix --- uadapy/plotting/boxplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uadapy/plotting/boxplot.py b/uadapy/plotting/boxplot.py index 167acc1..53dbc97 100644 --- a/uadapy/plotting/boxplot.py +++ b/uadapy/plotting/boxplot.py @@ -100,7 +100,7 @@ def plot_boxplot(distributions, num_samples, fig=None, axs=None, labels=None, ti boxprops = dict(facecolor=palette[k % len(palette)], edgecolor='black') whiskerprops = dict(color='black', linestyle='--') capprops = dict(color='black') - ax.boxplot(sample[index], positions=[k], patch_artist=True, boxprops=boxprops, + ax.boxplot(sample[:, index], positions=[k], patch_artist=True, boxprops=boxprops, showfliers=False, whiskerprops=whiskerprops, capprops=capprops, showmeans=True, meanline=True, meanprops=dict(color="black", linestyle='-'), medianprops=dict(linewidth=0), vert=vertical) From 27ba20896f1f557735ff983a7cda363dab87bba1 Mon Sep 17 00:00:00 2001 From: Nikhil Bhavikatti Date: Sat, 1 Jun 2024 16:23:03 +0200 Subject: [PATCH 5/5] Add function to plot 1D distributions --- example.py | 11 +- uadapy/plotting/boxplot.py | 121 ---------------------- uadapy/plotting/plots1D.py | 204 +++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+), 127 deletions(-) delete mode 100644 uadapy/plotting/boxplot.py create mode 100644 uadapy/plotting/plots1D.py diff --git a/example.py b/example.py index 8d6e897..e265fac 100644 --- a/example.py +++ b/example.py @@ -2,7 +2,7 @@ import uadapy.data as data import uadapy.dr.uamds as uamds import uadapy.plotting.plots2D as plots2D -import uadapy.plotting.boxplot as boxplot +import uadapy.plotting.plots1D as plots1D import numpy as np import matplotlib.pyplot as plt @@ -18,19 +18,18 @@ def example_kde(): distr = ua.distribution.distribution(samples) plots2D.plot_contour(distr) -def example_uamds_boxplot(): +def example_uamds_1d(): distribs_hi = data.load_iris_normal() distribs_lo = uamds.uamds(distribs_hi, dims=4) labels = ['setosa','versicolor','virginica'] titles = ['sepal length','sepal width','petal length','petal width'] colors = ['red','green', 'blue'] - fig, axs = plt.subplots(2, 2) - fig, axs = boxplot.plot_boxplot(distribs_lo, 10000, fig, axs, labels, titles, colors, vert=True, colorblind_safe=False) + fig, axs = plt.subplots(2, 2, figsize=(12, 8)) + fig, axs = plots1D.plot_1d_distribution(distribs_lo, 10000, ['violinplot','stripplot'], 444, fig, axs, labels, titles, colors, vert=True, colorblind_safe=False) fig.tight_layout() plt.show() - if __name__ == '__main__': example_uamds() # example_kde() - # example_uamds_boxplot() + # example_uamds_1d() diff --git a/uadapy/plotting/boxplot.py b/uadapy/plotting/boxplot.py deleted file mode 100644 index 53dbc97..0000000 --- a/uadapy/plotting/boxplot.py +++ /dev/null @@ -1,121 +0,0 @@ -import matplotlib.pyplot as plt -import uadapy.distribution as dist -from math import ceil, sqrt -import numpy as np -import glasbey as gb - -def plot_boxplot(distributions, num_samples, fig=None, axs=None, labels=None, titles=None, colors=None, **kwargs): - """ - Plot box plots for samples drawn from given distributions. - - Parameters - ---------- - distributions : list - List of distributions to plot. - num_samples : int - Number of samples per distribution. - fig : matplotlib.figure.Figure or None, optional - Figure object to use for plotting. If None, a new figure will be created. - axs : matplotlib.axes.Axes or array of Axes or None, optional - Axes object(s) to use for plotting. If None, new axes will be created. - labels : list or None, optional - Labels for each distribution. - titles : list or None, optional - Titles for each subplot. - colors : list or None, optional - List of colors to use for each distribution. If None, Glasbey colors will be used. - **kwargs : additional keyword arguments - Additional optional plotting arguments. - - vert : bool, optional - If True, boxes will be drawn vertically. If False, boxes will be drawn horizontally. - Default is True. - - colorblind_safe : bool, optional - If True, the plot will use colors suitable for colorblind individuals. - Default is False. - - Returns - ------- - matplotlib.figure.Figure - The figure object containing the plot. - list - List of Axes objects used for plotting. - """ - samples = [] - vertical = True - colorblind_safe = False - - if isinstance(distributions, dist.distribution): - distributions = [distributions] - - if kwargs: - for key, value in kwargs.items(): - if key == 'vert': - vertical = value - if key == 'colorblind_safe': - colorblind_safe = value - - # Calculate the layout of subplots - if axs is None: - num_plots = distributions[0].dim - num_rows = ceil(sqrt(num_plots)) - num_cols = ceil(num_plots / num_rows) - fig, axs = plt.subplots(num_rows, num_cols) - else: - # Case 1: axs is a 2D array (multiple rows and columns) - if isinstance(axs, np.ndarray): - dim = axs.shape - if (len(axs.shape) == 1): - num_rows, num_cols = 1, dim[0] - else: - num_rows, num_cols = dim - # Case 2: axs is not an array (single subplot) - else: - num_rows, num_cols = 1, 1 - num_plots = num_rows + num_cols - - # Ensure axs is a 2D array even if there's only one row or column - if num_rows == 1: - axs = [axs] - if num_cols == 1: - axs = [[ax] for ax in axs] - - for d in distributions: - samples.append(d.sample(num_samples)) - - # Generate Glasbey colors - if colors is None: - palette = gb.create_palette(palette_size=len(samples), colorblind_safe=colorblind_safe) - else: - # If colors are provided but fewer than the number of samples, add more colors from Glasbey palette - if len(colors) < len(samples): - additional_colors = gb.create_palette(palette_size=len(samples) - len(colors), colorblind_safe=colorblind_safe) - colors.extend(additional_colors) - palette = colors - - for i, ax_row in enumerate(axs): - for j, ax in enumerate(ax_row): - index = i * num_cols + j - if index < num_plots: - for k, sample in enumerate(samples): - boxprops = dict(facecolor=palette[k % len(palette)], edgecolor='black') - whiskerprops = dict(color='black', linestyle='--') - capprops = dict(color='black') - ax.boxplot(sample[:, index], positions=[k], patch_artist=True, boxprops=boxprops, - showfliers=False, whiskerprops=whiskerprops, capprops=capprops, - showmeans=True, meanline=True, meanprops=dict(color="black", linestyle='-'), - medianprops=dict(linewidth=0), vert=vertical) - if labels: - if vertical: - ax.set_xticks(range(len(labels))) - ax.set_xticklabels(labels, rotation=45, ha='right') - else: - ax.set_yticks(range(len(labels))) - ax.set_yticklabels(labels, rotation=45, ha='right') - if titles: - ax.set_title(titles[index] if titles and index < len(titles) else 'Distribution ' + str(index + 1)) - ax.yaxis.set_ticks_position('none') - ax.grid(True, linestyle=':', linewidth='0.5', color='gray') - else: - ax.set_visible(False) # Hide unused subplots - - return fig, axs \ No newline at end of file diff --git a/uadapy/plotting/plots1D.py b/uadapy/plotting/plots1D.py new file mode 100644 index 0000000..5b40ffe --- /dev/null +++ b/uadapy/plotting/plots1D.py @@ -0,0 +1,204 @@ +import numpy as np +import uadapy.distribution as dist +import matplotlib.pyplot as plt +from math import ceil, sqrt +import glasbey as gb +import seaborn as sns + +def calculate_dot_size(num_samples, scale_factor): + if num_samples < 100: + dot_size = 3.125 + else: + dot_size = scale_factor * (50 /(4 ** np.log10(num_samples))) + return dot_size + +def setup_plot(distributions, num_samples, seed, fig=None, axs=None, colors=None, **kwargs): + """ + Set up the plot for samples drawn from given distributions. + + Parameters + ---------- + distributions : list + List of distributions to plot. If a single distribution is passed, it will be converted into a list. + num_samples : int + Number of samples per distribution. + seed : int + Seed for the random number generator for reproducibility. + fig : matplotlib.figure.Figure or None, optional + Figure object to use for plotting. If None, a new figure will be created. + axs : matplotlib.axes.Axes or array of Axes or None, optional + Axes object(s) to use for plotting. If None, new axes will be created. + colors : list or None, optional + List of colors to use for each distribution. If None, Glasbey colors will be used. + **kwargs : additional keyword arguments + Additional optional arguments. + - colorblind_safe : bool, optional + If True, the plot will use colors suitable for colorblind individuals. + Default is False. + + Returns + ------- + fig : matplotlib.figure.Figure + The figure object containing the plot. + axs : list + List of Axes objects used for plotting. + samples : list + List of samples drawn from the distributions. + palette : list + List of colors to use for each distribution. + num_plots : int + Number of subplots. + num_cols : int + Number of columns in the subplot layout. + """ + + samples = [] + + if isinstance(distributions, dist.distribution): + distributions = [distributions] + + # Calculate the layout of subplots + if axs is None: + num_plots = distributions[0].dim + num_rows = ceil(sqrt(num_plots)) + num_cols = ceil(num_plots / num_rows) + fig, axs = plt.subplots(num_rows, num_cols) + else: + # Case 1: axs is a 2D array (multiple rows and columns) + if isinstance(axs, np.ndarray): + dim = axs.shape + if (len(axs.shape) == 1): + num_rows, num_cols = 1, dim[0] + else: + num_rows, num_cols = dim + # Case 2: axs is not an array (single subplot) + else: + num_rows, num_cols = 1, 1 + num_plots = num_rows + num_cols + + # Ensure axs is a 2D array even if there's only one row or column + if num_rows == 1: + axs = [axs] + if num_cols == 1: + axs = [[ax] for ax in axs] + + for d in distributions: + samples.append(d.sample(num_samples, seed)) + + # Generate Glasbey colors + if colors is None: + palette = gb.create_palette(palette_size=len(samples), colorblind_safe=kwargs.get('colorblind_safe', False)) + else: + # If colors are provided but fewer than the number of samples, add more colors from Glasbey palette + if len(colors) < len(samples): + additional_colors = gb.create_palette(palette_size=len(samples) - len(colors), colorblind_safe=kwargs.get('colorblind_safe', True)) + colors.extend(additional_colors) + palette = colors + + return fig, axs, samples, palette, num_plots, num_cols + +def plot_1d_distribution(distributions, num_samples, plot_types:list, seed=55, fig=None, axs=None, labels=None, titles=None, colors=None, **kwargs): + """ + Plot box plots, violin plots and dot plots for samples drawn from given distributions. + + Parameters + ---------- + distributions : list + List of distributions to plot. + num_samples : int + Number of samples per distribution. + plot_types : list + List of plot types to plot. Valid values are 'boxplot','violinplot', 'stripplot' and 'swarmplot'. + seed : int + Seed for the random number generator for reproducibility. It defaults to 55 if not provided. + fig : matplotlib.figure.Figure or None, optional + Figure object to use for plotting. If None, a new figure will be created. + axs : matplotlib.axes.Axes or array of Axes or None, optional + Axes object(s) to use for plotting. If None, new axes will be created. + labels : list or None, optional + Labels for each distribution. + titles : list or None, optional + Titles for each subplot. + colors : list or None, optional + List of colors to use for each distribution. If None, Glasbey colors will be used. + **kwargs : additional keyword arguments + Additional optional plotting arguments. + - vert : bool, optional + If True, boxes will be drawn vertically. If False, boxes will be drawn horizontally. + Default is True. + - colorblind_safe : bool, optional + If True, the plot will use colors suitable for colorblind individuals. + Default is False. + - dot_size : float, optional + This parameter determines the size of the dots used in the 'stripplot' and 'swarmplot'. + If not provided, the size is calculated based on the number of samples and the type of plot. + + Returns + ------- + matplotlib.figure.Figure + The figure object containing the plot. + list + List of Axes objects used for plotting. + """ + + fig, axs, samples, palette, num_plots, num_cols = setup_plot(distributions, num_samples, seed, fig, axs, colors, **kwargs) + + num_attributes = np.shape(samples)[2] + + for i, ax_row in enumerate(axs): + for j, ax in enumerate(ax_row): + index = i * num_cols + j + if index < num_plots and index < num_attributes: + for k, sample in enumerate(samples): + if 'boxplot' in plot_types: + boxprops = dict(facecolor=palette[k % len(palette)], edgecolor='black') + whiskerprops = dict(color='black', linestyle='--') + capprops = dict(color='black') + ax.boxplot(sample[:, index], positions=[k], patch_artist=True, boxprops=boxprops, + showfliers=False, whiskerprops=whiskerprops, capprops=capprops, + showmeans=True, meanline=True, meanprops=dict(color="black", linestyle='-'), + medianprops=dict(linewidth=0), vert=kwargs.get('vert', True)) + if 'violinplot' in plot_types: + parts = ax.violinplot(sample[:,index], positions=[k], showmeans=True, vert=kwargs.get('vert', True)) + for pc in parts['bodies']: + pc.set_facecolor(palette[k % len(palette)]) + pc.set_edgecolor(palette[k % len(palette)]) + pc.set_alpha(0.5) + parts['cbars'].remove() + parts['cmaxes'].remove() + parts['cmins'].remove() + parts['cmeans'].set_edgecolor('black') + if 'stripplot' in plot_types or 'swarmplot' in plot_types: + if 'dot_size' in kwargs: + dot_size = kwargs['dot_size'] + else: + if 'stripplot' in plot_types: + scale_factor = 1 + 0.5 * np.log10(num_samples/100) + else : + scale_factor = 1 + dot_size = calculate_dot_size(len(sample[:,index]), scale_factor) + if 'stripplot' in plot_types: + if kwargs.get('vert',True): + sns.stripplot(x=[k]*len(sample[:,index]), y=sample[:,index], color='black', size=dot_size * 1.5, jitter=0.25, ax=ax) + else: + sns.stripplot(x=sample[:,index], y=[k]*len(sample[:,index]), color='black', size=dot_size * 1.5, jitter=0.25, ax=ax, orient='h') + if 'swarmplot' in plot_types: + if kwargs.get('vert',True): + sns.swarmplot(x=[k]*len(sample[:,index]), y=sample[:,index], color='black', size=dot_size, ax=ax) + else: + sns.swarmplot(x=sample[:,index], y=[k]*len(sample[:,index]), color='black', size=dot_size, ax=ax, orient='h') + if labels: + if kwargs.get('vert', True): + ax.set_xticks(range(len(labels))) + ax.set_xticklabels(labels, rotation=45, ha='right') + else: + ax.set_yticks(range(len(labels))) + ax.set_yticklabels(labels, rotation=45, ha='right') + if titles: + ax.set_title(titles[index] if titles and index < len(titles) else 'Distribution ' + str(index + 1)) + ax.yaxis.set_ticks_position('none') + ax.grid(True, linestyle=':', linewidth='0.5', color='gray') + else: + ax.set_visible(False) # Hide unused subplots + + return fig, axs