Plot in Python with Matplotlib Step by Step | by Matias Eiletz | Towards Data Science

Matplotlib is the most popular graphics library in Python. For instance if we look in StackOverflow, we can see that there are ~47k questions tagged with Matoplotlib, whereas if we look for other comparable libraries like Seaborn or Plotly, we get ~4k and ~7k respectively.
Hence if you are getting started with plotting in Python, I strongly recommend you to use Matplotlib, not only for the popularity reason, but also because for example Seaborn is built on top of Matplotlib, so if you understand how to plot in Matplotlib, then you will easily understand how to handle Seaborn (and it’s not the same on the other way around).
If you already work with Matplotlib, I also encourage you to stay here because you may discover new things about plotting in Python.
So in this article I would like to show you how to plot in Matplotlib, going smoothly from simple plots, to more complex stuff, giving you also the necessary tools to discover how to plot new stuff on your own.
You won’t need to download any dataset. We are going to create the necessary ones during the article, so you would be able to copy and paste and try them on your own, in a Juptyer notebook for instance.
As a sneak peek, we will be seeing all the tools needed to go from this first basic plot in the first section to the following histogram presented at the end of the second section.
Importing necessary packages
Change plot size
Starting with the first basic plot
Change line style, markers, and width
Scatter plot
Bar plot
Combine several plots in one
Modify axes
Add grid and legend
More about modifying axes
Add values on top of the plot
Add lines on top of the plot
Histograms
Bonus: Twin axes
Let’s now get started!
First we have to import the Matplotlib package, and run the magic function %matplotlib inline. This magic function is the one that will make the plots appear in your Jupyter Notebook.
import matplotlib.pyplot as plt%matplotlib inline
Matplotlib comes pre-installed in Anaconda distribution for instance, but in case the previous commands fail because you don’t have Matplotlib package installed, you can do it running this in your terminal:
python -m pip install -U pippython -m pip install -U matplotlib
Also to follow all the examples we will be going through, you have to do the following basic imports:
import pandas as pdimport numpy as np
If we want to change the plot size output, we need to change from properties. First, we can see what are the current properties:
# get current sizefig_size = plt.rcParams["figure.figsize"]print ("Current size:", fig_size)# let's make the plots a bit bigger than the default# set figure width to 14 and height to 6fig_size[0] = 14fig_size[1] = 6plt.rcParams["figure.figsize"] = fig_sizeprint ("Current size:", fig_size)
As we said, we are going to generate our own datasets to work.
# datasets x and y are lists, but they can also be, for instance, numpy arrays or pd.Series.x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]# plotplt.plot(x, y)
From this, we get the simple plot:
It can be useful to do a quick check for the data you are working with, however, it is not really informative if you are going to put it into a presentation. Let’s continue and see how to put it on steroids.
Let’s say we want the line to be dashed and thicker, the markers to be circles and bigger, and the color to be green. Then, we add the five parameters that you can see after the x and y.
x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]plt.plot(x, y, color='green' , marker='o', markersize=20, linestyle='--', linewidth=4)
And now we get the following:
These are just some parameters you can modify, but you can find the whole list in the Matplotlib documentation (I will add the link at the bottom of the article).
If you are plotting some discrete data, you may want to plot only the dots (or the markers, more generally). Then, you can use plt.scatter, which works almost the same as plt.plot, for example:
x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]# specifying the type of marker (dots) and its sizesplt.scatter(x, y, marker='o', size=150)
Giving as a result:
And the same we can do if we want to plot the data in bars:
x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]# specifying the bar colorsplt.bar(x, y, color=’green’)
Getting the following plot:
It’s good to point out that we can combine the three of them, doing something like:
x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]plt.plot(x, y, color=’blue’, lw=4, alpha=0.5)plt.scatter(x, y, marker=’o’, s=400, color=’red’,alpha=0.5)plt.bar(x, y, color=’green’,alpha=0.5)
Giving as a result:
You may wonder what is that new variable alpha: it is a parameter that stands for the level of transparency. alpha=0 is totally transparent, and alpha=1 it is as if we didn’t set any alpha parameter (not transparent at all). It’s because of this transparency that you can see the red dots behind the bars, and the same with the blue line going on top of the bars. You can try the same with alpha=1 and you will see the difference!
The previous plots were nice, but there is still a lot we can do, especially if we want to make the plot more understandable or for a presentation. Maybe we want to put some limits or extend the axes from it. So let’s run the following and check the result:
x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]plt.plot(x, y)# here we modify the axes, specifying min and max for x and y axes.plt.axis(xmin=-1, xmax=10, ymin=0, ymax=40)
That’s a simple way. But what if you want to specify what is on the x-axis or in the y-axis? We can see for example that in the x-axis the numbers go 2 by 2. We can change it with x-ticks or y-ticks:
plt.plot(x, y)plt.axis(xmin=-1, xmax=12, ymin=0, ymax=40)plt.xticks(np.arange(0,13,1))
Sometimes we get a little lost in the plot, and we need a grid. That is very simple! We have to add the plt.grid method like this:
plt.plot(x, y)plt.axis(xmin=-1, xmax=12, ymin=0, ymax=40)plt.xticks(np.arange(0,13,1))plt.grid(True)
Another useful feature to add to a plot, especially if you are planning to add to a presentation to communicate findings and you want the audience to understand the plot as quickly as possible, it is to add a legend. This is very simple, and it goes like this:
plt.plot(x, y,label='Nice Blue Line')plt.axis(xmin=-1, xmax=12, ymin=0, ymax=40)plt.grid(True)plt.legend(loc='upper right',prop={'size':24})
In plt.legend() example from above, I choose the size from it and the location in the plot (upper right).
Let’s go back now to the axes, and let’s modify some stuff all at once:
Change the x-ticks names (for dates, in this case)
Rotate axis
Increase the size from the x-ticks and y-ticks in the axes
Add label to the axes (and choose their font size and separation from the axes with labelpad)
Add a title to the plot
plt.plot(x, y,label='Nice Blue Line')plt.axis(xmin=0, xmax=6, ymin=0, ymax=40)# here we change the x-ticksplt.xticks(x, ('2019-02-01', '2019-02-02', '2019-02-03','2019-02-04', '2019-02-05'))# here we rotate the x-ticksplt.xticks(rotation=45)# here we change the size from both axes ticksplt.tick_params(axis='both', labelsize = 16)# here we add the axes labelsplt.ylabel('Y Label', size=16, labelpad=20)plt.xlabel('X Label', size=16, labelpad=20)# here we add the titleplt.title('Plot Title', size=18)# here we add the gridplt.grid(True)
Rotating the labels is especially useful when we have dates in the x-axis like in this example or larger numbers that one value can get over the previous and next one.
Now that we have already made our plots neat and with clear references, let’s continue with the second section.
This second section is about adding extra features to the plot itself.
I will show you how to add the numbers indicating the values in the plot, how to add lines, and in the end as a bonus, an example of this all together in a histogram.
# we use the same data points as beforex = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]# we plot the data pointsplt.plot(x, y)plt.axis(xmin=0, xmax=6, ymin=10, ymax=40)plt.title('Plot Title', size=18)# here we add the value in one data pointplt.annotate(str(25),xy=(1,25+1), fontsize=14)
And there we have what we wanted.
In the plot.annotate method we pass as a first argument the string we want to show (the value 25 in the shape of a string in this case) and then the coordinates: x:1 and y:25+1 (we add the +1 so that the number is not on overlapping the line).
This is not useful, however, if we have a lot of data points and we want to plot all the values on top of the curve. So let’s look at this other way, in order to do it automated for all the values:
x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]# we plot the data pointsplt.plot(x, y)plt.axis(xmin=0, xmax=6, ymin=10, ymax=40)plt.title('Plot Title', size=18)# here we add all the values# we also specify font size and colorfor i,j in zip(x,y):    inplot = plt.annotate(str(j)                          ,xy=(i,j+1)                          ,fontsize=12                          ,color='black')
Getting, as a result, this plot:
So here as you can see, we did a for loop to plot all the values.
We used the zip function, a very useful one, that allows us to get more than one variable in each loop.
When you want to highlight one of the values from a plot, this is something that comes handy. You can use a vertical line if you want to highlight the x-axis value or a horizontal one if you want to do it with the y-axis value. For the purpose of this example, let’s use both of them:
x = [1, 2, 3, 4, 5]y = [25, 32, 34, 20, 25]plt.plot(x, y)plt.axis(xmin=0, xmax=6, ymin=10, ymax=40)plt.title('Plot Title', size=18)# here we add the vertical lineplt.axvline(x[1], color='red',linestyle='--',linewidth=1)# here we add the horizontal lineplt.axhline(y[1], color='orange',linestyle='--',linewidth=1)
As promised at the beginning of the second section, we are going to create a histogram. We are going to add a cumulative curve, and we are going to use all the extra features explained before, showing how all these features add valuable information to a plot.
The objective is to go from a very simple histogram, like from this one:
To (spoiler alert) this one:
Or to this one (with twin axes so that curve and bars have the same heigh):
I’m going to generate a toy dataset with the following code. You can copy and paste it in your Jupyter notebook to continue with the example. My objective is to show you the plot, not the creation of the dataset, so it’s because of that that I’m not going to explain the details of the following code. If you have any questions about this, however, please let me know in the comments.
data = np.random.chisquare(2,5000)r = range(12)df = pd.DataFrame(data)values = np.histogram(df, bins=r)[0]bins = np.histogram(df, bins=r)[1].astype(int)df = pd.DataFrame({'bins_left': bins[:-1],'bins_right': bins[1:], 'values': values})df['values_norm'] = df['values'] / df['values'].sum()df['labels'] = df.apply(lambda x: str(int(x['bins_left'])) + '-' + str(int(x['bins_right'])),axis=1)df['cumulative'] = df['values'].cumsum()df['perc_cumulative'] = df['cumulative']/df.cumulative.max()df['perc_cum_label'] = df.apply(lambda x: str(round(x['perc_cumulative']*100,1)) + '%',axis=1)df = df[['labels','values','values_norm','cumulative','perc_cumulative','perc_cum_label']]
So, in the end, we have this dataframe:
So in this data array, we have 5000 values, that for the sake of this example, let’s say that they represent the weights from a community of 5000 rabbits. So, we have 1935 rabbits weight between 0 and 1 kilo, 1232 of them weight between 1 and 2 kilos, and, only 15 of them, weight between 10 and 11 kilos. As the majority of them are very light, we can assume that is a community of baby rabbits with just some adults around.
The only values that are on our interest for the histogram x and y-axis, are the ones in the first two columns (labels and values).
The other columns will be useful for indicating other features in the plot.
Now, let’s go to build the histogram.
We can do a very simple one, doing this:
pd.DataFrame(data).hist()
Which will give us a plot, but far from being completely understandable at a first quick glance.
If someone asks us, “what percentage of the rabbits’ weight between 0 and 1 kilograms?” is difficult to answer looking at this plot.
If we want to make a more meaningful histogram plot, let’s make the one from before (the one from the spoiler alert) using only tools that we went over before:
# plot the bars from the histogramx = np.array(df['labels'])y = np.array(df['values_norm'])plt.bar(x,y)# plot the cumulative curvex = list(range(len(x)))y = list(df['perc_cumulative'])plt.plot(x,y,color='orange',marker='o')# change the y-axis limitsplt.axis(ymin=0, ymax=1.1)# plot the values from the cumulative curve on top of ity_labels = list(df['perc_cum_label'])for i,j in zip(x,y):    inplot = plt.annotate(str(y_labels[i])                          ,xy=(i-0.1,j+0.05)                          ,fontsize=12                          ,color='black')# draw vertical lines in 50, 75 and 90 percentile.plt.axvline(np.percentile(data,50)-1, color='maroon',linestyle='--',linewidth=1)plt.axvline(np.percentile(data,75)-1, color='maroon',linestyle='--',linewidth=1)plt.axvline(np.percentile(data,90)-1, color='maroon',linestyle='--',linewidth=1)# indicate which percentile is each linep_values = [np.percentile(data,50),np.percentile(data,75),np.percentile(data,90)]p_numbers = ['p50', 'p75', 'p90']for i, j in zip(p_values, p_numbers):    inplot = plt.annotate(j                          ,xy=(i-1 + 0.05, 1.05)                          ,fontsize=12                          ,color='maroon')# some final tweaking in title, axis and labelsplt.title('Histogram and cumulative curve',fontsize=15)plt.ylabel('Bins', size=15, labelpad=18)plt.xlabel('Frequency', size=15, labelpad=18)plt.tick_params(axis='both', labelsize = 14)
Giving as a result:
We can do the same plot, but having the bars at the same level as the curve. We achieve this by generating twin axes.
As you will see, the code is very similar, with the particularity that at the beginning we create the twin axes. Then, we generate both plots (bars and curve), but instead of starting both of them with plt., one of them will start with host. And the other one, with par.
# we import this package for the twin axesfrom mpl_toolkits.axes_grid1 import host_subplot# we generate the twin axeshost = host_subplot(111)par = host.twinx()# we plot the bars (use host. instead of plt.)x = np.array(df['labels'])y = np.array(df['values_norm'])p1 = host.bar(x, y, color='steelblue')# we plot the curve (use par. instead of plt.)x = list(range(len(x)))y = list(df['perc_cumulative'])p2 = par.plot(x, y, color='darkorange',marker='o')# we plot the values from the cumulative curve on top of ity_labels = list(df['perc_cum_label'])for i,j in zip(x,y):    inplot = par.annotate(str(y_labels[i])                         ,xy=(i-0.1,j+0.05)                         ,fontsize=12                         ,color='black')# some final tweaking in title, axis and labelsplt.title('Histogram and cumulative curve',fontsize=15)par.axis(ymin=0, ymax=1.1)host.set_xlabel("Bins", size=13, labelpad = 16)host.set_ylabel("Frequency", size=13, labelpad = 16)# draw vertical lines in 50, 75 and 90 percentile.plt.axvline(np.percentile(data,50)-1, color='maroon',linestyle='--',linewidth=1.5)plt.axvline(np.percentile(data,75)-1, color='maroon',linestyle='--',linewidth=1.5)plt.axvline(np.percentile(data,90)-1, color='maroon',linestyle='--',linewidth=1.5)# indicate which percentile is each linep_values = p_values = [np.percentile(data,50),np.percentile(data,75),np.percentile(data,90)]p_numbers = ['p50', 'p75', 'p90']for i, j in zip(p_values, p_numbers):    inplot = plt.annotate(j                          ,xy=(i-1 + 0.05, 0.39)                          ,fontsize=10                           ,color='maroon')
Getting, in the end, the desired plot:
And we reached the end! I hope you have enjoyed the article and that you have learned a lot (or at least a few) new things to improve your plotting skills in Python.
Follow me if you want to see more articles like this in the future.
You can also look at my previous articles, like:
Mastering Dates and Timestamps in Pandas (and Python in general) (same style as the current article)
Other articles:
Analyzing User behavior and UX performance with Feature importance
And as promised at the beginning of the First Section, a link to Matplotlib documentation where you can see everything about markers, line styles, etc:
Matplotlib Documentation
Thanks for reading!