How to make matplotlib graphs look professionally done like this? [closed] - python

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
Default matplotlib graphs look really unattractive and even unprofessional. I tried out couple of packages include seaborn as well as prettyplotlib but both of these just barely improves the styles.
So far I've gotten to following using seaborn package:
Below is the appearance I'm looking for which is far cry from above:
Notice the following niceness in the 2nd example:
Area under the graph is filled with much more eye pleasing color.
The graph line is thinker and nicely stands out.
Axis lines are thinker and again nicely stands out.
Area under the curve is transparent.
X-Axis tick marks are more denser.
My questions are: Do you recognize above as some kind of popular theme or style that I can quickly use in matplotlib? Or if I can use from some package? Failing that, is there anyway to set this style as my global preference? Failing that, is it even possible to do this in matlibplot?
Thanks!

This is really a matter of taste, and also a matter of target audience. matplotlib tries to produce clear illustrations for scientific purposes. This is - necessarily - a compromise, and the illustrations are not something you would print in a magazine or show in an advertisement.
There are some good news and some bad news about matplotlib in this sense.
Bad news:
There is no single magical command or package which would create beautiful plots with matplotlib.
Good news:
There are simple ways to change the default settings, see: http://matplotlib.org/users/customizing.html
The object model enables the user to change almost everything and introduce complex new features.
The source code is available, and even it can be changed quite easily by the user.
In my opinion the most difficult thing is to decide what you want. Then doing what you want is easier, even though there is a steepish learning curve in the beginning.
Just as an example:
import numpy as np
import matplotlib.pyplot as plt
# create some fictive access data by hour
xdata = np.arange(25)
ydata = np.random.randint(10, 20, 25)
ydata[24] = ydata[0]
# let us make a simple graph
fig = plt.figure(figsize=[7,5])
ax = plt.subplot(111)
l = ax.fill_between(xdata, ydata)
# set the basic properties
ax.set_xlabel('Time of posting (US EST)')
ax.set_ylabel('Percentage of Frontpaged Submissions')
ax.set_title('Likelihood of Reaching the Frontpage')
# set the limits
ax.set_xlim(0, 24)
ax.set_ylim(6, 24)
# set the grid on
ax.grid('on')
(Just a comment: The X-axis limits in the original image do not take the cyclicity of the data into account.)
This will give us something like this:
It is easy to understand that we need to do a lot of changes in order to be able to show this to a less-engineering-minded audience. At least:
make the fill transparent and less offensive in colour
make the line thicker
change the line colour
add more ticks to the X axis
change the fonts of the titles
# change the fill into a blueish color with opacity .3
l.set_facecolors([[.5,.5,.8,.3]])
# change the edge color (bluish and transparentish) and thickness
l.set_edgecolors([[0, 0, .5, .3]])
l.set_linewidths([3])
# add more ticks
ax.set_xticks(np.arange(25))
# remove tick marks
ax.xaxis.set_tick_params(size=0)
ax.yaxis.set_tick_params(size=0)
# change the color of the top and right spines to opaque gray
ax.spines['right'].set_color((.8,.8,.8))
ax.spines['top'].set_color((.8,.8,.8))
# tweak the axis labels
xlab = ax.xaxis.get_label()
ylab = ax.yaxis.get_label()
xlab.set_style('italic')
xlab.set_size(10)
ylab.set_style('italic')
ylab.set_size(10)
# tweak the title
ttl = ax.title
ttl.set_weight('bold')
Now we have:
This is not exactly as in the question, but everything can be tuned towards that direction. Many of the things set here can be set as defaults for matplotlib. Maybe this gives an idea of how to change things in the plots.

To get closer to the style you prefer, you could use the whitegrid style in seaborn. As the other answers have noted, you control the transparency of the fill with the alpha parameter to fill_between.
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
sns.set_style("whitegrid")
blue, = sns.color_palette("muted", 1)
x = np.arange(23)
y = np.random.randint(8, 20, 23)
fig, ax = plt.subplots()
ax.plot(x, y, color=blue, lw=3)
ax.fill_between(x, 0, y, alpha=.3)
ax.set(xlim=(0, len(x) - 1), ylim=(0, None), xticks=x)
More information on seaborn styles can be found in the docs.

matplotlib is almost infinitely flexible so you can do almost anything with it and if it doesn't exist you can write it yourself! Obviously the defaults are bland, this is because everyone has there own idea of what is "nice" so it is pointless to impose a predefined style.
Here is a really simple example that addresses 4 of your points.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
x = np.linspace(-10, 10, 1000)
y = 1+np.sinc(x)
ax = plt.subplot(111)
ax.plot(x, y, lw=2)
ax.fill_between(x, 0, y, alpha=0.2)
ax.grid()
majorLocator = MultipleLocator(1)
ax.xaxis.set_major_locator(majorLocator)
plt.show()
If your want to set defaults so all your plots look the same then you should generate a custom matplotlibrc file or use style. A useful guide is here. To view a list of all the available options just call print plt.rcParams from an interactive terminal.
Some of the other features such as filling will need to be done on a per plot basis. You can standardise this across your work by creating a function which adds the fill between given some input such as the axis instance and data.

You can customize plots style as follow:
import numpy as np
import matplotlib.pyplot as plt
plt.use_style('ggplot') # customize your plots style
x = np.linspace(0,2*np.pi,100)
y = np.sin(x)
plt.fill_between(x,y)
plt.show()

Related

matplotlib nice plot , who knows the scheme used?

May somebody know which is the scheme of color used in this post for the example plot ? this blue magenta, green ? which kind of style it is ??
EDIT ah ok I thought that was a known schemes
The shown plot comes from this answer. The colors in use are just hardcoded and do not belong to any official color scheme. The line colors are
["#7aa0c4", "#ca82e1", "#8bcd50"]
The background color is
"#f6f9fd"
The complete script to generate the plot is also available here.
In general, if you like to reproduce the colors from some image you find it's often easiest to use a color picker.
Since I invented this "scheme", you may of course ask how I would imagine it to continue for more colors. A quick guess would be the following:
["#7aa0c4", "#ca82e1", "#8bcd50", "#df9f53", "#64b9a1", "#745ea6", "#db7e76"]
Example:
import numpy as np
import matplotlib.pyplot as plt
cols = ["#7aa0c4", "#ca82e1", "#8bcd50", "#df9f53", "#64b9a1",
"#745ea6", "#db7e76"]
plt.rcParams["axes.prop_cycle"] = plt.cycler("color", cols)
x = np.linspace(0,4*np.pi, 101)
fig, (ax, ax2) = plt.subplots(nrows=2)
for i in range(7):
ax.plot(x, np.sin(x)-0.2*i)
ax2.plot(x, np.sin(x-np.pi*i/7))
plt.show()

Correctly setting the axes limits in matplotlib 3dplots? [duplicate]

This question already has answers here:
Removing axes margins in 3D plot
(2 answers)
Closed 4 years ago.
I'm having an issue with setting limits for my 3d plots in matplotlib; I'm finding that no matter how I set my limits for the x,y, and z axes, the plotting routine for 3dplots adds an extra buffer.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.axes.set_xlim3d(left=0, right=10)
ax.axes.set_ylim3d(bottom=0, top=10)
ax.axes.set_zlim3d(bottom=0, top=10)
plt.show()
This produces the following plot:
As you can see, the limits are supposed to be at x, y, z = {0, 10} however the 3D plotting always adds a little bit of a buffer to each edge. Does anyone know a way to turn this effect off?
I've also used plt.xlims(); and ax.axes.set_xlims() but they produce the same effect.
I think this is deliberate (see e.g. this), if you try plotting ax.axes.set_xlim3d(left=0.000001, right=9.9999999) then you get no 0 or 10 displayed on your figure.
Even making the numbers as arbitrarily close as possible doesn't work, e.g.
eps = 1e-16
ax.axes.set_xlim3d(left=0.-eps, right=10+eps)
ax.axes.set_ylim3d(bottom=0.-eps, top=10+eps)
ax.axes.set_zlim3d(bottom=0.-eps, top=10+eps)
The best solution I've found is to set the ticks manually and then slightly scale so the overlap is hidden.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
ax.set_xticks([0,2,4,6,8,10])
ax.set_yticks([0,2,4,6,8,10])
ax.set_zticks([0,2,4,6,8,10])
ax.axes.set_xlim3d(left=0.2, right=9.8)
ax.axes.set_ylim3d(bottom=0.2, top=9.8)
ax.axes.set_zlim3d(bottom=0.2, top=9.8)
plt.show()
This gives,
This is pretty hacky but could be made more general (and I always end up setting ticks manually for publication quality figures). Alternatively, it may be better to turn off the lowest grid line or hide the grid...

Remove grid lines, but keep frame (ggplot2 style in matplotlib)

Using Matplotlib I'd like to remove the grid lines inside the plot, while keeping the frame (i.e. the axes lines). I've tried the code below and other options as well, but I can't get it to work. How do I simply keep the frame while removing the grid lines?
I'm doing this to reproduce a ggplot2 plot in matplotlib. I've created a MWE below. Be aware that you need a relatively new version of matplotlib to use the ggplot2 style.
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import pylab as P
import numpy as np
if __name__ == '__main__':
values = np.random.uniform(size=20)
plt.style.use('ggplot')
fig = plt.figure()
_, ax1 = P.subplots()
weights = np.ones_like(values)/len(values)
plt.hist(values, bins=20, weights=weights)
ax1.set_xlabel('Value')
ax1.set_ylabel('Probability')
ax1.grid(b=False)
#ax1.yaxis.grid(False)
#ax1.xaxis.grid(False)
ax1.set_axis_bgcolor('white')
ax1.set_xlim([0,1])
P.savefig('hist.pdf', bbox_inches='tight')
OK, I think this is what you are asking (but correct me if I misunderstood):
You need to change the colour of the spines. You need to do this for each spine individually, using the set_color method:
for spine in ['left','right','top','bottom']:
ax1.spines[spine].set_color('k')
You can see this example and this example for more about using spines.
However, if you have removed the grey background and the grid lines, and added the spines, this is not really in the ggplot style any more; is that really the style you want to use?
EDIT
To make the edge of the histogram bars touch the frame, you need to either:
Change your binning, so the bin edges go to 0 and 1
n,bins,patches = plt.hist(values, bins=np.linspace(0,1,21), weights=weights)
# Check, by printing bins:
print bins[0], bins[-1]
# 0.0, 1.0
If you really want to keep the bins to go between values.min() and values.max(), you would need to change your plot limits to no longer be 0 and 1:
n,bins,patches = plt.hist(values, bins=20, weights=weights)
ax.set_xlim(bins[0],bins[-1])

Plotting dot plot with enough space of ticks in Python/matplotlib?

In the following code snippet:
import numpy as np
import pandas as pd
import pandas.rpy.common as com
import matplotlib.pyplot as plt
mtcars = com.load_data("mtcars")
df = mtcars.groupby(["cyl"]).apply(lambda x: pd.Series([x["cyl"].count(), np.mean(x["wt"])], index=["n", "wt"])).reset_index()
plt.plot(df["n"], range(len(df["cyl"])), "o")
plt.yticks(range(len(df["cyl"])), df["cyl"])
plt.show()
This code outputs the dot plot graph, but the result looks quite awful, since both the xticks and yticks don't have enough space, that it's quite difficult to notice both 4 and 8 of the cyl variable output its values in the graph.
So how can I plot it with enough space in advance, much like you can do it without any hassles in R/ggplot2?
For your information, both of this code and this doesn't work in my case. Anyone knows the reason? And do I have to bother to creating such subplots in the first place? Is it impossible to automatically adjust the ticks with response to the input values?
I can't quite tell what you're asking...
Are you asking why the ticks aren't automatically positioned or are you asking how to add "padding" around the inside edges of the plot?
If it's the former, it's because you've manually set the tick locations with yticks. This overrides the automatic tick locator.
If it's the latter, use ax.margins(some_percentage) (where some_percentage is between 0 and 1, e.g. 0.05 is 5%) to add "padding" to the data limits before they're autoscaled.
As an example of the latter, by default, the data limits can be autoscaled such that a point can lie on the boundaries of the plot. E.g.:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(10), 'ro')
plt.show()
If you want to avoid this, use ax.margins (or equivalently, plt.margins) to specify a percentage of padding to be added to the data limits before autoscaling takes place.
E.g.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(range(10), 'ro')
ax.margins(0.04) # 4% padding, similar to R.
plt.show()

How to format contour lines from Matplotlib

I am working on using Matplotlib to produce plots of implicit equations (eg. y^x=x^y). With many thanks to the help I have already received I have got quite far with it. I have used a contour line to produce the plot. My remaining problem is with formatting the contour line eg width, color and especially zorder, where the contour appears behind my gridlines. These work fine when plotting a standard function of course.
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
import numpy as np
fig = plt.figure(1)
ax = fig.add_subplot(111)
# set up axis
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
# setup x and y ranges and precision
x = np.arange(-0.5,5.5,0.01)
y = np.arange(-0.5,5.5,0.01)
# draw a curve
line, = ax.plot(x, x**2,zorder=100,linewidth=3,color='red')
# draw a contour
X,Y=np.meshgrid(x,y)
F=X**Y
G=Y**X
ax.contour(X,Y,(F-G),[0],zorder=100,linewidth=3,color='green')
#set bounds
ax.set_xbound(-1,7)
ax.set_ybound(-1,7)
#add gridlines
ax.xaxis.set_minor_locator(MultipleLocator(0.2))
ax.yaxis.set_minor_locator(MultipleLocator(0.2))
ax.xaxis.grid(True,'minor',linestyle='-',color='0.8')
ax.yaxis.grid(True,'minor',linestyle='-',color='0.8')
plt.show()
This is rather hackish but...
Apparently in the current release Matplotlib does not support zorder on contours. This support, however, was recently added to the trunk.
So, the right way to do this is either to wait for the 1.0 release or just go ahead and re-install from trunk.
Now, here's the hackish part. I did a quick test and if I changed line 618 in
python/site-packages/matplotlib/contour.py
to add a zorder into the collections.LineCollection call, it fixes your specific problem.
col = collections.LineCollection(nlist,
linewidths = width,
linestyle = lstyle,
alpha=self.alpha,zorder=100)
Not the right way to do things, but might just work in a pinch.
Also off-topic, if you accept some responses to your previous questions, you probably get quicker help around here. People love those rep points :)

Categories

Resources