Format the x-axis ticks - python

I've got the following code that generates a surface density plot.
x and y are position co=ordinates and z axis represents the density. All the values are pre calculated and is stored in a numpy array.
#set up the grid
xi, yi = np.linspace(x.min(), x.max(), 200), np.linspace(y.min(), y.max(), 200)
xi, yi = np.meshgrid(xi, yi)
#interpolate
rbf = scipy.interpolate.Rbf(x, y, z, function='linear')
zi = rbf(xi, yi)
plt.imshow(zi, vmin=z.min(), vmax=z.max(), origin='lower', extent=[x.min(), x.max(), y.min(), y.max()])
plt.scatter(x, y, c=z,marker='o')
plt.colorbar()
plt.scatter(xo,yo, c='b', marker='*')
plt.xlabel("RA(degrees)")
plt.ylabel("DEC(degrees)")
plt.title('Surface Density Plot 2.0 < z < 2.2')
plt.savefig('2.0-2.2.png', dpi= 300 )
plt.show()
The problem I have is the xaxis ticks are not in user friendly terms, they are values between 150-152 but I can't seem to change the ticks positions using the xticks() function.
Would anyone have a suggestion how I can go about to formatting the x axis?
edit-
These are the values for xyz used for the plot. x,y,z are three numpy arrays- https://www.dropbox.com/s/l03pkzplqmfm1su/xyz.csv
the first row is x values, second the y and third the z.

When using the pyplot interface, you can set the xticks via (provided you imported matplotlib.pyplot as plt)
plt.xticks(*args, **kwargs)
You can give the ticks-locations eg. as a list or a numpy array and the tick-labels as a touple (or list, ...).
However, please include a minimal example of code that we can run, so we can test if it's working and see why not, if that's the case. Also, you seem to have imported matplotlib as plt, but some of your commands (like xlabel) lack the plt. part. Is this just a typo or copy/paste error?
If you want more fine-tuning for your ticks and the tick-format, you should consider using the OO interface of matplotlib. Yes, it's more verbose and you have to type some more letters, but in my opinion the code gets much clearer and you have more possibilities to adapt the graph to your expectations.
Edit: As I understand from your comments, you are not satisfied with the format of the xtick labels. So instead of "0.0" "+1.5e2" you probably want to have "150.0" or so. The function to look into (using the pyplot interface) is:
plt.ticklabel_format(**kwargs)
The kwargs are shown here here. You should try, if style='plain' fits your demands.
Again I want to stress, that the OO interface grants you far more versatility to change the format of the tick labels. The respective functions would be:
matplotlib.axes.yaxis.set_major_formatter()
matplotlib.axes.xaxis.set_major_formatter()
You can choose between several formatters or even write your own formating function. If you want to do that, I can give you further advice.

Firefly, based on the dropbox image you have given in the comments I believe the following describes your problem. The magnitude of the x data is much larger than the variations, so python has a list of values like
[150.05,150.10,150.15,150.20,150.25]
which is too large for the xaxis in this figure so python does some clever business which you do not like (and I agree).
One fix could be to simply set the xticks vertical e.g
py.xticks(rotation='vertical')
Failing that you could manually do what python has attempted, subtract 150 degrees from the x axis and change your xlabel such that you have
plt.xlabel("RA+150(degrees)")
If your data was not degrees I would instead suggest rescaling instead (e.g divide by 1e2) but with degrees this looks very strange.

Related

Best way to plot 3D data over a large range in Python

I have some function f(x,y) --> z
The range of x is small, typically ~0.1 -1
The range of y is large, typically 1e8 - 1e14
The output of the function z also has a large range, from ~ 1e25 - 1e45
What is the best way to plot this function in Python?
Clearly I need some kind of logarithmic axes, but log scaling 3d plots in matplotlib lib is an issue (see here)
I have tried a colorbar plot, e.g.
fig, ax = plt.subplots()
x = np.linspace(1e8,1e14,500)
y = np.linspace(0.01,1,500)
X,Y = np.meshgrid(x,y)
Z = process(X,Y).T
im = plt.imshow(Z,cmap=cm.RdBu,norm=LogNorm())
im.set_interpolation('bilinear')
cb = fig.colorbar(im)
But the colours tend to 'wash out', e.g. the plot is mostly composed of just one colour and the x/y ticks go weird due to the normalisation which I have not found a way to fix.
Any ideas?

Plotting KDE with logarithmic x-data in Matplotlib

I want to plot a KDE for some data with data that covers a large range in x-values. Therefore I want to use a logarithmic scale for the x-axis. For plotting I was using seaborn and the solution from Plotting 2D Kernel Density Estimation with Python, both of which fail once I set the xscale to logarithmic. When I take the logarithm of my x-data beforehand, everything looks fine, except the tics and ticlabels are still linear with the logarithm of the actual values as the labels. I could manually change the tics using something like:
labels = np.array(ax.get_xticks().tolist(), dtype=np.float64)
new_labels = [r'$10^{%.1f}$' % (labels[i]) for i in range(len(labels))]
ax.set_xticklabels(new_labels)
but in my eyes that looks just wrong and is nothing close to the axis labels (including the minor tics) when I would just use
ax.set_xscale('log')
Is there an easier way to plot a KDE with logarithmic x-data? Or is it possible to just change the tic- or label-scale without changing the scaling of the data, so that I could plot the logarithmic values of x and change the scaling of the labels afterwards?
Edit:
The plot I want to create looks like this:
The two right columns are what it is supposed to look like. There I used the the x data with the logarithm already applied. I don't like the labels on the x-axis, though.
The left column displays the plots, when the original data is used for the kde and all the other plots, and afterwards the scale is changed using
ax.set_xscale('log')
For some reason the kde, does not look like it is supposed to look. This is also not a result of erroneous data, since it looks just fine if the logarithmic data is used.
Edit 2:
A working example of code is
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
data = np.random.multivariate_normal((0, 0), [[0.8, 0.05], [0.05, 0.7]], 100)
x = np.power(10, data[:, 0])
y = data[:, 1]
fig, ax = plt.subplots(2, 1)
sns.kdeplot(data=np.log10(x), data2=y, ax=ax[0])
sns.kdeplot(data=x, data2=y, ax=ax[1])
ax[1].set_xscale('log')
plt.show()
The ax[1] plot is not displayed correctly for me (the x-axis is inverted), but the general behavior is the same as for the case described above. I believe the problem lies with the bandwidth of the kde, which should probably account for the logarithmic x-data.
I found an answer that works for me and wanted to post it in case someone else has a similar problem.
Based on the accepted answer from this post, I defined a function that first applies the logarithm to the x-data and after the KDE was performed, transforms the x-values back to the original values. Afterwards I can simply plot the contours and use ax.set_xscale('log')
import numpy as np
import scipy.stats as st
def logx_kde(x, y, xmin, xmax, ymin, ymax):
x = np.log10(x)
# Peform the kernel density estimate
xx, yy = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([xx.ravel(), yy.ravel()])
values = np.vstack([x, y])
kernel = st.gaussian_kde(values)
f = np.reshape(kernel(positions).T, xx.shape)
return np.power(10, xx), yy, f

Setting both axes logarithmic in bar plot matploblib

I have already binned data to plot a histogram. For this reason I'm using the plt.bar() function. I'd like to set both axes in the plot to a logarithmic scale.
If I set plt.bar(x, y, width=10, color='b', log=True) which lets me set the y-axis to log but I can't set the x-axis logarithmic.
I've tried plt.xscale('log') unfortunately this doesn't work right. The x-axis ticks vanish and the sizes of the bars don't have equal width.
I would be grateful for any help.
By default, the bars of a barplot have a width of 0.8. Therefore they appear larger for smaller x values on a logarithmic scale. If instead of specifying a constant width, one uses the distance between the bin edges and supplies this to the width argument, the bars will have the correct width. One would also need to set the align to "edge" for this to work.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
x = np.logspace(0, 5, num=21)
y = (np.sin(1.e-2*(x[:-1]-20))+3)**10
fig, ax = plt.subplots()
ax.bar(x[:-1], y, width=np.diff(x), log=True,ec="k", align="edge")
ax.set_xscale("log")
plt.show()
I cannot reproduce missing ticklabels for a logarithmic scaling. This may be due to some settings in the code that are not shown in the question or due to the fact that an older matplotlib version is used. The example here works fine with matplotlib 2.0.
If the goal is to have equal width bars, assuming datapoints are not equidistant, then the most proper solution is to set width as
plt.bar(x, y, width=c*np.array(x), color='b', log=True) for a constant c appropriate for the plot. Alignment can be anything.
I know it is a very old question and you might have solved it but I've come to this post because I was with something like this but at the y axis and I manage to solve it just using ax.set_ylim(df['my data'].min()+100, df['my data'].max()+100). In y axis I have some sensible information which I thouhg the best way was to show in log scale but when I set log scale I couldn't see the numbers proper (as this post in x axis) so I just leave the idea of use log and use the min and max argment. It sets the scale of my graph much like as log. Still looking for another way for doesnt need use that -+100 at set_ylim.
While this does not actually use pyplot.bar, I think this method could be helpful in achieving what the OP is trying to do. I found this to be easier than trying to calibrate the width as a function of the log-scale, though it's more steps. Create a line collection whose width is independent of the chart scale.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.collections as coll
#Generate data and sort into bins
a = np.random.logseries(0.5, 1000)
hist, bin_edges = np.histogram(a, bins=20, density=False)
x = bin_edges[:-1] # remove the top-end from bin_edges to match dimensions of hist
lines = []
for i in range(len(x)):
pair=[(x[i],0), (x[i], hist[i])]
lines.append(pair)
linecoll = coll.LineCollection(lines, linewidths=10, linestyles='solid')
fig, ax = plt.subplots()
ax.add_collection(linecoll)
ax.set_xscale("log")
ax.set_yscale("log")
ax.set_xlim(min(x)/10,max(x)*10)
ax.set_ylim(0.1,1.1*max(hist)) #since this is an unweighted histogram, the logy doesn't make much sense.
Resulting plot - no frills
One drawback is that the "bars" will be centered, but this could be changed by offsetting the x-values by half of the linewidth value ... I think it would be
x_new = x + (linewidth/2)*10**round(np.log10(x),0).

Matplotlib plot_surface transparency artefact

I'm trying to plot a surface in 3D from a set of data which specifies the z-values. I get some weird transparency artefact though, where I can see through the surface, even though I set alpha=1.0.
The artefact is present both when plotting and when saved to file (both as png and pdf):
I have tried changing the line width, and changing the number of strides from 1 to 10 (in the latter case, the surface is not visible though due to too rough resolution).
Q: How can I get rid of this transparency?
Here is my code:
import sys
import numpy as np
import numpy.ma as ma
import matplotlib as mpl
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
y_label = r'x'
x_label = r'y'
z_label = r'z'
x_scale = 2.0*np.pi
y_scale = 2.0*np.pi
y_numPoints = 250
x_numPoints = 250
def quasiCrystal(x, y):
z = 0
for i in range(0,5):
z += np.sin(x * np.cos(float(i)*np.pi/5.0) +
y * np.sin(float(i)*np.pi/5.0))
return z
x = np.linspace(-x_scale, x_scale, x_numPoints)
y = np.linspace(-y_scale, y_scale, y_numPoints)
X,Y = np.meshgrid(x,y)
Z = quasiCrystal(X, Y)
f = plt.figure()
ax = f.gca(projection='3d')
surf = ax.plot_surface( X, Y, Z,
rstride=5, cstride=5,
cmap='seismic',
alpha=1,
linewidth=0,
antialiased=True,
vmin=np.min(Z),
vmax=np.max(Z)
)
ax.set_zlim3d(np.min(Z), np.max(Z))
f.colorbar(surf, label=z_label)
ax.set_xlabel(x_label)
ax.set_ylabel(y_label)
ax.set_zlabel(z_label)
plt.show()
Here is another picture of my actual data where it is easier to see the artefact:
Matplotlib is not a "real" 3D engine. This is a very well known problem and once in a while a similar question to yours appears appears (see this and this). The problem is that the same artefact can originate problems that seem to be different. I believe such is the case for you.
Before going on with my recommendations let me just quote this information from the maplotlib website:
My 3D plot doesn’t look right at certain viewing angles
This is probably the most commonly reported issue with mplot3d. The problem is
that – from some viewing angles – a 3D object would appear in front of
another object, even though it is physically behind it. This can
result in plots that do not look “physically correct.”
Unfortunately, while some work is being done to reduce the occurance
of this artifact, it is currently an intractable problem, and can not
be fully solved until matplotlib supports 3D graphics rendering at its
core.
The problem occurs due to the reduction of 3D data down to 2D +
z-order scalar. A single value represents the 3rd dimension for all
parts of 3D objects in a collection. Therefore, when the bounding
boxes of two collections intersect, it becomes possible for this
artifact to occur. Furthermore, the intersection of two 3D objects
(such as polygons or patches) can not be rendered properly in
matplotlib’s 2D rendering engine.
This problem will likely not be solved until OpenGL support is added
to all of the backends (patches are greatly welcomed). Until then, if
you need complex 3D scenes, we recommend using MayaVi.
It seems that Mayavi has finally moved on to Python 3, so its certainly a possibility. If you want to stick with matplotlib for this kind of plot my advice is that you work with rstride and cstride values to see which ones produce a plot satisfactory to you.
surf = ax.plot_surface( X, Y, Z,
rstride=5, cstride=5,
cmap='jet',
alpha=1,
linewidth=0,
antialiased=True,
vmin=0,
rstride=10,
cstride=10,
vmax=z_scale
)
Other possibility is to try to see if other kinds of 3D plots do better. Check plot_trisurf, contour or contourf. I know its not ideal but in the past I also managed to circumvent other type of artefacts using 3D polygons.
Sorry for not having a more satisfactory answer. Perhaps other SO users have better solutions for this. Best of luck.
I ran into some similar issues and found that they were antialiasing artifacts and could be fixed by setting antialiased=False in plot_surface.

How to make colorbar display set limits with 3D data

I've found many examples for this using features like pcolor and clim, but unless I am misusing them, they seem to only want to work for 2 dimensional data with x, y, values.
My data is formatted as follows:
x y z values
Before I get to plotting in the script I am performing an interpolation which gives me newly gridded data, but with no change to the range of values, meaning a single colorbar will suffice between the two figures. This interpolated data is formatted as follows:
xi yi zi interp
The code I am using to plot it is as follows:
fig = plt.figure()
ax = fig.add_subplot(121, projection = '3d')
ax.scatter(xi, yi, zi, c=interp, alpha=0.08, edgecolors='none'
ax=fig.add_subplot(122, projection = '3d')
s = ax.scatter(x, y, z, c=values, alpha=0.3, edgecolors='none'
plt.colorbar(s)
All of this works just fine, however the problem arises when I load in a separate data-set with similar, but not identical range of values. The colorbars between the two data-sets display different ranges which is not ideal to draw comparisons between the two. All I am looking to do is to forcibly set the min (2100) and max (2600) for the colorbar so that I can apply it to any and all data-sets.
Sorry if this is an easy question, but as I said, I can't seem to find a solution that works for 3D data.
Cheers, Vlad.
I think you are looking for vmin and vmax , for example
ax.scatter(x, y, z, c=values, alpha=0.3, edgecolors='none',vmin=2100,vmax=2600)

Categories