When visualizing data, the appearance of your plots can be just as crucial as the data itself. Matplotlib’s default styles are useful for quick prototypes, but customizing colors and styles is essential to achieve a clean, professional look. In this topic, we’ll explore how to tailor the appearance of Matplotlib graphs—customizing lines, markers, and axes—to make your plots stand out.
Choosing a style sheet and configuring Matplotlib
Before diving into plot customization, it’s important to understand how to configure Matplotlib to suit your needs. Matplotlib offers a variety of settings to control the overall look and feel of your plots. By configuring these, you can set a consistent style across all your visualizations, making them cohesive and visually appealing.
Matplotlib includes various built-in style sheets that can be easily applied to your plots. These style sheets offer pre-defined aesthetics, such as "classic", "fivethirtyeight", "ggplot", and many others. Use plt.style.available to view the complete list of options. To apply a style sheet, use the plt.style.use function:
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.style.use('ggplot')
fig, axs = plt.subplots()
plt.show()If none of the built-in styles meet your needs, you can further customize your plots using the plt.rcParams dictionary. Before exploring this, run the code below to view the default parameters for your plot:
# Get the default figure size
print(plt.rcParams["figure.figsize"]) # [6.4, 4.8]
# Get the default font family
print(plt.rcParams["font.family"]) # ['sans-serif']
# Get the default font weight
print(plt.rcParams["font.weight"]) # normal
# Get the default grid alpha
print(plt.rcParams["grid.alpha"]) # 1.0
# Print all the default parameters
print(plt.rcParams)To adjust the parameters, you can use the plt.rcParams keys:
plt.rcParams["figure.figsize"] = [10, 4]
plt.rcParams["font.family"] = "monospace"
plt.rcParams["font.weight"] = 500
plt.rcParams["grid.alpha"] = 0.4You can also define a style dictionary and apply it using the plt.rcParams.update function with the plt.rcParams keys and values:
style_dict = {
"figure.figsize" : [10, 4],
"font.family": "monospace",
"font.weight": 700,
"grid.alpha": 0.4,
}
plt.rcParams.update(style_dict)Alternatively, you can adjust the style by using the plt.rc function. It allows you to modify various plotting parameters in a more organized way. The first argument specifies the category of settings you want to adjust, and the second argument is a keyword argument. This method is more modular and can be useful when you want to set multiple parameters at once or make changes to different categories of settings:
# Make changes to figsize in the figure category
plt.rc("figure", figsize=[10, 4])
# Make changes to the family and weight in the font category
plt.rc("font", family="monospace", weight=900)
# Make changes to alpha in the grid category
plt.rc("grid", alpha=0.1)If you've applied a custom style and wish to revert to Matplotlib's default style, use:
plt.style.use("default")Enhancing line plots with custom colors
We'll be working with sales data. Let's first import the data and define our style dictionary:
import pandas as pd
df_sales = pd.read_csv("sales_data_sample.csv", parse_dates=True, encoding='ISO-8859-1')
style_dict = {
"axes.linewidth": 1.5, "grid.color": "#DDDDDD", "font.family": "monospace",
"font.size": 12.0, "xtick.labelsize": 10, "ytick.labelsize": 10,
"xtick.major.size": 6, "ytick.major.size": 6, "xtick.minor.size": 3,
"ytick.minor.size": 3, "legend.fontsize": 9, "legend.framealpha": 0.5,
"figure.figsize": (8, 4), "savefig.dpi": 1000, "savefig.format": "png"
}Next, let's preprocess the data and make a plot of the sales by the end of each quarter ("QE"):
df = (df_sales.astype({"ORDERDATE": 'datetime64[ns]'})
.assign(TOTAL = lambda x: x.QUANTITYORDERED * x.PRICEEACH)
.groupby(by=[pd.Grouper(key="ORDERDATE", freq="QE"), "COUNTRY"])[["TOTAL"]]
.sum(numeric_only=True).unstack().fillna(0))
# Use custom style
plt.rcParams.update(style_dict)
# Plot data with no legend
plt.plot(df, label='_nolegend_')
plt.show()In the plot above, the lines are too close together, which makes it hard to distinguish the trends for each country. To minimize clutter, we’ve omitted the legend. Additionally, the date labels on the x-axis might be difficult to read if they are too close together. We can also improve clarity by formatting the y-axis to display dollar amounts with comma separators for thousands. To further enhance readability, let's apply gray-level shading to the lines and adjust the axis format.
import matplotlib.ticker as mtick
def plot(df, choose):
for (total, country) in df.columns:
if country == choose:
# Make the line for the selected country red and bring it to the front with zorder
plt.plot(df[(total, country)], color="#FF0000", zorder=len(df.columns), label=country)
else:
# Set other countries to a shade of gray
plt.plot(df[(total, country)], color="0.75")
# Get the current axes (plt.gca()) and use the string method to format y-tick labels
plt.gca().yaxis.set_major_formatter(mtick.StrMethodFormatter('${x:,.0f}'))
# Get the current figure (plt.gcf()) and rotate the date tick labels by 30 degrees
plt.gcf().autofmt_xdate(rotation=30)
plt.legend()
plt.show()
plot(df, "Finland")The plot looks much better now. We used the hexadecimal color value #FF0000 for red, but we could have just specified the color as "red" or "r" instead. Gray levels are typically represented as values between "0.0" and "1.0", where "0.0" is pure black, "1.0" is pure white, and values in between represent shades of gray. Therefore, ".75" corresponds to a medium gray color.
Customizing axis colors, figure backgrounds, and line styles
Once you have configured Matplotlib and chosen a style, the next step is to fine-tune the appearance of your plots. This involves customizing the colors of axes and figures, as well as adjusting line styles to enhance the clarity and visual appeal of your data. We'll illustrate this using a cumulative distribution plot for student performance data.
import numpy as np
from scipy.stats import norm
df_scores = pd.read_csv("StudentsPerformance.csv")
mean_math = df_scores["math score"].mean()
stddev_math = df_scores["reading score"].std(ddof=0)
X_math = np.linspace(mean_math - 3 * stddev_math, mean_math + 3 * stddev_math, 1000)
mean_reading = df_scores["reading score"].mean()
stddev_reading = df_scores["reading score"].std(ddof=0)
X_reading = np.linspace(mean_reading - 3 * stddev_reading, mean_reading + 3 * stddev_reading, 1000)
mean_writing = df_scores["writing score"].mean()
stddev_writing = df_scores["writing score"].std(ddof=0)
X_writing = np.linspace(mean_writing - 3 * stddev_writing, mean_writing + 3 * stddev_writing, 1000)
pdf_math = norm.pdf(X_math, mean_math, stddev_math)
pdf_reading = norm.pdf(X_reading, mean_reading, stddev_reading)
pdf_writing = norm.pdf(X_writing, mean_writing, stddev_writing)The colors of your plot’s axes and figure background play a significant role in its overall appearance. Customizing these elements can help make your plots more readable and visually engaging. Let's customize the axes and figure with the plt.rc function and plot with distinct linestyles:
plt.style.use("default")
plt.rcParams.update(style_dict)
# Set the default color for all text in the plot (labels, titles, and annotations)
plt.rc('text', color = 'w')
# Set the axes facecolor and edgecolor
plt.rc('axes', facecolor = '.0', edgecolor = '#697565')
# Set the figure facecolor
plt.rc('figure', facecolor = '.25')
# Set the xtick and ytick labelcolor
plt.rc('xtick', labelcolor="w")
plt.rc('ytick', labelcolor="w")
# Set the xtick and ytick minor axes visibility
plt.rc('xtick.minor', visible=True)
plt.rc('ytick.minor', visible=True)
# Plot with the dashdot (-.) linestyle
plt.plot(X_math, pdf_math, color="cyan", linestyle="dashdot")
# Plot with the dashed (--) line style
plt.plot(X_reading, pdf_reading, color="yellow", linestyle="dashed")
# Plot with the solid (-) line style
plt.plot(X_writing, pdf_writing, color="magenta", linestyle="solid")
# Set the legend of the plots
plt.legend(["math", "reading", "writing"])
plt.title("Probability Density Plot")
plt.show()You can specify multiple styling options in a single, concise string, eliminating the need to set the color, marker, and linestyle separately. Matplotlib allows you to combine these options into one short string, and the order doesn't matter—Matplotlib will understand it. This approach makes your code more compact and easier to write, especially when specifying simple styles.
# Combining color and linestyle options in a single string
plt.plot(X_math, pdf_math, "c-.")
plt.plot(X_reading, pdf_reading, "y--")
plt.plot(X_writing, pdf_writing, "m-")Customizing bar plots
Bar graphs are a versatile way to visualize categorical data and compare different groups or categories. In Matplotlib, you can create bar graphs that are not only functional but also visually appealing. In this section, we’ll explore how to customize bar graphs with a focus on the colors, hatches, and layering of bars. Let's create a bar plot comparing average scores across different ethnic groups.
df_bar = (df_scores
.assign(ethnicity = lambda x: x["race/ethnicity"].str.title())
.groupby(by="ethnicity").mean(numeric_only=True))
ethnicity = df_bar.index
math_score = df_bar['math score']
reading_score = df_bar['reading score']
writing_score = df_bar['writing score']
plt.style.use("default")
plt.rcParams.update(style_dict)
# Plot bar with circular hatch pattern, orange edges, white fill, and 60% transparency
plt.bar(x=ethnicity, height=math_score, hatch='O', edgecolor='orange', color='w', alpha=.6)
# Plot stacked bar with cross-hatch pattern, green edges, white fill, and 60% transparency
plt.bar(x=ethnicity, height=reading_score, bottom=math_score, hatch='x', edgecolor='g', color='w', alpha=.6)
# Plot stacked bar with plus hatch pattern, blue edges, white fill, and 60% transparency
plt.bar(x=ethnicity, height=writing_score, bottom=(math_score + reading_score), hatch="+", edgecolor="b", color='w', alpha=.6)
# Add grid lines to the plot
plt.grid(True)
# Add a legend outside the plot to identify each score category
plt.legend(['Math', 'Reading', 'Writing'], bbox_to_anchor=(1., 1.))
plt.show()Horizontal bar plots are ideal for creating tornado graphs, commonly used in sensitivity analysis to visually demonstrate the impact of various variables on a specific outcome. While Matplotlib does not include a built-in tornado plot feature, you can construct one using horizontal bar plots, effectively displaying bars of varying lengths to represent different levels of impact.
plt.style.use("default")
plt.rcParams.update(style_dict)
# Plot the right bar
plt.barh(ethnicity, math_score, color='white', edgecolor='black', label='Math', hatch='O.', height=.7)
# Plot the left bar
plt.barh(ethnicity, -writing_score, color='white', edgecolor='black', label='Writing', hatch="oo", height=.7)
# Add a legend outside the box
plt.legend(bbox_to_anchor=(1., .5), fontsize=12)
# Get current axes
ax = plt.gca()
# Customizing the spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
plt.show()When working with bar plots, you have a wide range of customization options to control their appearance and behavior. Apart from the basic plt.rcParams dictionary mentioned earlier, you can use additional parameters and hatch patterns to further customize your plots.
Customizing box plots
Box plots are an excellent way to visualize data distribution when comparing different groups. However, the default styling in Matplotlib may not always meet the visual or clarity standards required for your presentation or analysis. Customizing box plots can help highlight key data features, enhancing both the informativeness and visual appeal.
In Matplotlib, you can customize nearly every aspect of a box plot. For example, when creating a box plot for reading scores, setting patch_artist=True is essential. By default, this is set to False, rendering the boxes as outlines without a fill. This allows control over edge color and width but leaves the interior empty. Enabling patch_artist=True fills the boxes with color and makes them fully customizable, improving the overall aesthetics.
To customize a box plot in Matplotlib, you can adjust several key parameters, each using a dictionary of styling options. boxprops controls the appearance of the main box, whiskerprops adjusts the lines extending to the smallest and largest values within 1.5 times the IQR, and capprops modifies the caps at the ends of the whiskers. medianprops sets the style of the median line, while flierprops customizes the outliers, including their marker shape, color, and transparency. Now, let's see how to customize a box plot for reading scores:
# Define the style for the reading score
style_reading = dict(
# Customize the box appearance/boxprops dictionary: white fill, green border, and thicker line
boxprops=dict(facecolor='white', edgecolor='green', linewidth=1.5),
# Customize the whiskers/whiskerprops dictionary: green color and thicker line
whiskerprops=dict(color='green', linewidth=1.5),
# Customize the caps/capprops dictionary: green color and thicker line
capprops=dict(color='green', linewidth=1.5),
# Customize the median line/medianprops dictionary: green color and thicker line
medianprops=dict(color='green', linewidth=2),
# Customize the outliers/flierprops dictionary: green squares with 50% transparency
flierprops=dict(marker='s', color='green', alpha=0.5))
# Plot the boxplot with the specified style
b = plt.boxplot(
reading_score, # The data to be plotted
patch_artist=True, # Allow filling of the box with color
widths=0.3, # Width of the box plot
positions=[1], # Position on the x-axis
boxprops=style_reading["boxprops"], # Apply custom box style
whiskerprops=style_reading["whiskerprops"], # Apply custom whisker style
capprops=style_reading["capprops"], # Apply custom cap style
medianprops=style_reading["medianprops"], # Apply custom median line style
flierprops=style_reading["flierprops"] # Apply custom outlier style
)
# Iterate over each component of the boxplot to set the line style
for name, line_list in b.items():
for line in line_list:
line.set_linestyle("dashdot")
# Set the x-axis tick label
plt.xticks([1], ['Reading'])
# Set the y-axis limits
plt.ylim([62.5, 75])
plt.show()To display multiple box plots in a single diagram, use the positions parameter to arrange them. For example, you can add a customized box plot for writing scores alongside the reading scores by specifying their positions.
# Define the style for the writing score
style_writing = dict(boxprops=dict(facecolor='white', edgecolor='blue', linewidth=1.5),
whiskerprops=dict(color='blue', linewidth=1.5),
capprops=dict(color='blue', linewidth=1.5),
medianprops=dict(color='blue', linewidth=2),
flierprops=dict(marker='s', color='blue', alpha=1.0))
# Plot the boxplot with the specified style
d = plt.boxplot(
writing_score,
patch_artist=True,
widths=0.3,
positions=[2], # Set the positions to 2
boxprops=style_writing["boxprops"],
whiskerprops=style_writing["whiskerprops"],
capprops=style_writing["capprops"],
medianprops=style_writing["medianprops"],
flierprops=style_writing["flierprops"]
)
for name, line_list in d.items():
for line in line_list:
line.set_linestyle("dashdot")
plt.xticks([1, 2], ['Reading', 'Writing'])
plt.ylim([60, 75])
plt.show()Customizing scatter plot
Scatter plots are a great way to illustrate relationships between variables. By customizing scatter plots, you can significantly improve their clarity and visual appeal. Key elements to focus on include markers, colors, sizes, and other visual attributes.
Markers are crucial for identifying individual data points in a scatter plot. Customizing markers involves changing their shape, size, and edge properties. You can use different marker shapes to represent various categories or highlight specific data points. Common shapes include circles ('o'), squares ('s'), triangles ('^'), and diamonds ('D'). To set the marker shape, use the marker parameter in the plt.scatter() function:
np.random.seed(42)
x = np.random.randn(100)
y = np.random.randn(100)
sizes = 100 * np.abs(np.random.randn(100))
colors = np.random.rand(100)plt.scatter(x, y, marker='o') # Circle markers
plt.scatter(x, y, marker='s') # Square markersThe size of markers can be adjusted to emphasize or de-emphasize certain data points. Use the s parameter to set the marker size. Larger values increase the size. Customizing marker edges can improve visibility. Use the edgecolor and linewidth parameters to change the color and width of marker edges.
plt.scatter(x, y, s=100, edgecolor='black', linewidth=1) # Large markers, black edges with width of 1Colors can be used to differentiate between data categories or highlight data points. Set a uniform color for all markers using the c parameter.
plt.scatter(x, y, c='blue') # All markers in blueTo represent a third dimension or variable, use a colormap with the c parameter, providing an array of values for the colormap to map to colors:
plt.scatter(x, y, c=colors >= 0.5, cmap="viridis") # Use colormap 'viridis'You can view the full list of color maps by using list(mpl.colormaps) or list(plt.colormaps). Let's select a color map from this list for the plot:
import matplotlib.cm as cm
plt.scatter(x, y, c=colors >= 0.5, cmap=cm.cool)Let's illustrate how to create a customized scatter plot in Matplotlib that includes varying marker sizes and colors, a color bar, a grid, custom axis tick labels, and a legend explaining the marker sizes:
plt.style.use("default")
plt.rcParams.update(style_dict)
# Create scatter plot with customization
scatter = plt.scatter(x, y, s=sizes, c=colors, cmap=cm.viridis, alpha=0.6, edgecolor='black', marker='o')
# Add a color bar to indicate what the colors represent
plt.colorbar(scatter, label='Color Intensity')
# Add grid lines to the plot
plt.grid(True, which='both', linestyle='--', color='gray', alpha=0.7)
# Customize x-axis and y-axis tick labels
plt.xticks([-2, -1, 0, 1, 2], ["A", "B", "C", "D", "E"])
plt.yticks([-2, -1, 0, 1, 2], ["A", "B", "C", "D", "E"]);
# Create dummy scatter plots for legend to represent different marker sizes
for size in [50, 100, 200]:
plt.scatter([], [], s=size, edgecolor='black', color='none', label=str(size) + ' Size')
# Add legend to explain marker sizes
plt.legend(title="Point Size")
plt.show()We used a dummy plot to customize the legend independently of the main scatter plot. This approach allows us to control the appearance, size, and style of legend markers without affecting the actual data visualization.
Conclusion
Customizing Matplotlib plots is essential for creating professional and visually appealing visualizations. While the default styles are convenient for quick prototypes, adjusting colors, markers, line styles, and more can significantly enhance the readability and impact of your graphs. Explore the matplotlib documentation to learn more.