Skip to content

Commit

Permalink
readme and examples commented and finished
Browse files Browse the repository at this point in the history
  • Loading branch information
mpecchi committed May 1, 2024
1 parent 8341139 commit 569e537
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 47 deletions.
86 changes: 84 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,84 @@
# myfigure
a class for standardized accessible plots
# MyFigure Module

A class for standardized accessible scientific plots.

## Overview
MyFigure is a Python module tailored for creating and customizing sophisticated visualizations with Matplotlib and Seaborn. It offers an intuitive interface to streamline figure setup, axis configuration, and style management, making it ideal for both simple plots and complex graphical representations in publications and presentations.

## Features
- **Seaborn and Matplotlib Integration**: Leverages the advanced styling and plotting capabilities of both Seaborn and Matplotlib to produce beautiful, scientific-grade figures.
- **Default Keyword Arguments**: Supports easy customization and flexibility by allowing default configurations that can be overridden according to user needs.
- **Broadcast Keyword Arguments**: Automatically broadcasts single values across multiple axes or applies unique values per axis, enhancing customization without additional code for each axis.
- **Automatic Axis Management**: Always creates axes as a list, simplifying access and manipulation (e.g., `mf.axs[0]`), including support for `twinx` without raising exceptions.
- **Automatic Application of Hatches**: Applies hatch patterns to bars in bar plots automatically, improving visibility in colorblind-friendly and black-and-white printouts.
- **Annotate Outliers**: Offers functionality to annotate outliers to address scaling issues with a few outlying data points.
- **Edge Padding in Limits**: Automatically adds a 5% padding to `x_lim`, `y_lim`, and `yt_lim` to avoid the "Excel-effect" where lines touch the edges of the plot.
- **Legend Integration**: Seamlessly integrates legends from multiple axes and `twinx` in a unified view.
- **Consistent Subplot Annotations**: Automatically places letters or annotations in subplots to ensure consistent location across figures.
- **Label Rotation and Anchoring**: When labels on the x-axis are rotated, they are anchored on their right to enhance readability.
- **Mask Insignificant Data**: Temporarily masks data in bar plots where the error (standard deviation) is greater than the mean, applying a transparency effect to highlight significant results.
- **Inset Plots**: Simplifies the creation of inset plots within larger axes for detailed examinations of data subsets.
- **Flexible Saving Options**: Allows figures to be saved in various formats including PNG, PDF, SVG, EPS, and TIFF, with options for resolution settings and transparency. This provides versatility for different publishing needs and ensures high-quality outputs.

## Installation
Install MyFigure using pip:
```bash
pip install myfigure
```

## Examples

Examples are available in the ``examples`` folder.
To run examples:
1. Install ``myfigure`` in your Python environment
2. Download or copy-paste the example code in your editor
3. Run the code
4. If you run the scripts as Jupyter Notebooks, replace the relative path at the beginning of the example with the absolute path to the folder where you want to save the plots.

## How to use MyFigure inside functions
```bash
# minimum example of function that uses MyFigure to plot its results
def function_using_myfigure(
your_other_parameters,
**kwargs,
) -> MyFigure:

# core computation of the function, to compute the data that
# will need to be plotted using myfunction

# if you want to specify a different out_path for the output of this function
out_path = your_alternative_path # (using plib)
out_path.mkdir(parents=True, exist_ok=True) # create the folder if missing

# example of default parameter that are specific for this function
default_kwargs = {
"height": 4, # this can be changed, but the default is 4
"width": 4, # this can be changed, but the default is 4
"y_lab": "very_specific_y_lab", # this can be changed, but the default is "very_specific_y_lab"
}
# Update kwargs with the default key-value pairs if the key is not present in kwargs
kwargs = {**default_kwargs, **kwargs}

myfig = MyFigure(
rows=1, # this cannot be modified, function_using_myfigure(rows=2) gives an error
cols=1, # this cannot be modified, function_using_myfigure(rows=2) gives an error
**kwargs, # this ensures full customization of the object
)
myfig.save_figure()
# it is advisable to return the object so that furter modificaiton can be performed
# outside the function if needed (then calling again myfig.save_figure() will simply overwrite the
# version of the figure since the output path is stored in myfig)
return myfig


# how to call the function
mf_default = function_using_myfigure() # with default values
# %%
mf_non_default = function_using_myfigure(
x_lim=(0, 1), height=6, x_lab="a", y_lab="another_specific_label"
) # customized
#
mf = function_using_myfigure()
mf.axs[0].plot([1], [1]) # something that was impossible inside the function
mf.save_figure()
```
75 changes: 30 additions & 45 deletions examples/example_myfigure.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,46 @@
import pandas as pd
from myfigure.myfigure import MyFigure, create_inset, colors, linestyles, markers, hatches, letters

a = hatches

# Define the output path for saving figures
out_path = plib.Path(__file__).resolve().parent / "output"

out_path = plib.Path("/Users/matteo/Projects/myfigure/examples/output")

# create some random data for plotting
x0 = np.linspace(0, 10, 10)
y0 = np.linspace(0, 10, 10)
y1 = np.linspace(15, 5, 10)

# %%
# Create a MyFigure instance and save it with default settings
f0 = MyFigure(filename="f0", out_path=out_path)
f0.save_figure()

# %%
# Create another MyFigure instance, plot line and scatter plot, then save
f1 = MyFigure(filename="f1", out_path=out_path)
f1.axs[0].plot(x0, y0, color=colors[5], linestyle=linestyles[1])
f1.axs[0].scatter(x0, y1, color=colors[1], marker=markers[2])
f1.save_figure()
# %%
# Create a figure with twin axes, plot on both, and save the figure
f2 = MyFigure(filename="f2", out_path=out_path, twinx=True, legend_loc="lower center")
f2.axs[0].plot(x0, y0, label="y0")
f2.axts[0].plot(x0, y1, label="y1", color=colors[1])
f2.save_figure()

# %%
f3 = MyFigure(filename="f3", out_path=out_path, rows=2, width=4, x_lab=["aaa", "ass"])
# Create a figure with two rows and custom labels, then save it
f3 = MyFigure(filename="f3", out_path=out_path, rows=2, width=4, x_lab=["aaa", "bbb"])
f3.axs[0].plot(x0, y0, color=colors[5], linestyle=linestyles[1], label="1")
f3.axs[0].scatter(x0, y1, color=colors[1], marker=markers[2], label="2")
f3.axs[1].plot(x0, y0, color=colors[5], linestyle=linestyles[1], label="1")
f3.axs[1].scatter(x0, y1, color=colors[1], marker=markers[2], label="2")
f3.save_figure()

# %%
# Create a figure with twin axes on two rows, apply LaTeX labels, and save
f4 = MyFigure(
filename="f4",
out_path=out_path,
Expand All @@ -52,6 +62,7 @@
f4.axts[1].scatter(x0, y1, color=colors[1], marker=markers[1], label="y1")
f4.save_figure()
# %%
# Create a figure with custom axes limits and tick settings, then save
f5 = MyFigure(
filename="f5",
out_path=out_path,
Expand All @@ -70,6 +81,7 @@
f5.axs[1].scatter(x0, y1, color=colors[1], marker=markers[2])
f5.save_figure()
# %%
# More complex example with twinx, multiple rows, and extensive customization
f6 = MyFigure(
filename="f6",
out_path=out_path,
Expand All @@ -93,12 +105,13 @@
f6.axts[1].scatter(x0, y0, color=colors[3], marker=markers[3], label="4")
f6.save_figure()
# %%

# create additional random dataframe for plotting
df_ave = pd.DataFrame(data=[[1, 2, 3, 4, 5], [6, 5, 4, 3, 2]], columns=["1", "2", "3", "4", "5a"])
df_std = pd.DataFrame(
data=[[0.1, 0.2, 0.3, 0.4, 0.5], [0.6, 0.65, 0.4, 0.3, 0.2]], columns=["1", "2", "3", "4", "5a"]
)

# %%
# Create a figure to display bar plots with error bars, specific outlier annotations, and save
f7 = MyFigure(
filename="f7",
out_path=out_path,
Expand All @@ -112,21 +125,18 @@
df_ave.plot(ax=f7.axs[0], kind="bar", yerr=df_std, capsize=2, edgecolor="k")
f7.save_figure()
# %%
df_ave = pd.DataFrame(data=[[1, 2, 3, 4], [6, 5, 4, 3], [3, 5, 4, 6]], columns=["1", "2", "3", "4"])
df_std = pd.DataFrame(
data=[[0.1, 0.2, 0.3, 0.4], [0.6, 0.65, 0.4, 0.3], [3, 4, 5, 1]], columns=["1", "2", "3", "4"]
)

f7 = MyFigure(
filename="f7",
# as f7 but with only 1 decimal place in annotate ouliers
f7b = MyFigure(
filename="f7b",
out_path=out_path,
annotate_outliers=True,
annotate_outliers_decimal_places=1,
y_lim=[0, 3],
)
df_ave.plot(ax=f7.axs[0], kind="bar", yerr=df_std, capsize=2)
f7.save_figure()
f7b.save_figure()
# %%
# Create a figure to display inverted bar plots with annotations for outliers and save
f8 = MyFigure(
filename="f8",
out_path=out_path,
Expand All @@ -137,6 +147,7 @@
df_ave.mul(-1).plot(ax=f8.axs[0], kind="bar", yerr=df_std, capsize=2)
f8.save_figure()
# %%
# Create a figure with twin axes to display separate bar and line plots and save
f9 = MyFigure(
filename="f9",
out_path=out_path,
Expand All @@ -146,7 +157,8 @@
df_ave[["1", "2"]].plot(ax=f9.axts[0], kind="bar", yerr=df_std, capsize=2, legend=False)
df_ave[["3", "4"]].plot(ax=f9.axs[0], kind="line", yerr=df_std, capsize=2, color=colors[3:5])
f9.save_figure()

# %%
# additional random data
df_ave = pd.DataFrame(
data=[[1, 2, 3, 4, 5], [6, 5, 4, 3, 2]], columns=["1", "2", "3", "4", "5a"], index=["i1", "i2"]
)
Expand All @@ -155,6 +167,7 @@
columns=["1", "2", "3", "4", "5a"],
index=["i1", "i2"],
)
# Create a figure to display a bar plot with error bars and outlier annotations, with specific y-axis limits
f10 = MyFigure(
filename="f10",
out_path=out_path,
Expand All @@ -166,6 +179,8 @@
f10.save_figure()

# %%
# Create a figure with twin axes and multiple rows for plotting complex data and save

f11 = MyFigure(
filename="f11",
out_path=out_path,
Expand All @@ -187,7 +202,7 @@
df_std = pd.DataFrame(
data=[[1.1, 0.2, 3.3, 0.4], [0.6, 5.65, 0.4, 0.3], [1.3, 4, 5, 7]], columns=["1", "2", "3", "4"]
)

# Create a figure that automatically masks insignificant data and save
f12 = MyFigure(
filename="f12",
out_path=out_path,
Expand All @@ -200,41 +215,11 @@
df_ave.plot(ax=f12.axs[0], kind="bar", yerr=df_std, capsize=2)
f12.save_figure()
# %%

# %%
# add an inset to the plot
f13 = MyFigure(filename="f13", out_path=out_path)
f13.axs[0].plot(x0, y0, color=colors[5], linestyle=linestyles[1])
f13.axs[0].scatter(x0, y1, color=colors[1], marker=markers[2])
ins = create_inset(f13.axs[0], x_loc=(0.1, 0.4), y_loc=(0.35, 0.65), x_lim=(5, 7), y_lim=(4, 10))
ins.plot(x0, y0, color=colors[5], linestyle=linestyles[1])
ins.scatter(x0, y1, color=colors[1], marker=markers[2])
f13.save_figure()


# %%
# minimum example of function that uses MyFigure
def function_using_myfigure(
your_other_parameters,
**kwargs,
) -> MyFigure:

# if you want to specify a different out_path
# out_path = your alternative path (using plib)
# out_path.mkdir(parents=True, exist_ok=True)

# example of default parameter that are specific for this function
default_kwargs = {
"height": 3.2,
"width": 3.2,
"y_lab": "very_specific_y_lab",
}
# Update kwargs with the default key-value pairs if the key is not present in kwargs
kwargs = {**default_kwargs, **kwargs}

myfig = MyFigure(
rows=1,
cols=1,
**kwargs,
)
myfig.save_figure()
return myfig

0 comments on commit 569e537

Please sign in to comment.