Placing Text Boxes in Matplotlib

MatplotlibMatplotlibBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

When creating data visualizations, adding annotations can greatly enhance the viewer's understanding of your plots. Text boxes are an effective way to include additional information directly within your visualizations. Matplotlib, a popular Python library for creating static, animated, and interactive visualizations, offers powerful tools for adding customizable text boxes to your plots.

In this lab, you will learn how to place text boxes in Matplotlib plots using Python. You will discover how to position text in axes coordinates, which keeps the text in a fixed position relative to the plot, regardless of changes to data scales. Additionally, you will learn how to customize text boxes with different styles, colors, and transparency levels using the bbox property.

By the end of this lab, you will be able to create informative and visually appealing plots with strategically placed annotations for your data visualization projects.

VM Tips

After the VM startup is complete, click the top left corner to switch to the Notebook tab to access Jupyter Notebook for practice.

click-notebook

You may need to wait a few seconds for Jupyter Notebook to finish loading. Due to limitations in Jupyter Notebook, the validation of operations cannot be automated.

If you encounter any issues during the lab, feel free to ask Labby for assistance. We appreciate your feedback after the session to help us improve the lab experience.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL matplotlib(("Matplotlib")) -.-> matplotlib/BasicConceptsGroup(["Basic Concepts"]) matplotlib(("Matplotlib")) -.-> matplotlib/PlottingDataGroup(["Plotting Data"]) matplotlib(("Matplotlib")) -.-> matplotlib/PlotCustomizationGroup(["Plot Customization"]) matplotlib/BasicConceptsGroup -.-> matplotlib/importing_matplotlib("Importing Matplotlib") matplotlib/BasicConceptsGroup -.-> matplotlib/figures_axes("Understanding Figures and Axes") matplotlib/PlottingDataGroup -.-> matplotlib/histograms("Histograms") matplotlib/PlotCustomizationGroup -.-> matplotlib/titles_labels("Adding Titles and Labels") matplotlib/PlotCustomizationGroup -.-> matplotlib/text_annotations("Text Annotations") subgraph Lab Skills matplotlib/importing_matplotlib -.-> lab-48868{{"Placing Text Boxes in Matplotlib"}} matplotlib/figures_axes -.-> lab-48868{{"Placing Text Boxes in Matplotlib"}} matplotlib/histograms -.-> lab-48868{{"Placing Text Boxes in Matplotlib"}} matplotlib/titles_labels -.-> lab-48868{{"Placing Text Boxes in Matplotlib"}} matplotlib/text_annotations -.-> lab-48868{{"Placing Text Boxes in Matplotlib"}} end

Creating a Jupyter Notebook and Preparing the Data

In this first step, we will create a new Jupyter Notebook and set up our data for visualization.

Creating a New Notebook

In the first cell of the notebook, let's import the necessary libraries. Type the following code and run it by clicking the "Run" button or pressing Shift+Enter:

import matplotlib.pyplot as plt
import numpy as np
libraries-imported

This code imports two essential libraries:

  • matplotlib.pyplot: A collection of functions that make matplotlib work like MATLAB
  • numpy: A fundamental package for scientific computing in Python

Creating Sample Data

Now, let's create some sample data that we will visualize. In a new cell, enter and run the following code:

## Set a random seed for reproducibility
np.random.seed(19680801)

## Generate 10,000 random numbers from a normal distribution
x = 30 * np.random.randn(10000)

## Calculate basic statistics
mu = x.mean()
median = np.median(x)
sigma = x.std()

## Display the statistics
print(f"Mean (μ): {mu:.2f}")
print(f"Median: {median:.2f}")
print(f"Standard Deviation (σ): {sigma:.2f}")

When you run this cell, you should see output similar to:

Mean (μ): -0.31
Median: -0.28
Standard Deviation (σ): 29.86

The exact values may vary slightly. We've created a dataset with 10,000 random numbers generated from a normal distribution and calculated three important statistics:

  1. Mean (μ): The average value of all data points
  2. Median: The middle value when data is arranged in order
  3. Standard Deviation (σ): A measure of how spread out the data is

These statistics will be used later to annotate our visualization.

Creating a Basic Histogram

Now that we have our data, let's create a histogram to visualize its distribution. A histogram divides the data into bins (ranges) and shows the frequency of data points within each bin.

Creating the Histogram

In a new cell in your Jupyter Notebook, enter and run the following code:

## Create a figure and axes
fig, ax = plt.subplots(figsize=(10, 6))

## Create a histogram with 50 bins
histogram = ax.hist(x, bins=50, color='skyblue', edgecolor='black')

## Add title and labels
ax.set_title('Distribution of Random Data', fontsize=16)
ax.set_xlabel('Value', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)

## Display the plot
plt.tight_layout()
plt.show()

When you run this cell, you should see a histogram displaying the distribution of your random data. The output will look like a bell-shaped curve (normal distribution) centered near zero.

Understanding the Code

Let's break down what each line in the code does:

  1. fig, ax = plt.subplots(figsize=(10, 6)): Creates a figure and axes object. The figsize parameter sets the size of the plot in inches (width, height).

  2. histogram = ax.hist(x, bins=50, color='skyblue', edgecolor='black'): Creates a histogram of our data x with 50 bins. The bins are colored skyblue with black edges.

  3. ax.set_title('Distribution of Random Data', fontsize=16): Adds a title to the plot with a font size of 16.

  4. ax.set_xlabel('Value', fontsize=12) and ax.set_ylabel('Frequency', fontsize=12): Add labels to the x and y axes with a font size of 12.

  5. plt.tight_layout(): Automatically adjusts the plot to fit the figure area.

  6. plt.show(): Displays the plot.

The histogram shows how our data is distributed. Since we used np.random.randn(), which generates data from a normal distribution, the histogram has a bell shape centered around 0. The height of each bar represents how many data points fall within that range.

Adding a Text Box with Statistics

Now that we have a basic histogram, let's enhance it by adding a text box that displays the statistical information about our data. This will make the visualization more informative for viewers.

Creating the Text Content

First, we need to prepare the text content that will go inside our text box. In a new cell, enter and run the following code:

## Create a string with the statistics
textstr = '\n'.join((
    r'$\mu=%.2f$' % (mu,),           ## Mean
    r'$\mathrm{median}=%.2f$' % (median,),  ## Median
    r'$\sigma=%.2f$' % (sigma,)       ## Standard deviation
))

print("Text content for our box:")
print(textstr)

You should see output similar to:

Text content for our box:
$\mu=-0.31$
$\mathrm{median}=-0.28$
$\sigma=29.86$

This code creates a multi-line string containing the mean, median, and standard deviation of our data. Let's examine some interesting aspects of this code:

  1. The \n'.join(...) method joins multiple strings with a newline character between them.
  2. The r before each string makes it a "raw" string, which is useful when including special characters.
  3. The $...$ notation is used for LaTeX-style mathematical formatting in matplotlib.
  4. \mu and \sigma are LaTeX symbols for the Greek letters μ (mu) and σ (sigma).
  5. %.2f is a formatting specifier that displays a floating-point number with two decimal places.

Creating and Adding the Text Box

Now, let's recreate our histogram and add the text box to it. In a new cell, enter and run the following code:

## Create a new figure and axes
fig, ax = plt.subplots(figsize=(10, 6))

## Create a histogram with 50 bins
histogram = ax.hist(x, bins=50, color='skyblue', edgecolor='black')

## Add title and labels
ax.set_title('Distribution of Random Data with Statistics', fontsize=16)
ax.set_xlabel('Value', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)

## Define the properties of the text box
properties = dict(boxstyle='round', facecolor='wheat', alpha=0.5)

## Add the text box to the plot
## Position the box in the top left corner (0.05, 0.95) in axes coordinates
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14,
        verticalalignment='top', bbox=properties)

## Display the plot
plt.tight_layout()
plt.show()

When you run this cell, you should see your histogram with a text box in the top-left corner displaying the statistics.

Understanding the Text Box Code

Let's break down the important parts of the code that create the text box:

  1. properties = dict(boxstyle='round', facecolor='wheat', alpha=0.5):

    • This creates a dictionary with properties for the text box.
    • boxstyle='round': Makes the box have rounded corners.
    • facecolor='wheat': Sets the background color of the box to wheat.
    • alpha=0.5: Makes the box semi-transparent (50% opacity).
  2. ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14, verticalalignment='top', bbox=properties):

    • This adds text to the axes at position (0.05, 0.95).
    • transform=ax.transAxes: This is crucial - it means the coordinates are in axes units (0-1) rather than data units. So (0.05, 0.95) means "5% from the left edge and 95% from the bottom edge of the plot."
    • fontsize=14: Sets the font size.
    • verticalalignment='top': Aligns the text so that the top of the text is at the specified y-coordinate.
    • bbox=properties: Applies our text box properties.

The text box will remain in the same position relative to the plot axes, even if you zoom in on the plot or change the data range. This is because we used transform=ax.transAxes, which uses axes coordinates instead of data coordinates.

Customizing the Text Box

Now that we have successfully added a text box to our plot, let's explore various customization options to make it more visually appealing and suitable for different contexts.

Experimenting with Different Styles

Let's create a function to make it easier to experiment with different text box styles. In a new cell, enter and run the following code:

def plot_with_textbox(boxstyle, facecolor, alpha, position=(0.05, 0.95)):
    """
    Create a histogram with a custom text box.

    Parameters:
    boxstyle (str): Style of the box ('round', 'square', 'round4', etc.)
    facecolor (str): Background color of the box
    alpha (float): Transparency of the box (0-1)
    position (tuple): Position of the box in axes coordinates (x, y)
    """
    ## Create figure and plot
    fig, ax = plt.subplots(figsize=(8, 5))
    ax.hist(x, bins=50, color='skyblue', edgecolor='black')

    ## Set title and labels
    ax.set_title(f'Text Box Style: {boxstyle}', fontsize=16)
    ax.set_xlabel('Value', fontsize=12)
    ax.set_ylabel('Frequency', fontsize=12)

    ## Create text box properties
    box_props = dict(boxstyle=boxstyle, facecolor=facecolor, alpha=alpha)

    ## Add text box
    ax.text(position[0], position[1], textstr, transform=ax.transAxes,
            fontsize=14, verticalalignment='top', bbox=box_props)

    plt.tight_layout()
    plt.show()

Now, let's use this function to try different box styles. In a new cell, enter and run:

## Try a square box with light green color
plot_with_textbox('square', 'lightgreen', 0.7)

## Try a rounded box with light blue color
plot_with_textbox('round', 'lightblue', 0.5)

## Try a box with extra rounded corners
plot_with_textbox('round4', 'lightyellow', 0.6)

## Try a sawtooth style box
plot_with_textbox('sawtooth', 'lightcoral', 0.4)

When you run this cell, you'll see four different plots, each with a different text box style.

Changing the Text Box Position

The position of a text box can be crucial for visualization. Let's place text boxes in different corners of the plot. In a new cell, enter and run:

## Create a figure with a 2x2 grid of subplots
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
axes = axes.flatten()  ## Flatten to easily iterate

## Define positions for the four corners
positions = [
    (0.05, 0.95),  ## Top left
    (0.95, 0.95),  ## Top right
    (0.05, 0.05),  ## Bottom left
    (0.95, 0.05)   ## Bottom right
]

## Define alignments for each position
alignments = [
    ('top', 'left'),          ## Top left
    ('top', 'right'),         ## Top right
    ('bottom', 'left'),       ## Bottom left
    ('bottom', 'right')       ## Bottom right
]

## Corner labels
corner_labels = ['Top Left', 'Top Right', 'Bottom Left', 'Bottom Right']

## Create four plots with text boxes in different corners
for i, ax in enumerate(axes):
    ## Plot histogram
    ax.hist(x, bins=50, color='skyblue', edgecolor='black')

    ## Set title
    ax.set_title(f'Text Box in {corner_labels[i]}', fontsize=14)

    ## Create text box properties
    box_props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)

    ## Add text box
    ax.text(positions[i][0], positions[i][1], textstr,
            transform=ax.transAxes, fontsize=12,
            verticalalignment=alignments[i][0],
            horizontalalignment=alignments[i][1],
            bbox=box_props)

plt.tight_layout()
plt.show()

This code creates a 2x2 grid of histograms, each with a text box in a different corner.

Understanding Text Box Positioning

There are several key parameters that control text box positioning:

  1. Position coordinates: The (x, y) coordinates determine where the text box is placed. When using transform=ax.transAxes, these are in axes coordinates where (0, 0) is the bottom-left corner and (1, 1) is the top-right corner.

  2. Vertical alignment: The verticalalignment parameter controls how the text is aligned vertically relative to the y-coordinate:

    • 'top': The top of the text is at the specified y-coordinate.
    • 'center': The center of the text is at the specified y-coordinate.
    • 'bottom': The bottom of the text is at the specified y-coordinate.
  3. Horizontal alignment: The horizontalalignment parameter controls how the text is aligned horizontally relative to the x-coordinate:

    • 'left': The left edge of the text is at the specified x-coordinate.
    • 'center': The center of the text is at the specified x-coordinate.
    • 'right': The right edge of the text is at the specified x-coordinate.

These alignment options are particularly important when placing text in corners. For example, in the top-right corner, you would want to use horizontalalignment='right' so the right edge of the text aligns with the right edge of the plot.

Creating a Final Visualization with Multiple Text Elements

In this final step, we will combine everything we've learned to create a comprehensive visualization that includes multiple text elements with different styles. This will demonstrate how text boxes can be used to enhance data storytelling.

Creating an Advanced Visualization

Let's create a more sophisticated plot that includes both our histogram and some additional visual elements. In a new cell, enter and run the following code:

## Create a figure with a larger size for our final visualization
fig, ax = plt.subplots(figsize=(12, 8))

## Plot the histogram with more bins and a different color
n, bins, patches = ax.hist(x, bins=75, color='lightblue',
                           edgecolor='darkblue', alpha=0.7)

## Add title and labels with improved styling
ax.set_title('Distribution of Random Data with Statistical Annotations',
             fontsize=18, fontweight='bold', pad=20)
ax.set_xlabel('Value', fontsize=14)
ax.set_ylabel('Frequency', fontsize=14)

## Add grid for better readability
ax.grid(True, linestyle='--', alpha=0.7)

## Mark the mean with a vertical line
ax.axvline(x=mu, color='red', linestyle='-', linewidth=2,
           label=f'Mean: {mu:.2f}')

## Mark one standard deviation range
ax.axvline(x=mu + sigma, color='green', linestyle='--', linewidth=1.5,
           label=f'Mean + 1σ: {mu+sigma:.2f}')
ax.axvline(x=mu - sigma, color='green', linestyle='--', linewidth=1.5,
           label=f'Mean - 1σ: {mu-sigma:.2f}')

## Create a text box with statistics in the top left
stats_box_props = dict(boxstyle='round', facecolor='lightyellow',
                      alpha=0.8, edgecolor='gold', linewidth=2)

stats_text = '\n'.join((
    r'$\mathbf{Statistics:}$',
    r'$\mu=%.2f$ (mean)' % (mu,),
    r'$\mathrm{median}=%.2f$' % (median,),
    r'$\sigma=%.2f$ (std. dev.)' % (sigma,)
))

ax.text(0.05, 0.95, stats_text, transform=ax.transAxes, fontsize=14,
        verticalalignment='top', bbox=stats_box_props)

## Add an informational text box in the top right
info_box_props = dict(boxstyle='round4', facecolor='lightcyan',
                     alpha=0.8, edgecolor='deepskyblue', linewidth=2)

info_text = '\n'.join((
    r'$\mathbf{About\ Normal\ Distributions:}$',
    r'$\bullet\ 68\%\ of\ data\ within\ 1\sigma$',
    r'$\bullet\ 95\%\ of\ data\ within\ 2\sigma$',
    r'$\bullet\ 99.7\%\ of\ data\ within\ 3\sigma$'
))

ax.text(0.95, 0.95, info_text, transform=ax.transAxes, fontsize=14,
        verticalalignment='top', horizontalalignment='right',
        bbox=info_box_props)

## Add a legend
ax.legend(fontsize=12)

## Tighten the layout and show the plot
plt.tight_layout()
plt.show()

When you run this cell, you'll see a comprehensive visualization with:

  • A histogram of the data with improved styling
  • Vertical lines marking the mean and one standard deviation range
  • A statistics text box in the top-left corner
  • An informational text box about normal distributions in the top-right corner
  • A legend explaining the vertical lines

Understanding the Advanced Elements

Let's examine some of the new elements we've added:

  1. Vertical Lines with axvline():

    • These lines mark important statistics directly on the plot.
    • The label parameter allows these lines to be included in the legend.
  2. Multiple Text Boxes with Different Styles:

    • Each text box serves a different purpose and uses a distinct style.
    • The statistics box shows the computed values from our data.
    • The informational box provides context about normal distributions.
  3. Enhanced Formatting:

    • LaTeX formatting is used to create bold text with \mathbf{}
    • Bullet points are created with \bullet
    • Spacing is controlled with \ (backslash followed by a space)
  4. Grid and Legend:

    • The grid helps viewers read values from the chart more accurately.
    • The legend explains the meaning of the colored lines.

Final Notes on Text Box Placement

When placing multiple text elements in a visualization, consider:

  1. Visual hierarchy: The most important information should stand out the most.
  2. Positioning: Place related information near the relevant parts of the visualization.
  3. Contrast: Ensure text is readable against its background.
  4. Consistency: Use consistent styling for similar types of information.
  5. Clutter: Avoid overcrowding the visualization with too many text elements.

By thoughtfully placing and styling text boxes, you can create visualizations that are both informative and visually appealing, guiding viewers to understand the key insights from your data.

Summary

In this lab, you have learned how to effectively use text boxes in Matplotlib to enhance your data visualizations. Let's recap the key points:

Key Concepts Covered

  1. Creating Basic Text Boxes: You learned how to add text boxes to plots using the matplotlib.pyplot.text() function with the bbox parameter.

  2. Text Box Positioning: You discovered how to position text boxes using axes coordinates with transform=ax.transAxes, which keeps text in a fixed position regardless of data scaling.

  3. Text Box Styling: You explored how to customize text boxes with different styles (boxstyle), colors (facecolor), transparency levels (alpha), and border properties.

  4. Text Alignment: You practiced using verticalalignment and horizontalalignment to properly position text in different parts of your visualization.

  5. LaTeX Formatting: You utilized LaTeX notation to add mathematical symbols and formatting to your text.

  6. Comprehensive Visualization: You created a complete visualization that combines multiple text elements with different styles to tell a cohesive data story.

Practical Applications

The techniques you learned in this lab can be applied to:

  • Adding statistical summaries to plots
  • Labeling key features in your data
  • Providing context or explanations within visualizations
  • Creating legends or keys with custom formatting
  • Highlighting important findings or insights

Next Steps

To further enhance your data visualization skills with Matplotlib, consider exploring:

  • Advanced annotation techniques like arrows and annotation boxes
  • Interactive text elements using Matplotlib's event handling
  • Customizing text with different fonts and styles
  • Creating subplots with coordinated annotations
  • Saving your visualizations with text elements for publication

By mastering the art of text annotations in Matplotlib, you can create more informative and professional data visualizations that effectively communicate your insights to your audience.