I'm trying to get a plot with custom aspect ratio to display properly. I am using Jupyter notebooks for the rendering, but the way I've normally done this is to adjust the 'figsize' attribute in the subplots. I've done it like below:
from matplotlib import pyplot as plt
fig,axes = plt.subplots(1,1,figsize=(16.0,8.0),frameon=False)
The problem is that, while the aspect ratio seems to come out correct (judging by eye), the figure does not use up even close to the whole page width, and is therefore tiny and hard to read.
I guess it's behaving like there are some sort of margins set on the left and right, but I can't find the global setting that controls this. I have been using the list of settings here, with no success finding a relevant one.
My question(s) are
How do I adjust the aspect ratio without impacting the overall size of the figure (think font sizes of the axis labels)? I don't need the width of my screen to be a constraint, I'd be perfectly happy for Jupyter notebooks to give me a horizontal scroll bar.
Is there a place with a more comprehensive and well-written documentation of all the matplotlib parameters that are available? The one I linked above is awkward because it gives the parameters in the form of an example matplotlibrc file. I'd like to know if a single page with (good) descriptions of all the parameters exists.
EDIT: it has been pointed out that this could be a jupyter problem and that I am setting the aspect ratio correctly. I'm using Jupyter version 1.0.0. Below is a picture of the output of a simplified notebook.
It's easy to see that the figure does not use even close to the available horizontal space.
The code in the notebook is:
#imports
import numpy as np
#set up a plot
import matplotlib as mpl
from matplotlib import pyplot as plt
#got smarter about the mpl config: see mplstyles/ directory
plt.style.use('standard')
#set up a 2-d plot
fig,axes = plt.subplots(1,1,figsize=(16.0,8.0),frameon=False)
ax1 = axes
#need to play with axis
mpl.rcParams['ytick.minor.visible'] = False
xmin = -10
xmax = 10
ymin = -10
ymax = 10
x = np.random.normal(0,5,(20000,))
y = np.random.normal(0,5,(20000,))
h = ax1.hist2d(x,y, bins=200, cmap='inferno')
ax1.set_xlim(xmin,xmax)
ax1.set_ylim(ymin,ymax)
ax1.set_xlabel('epoch time [Unix]',**axis_font)
ax1.set_ylabel(r'relative time [$\mu$s]',**axis_font)
ax1.grid(True)
#lgnd= ax1.legend(loc=2,prop={'size':22})
for axis in ['top','bottom','left','right']:
ax1.spines[axis].set_linewidth(2)
plt.tight_layout()
#plt.savefig('figures/thefigure.eps')
plt.show()
The mpl style file that I use in the plt.style.use command is:
#trying to customize here, see:
#https://matplotlib.org/users/customizing.html
#matplotlib.rc('figure', figsize=(3.4, 3.4*(4/6)))
lines.linewidth : 2
#ticks
xtick.top : False
xtick.bottom : True
xtick.minor.visible : True
xtick.direction : in
xtick.major.size : 8
xtick.minor.size : 4
xtick.major.width : 2
xtick.minor.width : 1
xtick.labelsize : 22
ytick.left : True
ytick.right : False
ytick.minor.visible : True
ytick.direction : in
ytick.major.size : 8
ytick.minor.size : 4
ytick.major.width : 2
ytick.minor.width : 1
ytick.labelsize : 22
#error bars
#errorbar.capsize : 3
#axis stuff
axes.labelsize : 22
EDIT 2: restricting the range of the vectors to the range of the axes before plotting results in the desired output. See the below figure:
The added/modified lines were:
xnew = x[(np.abs(x)<10) & (np.abs(y)<10)]
ynew = y[(np.abs(x)<10) & (np.abs(y)<10)]
h = ax1.hist2d(xnew,ynew, bins=200, cmap='inferno')
Apparently there was a bug in matplotlib 2.2.2 which got fixed by now in the development version. You may of course install the current development version from github.
The Problem comes from setting axes limits (ax1.set_xlim(-10,10)) which are smaller than the initial image. For some reason the original limits still got used to calculate the tight bbox for saving as png.
The workaround would be not to set any axes limits manually, but let the histogram plot be calculated directly with the desired limits in mind. In this case -10,10, e.g.:
x = np.random.normal(0,5,(20000,))
y = np.random.normal(0,5,(20000,))
bins = np.linspace(-10,10,201)
h = ax1.hist2d(x,y, bins=bins, cmap='inferno')
To change the font sizes of the axis label's, you'd have to use plt.rc or plt.rcParams (more on this here), so you needn't worry about doing that when using figsize.
I don't see any problems with the code you posted, could you post a picture of what you get and what you'd like to get? This is what I get using that configuration, on Jupyter notebooks, just plotting a very simple graph:
Do note, however, Jupyter limits the size of your plots automatically (see below):
And I'm afraid I can't help you with your second question, as I've always found matplotlib's documentation sufficient for all my needs... good luck!
Related
I generate plots like below:
from pylab import *
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
import matplotlib.ticker as ticker
rcParams['axes.linewidth'] = 2 # set the value globally
rcParams['font.size'] = 16# set the value globally
rcParams['font.family'] = ['DejaVu Sans']
rcParams['mathtext.fontset'] = 'stix'
rcParams['legend.fontsize'] = 24
rcParams['axes.prop_cycle'] = cycler(color=['grey','b','g','r','orange'])
rc('lines', linewidth=2, linestyle='-',marker='o')
rcParams['axes.xmargin'] = 0
rcParams['axes.ymargin'] = 0
t = arange(0,21,1)
v = 2.0
s = v*t
plt.figure(figsize=(12, 4))
plt.plot(t,s,label='$s=%1.1f\cdot t$'%v)
plt.title('Wykres drogi w czasie $s=v\cdot t$')
plt.xlabel('Czas $t$, s')
plt.ylabel('Droga $s$, m')
plt.autoscale(enable=True, axis='both', tight=None)
legend(loc='best')
plt.xlim(min(t),max(t))
plt.ylim(min(s),max(s))
plt.grid()
plt.show()
When I am changing the value t = arange(0,21,1) for example to t = arange(0,20,1) which gives me for example on the x axis max value= 19.0 my max value dispirs from the x axis. The same situation is of course with y axis.
My question is how to force matplotlib to produce always plots where on the axes are max values just at the end of the axes like should be always for my purposes or should be possible to chose like an option?
Imiage from my program in Fortan I did some years ago
Matplotlib is more efficiens that I use it but there should be an opition like that (the picture above).
In this way I can always observe max min in text windows or do take addiional steps to make sure about max min values. I would like to read them from axes and the question is ...Are there such possibilites in mathplotlib ??? If not I will close the post.
Axes I am thinking about more or less
I see two ways to solve the problem.
Set the axes automatic limit mode to round numbers
In the rcParams you can do this with
rcParams['axes.autolimit_mode'] = 'round_numbers'
And turn off the manual axes limits with min and max
plt.xlim(min(t),max(t))
plt.ylim(min(s),max(s))
This will produce the image below. Still, the extreme values of the axes are shown at the nearest "round numbers", but the user can approximately catch the data range limits. If you need the exact value to be displayed, you can see the second solution which cannot be directly used from the rcParams.
or – Manually generate axes ticks
This solution implies explicitly asking for a given number of ticks. I guess there is a way to automatize it depending on the axes size etc. But if you are dealing with more or less every time the same graph size, you can decide a fixed number of ticks manually. This can be done with
plt.xlim(min(t),max(t))
plt.ylim(min(s),max(s))
plt.xticks(np.linspace(t.min(), t.max(), 7)) # arbitrary chosen
plt.yticks(np.linspace(s.min(), s.max(), 5)) # arbitrary chosen
generated the image below, quite similar to your image example.
I am a recent migrant from Matlab to Python and have recently worked with Numpy and Matplotlib. I recoded one of my scripts from Matlab, which employs Matlab's contourf-function, into Python using matplotlib's corresponding contourf-function. I managed to replicate the output in Python, apart that the contourf-plots are not exacly the same, for a reason that is unknown to me. As I run the contourf-function in matplotlib, I get this otherwise nice figure but it has these sharp edges on the contour-levels on top and bottom, which should not be there (see Figure 1 below, matplotlib-output). Now, when I export the arrays I used in Python to Matlab (i.e. the exactly same data set that was used to generate the matplotlib-contourf-plot) and use Matlab's contourf-function, I get a slightly different output, without those sharp contour-level edges (see Figure 2 below, Matlab-output). I used the same number of levels in both figures. In figure 3 I have made a scatterplot of the same data, which shows that there are no such sharp edges in the data as shown in the contourf-plot (I added contour-lines just for reference). Example dataset can be downloaded through Dropbox-link given below. The data set contains three txt-files: X, Y, Z. Each of them are an 500x500 arrays, which can be directly used with contourf(), i.e. plt.contourf(X,Y,Z,...). The code that used was
plt.contourf(X,Y,Z,10, cmap=plt.cm.jet)
plt.contour(X,Y,Z,10,colors='black', linewidths=0.5)
plt.axis('equal')
plt.axis('off')
Does anyone have an idea why this happens? I would appreciate any insight on this!
Cheers,
Jussi
Below are the details of my setup:
Python 3.7.0
IPython 6.5.0
matplotlib 2.2.3
Matplotlib output
Matlab output
Matplotlib-scatter
Link to data set
The confusing thing about the matlab plot is that its colorbar shows much more levels than there are actually in the plot. Hence you don't see the actual intervals that are contoured.
You would achieve the same result in matplotlib by choosing 12 instead of 11 levels.
import numpy as np
import matplotlib.pyplot as plt
X, Y, Z = [np.loadtxt("data/roundcontourdata/{}.txt".format(i)) for i in list("XYZ")]
levels = np.linspace(Z.min(), Z.max(), 12)
cntr = plt.contourf(X,Y,Z,levels, cmap=plt.cm.jet)
plt.contour(X,Y,Z,levels,colors='black', linewidths=0.5)
plt.colorbar(cntr)
plt.axis('equal')
plt.axis('off')
plt.show()
So in conclusion, both plots are correct and show the same data. Just the levels being automatically chosen are different. This can be circumvented by choosing custom levels depending on the desired visual appearance.
Python version: 3.6.4 (Anaconda on Windows)
Seaborn: 0.8.1
Matplotlib: 2.1.2
I'm trying to create a 2D Kernel Density plot using Seaborn but I want each step in the colourmap to have a different alpha value. I had a look at this question to create a matplotlib colourmap with alpha values: Add alpha to an existing matplotlib colormap.
I have a problem in that the lines between contours are visible. The result I get is here:
I thought that I had found the answer when I found this question: Hide contour linestroke on pyplot.contourf to get only fills. I tried the method outlined in the answer (using set_edgecolor("face") but it did not work in this case. That question also seemed to be related to vector graphics formats and I am just writing out a PNG.
Here is my script:
import numpy as np
import seaborn as sns
import matplotlib.colors as cols
import matplotlib.pyplot as plt
def alpha_cmap(cmap):
my_cmap = cmap(np.arange(cmap.N))
# Set a square root alpha.
x = np.linspace(0, 1, cmap.N)
my_cmap[:,-1] = x ** (0.5)
my_cmap = cols.ListedColormap(my_cmap)
return my_cmap
xs = np.random.uniform(size=100)
ys = np.random.uniform(size=100)
kplot = sns.kdeplot(data=xs, data2=ys,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30)
plt.savefig("example_plot.png")
Guided by some comments on this question I have tried some other methods that have been successful when this problem has come up. Based on this question (Matplotlib Contourf Plots Unwanted Outlines when Alpha < 1) I have tried altering the plot call to:
sns.kdeplot(data=xs, data2=ys,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30,
antialiased=True)
With antialiased=True the lines between contours are replaced by a narrow white line:
I have also tried an approach similar to this question - Pyplot pcolormesh confused when alpha not 1. This approach is based on looping over the PathCollections in kplot.collections and tuning the parameters of the edges so that they become invisible. I have tried adding this code and tweaking the linewidth -
for thing in kplot.collections:
thing.set_edgecolor("face")
thing.set_linewidth(0.01)
fig.canvas.draw()
This results in a mix of white and dark lines - .
I believe that I will not be able to tune the line width to make the lines disappear because of the variable width of the contour bands.
Using both methods (antialiasing + linewidth) makes this version, which looks cool but isn't quite what I want:
I also found this question - Changing Transparency of/Remove Contour Lines in Matplotlib
This one suggests overplotting a second plot with a different number of contour levels on the same axis, like:
kplot = sns.kdeplot(data=xs, data2=ys,
ax=ax,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=30,
antialiased=True)
kplot = sns.kdeplot(data=xs, data2=ys,
ax=ax,
cmap=alpha_cmap(plt.cm.viridis),
shade=True,
shade_lowest=False,
n_levels=35,
antialiased=True)
This results in:
This is better, and almost works. The problem here is I need variable (and non-linear) alpha throughout the colourmap. The variable banding and lines seem to be a result of the combinations of alpha when contours are plotted over each other. I also still see some clear/white lines in the result.
I'm using the "PyPlot" package in Julia, and I want to add gridlines at specified locations. I'm not familiar enough with Python/Matlab to use their documentation pages to help - the commands differ in Julia. I want a basic plot, with gridlines on both axes at intervals of 1:
using PyPlot
fig=figure("Name")
grid("on")
scatter([1,2,3,4],[4,5,6,7])
Help appreciated...
PyPlot is just an interface to Matplotlib, so the commands
to customize the grid are Matplotlib's commands.
One way to configure the gridlines on both axes at intervals of 1 (for the given data) is:
using PyPlot
fig=figure(figsize=[6,3])
ax1=subplot(1,1,1) # creates a subplot with just one graphic
ax1[:xaxis][:set_ticks](collect(1:4)) # configure x ticks from 1 to 4
ax1[:yaxis][:set_ticks](collect(4:7)) # configure y ticks from 4 to 7
grid("on")
scatter([1,2,3,4],[4,5,6,7])
This code was tested inside an IJulia's notebook, and produces the following output:
Take a look at Various Julia plotting examples using PyPlot.
tested with Julia Version 0.4.3
The values where grid lines are drawn can be controlled by passing an array to the xticks() and yticks() functions.
A simple example:
using PyPlot
fig=figure("Name")
grid("on")
xticks(0:5)
yticks(3:8)
scatter([1,2,3,4],[4,5,6,7])
If you want it to be more flexible you can figure out the limits based on your data and set the tick interval to something else.
One little more dynamic way to configure the x-axis of the grid could be:
x_data = [1,2,3,4]
x_tick_interval = 2;
x_tick_start = minimum(xdata)
x_tick_end = maximum(xdata)
xticks(x_tick_start:x_tick_interval:x_tick_end)
I'm using pandas to work with a data set and am tring to use a simple line plot with error bars to show the end results. It's all working great except that the plot looks funny.
By default, it will put my 2 data groups at the far left and right of the plot, which obscures the error bar to the point that it's not useful (the error bars in this case are key to intpretation so I want them plainly visible).
Now, I fix that problem by setting xlim to open up some space on either end of the x axis so that the error bars are plainly visible, but then I have an offset from where the x labels are to where the actual x data is.
Here is a simplified example that shows the problem:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df6 = pd.DataFrame( [-0.07,0.08] , index = ['A','B'])
df6.plot(kind='line', linewidth=2, yerr = [ [0.1,0.1],[0.1,0.1 ] ], elinewidth=2,ecolor='green')
plt.xlim(-0.2,1.2) # Make some room at ends to see error bars
plt.show()
I tried to include a plot (image) showing the problem but I cannot post images yet, having just joined up and do not have anough points yet to post images.
What I want to know is: How do I shift these labels over one tick to the right?
Thanks in advance.
Well, it turns out I found a solution, which I will jsut post here in case anyone else has this same issue in the future.
Basically, it all seems to work better in the case of a line plot if you just specify both the labels and the ticks in the same place at the same time. At least that was helpful for me. It sort of forces you to keep the length of those two lists the same, which seems to make the assignment between ticks and labels more well behaved (simple 1:1 in this case).
So I coudl fix my problem by including something like this:
plt.xticks([0, 1], ['A','B'] )
right after the xlim statement in code from original question. Now the A and B align perfectly with the place where the data is plotted, not offset from it.
Using above solution it works, but is less good-looking since now the x grid is very coarse (this is purely and aesthetic consideration). I could fix that by using a different xtick statement like:
plt.xticks([-0.2, 0, 0.2, 0.4, 0.6, 0.8, 1.0], ['','A','','','','','B',''])
This gives me nice looking grid and the data where I need it, but of course is very contrived-looking here. In the actual program I'd find a way to make that less clunky.
Hope that is of some help to fellow seekers....