import matplotlib.pyplot as plt
import seaborn as sns
# Just import maidr package
import maidr
# Load the penguins dataset
penguins = sns.load_dataset("penguins")
# Create a bar plot showing the average body mass of penguins by species
fig, ax = plt.subplots(figsize=(6, 6))
# Assign the plot to a variable
bar_plot = sns.barplot(
x="species", y="body_mass_g", data=penguins, errorbar="sd", palette="Blues_d", ax=ax
)
ax.set_title("Average Body Mass of Penguins by Species")
ax.set_xlabel("Species")
ax.set_ylabel("Body Mass (g)")
# Add number formatter for better screen reader output
ax.yaxis.set_major_formatter("{x:,.0f}")
# Use maidr.show() to display your plot
maidr.show(bar_plot) Example Gallery
Example Gallery
Making accessible data representation with maidr is easy and straightforward. If you already have data visualization code using matplotlib or seaborn, you can make your plots accessible with maidr in just a few lines of code.
Simply import the maidr package and use the maidr.show() function to display your plots. maidr will automatically generate accessible versions of your plots in your default browser. You can then interact with the accessible versions using keyboard shortcuts (refer to Table 1).
Bar Plots
Simple Bar Plot
Count Plots
import matplotlib.pyplot as plt
import seaborn as sns
import maidr
# Load the Titanic dataset
titanic = sns.load_dataset("titanic")
# Create a count plot
fig, ax = plt.subplots(figsize=(6, 6))
count_plot = sns.countplot(x="class", data=titanic, palette="viridis", ax=ax)
ax.set_title("Passenger Class Distribution on the Titanic")
ax.set_xlabel("Passenger Class")
ax.set_ylabel("Count")
# Add number formatter for better screen reader output
ax.yaxis.set_major_formatter("{x:.0f}")
maidr.show(count_plot) Vertical Stacked Bar Plot
import matplotlib.pyplot as plt
import numpy as np
import maidr
species = (
"Adelie",
"Chinstrap",
"Gentoo",
)
weight_counts = {
"Below": np.array([70, 31, 58]),
"Above": np.array([82, 37, 66]),
}
width = 0.5
fig, ax = plt.subplots()
bottom = np.zeros(3)
for boolean, weight_count in weight_counts.items():
p = ax.bar(species, weight_count, width, label=boolean, bottom=bottom)
bottom += weight_count
ax.set_xlabel("Species of Penguins")
ax.set_ylabel("Average Body Mass")
ax.set_title("Number of penguins with above average body mass")
ax.legend(loc="upper right")
# Add number formatter for better screen reader output
ax.yaxis.set_major_formatter("{x:.0f}")
maidr.show(p) Side-By-Side Dodged Bar Plot
import matplotlib.pyplot as plt
import numpy as np
import maidr
species: tuple[str, str, str] = (
"Adelie",
"Chinstrap",
"Gentoo",
)
weight_counts: dict[str, np.ndarray] = {
"Below": np.array([70, 31, 58]),
"Above": np.array([82, 37, 66]),
}
x: np.ndarray = np.arange(len(species))
total_groups: int = len(weight_counts)
width: float = 0.35
fig, ax = plt.subplots()
offsets: list[float] = [(-width / 2) + i * width for i in range(total_groups)]
for offset, (category, counts) in zip(offsets, weight_counts.items()):
positions = x + offset
p = ax.bar(positions, counts, width, label=category)
# Set x-axis labels and title
ax.set_xticks(x)
ax.set_xticklabels(species)
ax.set_xlabel("Species")
ax.set_ylabel("Weight")
ax.set_title("Dodged Bar Plot: Penguin Weight Counts")
ax.legend(loc="upper right")
# Add number formatter for better screen reader output
ax.yaxis.set_major_formatter("{x:.0f}")
# Show plot using maidr.show
maidr.show(p) Histogram
import matplotlib.pyplot as plt
import seaborn as sns
import maidr
# Load the Iris dataset
iris = sns.load_dataset("iris")
# Select the petal lengths
petal_lengths = iris["petal_length"]
# Plot a histogram of the petal lengths
fig, ax = plt.subplots(figsize=(6, 6))
hist_plot = sns.histplot(petal_lengths, kde=True, color="blue", binwidth=0.5, ax=ax)
ax.set_title("Petal Lengths in Iris Dataset")
ax.set_xlabel("Petal Length (cm)")
ax.set_ylabel("Frequency")
# Add number formatters for better screen reader output
ax.xaxis.set_major_formatter("{x:.1f}")
ax.yaxis.set_major_formatter("{x:.0f}")
maidr.show(hist_plot) KDE (Kernel Density Estimation) Plots
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import maidr
# Generate sample data
np.random.seed(42)
data = np.random.randn(500)
# Create a KDE plot
fig, ax = plt.subplots(figsize=(6, 6))
kde_plot = sns.kdeplot(data, color="blue", ax=ax)
ax.set_title("KDE Plot of Random Data")
ax.set_xlabel("Value")
ax.set_ylabel("Density")
# Add number formatters for better screen reader output
ax.xaxis.set_major_formatter("{x:.1f}")
ax.yaxis.set_major_formatter("{x:.3f}")
maidr.show(kde_plot) Line Plots
Single Line Plot
import matplotlib.pyplot as plt
import seaborn as sns
import maidr
# Load the 'tips' dataset from seaborn
tips = sns.load_dataset("tips")
# Choose a specific subset of the dataset (e.g., data for 'Thursday')
subset_data = tips[tips["day"] == "Thur"]
# Create a line plot
fig, ax = plt.subplots(figsize=(6, 6))
line_plot = sns.lineplot(
data=subset_data,
x="total_bill",
y="tip",
markers=True,
style="day",
legend=False,
ax=ax,
)
ax.set_title("Tips vs Total Bill (Thursday)")
ax.set_xlabel("Total Bill")
ax.set_ylabel("Tip")
# Add currency formatters for better screen reader output
ax.xaxis.set_major_formatter("${x:.2f}")
ax.yaxis.set_major_formatter("${x:.2f}")
maidr.show(line_plot) Multiline Plot
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
import maidr
# Create sample data points
x = np.array([1, 2, 3, 4, 5, 6, 7, 8])
y1 = np.array([2, 4, 1, 5, 3, 7, 6, 8])
y2 = np.array([1, 3, 5, 2, 4, 6, 8, 7])
y3 = np.array([3, 1, 4, 6, 5, 2, 4, 5])
# Convert to pandas DataFrame for seaborn
data = pd.DataFrame(
{
"x": np.tile(x, 3),
"y": np.concatenate([y1, y2, y3]),
"series": np.repeat(["Series 1", "Series 2", "Series 3"], len(x)),
}
)
# Create the plot
fig, ax = plt.subplots(figsize=(6, 6))
# Use seaborn lineplot for multiple lines
lineplot = sns.lineplot(
x="x", y="y", hue="series", style="series", markers=True, dashes=True, data=data, ax=ax
)
# Customize the plot
ax.set_title("Seaborn Multiline Plot")
ax.set_xlabel("X values")
ax.set_ylabel("Y values")
# Add number formatters for better screen reader output
ax.xaxis.set_major_formatter("{x:.0f}")
ax.yaxis.set_major_formatter("{x:.0f}")
# Display the plot using maidr
maidr.show(lineplot) Heat Map
import matplotlib.pyplot as plt
import seaborn as sns
import maidr
# Load an example dataset from seaborn
glue = sns.load_dataset("glue").pivot(index="Model", columns="Task", values="Score")
# Plot a heatmap
fig, ax = plt.subplots(figsize=(8, 8))
heatmap = sns.heatmap(glue, annot=True, fill_label="Score", ax=ax)
ax.set_title("Model Scores by Task")
# Add number formatter for colorbar for better screen reader output
cbar = heatmap.collections[0].colorbar
if cbar:
cbar.ax.yaxis.set_major_formatter("{x:.1f}")
# Show the plot
# plt.show()
maidr.show(heatmap) Box Plot
import matplotlib.pyplot as plt
import seaborn as sns
from seaborn import load_dataset
import maidr
# Load the iris dataset
iris = load_dataset("iris")
# Create the horizontal boxplot
fig, ax = plt.subplots()
horz_box_plot = sns.boxplot(x="petal_length", y="species", data=iris, orient="h", ax=ax)
ax.set_ylabel("Species")
ax.set_xlabel("Petal Length (cm)")
ax.set_title("Petal Length by Species from Iris Dataset")
# Add number formatter for better screen reader output
ax.xaxis.set_major_formatter("{x:.1f}")
# Show the plot
maidr.show(horz_box_plot) Scatter Plot
import matplotlib.pyplot as plt
import seaborn as sns
import maidr
# Create a scatter plot
fig, ax = plt.subplots()
scatter_plot = sns.scatterplot(
data=iris, x="sepal_length", y="sepal_width", hue="species", ax=ax
)
# Adding title and labels
ax.set_title("Iris Sepal Length vs Sepal Width")
ax.set_xlabel("Sepal Length (cm)")
ax.set_ylabel("Sepal Width (cm)")
# Add number formatters for better screen reader output
ax.xaxis.set_major_formatter("{x:.1f}")
ax.yaxis.set_major_formatter("{x:.1f}")
# Show the plot
maidr.show(scatter_plot) Regression Plots
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import maidr
# Generate sample data
np.random.seed(42)
x = np.linspace(0, 10, 50)
y = 2 * x + 1 + np.random.normal(0, 2, 50)
# Create a regression plot
fig, ax = plt.subplots(figsize=(6, 6))
reg_plot = sns.regplot(
x=x,
y=y,
scatter_kws={"s": 50, "alpha": 0.7},
line_kws={"color": "red", "lw": 2},
ax=ax,
)
ax.set_title("Regression Plot with Fitted Line")
ax.set_xlabel("X values")
ax.set_ylabel("Y values")
# Add number formatters for better screen reader output
ax.xaxis.set_major_formatter("{x:.1f}")
ax.yaxis.set_major_formatter("{x:.2f}")
maidr.show(reg_plot) Multi-Layered Plots
import matplotlib.pyplot as plt
import numpy as np
import maidr
# Generate sample data
x = np.arange(5)
bar_data = np.array([3, 5, 2, 7, 3])
line_data = np.array([10, 8, 12, 14, 9])
# Create a figure and a set of subplots
fig, ax1 = plt.subplots(figsize=(8, 5))
# Create the bar chart on the first y-axis
ax1.bar(x, bar_data, color="skyblue", label="Bar Data")
ax1.set_xlabel("X values")
ax1.set_ylabel("Bar values", color="blue")
ax1.tick_params(axis="y", labelcolor="blue")
# Create a second y-axis sharing the same x-axis
ax2 = ax1.twinx()
# Create the line chart on the second y-axis
ax2.plot(x, line_data, color="red", marker="o", linestyle="-", label="Line Data")
ax2.set_xlabel("X values")
ax2.set_ylabel("Line values", color="red")
ax2.tick_params(axis="y", labelcolor="red")
# Add title and legend
plt.title("Multilayer Plot Example")
# Add legends for both axes
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc="upper left")
# Add number formatters for better screen reader output
ax1.xaxis.set_major_formatter("{x:.0f}")
ax1.yaxis.set_major_formatter("{x:.0f}")
ax2.yaxis.set_major_formatter("{x:.0f}")
# Adjust layout
fig.tight_layout()
maidr.show(fig) Multi-Panel Plots (Multiple Subplots)
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import maidr
# Set the plotting style
sns.set_theme(style="whitegrid")
# Data for line plot
x_line = np.array([1, 2, 3, 4, 5, 6, 7, 8])
y_line = np.array([2, 4, 1, 5, 3, 7, 6, 8])
line_data = {"x": x_line, "y": y_line}
# Data for first bar plot
categories = ["A", "B", "C", "D", "E"]
values = np.random.rand(5) * 10
bar_data = {"categories": categories, "values": values}
# Data for second bar plot
categories_2 = ["A", "B", "C", "D", "E"]
values_2 = np.random.randn(5) * 100
bar_data_2 = {"categories": categories_2, "values": values_2}
# Create a figure with 3 subplots arranged vertically
fig, axs = plt.subplots(3, 1, figsize=(6, 12))
# First panel: Line plot using seaborn
sns.lineplot(x="x", y="y", data=line_data, color="blue", linewidth=2, ax=axs[0])
axs[0].set_title("Line Plot: Random Data")
axs[0].set_xlabel("X-axis")
axs[0].set_ylabel("Values")
# Second panel: Bar plot using seaborn
sns.barplot(
x="categories", y="values", data=bar_data, color="green", alpha=0.7, ax=axs[1]
)
axs[1].set_title("Bar Plot: Random Values")
axs[1].set_xlabel("Categories")
axs[1].set_ylabel("Values")
# Third panel: Bar plot using seaborn
sns.barplot(
x="categories", y="values", data=bar_data_2, color="blue", alpha=0.7, ax=axs[2]
)
axs[2].set_title("Bar Plot 2: Random Values") # Fixed the typo in the title
axs[2].set_xlabel("Categories")
axs[2].set_ylabel("Values")
# Add number formatters for better screen reader output
axs[0].xaxis.set_major_formatter("{x:.0f}")
axs[0].yaxis.set_major_formatter("{x:.0f}")
axs[1].yaxis.set_major_formatter("{x:.1f}")
axs[2].yaxis.set_major_formatter("{x:.1f}")
# Adjust layout to prevent overlap
plt.tight_layout()
# Display the figure
maidr.show(fig) Facet Plot
import matplotlib.pyplot as plt
import numpy as np
import maidr
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=(7, 7), 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)
# Add number formatters for better screen reader output
for ax in axs:
ax.yaxis.set_major_formatter("{x:.1f}")
# Adjust layout
plt.tight_layout(rect=(0.08, 0.08, 0.98, 0.95))
maidr.show(fig) Candlestick Chart
import mplfinance as mpf
import pandas as pd
import matplotlib.dates as mdates
import maidr
# Load the sample data
daily = pd.read_csv("../example/candle_stick/volcandat.csv", index_col=0, parse_dates=True)
# Create the candlestick chart with moving averages and volume
fig, axlist = mpf.plot(
daily,
type="candle",
volume=True,
mav = (3,6,9),
returnfig=True,
ylabel="Price",
ylabel_lower="Volume",
xlabel="Date",
title="Stock Price with Volume",
)
# Add formatters for better screen reader output
# Price axis - Currency format
axlist[0].yaxis.set_major_formatter("${x:,.2f}")
# Date axis - Custom date format like "Nov 18, 2016"
axlist[0].xaxis.set_major_formatter(mdates.DateFormatter("%b %d, %Y"))
# Volume axis - Integer with thousands separator
axlist[2].yaxis.set_major_formatter("{x:,.0f}")
fig.tight_layout()
# Display with maidr
maidr.show(fig) Reactive Dashboard
Shiny
Check out a reactive Shiny dashboard example with maidr and its source code is available on GitHub.
Streamlit
Check out this Streamlit dashboard with maidr, and its source code is available on GitHub.
* Note: `Streamlit` framework has some "Unlabeled 0 Button" which does not have to do with our maidr package. This issue needs to be addressed by the `Streamlit` team.
Interactive Computing (Jupyter Notebooks, Jupyter Labs, Google Colab)
Check out this interactive notebook in Google Colab.
Other Examples
We provide some example code for using py-maidr with matplotlib, seaborn, Jupyter Notebook, Quarto, Shiny, and Streamlit.
Keyboard Shortcuts and Controls
To interact with the plots using maidr, follow these steps:
- Press the Tab key to focus on the plot element.
- Use the arrow keys to move around the plot.
- Press B to toggle Braille mode.
- Press T to toggle Text mode.
- Press S to toggle Sonification (tones) mode.
- Press R to toggle Review mode.
Below is a detailed list of keyboard shortcuts for various functions:
| Function | Windows and Linux Key | Mac Key |
|---|---|---|
| Toggle Braille Mode | b | b |
| Toggle Text Mode | t | t |
| Toggle Sonification Mode | s | s |
| Toggle Review Mode | r | r |
| Move around plot | Arrow keys | Arrow keys |
| Go to the very left right up down | Ctrl + Arrow key | CMD + Arrow key |
| Select the first element | Ctrl + Home | CMD + Home |
| Select the last element | Ctrl + End | CMD + End |
| Repeat current sound | Space | Space |
| Auto-play outward in direction of arrow | Ctrl + Shift + Arrow key | CMD + Shift + Arrow key |
| Stop Auto-play | Ctrl | Ctrl |
| Auto-play speed up | Period (.) | Period (.) |
| Auto-play speed down | Comma (,) | Comma (,) |
| Auto-play speed reset | Slash (/) | Slash (/) |
| Check label for the title of current plot | l t | l t |
| Check label for the x axis of current plot | l x | l x |
| Check label for the y axis of current plot | l y | l y |
| Check label for the fill (z) axis of current plot | l f | l f |
| Switch to next layer | PageUp | PageUp |
| Switch to previous layer | PageDown | PageDown |
| Move around subplot list | Arrow keys | Arrow keys |
| Activate selected subplot in the list | Enter | Enter |
| Escape from current subplot to return to the subplot list | ESC | ESC |
| Open settings | Ctrl + comma (,) | CMD + comma (,) |
| Open Chat View | Question (?) | Question (?) |
| Open keyboard help | Ctrl + Slash (/) | CMD + Slash (/) |
Demo Video
Bug Report
If you encounter a bug, have usage questions, or want to share ideas to make this package better, please feel free to file an issue.
Code of Conduct
Please note that the maidr project is released with a contributor code of conduct.
By participating in this project you agree to abide by its terms.
📄 License
maidr is licensed under the GPL3 license.
🏛️ Governance
This project is primarily maintained by JooYoung Seo and Saairam Venkatesh. Other authors may occasionally assist with some of these duties.