Skip to content

feat: support facet plot in py-maidr using maidr-ts #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions example/facet-subplots/matplotlib/example_mpl_facet_bar_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""
Example of creating a facet plot with matplotlib.

This script demonstrates how to create a figure with multiple panels
containing bar plots that share the same axes, creating a facet plot.
Each panel represents data for a different category or condition.
"""


import matplotlib.pyplot as plt
import numpy as np

import maidr

maidr.set_engine("ts")

categories = ["A", "B", "C", "D", "E"]

np.random.seed(42)
data_group1 = np.random.rand(5) * 10
data_group2 = np.random.rand(5) * 100
data_group3 = np.random.rand(5) * 36
data_group4 = np.random.rand(5) * 42

data_sets = [data_group1, data_group2, data_group3, data_group4]
condition_names = ["Group 1", "Group 2", "Group 3", "Group 4"]

fig, axs = plt.subplots(2, 2, figsize=(12, 10), sharey=True, sharex=True)
axs = axs.flatten()

all_data = np.concatenate(data_sets)
y_min, y_max = np.min(all_data) * 0.9, np.max(all_data) * 1.1

# Create a bar plot in each subplot
for i, (data, condition) in enumerate(zip(data_sets, condition_names)):
axs[i].bar(categories, data, color=f"C{i}", alpha=0.7)
axs[i].set_title(f"{condition}")
axs[i].set_ylim(y_min, y_max) # Set consistent y-axis limits

# Add value labels on top of each bar
for j, value in enumerate(data):
axs[i].text(
j,
value + (y_max - y_min) * 0.02,
f"{value:.1f}",
ha="center",
va="bottom",
fontsize=9,
)

# Add common labels
fig.text(0.5, 0.04, "Categories", ha="center", va="center", fontsize=14)
fig.text(
0.06, 0.5, "Values", ha="center", va="center", rotation="vertical", fontsize=14
)

# Add a common title
fig.suptitle("Facet Plot: Bar Charts by Condition", fontsize=16)

# Adjust layout
plt.tight_layout(rect=(0.08, 0.08, 0.98, 0.95))

maidr.show(fig)
121 changes: 121 additions & 0 deletions example/facet-subplots/matplotlib/example_mpl_facet_combined_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Example of creating a facet plot with matplotlib that has shared axes.
One column contains line plots and another contains bar plots.
"""

from typing import List, Tuple

import matplotlib.pyplot as plt
import numpy as np

import maidr

maidr.set_engine("ts")


def generate_simple_data(
num_rows: int = 3, num_points: int = 5
) -> Tuple[np.ndarray, List[np.ndarray], List[np.ndarray]]:
"""
Generate simple data for line and bar plots.

Parameters
----------
num_rows : int, optional
Number of rows (facets) to generate data for, by default 3
num_points : int, optional
Number of data points per plot, by default 5

Returns
-------
Tuple[np.ndarray, List[np.ndarray], List[np.ndarray]]
A tuple containing:
- x values common for all plots
- list of y values for line plots
- list of y values for bar plots

Examples
--------
>>> x, line_data, bar_data = generate_simple_data(2, 5)
>>> len(line_data)
2
"""
# Generate x values common to both plot types
x = np.arange(num_points)

# Generate line plot data with different linear trends
line_data = []
for i in range(num_rows):
# Create a simple linear trend with different slopes
y = np.random.rand(5) * 10
line_data.append(y)

# Generate bar plot data with different patterns
bar_data = []
for i in range(num_rows):
# Create simple bar heights
y = np.abs(np.sin(x + i) * 5) + i
bar_data.append(y)

return x, line_data, bar_data


def create_facet_plot(
x: np.ndarray, line_data: List[np.ndarray], bar_data: List[np.ndarray]
) -> Tuple[plt.Figure, np.ndarray]: # type: ignore
"""
Create a facet plot with one column of line plots and one column of bar plots.

Parameters
----------
x : np.ndarray
The x-values for all plots
line_data : List[np.ndarray]
List of y-values for the line plots, one array per row
bar_data : List[np.ndarray]
List of y-values for the bar plots, one array per row

Returns
-------
Tuple[plt.Figure, np.ndarray]
The figure and axes objects
"""
num_rows = len(line_data)

# Create a figure with a 2-column grid of subplots
fig, axs = plt.subplots(num_rows, 2, figsize=(10, 3 * num_rows), sharey="row")

# Loop through each row to create the facet plots
for row in range(num_rows):
# Left column: line plot
axs[row, 0].plot(x, line_data[row], "o-", linewidth=2, color=f"C{row}")
axs[row, 0].set_title(f"Line Plot {row+1}")

# Right column: bar plot
axs[row, 1].bar(x, bar_data[row], color=f"C{row}", alpha=0.7)
axs[row, 1].set_title(f"Bar Plot {row+1}")

# Add a y-axis label only on the left column
axs[row, 0].set_ylabel(f"Values (Row {row+1})")

# Add x-labels to the bottom row only
for col in range(2):
axs[-1, col].set_xlabel("X Values")

# Add a global title
fig.suptitle("Facet Plot Example with Shared Y-Axes", fontsize=16, y=0.98)

# Adjust spacing between subplots
plt.tight_layout()

return fig, axs


x, line_data, bar_data = generate_simple_data(num_rows=3)

# Create and display the facet plot
fig, axs = create_facet_plot(x, line_data, bar_data)

maidr.show(fig)
97 changes: 97 additions & 0 deletions example/facet-subplots/seaborn/example_sns_facet_bar_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from typing import Any, Dict, List

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

import maidr

maidr.set_engine("ts")


def create_sample_data() -> pd.DataFrame:
"""
Create a simple sample DataFrame for facet bar plotting.

Returns
-------
pd.DataFrame
A DataFrame with product categories, regions, and sales data.

Examples
--------
>>> df = create_sample_data()
>>> df.head()
category region sales
0 Product1 North 25
1 Product1 South 18
2 Product1 East 30
3 Product1 West 22
4 Product2 North 15
"""
# Define data for our example
data: Dict[str, List[Any]] = {
"category": [
"Product1",
"Product1",
"Product1",
"Product1",
"Product2",
"Product2",
"Product2",
"Product2",
"Product3",
"Product3",
"Product3",
"Product3",
],
"region": ["North", "South", "East", "West"] * 3,
"sales": [25, 18, 30, 22, 15, 29, 10, 24, 32, 17, 22, 35],
}

return pd.DataFrame(data)


def create_facet_bar_plot(data: pd.DataFrame) -> None:
"""
Create a facet grid of bar plots with shared axis.

Parameters
----------
data : pd.DataFrame
The DataFrame containing the data to plot.

Examples
--------
>>> df = create_sample_data()
>>> create_facet_bar_plot(df)
"""
# Set the aesthetic style of the plots
sns.set_theme(style="whitegrid")

# Initialize the FacetGrid object with category as rows
# This will create a separate subplot for each product category
g = sns.FacetGrid(data, row="category", height=3, aspect=1.5, sharey=True)

# Map a barplot using region as x and sales as y
# This creates a bar plot for each category showing sales by region
g.map_dataframe(sns.barplot, x="region", y="sales", palette="viridis")

# Add titles and labels
g.set_axis_labels("Region", "Sales")
g.set_titles(row_template="{row_name}")

# Adjust the layout and add a title
plt.tight_layout()
plt.subplots_adjust(top=0.9)
g.figure.suptitle("Sales by Region for Different Products", fontsize=16)

# Show the plot
maidr.show(g.figure)


# Create sample data
df = create_sample_data()

# Create and display the facet bar plot
create_facet_bar_plot(df)
Loading