Hands-on Tutorials, Introduction to Matplotlib
This story is the next part of Python Data Visualization with Matplotlib – Part 1. In part 1, we have learned how to generate and customize the scatter plot, line plot, histogram, and bar chart. This story will continue the study in Python plotting with Matplotlib concerning generating and customizing box plot, violin plot, pie chart, polar chart, geographic projection, 3D plot, and contour plot.
As in the part 1, I tell you that I have customized the default parameters in Matplotlib. Here is my plotting style
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['text.usetex'] = True
plt.rcParams['font.size'] = 15
plt.rcParams['font.family'] = "serif"
tdir = 'in'
major = 5.0
minor = 3.0
plt.rcParams['xtick.direction'] = tdir
plt.rcParams['ytick.direction'] = tdir
plt.rcParams['xtick.major.size'] = major
plt.rcParams['xtick.minor.size'] = minor
plt.rcParams['ytick.major.size'] = major
plt.rcParams['ytick.minor.size'] = minor
I use LaTeX font for my default in serif family typeface. If you have an error to activate LaTeX font, you need to read [this story](https://towardsdatascience.com/matplotlib-styles-for-scientific-plotting-d023f74515b4) to find the solution. I also customize the xtick and ytick settings changing the tick direction from ‘out’ to ‘in’ tick direction and adjusting the size of the ticks. You can read this story to understand it. Let’s start it.
01. Box plot
Do you know the box plot? Wikipedia defines the box plot as a method for graphically depicting groups of numerical data through their quartiles. It is used in descriptive statistics. You can see an example of a box plot in Figure 1.

In general, the box plot represents the picture of a distribution. It is constructed by a box, the whiskers, and the outliers. In Figure 1, there is no outlier there. In the box element, you can show the median or mean of the distribution. We can see the median in Figure 1. The box is limited by the value of Q1 (first quartile) and Q3 (third quartile). The difference value of Q1 and Q3 is called interquartile (IQR). In default, the whiskers show the boundaries of the distribution, the minimum value, and the maximum value.

In Figure 2, you can see there is an outlier. How can the box plot detect the outliers? An outlier is detected in the box plot when its value is smaller than Q1–1.5 x IQR or larger than Q3+1.5 x IQR.
Before I create a box plot with Matplotlib, I will generate the mock data using this code
N = 50
np.random.seed(100)
box1 = np.random.randn(N) * 2 + 1
To show the variable box1 in the form of a box plot, you can use this code
plt.figure()
plt.boxplot(box1)
You can see the result in Figure 3 when you run the code above

Horizontal box plot
If you want to change the box plot’s orientation horizontally, you need to apply the following argument in the plt.boxplot() code.
vert = False
You can see the horizontal box plot result in Figure 4.

In the next example, I will create a distribution with an outlier by changing the seed number, as shown in the following code
N = 50
np.random.seed(140)
box1 = np.random.randn(N) * 2 + 1
plt.boxplot(box1, vert=False)
If you run the code above, you will get a box plot, as shown in Figure 5.

I will show you how Matplotlib detect the outliers by calculating the value of Q1–1.5 x IQR, Q3+1.5 x IQR, and sorting the distribution using this code
q1 = np.quantile(box1, .25)
q3 = np.quantile(box1, .75)
iqr = q3-q1
lower_boundary = q1 - 1.5 * iqr
upper_boundary = q3 + 1.5 * iqr
sort = np.sort(box1)
To visualize the values I need, I run the following code
If you run the code above, it will show you a figure, as shown in Figure 6.

In Figure 6, you can see the random I have generated in red dots. The lower boundary (Q1–1.5 x IQR) equals -3.69, and the minimum value of the distribution is -2.41. So, the left whisker limit in Figure 6 is in the minimum value, -2.41. The upper boundary (Q3 + 1.5 x IQR) is on 5.98, so that the data with value 7.0 is defined as the outlier. The right whisker limit will be ended in 5.47, on the maximum value before the upper boundary. To place the legend on the outside of the figure, you need to read this story.
In the next example, I will show the mean values of the four distributions. I need to create the mock data for four distributions, using this code
np.random.seed(1214)
data = [np.random.normal(0, std, 100) for std in range(10, 14)]
Showing means value
Variable data will generate four normal distribution with mu of 0 and different sigma values for each distribution (10, 11, 12, and 13). To show the means values in the box plot, you need to use this code
plt.boxplot(data, showmeans=True)
If you run the code above, it will generate a box plot, as shown in Figure 7. The green triangle represents the means value for each box plot.

You can also show it in the horizontal box plot, using this code
plt.boxplot(data, showmeans=True, vert = False)
The code above will generate a figure, as shown in Figure 8.

You can change the symbol of means value by adding this argument
meanline=True
or hide the box shape using this argument
showbox=False
Some customization in the box plot is shown in Figure 8.

Do you want to change the color of the box? You can do it using this some settings. First, you need to activate the patch artist, as shown in the following code
patch_artist=True
Then, prepare your colors and apply it into the box plot using this code
colors = ['royalblue', 'lightblue', 'lightgreen', 'pink']
for patch, color in zip(box['boxes'], colors):
patch.set_facecolor(color)
Here is the full code
np.random.seed(123)
all_data = [np.random.normal(0, std, 100) for std in range(10, 14)]
box = plt.boxplot(all_data, notch=False, patch_artist=True)
colors = ['royalblue', 'lightblue', 'lightgreen', 'pink']
for patch, color in zip(box['boxes'], colors):
patch.set_facecolor(color)
plt.ylim(-50, 50)
The code above will generate a figure, as shown in Figure 9.

Activate notch
You can show the notch in the box plot, as shown in Figure 10, using this argument
notch=True

You can reproduce Figure 10 with this code
np.random.seed(123)
all_data = [np.random.normal(0, std, 100) for std in range(10, 14)]
box = plt.boxplot(all_data, notch=True, patch_artist=True)
colors = ['royalblue', 'lightblue', 'lightgreen', 'pink']
for patch, color in zip(box['boxes'], colors):
patch.set_facecolor(color)
plt.ylim(-50, 50)

02. Violin plot
Violin plot is almost similar visualization with box plot but with no outliers defined. All of the data in the distribution is shown in a kernel function. To create a violin plot, I use this code
N = 50
np.random.seed(140)
viol = np.random.randn(N) * 2 + 1
plt.figure()
plt.violinplot(viol)
The code will show you a violin plot, as shown in Figure 11.

To show it horizontally, you can use the same argument in the box plot
vert=False
By default, the violin plot is not showing the median and means value. If you want to show it, you need to insert these arguments
showmeans=True, showmedians=True
If I combine the arguments above and run it, I will be shown a violin plot shown in Figure 12.

In Figure 12, I think you can not differ where is the median and where is the mean value because they have a similar color. To change the color of each elements in the violin plot such as color of the bars, median value, mean value, minimum value, and maximum value, you need to define the box plot code as a variable, as shown in the following code
violin_parts = plt.violinplot(data, showmedians=True,
showmeans=True)
I generate four different normal distributions and define it as data variable.
To change the color of median, mean, bars, minimum, and maximum value, you can use this code
vmedian = violin_parts['cmedians']
vmedian.set_edgecolor('r')
vmean = violin_parts['cmeans']
vmean.set_edgecolor('k')
vbars = violin_parts['cbars']
vbars.set_edgecolor('k')
vmax = violin_parts['cmaxes']
vmax.set_edgecolor('darkorange')
vmin = violin_parts['cmins']
vmin.set_edgecolor('darkorange')
The customized violin plot is shown in Figure 13.

Here is the full code to generate Figure 13.
np.random.seed(1214)
data = [np.random.normal(0, std, 100) for std in range(10, 14)]
plt.figure(figsize = (10, 6))
violin_parts = plt.violinplot(data, showmedians=True,
showmeans=True)
vbars = violin_parts['cbars']
vbars.set_edgecolor('k')
vmedian = violin_parts['cmedians']
vmedian.set_edgecolor('r')
vmean = violin_parts['cmeans']
vmean.set_edgecolor('k')
vmax = violin_parts['cmaxes']
vmax.set_edgecolor('darkorange')
vmin = violin_parts['cmins']
vmin.set_edgecolor('darkorange')
To change the bodies (contour) color, you need to use this code
for vp in violin_parts['bodies']:
vp.set_facecolor('orange')
vp.set_edgecolor('k')
vp.set_linewidth(3)
The code above will change the bodies color to orange with black as the edge color and adjust the line width to 3, as shown in Figure 14.

In Figure 14, I just show the median values for each distribution. You can reproduce Figure 14 with the following code
np.random.seed(1214)
data = [np.random.normal(0, std, 100) for std in range(10, 14)]
plt.figure(figsize = (10, 6))
violin_parts = plt.violinplot(data, widths=0.9, showmedians=True)
for vp in violin_parts['bodies']:
vp.set_facecolor('orange')
vp.set_edgecolor('k')
vp.set_linewidth(3)
I create a full customization of my violin plots, as shown in Figure 15.

You can generate Figure 15 using this code
np.random.seed(1214)
data = [np.random.normal(0, std, 100) for std in range(10, 14)]
# Create violin plot objects:
plt.figure(figsize=(10, 6))
violin_parts=plt.violinplot(data, showmedians=True, showmeans=True)
# Make the violin body blue with a red border:
for vp in violin_parts['bodies']:
vp.set_facecolor('royalblue')
vp.set_edgecolor('k')
vp.set_linewidth(2)
vp.set_alpha(.8)
colors = ['k', 'red', 'red', 'w', 'k']
part = ['cbars','cmins','cmaxes','cmeans','cmedians']
for i in range(len(part)):
vp = violin_parts[part[i]]
vp.set_edgecolor(colors[i])
vp.set_linewidth(2)

03. Pie chart
Before generating a pie chart, I create data on the numbers of some animals using this code
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
numbers = [15, 30, 45, 10]
To visualize it in a pie chart, you can use this code
plt.pie(numbers)
To code above will generate a pie chart, as shown in Figure 16.

Figure 16 shows a simple pie chart. You can give the labels for each animal data, using this argument
labels = labels
If you want to show the number of each animal, you need to insert this argument
autopct='%1.1f%%'
You can see the result of your pie chart after adding labels and numbers in Figure 17.

Here is the full code to create Figure 17.
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
plt.figure()
plt.pie(sizes, labels = labels, autopct='%1.1f%%')
You can change the starting angle for your pie chart using this argument
startangle=90
The default starting angle is 0, applied for the first data (Frogs). I customize the starting angle in two different angles, as shown in Figure 18.

Here is the code to generate Figure 18
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
explode1 = (0, 0.1, 0, 0)
explode2 = (0, 0, 0.1, 0)
plt.figure(figsize=(10, 10))
plt.subplot(121)
plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90)
plt.title('Startangle = 90$^circ$')
plt.subplot(122)
plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=180)
plt.title('Startangle = 180$^circ$')
You can also make a margin for a specific pie chart, as shown in Figure 19.

You can reproduce Figure 19 using the following code
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
explode1 = (0, 0.1, 0, 0)
explode2 = (0, 0, 0.1, 0)
plt.figure(figsize=(10, 10))
plt.subplot(121)
plt.pie(sizes, explode=explode1, labels=labels,
autopct='%1.1f%%', startangle=90)
plt.subplot(122)
plt.pie(sizes, explode=explode2, labels=labels,
autopct='%1.1f%%', startangle=180)
You can change the margin by adjusting the value in the variable explode.
Applying different styles in the pie chart
Do you want to visualize your data in a pie chart with a different style? Yups, you can see Figure 20.

The difference with the previous pie chart is I place the labels in the legend. To do it, you need to use this argument
textprops={'color':"w"}
and this code
plt.legend(labels, bbox_to_anchor = (1., .95), title="Labels Name")
You can reproduce Figure 20 using the following code
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10]
explode = (0, 0.05, 0, 0)
plt.figure(figsize=(7, 7))
plt.pie(sizes, autopct='%1.1f%%', textprops={'color':"w"},
explode = explode, startangle = 90)
Shadow in a pie chart
You can add a shadow in your pie chart using this argument
shadow=True
The result is shown in Figure 21.

I think I know what you are thinking about :D. The shadow is ugly, right? I have tried many alternatives for it; here are they
a. If you are using Jupyter, you can save your pie chart manually by right-clicking the figure. Then, click Save Image As, as shown in Figure 22.

The saved image is shown in Figure 23.

b. The other alternative is adding the white stroke as the foreground. To apply it, you need to import path_effects from Matplotlib using this code
import matplotlib.patheffects as path_effects
Then, define the variable name for your pie chart, as shown in the following code
patches, texts, autotexts = plt.pie(sizes, autopct='%1.1f%%',
textprops={'color':"w"},
explode = explode,
startangle = 90, shadow=True)
Next, customize the path_effects with this code
for patch in patches:
patch.set_path_effects([path_effects.Stroke(linewidth=2.5,
foreground = 'w')])
Here is the full code
If you run the code above, it will create a pie chart, as shown in Figure 24.

It is not perfect, but I think it is prettier than without adding a stroke. You can also combine the first and the second alternatives and get a pie chart shown in Figure 25.

Doughnut chart
From the pie chart features in Matplotlib, you can create a doughnut chart, as shown in Figure 26.

Here is the code to generate the data for Figure 26.
size = 0.3
vals = np.array([[60.], [37.], [29.]])
cmap = plt.get_cmap("tab20c")
outer_colors = cmap(np.arange(3)*4)
inner_colors = cmap(np.array([1, 2, 5, 6, 9, 10]))
Doughnut chart is similar with the pie chart but without full radius from the center. In Figure 26, I set the radius equals 0.3. Here is the code to generate Figure 26.
plt.figure(figsize=(10,10))
plt.pie(vals.sum(axis=1), radius=1, colors=outer_colors,
wedgeprops=dict(width=size, edgecolor='w'))
I also create two different radius sizes, as shown in Figure 27.

You can reproduce Figure 27 with the following code.
vals = np.array([[60.], [37.], [29.]])
plt.figure(figsize=(10,10))
plt.subplot(121)
plt.pie(vals.sum(axis=1), radius=1, colors=outer_colors,
wedgeprops=dict(width=.3, edgecolor='w'))
plt.title('Size = 0.3')
plt.subplot(122)
plt.pie(vals.sum(axis=1), radius=1, colors=outer_colors,
wedgeprops=dict(width=.5, edgecolor='w'))
plt.title('Size = 0.5')
You can also create a double doughnut chart, as shown in Figure 28.

Here is the code to generate Figure 28.
plt.figure(figsize=(10, 10))
size = 0.3
vals = np.array([[60., 32.], [37., 40.], [29., 10.]])
cmap = plt.get_cmap("tab20c")
outer_colors = cmap(np.arange(3)*4)
inner_colors = cmap(np.array([1, 2, 5, 6, 9, 10]))
plt.pie(vals.sum(axis=1), radius=1, colors=outer_colors,
wedgeprops=dict(width=size, edgecolor='w'))
plt.pie(vals.flatten(), radius=1-size, colors=inner_colors,
wedgeprops=dict(width=size, edgecolor='w'))
If you read the documentation of doughnut chart in Matplotlib site, you will meet a nice doughnut chart, as shown in Figure 29.

You can create Figure 29 using this code.

04. Polar chart
In planar projection, you will have an x-axis and y-axis. In polar projection, you need to define it in the form of radius and angle, as shown in Figure 30.

In polar projection, the radius axis is shown in the size of circle radius and the angle is projected as the angle of each circle with 0 degree is the starting point. To generate a polar projection, you need to define the projection type as polar, as shown in the following argument.
projection='polar'
Here is the full code to generate Figure 30.
r = np.linspace(0, 2, 100)
theta = 2 * np.pi * r
fig = plt.figure(figsize=(13, 4))
ax1 = plt.subplot(121, projection='polar')
ax1.scatter(theta, r, label = 'Polar Projection', s = 10)
ax1.legend(bbox_to_anchor = (.85, 1.35))
ax2 = plt.subplot(122)
ax2.scatter(theta, r, label = 'Planar Projection', s = 10)
ax2.legend(bbox_to_anchor = (0.85, 1.35))
ax2.set_xlabel('R')
ax2.set_ylabel(r'$theta$')
You can also present your data in plot line styles in the polar projection, as shown in Figure 31.

You can reproduce Figure 31 with this code
Next is creating a bar chart in the polar projection, as shown in Figure 32.

When you see Figure 32, maybe you are remembering the logo of Matplotlib 😀 To create Figure 32, you need to generate the data, using this code
np.random.seed(10130)
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.random.rand(N) * .8 - .1
colors = plt.cm.Spectral(radii / 10)
To visualize it, you can use the following code
plt.figure(figsize=(7, 7))
ax = plt.subplot(111, projection='polar')
ax.bar(theta, radii, width=width, bottom=0.0,
color=colors, alpha=0.5)
If you are intersted in generating Matplotlib logo using the polar bar chart, you can read this documentation.

05. Geographic projection
To visualize your geographical data, you can use geographical projection provided by Matplotlib. There are four types of projections: Aitoff, Hammer, Mollweide, and Lambert projection. To understand it, I will create a mock data using this code
N = 100
np.random.seed(157)
long = np.random.random(N) * 360 - 180
lat = np.random.random(N) * 180 - 90
To visualize it in the Aitoff projection, you can use this code
plt.figure(figsize=(12, 7))
plt.subplot(111, projection="aitoff")
plt.scatter(long, lat, marker = '*', color = 'red', s = 40)
plt.title("Aitoff")
plt.grid(True)
The code will show you a figure, as shown in Figure 33.

In Aitoff projection, you need to ensure your data is in the unit of degree.
Next is Hammer projection. You can present your data in the Hammer projection using this code
plt.figure(figsize=(12, 7))
plt.subplot(111, projection="hammer")
plt.scatter(long, lat, marker = '*', color = 'red', s = 40)
plt.title("Hammer")
plt.grid(True)
The unit of long and lat variable shown in Hammer projection is in the degree. The code above will generate a figure, as shown in Figure 34.

I am not sure what are the differences between Aitoff and Hammer projection. If you need some explanation, you can read these links.
In Mollweide projection, the unit of the data needs to be converted in the radians. Here is the code to generate the data in radians.
N = 100
np.random.seed(157)
long = np.random.random(N) * 2 * np.pi - np.pi
lat = np.random.random(N) * np.pi - (np.pi / 2)
To visualize it in the Mollweide projection, you can use this code
plt.figure(figsize=(12, 7))
plt.subplot(111, projection="mollweide")
plt.scatter(long, lat, marker = '*', color = 'red', s = 40)
plt.title("Mollweide")
plt.grid(True)
If you run the code above, it will create a figure, as shown in Figure 35.

The last projection is the Lambert projection, as shown in Figure 36.

The unit of the data is in the radians. You can reproduce Figure 36 using this code.
N = 100
np.random.seed(157)
long = np.random.random(N) * 2 * np.pi - np.pi
lat = np.random.random(N) * np.pi - (np.pi / 2)
plt.figure(figsize=(12, 12))
plt.subplot(111, projection="lambert")
plt.scatter(long, lat, marker = '*', color = 'red', s = 40)
plt.title("Lambert")
plt.grid(True)

06. 3D plot
To create a 3D plot, you need to define the projection type as 3d, as shown in the following argument
projection = '3d'
The 3D projection will give you a result shown in Figure 37.

I generate the mock data with this code.
N = 250
np.random.seed(124)
x = 15 * np.random.random(N)
y = np.sin(x) + 0.25 * np.random.random(N)
z = np.cos(x) + 0.25 * np.random.random(N)
To visualize the data in the 3D scatter plot, you can use this code.
plt.figure(figsize=(9, 6))
ax = plt.axes(projection = '3d')
ax.scatter3D(x, y, z, color = 'r')
ax.set_xlabel('x', fontsize = 20, labelpad = 20)
ax.set_ylabel('y', fontsize = 20, labelpad = 20)
ax.set_zlabel('z', fontsize = 20, labelpad = 20)
The code will generate a 3d plot, as shown in Figure 38.

As I know, in the newest Matplotlib version, the aspect is always equal for each axis. To change it, you can use this code
ax.set_box_aspect((2., 1.5, 1.2))
I change the aspect ratio for the x-, y-, and z-axis to 2:1.5:1.2. After applying the code above, you will have a plot, as shown in Figure 39.

3D plotline
You can create a 3D plotline, as shown in Figure 40.

You can reproduce Figure 40 with this code.
N = 100
np.random.seed(124)
xline = np.linspace(0, 15, N)
yline = np.sin(xline)
zline = np.cos(xline)
fig = plt.figure(figsize=(9, 6))
ax = plt.axes(projection = '3d')
ax.plot3D(xline, yline, zline)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_box_aspect((2, 1.5, 1.2))
You can change the view angle with this code
ax.view_init(10, 180)
The first argument in the view_init is the elevation angle and the second one is for azimuthal angle. You can see the different representation for different angles, as shown in Figure 41.

Triangular 3D surfaces
To generate a triangular 3D surfaces in Matplotlib, you can use this code.
ax.plot_trisurf()
I generate the data to be visualized in the triangular 3D surfaces with this code
N = 2000
np.random.seed(124)
r = 2 * np.pi * np.random.random(N)
theta = 20 * np.pi * np.random.random(N)
xdata = np.ravel(r * np.sin(theta))
ydata = np.ravel(r * np.cos(theta))
zdata = np.sin(xdata) + np.cos(ydata)
You can see the triangular 3D surfaces plot from the data above in Figure 42.

You can reproduce Figure 42 with this code
3D contour plot
Matplotlib provides a 3D contour plot. You can create it using this code
ax.contour3D()
I generate the mock data using the following code
N = 100
np.random.seed(3124)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
To visualize it, I run this code
fig = plt.figure(figsize=(9, 6))
ax = plt.axes(projection = '3d')
ax.contour3D(X, Y, Z, cmap = 'Spectral')
After customization, my 3D contour plot is shown in Figure 43.

You can reproduce Figure 43 using this code
By default, Matplotlib generates a 3D contour plot in the contour of 7 counts. You can change it by writing down the counts you want in the fourth argument of ax.contour3D(), as shown in the code below
ax.contour3D(X, Y, Z, 256, cmap = 'Spectral')
You can see the different 3D contour plots in Figure 44.

Here is the full code to generate Figure 44.
To show the color bar, you can use this code
plt.colorbar()
The argument needed is your 3D contour plot. So, it would be best if you defined your plot into a variable. In my code, it is defined as variable p.
You can use the same code applied in the previous plots to change the view angle, as shown in Figure 45.

Bugs in 3D contour Matplotlib
I think there is a bug in Matplotlib 3D contour, as shown in Figure 46.

To create Figure 46, you change the value of elevation and azimuth angle in the previous code (code to create Figure 45). Did you get what I mean by the bugs? If you analyze Figure 46 carefully, you will get a strange feature. You can see it in Figure 47.

The area covered by the blue line is not in the right place. We should not see it because it is placed at the back. I am not sure it is a bug or not, but I think it needs to be repaired by Matplotlib.
To understand it in more detail, I introduce two different contour representations in Matplotlib, ax.contour() and ax.contourf(), as shown in Figure 48.

contourf in ax.contourf means filled contour. So, you can see the difference between ax.contour() and ax.contourf() in Figure 48. ax.contour has similar default contour counts with ax.contour3D(), 7 counts. But, ax.contourf() has different counts, it is 8. You can reproduce Figure 48 with this code
N = 100
np.random.seed(3124)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
plt.figure(figsize=(14, 6))
ax1 = plt.subplot(121, projection = '3d')
ax1.contour(X, Y, Z, cmap = 'Spectral')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('z')
ax1.set_box_aspect((3, 3, 1))
ax1.view_init(10, 100)
ax1.set_title('Contour Default, elevation = 10, azimuth = 100')
ax2 = plt.subplot(122, projection = '3d')
ax2.contourf(X, Y, Z, cmap = 'Spectral')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_zlabel('z')
ax2.set_box_aspect((3, 3, 1))
ax2.view_init(10, 100)
ax2.set_title('Contourf Default, elevation = 10, azimuth = 100')
You can observe carefully in the right panel Figure 48 (contourf plot). The bugs I mentioned in 3D contour is not detected. You can see it more detail in Figure 49.

You can see that if ax.contour() will give you the same error but not for ax.contourf(). So, I recommend you to use ax.contourf() if you want to visualize contour plot in 3D projection.
Wireframe plot
You can generate a wireframe plot in Matplotlib using this code
ax.plot_wireframe()
Here is the code to generate the mock data to be visualized in the wireframe plot
N = 100
np.random.seed(3124)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
The default wireframe plot as shown in Figure 50.

You can reproduce Figure 50 with the following code
N = 100
np.random.seed(3124)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection = '3d')
ax.plot_wireframe(X, Y, Z, color = 'k', alpha = .2)
The customized wireframe plot is shown in Figure 51.

I change the number of wireframe layer to 5, set the elevation angle to 60 degrees and the azimuth angle to 100 degrees. You can use this code to generate Figure 51.
N = 100
np.random.seed(3124)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
fig = plt.figure(figsize=(9, 6))
ax = plt.axes(projection = '3d')# 3d contour plot
ax.plot_wireframe(X, Y, Z, 5, color = 'k', alpha = .2)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_box_aspect((2, 2, 1))
ax.view_init(60, 100)
ax.set_title('Wireframe counts = 5, elevation = 60, azimuth = 100')
If you want to check whereas we meet the similar bugs in ax.contour() and ax.contour3D() or not, you can change the elevation angle to 10 degrees and the azimuth angle to 100 degrees, as shown in Figure 52.

We do not meet the bugs in the wireframe plot.
3D surface plot
You can generate a 3D surface plot in Matplotlib with this code.
ax.plot_surface()
The customized 3D surface plot is shown in Figure 53.

You can reproduce Figure 53 with this code
N = 100
np.random.seed(3124)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
fig = plt.figure(figsize=(8, 8))
ax = plt.axes(projection = '3d')# 3d contour plot
ax.plot_surface(X, Y, Z, )
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_box_aspect((2, 2, 1))
ax.view_init(10, 100)
ax.set_title('Plot surface Default, elevation = 10, azimuth = 100')
You can the values for cstride and rstride using this argument
rstride = 1, cstride = 1
You can see the difference between the default cstride and rstride value to the customized one in Figure 54.

To generate Figure 54, you can use this code
You will not meet the bugs you meet in the contour plot, as shown in Figure 55.

Creating a ball in Matplotlib
You can create a ball in Matplotlib, as shown in Figure 56.

You can create Figure 56 with the following code
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = 10 * np.outer(np.cos(u), np.sin(v))
y = 10 * np.outer(np.sin(u), np.sin(v))
z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))
plt.figure(figsize=(10, 10))
ax = plt.subplot(projection = '3d')
ax.plot_surface(x, y, z, cmap = 'inferno')
You can customize the view angle for it, as shown in Figure 57.

You can reproduce Figure 57 using this code

07. 2D Contour plot
The last plot type I will show you is the 2D contour plot. You can generate a 2D contour plot using this code
plt.contour()
Although the name is 2D contour plot, but it shows the 3D data. I create the mock data using this code
N = 100
np.random.seed(100)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
I present the mock data into a contour plot, as shown in Figure 58.

You can reproduce Figure 58 with the following code
N = 100
np.random.seed(100)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
plt.figure(figsize=(7, 5))
plt.contour(X, Y, Z)
plt.title('Contour 2D Default', pad = 10)
I generate two customized contour plots, as shown in Figure 59.

To create Figure 59, you can use this code
N = 100
np.random.seed(100)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)
plt.figure(figsize=(15, 5))
plt.subplot(121)
plt.contour(X, Y, Z, 256)
plt.title('Contour 2D counts = 256, cmap = viridis', pad = 10)
plt.colorbar()
plt.subplot(122)
plt.contour(X, Y, Z, 256, cmap = 'Spectral')
plt.colorbar()
plt.title('Contour 2D counts = 256, cmap = Spectral', pad = 10)
Also, you can generate contour filled into 2D projection, as shown in Figure 60.

You can generate Figure 60 with the following code
N = 100
np.random.seed(100)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)**3
plt.figure(figsize=(7, 5))
plt.contourf(X, Y, Z, cmap = 'Spectral')
plt.colorbar()
plt.title('Contourf 2D Default', pad = 10)
Here I generate two different contourf plots, as shown in Figure 61.

You can generate Figure 61 using this code
N = 100
np.random.seed(100)
x = np.linspace(-2, 2, N) + np.random.random(N)
y = np.linspace(-2, 2, N) + np.random.random(N)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) + np.cos(Y)**3
plt.figure(figsize=(15, 5))
plt.subplot(121)
plt.contourf(X, Y, Z, 50, cmap = 'inferno')
plt.colorbar()
plt.title('Contourf 2D counts = 50', pad = 10)
plt.subplot(122)
plt.contourf(X, Y, Z, 200, cmap = 'inferno')
plt.colorbar()
plt.title('Contourf 2D counts = 200', pad = 10)

Conclusion
Data visualization is significant in analyzing data from small data or Big Data in the technological era. We need it to have a global picture of our data. Various types of visualization you can use with Matplotlib. This is just a small part of python plotting with Matplotlib. In this Matplotlib guideline series (part 1 and part 2), I have generated 101 figures. If you read all of the part, you need to spend your 48 minutes 😀 I hope this story can help you to visualize your data into various plot types.
If you liked this article, here are some other articles you may enjoy:
5 Powerful Tricks to Visualize Your Data with Matplotlib
Matplotlib Styles for Scientific Plotting
Creating Colormaps in Matplotlib
Customizing Multiple Subplots in Matplotlib
Introduction to Big Data with Vaex – A Simple Code to Read 1.25 Billion Rows
That’s all. Thanks for reading this story. Comment and share if you like it. I also recommend you follow my account to get a notification when I post my new story.