How can I "fake" the axis ticks in matplotlib? - python

I have a plot in matplotlib that looks like this:
I also have a different plot that looks like this:
What I want to do is create a plot that looks like this:
That is, I want to keep the data points exactly where they are in the first plot, but replace the x axis ticks and labels with the ones from the second plot. (In this case they are generated with plt.xscale('symlog').) I can't seem to find a straightforward way to do this. Is there one?
Note: it shouldn't really need saying, but the plot in this question isn't my actual plot, and I do have a reason to want to do this. I'm happy to clarify the question I'm asking, but I don't think it's helpful to ask for the details of why I need it. The question is about how to do the specific thing I've asked how to do, and on-topic responses are appreciated.

I looks to me that this question asks for a workaround in order to avoid transforming the data to the desired scale. This should be pretty straight forward by applying the inverse function to the data before plotting. Yet, there is not enough information given in the question as to what the problem would be for such solution.
Therefore one can only answer the question as it is; this basically translates into creating a completely unrelated axis.
import numpy as np
import matplotlib.pyplot as plt
x,y = np.random.rand(2,30)
fig, ax = plt.subplots()
ax.plot(x,y, ls="", marker="+")
ax2 = ax.twiny()
ax2.set_xscale("symlog")
ax2.tick_params(axis="x", which="both", bottom=True, labelbottom=True,
top=False, labeltop=False)
ax.tick_params(axis="x", bottom=False, labelbottom=False)
ax2.set_xlim(-50,50)
plt.show()
Here the x limits are chosen arbitrarily. The problem will be to relate the limits to the original data. Due to a 5% default margin on both sides, this will in general not be trivial and any function doing this task will become much more complicated than a function computing the inverse transform on the original data.

you can simply set the labels of the xticks like this.
plt.xticks(np.arange(3), [10, 10^2, 10^5])

In case anyone has the same problem in the future, here's a solution that works. I don't know if it's the best way.
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
# use ax1 to plot the data
ax1.axes("off")
ax2 = fig.add_axes(ax1.get_position())
ax2.patch.set_alpha(0)
# then do whatever you want to set up the axes of ax2. In my case:
ax1_xlim = ax1.get_xlim()
ax2.set_xlim([-np.power(10,-ax1_xlim[0]), np.power(10,ax1_xlim[1])])
I do not understand why all the comments and other answers were so focused on the 'why' of this simple task. I hope this will be helpful to others in spite of all the noise.

ImportanceOfBeingErnest said this is a rescaling problem, and I agree with him.
General principle
From what I understood, your are trying to do something like :
Plot your function with 'vanilla' unit => y = f(x)
Express the x axis in another unit => x [unit] = x' [deg]
Plot your function with new unit => y = f(x')
Choose nice xlim to have the point at the 'same location' as before (see ImportanceOfBeingErnest answer)
NB : you can write x' = g(x) another function and then have y = f ( g(x) )
All you need to know is this g function in order to rescale.
I repeat, I see this problem as a unit change problem. So the answer depends on the problem, its nature, its parameters. This is what hidden behind this g function.
Nearly the answer you need
In your case, according to the plot you shown, I can give you an approximate answer :
x' = 10 * ( x - 0.5)
np.semilogx(x',y)
0.5 because the new 0 seems to be where you had 0.5. I substract in order to center the new points in 0.5.
10 * because you transform 0.1 into 10^0.

Related

Matplotlib - Plot content vanishes using plt.yscale('log') [duplicate]

I am currently using logscale in order to have greater possibilities of plotting my data. Nevertheless, my data consists also of zero values. I know that these zero values will not work on logscale as log(0) is not defined.
So e.g.,
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot([0,1,2],[10,10,100],marker='o',linestyle='-')
ax.set_yscale('log')
ax.set_xscale('log')
completely omits the zero value. Is this behavior acceptable? At least there should be some kind of warning. I only recognized it by accident. Is there maybe also a way of plotting zero value data in logscale?
Thanks!
P.S.: I hope this fits to stackoverflow. I did not find a mailing list of matplotlib.
It's easiest to use a "symlog" plot for this purpose. The interval near 0 will be on a linear scale, so 0 can be displayed.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([0,1,2],[10,10,100],marker='o',linestyle='-')
ax.set_yscale('symlog')
ax.set_xscale('symlog')
plt.show()
Symlog sets a small interval near zero (both above and below) to use a linear scale. This allows things to cross 0 without causing log(x) to explode (or go to -inf, rather).
There's a nice visual comparison as an SO answer here: https://stackoverflow.com/a/3513150/325565

is it possible to use a non gaussian kernel for the two lateral distributions in seaborn jointplot

My data look like:
s1 = sns.jointplot(data.columns[i],
data.columns[j],
data=data,
space=0, color="b", stat_func=None)
if I use kde instead
s1 = sns.jointplot(data.columns[i],
data.columns[j],
data=data, kind = 'kde',
space=0, color="b", stat_func=None)
I am quite happy with the two dimensional kde interpolation, less with the lateral one. The problem is both placed so close together actually suggest the maximum of the distribution lying at two different points which might be quite misleading.
So now the actual question: is it possible to specify something different from gaussian as a kernel (blue) for the two lateral distributions? (I know that gaussian is thw only option in 2D). Because for example 'biw' (green) might esthetically look better (I am still not convinced that it is mathematically speaking a good thing to place the interpolations done with the different kernel close together making them seem the same thing...). So my question is whether I can specify the different kernel somewhere in sns.jointplot or is the only way to overwrite the lateral distribution by anotherone calculated in a second moment.
ax1 = sns.distplot(data[data.columns[j]])
sns.kdeplot(data[data.columns[j]], kernel= 'biw', ax = ax1)
You can set a different kernel for the marginal plots:
s1 = sns.jointplot(data.columns[i],
data.columns[j],
data=data, kind = 'kde',
space=0, color="b", stat_func=None,
marginal_kws={"kernel":"biw"}) # like this
or, if you want to change just one marginal plot, you can replot on them:
s1.ax_marg_y.cla() # clear axis
sns.kdeplot(data.y, ax=s1.ax_marg_y, # choose the ax
kernel="biw", # choose your kernel
legend=0, # remove the legend
vertical=True) # swap axis
vertical=True allows you to switch x and y axis, ie not needed if you change the top-margin plot.

Reduce space between first histogram bar and y-axis

I have a histogram shown here which I made using the following:
import pylab as pl
fd = FreqDist(list(industries))
X = np.arange(len(fd))
pl.bar(X, fd.values(), align='center', width=0.15)
pl.xticks(X, fd.keys(), rotation=90)
pl.tick_params(labelsize=8)
ymax = max(fd.values()) + 1
pl.ylim(0, ymax)
pl.subplots_adjust(bottom=0.3)
pl.savefig('internalDoorCount.jpg')
However I need the gap to reduce between the y-axis and the first histogram bar. Also how do you prevent overlapping of text?
You can try to avoid overlapping of the text by using this function:
pl.gcf().autofmt_xdate(bottom=0.3, rotation=-30, ha="left")
It's created for rotating date tick labels, but it should work good here. But you most probably have to either reduce the font size, and/or increase the width of your figure.
Assuming pl is matplotlib.pyplot, use pl.xlim. Because I'm not sure what values your x-axis takes, try
pl.xlim(min(X), max(X))
I cannot upvote and I'm amazed how old answers here are still helpful. So, as I still don't have points to comment, I'm answering here to correct a typo from #wflynny and say his answer is simple and works. The actual beginning of the code is "plt", instead of "pl":
plt.xlim(min(x),max(x))
The complete documentation is here.

physically stretch plot in horizontal direction in python

I want a simple x,y plot created with matplotlib stretched physically in x-direction.
The intention is to get a result were it is easier for me to detect features in the signal.
So I don't want to change any scales or values or limits. Just change the distance between two gridpoint in my output file...
I want to do that on four subplots which should have the same size afterwards.
Thanks in advance... I tried for hours now and I think one of you could probably help me...
David Zwicker already solved my problem in this special case, thanks a lot for that, but in general... If I plot 2 subplots like in this code:
fig = plt.figure()
ax1 = fig.add_subplot(1,2,1)
plot(u_av,z)
ax2 = fig.add_subplot(1,2,2)
plot(pgrd_av,z)
clf()
and want to stretch only one of them. What can I do?
You can change the figure size by using plt.figure(figsize=(20,5)). See the documentation of the figure command.
I know, this is a bit out of the context. But if someone is looking for a solution while using pandas plot which internally uses matplotlib. Here is the solution.
df.plot('col_x', 'col_y', title='stretched_plot', figsize=(20, 1))
You can directly add axes to the canvas at an arbitrary position with plt.axes(). For instance:
ax1 = plt.axes([0, 0, 3, 0.5])
ax2 = plt.axes([0, 0.6, 1, 1])
You can do this:
x = 1.5 # or your needed amount
plt.plot(x_array * x, y_array)
Your line or graph will move to the right depending on your x value

Matplotlib/pyplot: How to enforce axis range?

I would like to draw a standard 2D line graph with pylot, but force the axes' values to be between 0 and 600 on the x, and 10k and 20k on the y. Let me go with an example...
import pylab as p
p.title(save_file)
p.axis([0.0,600.0,1000000.0,2000000.0])
#define keys and items elsewhere..
p.plot(keys,items)
p.savefig(save_file, dpi=100)
However, the axes still adjust to the size of the data. I'm interpreting the effect of p.axis to be setting what the max and min could be, not enforcing them to be the max or min. The same happens when I try to use p.xlim() etc.
Any thoughts?
Thanks.
Calling p.plot after setting the limits is why it is rescaling. You are correct in that turning autoscaling off will get the right answer, but so will calling xlim() or ylim() after your plot command.
I use this quite a lot to invert the x axis, I work in astronomy and we use a magnitude system which is backwards (ie. brighter stars have a smaller magnitude) so I usually swap the limits with
lims = xlim()
xlim([lims[1], lims[0]])
To answer my own question, the trick is to turn auto scaling off...
p.axis([0.0,600.0, 10000.0,20000.0])
ax = p.gca()
ax.set_autoscale_on(False)
I tried all of those above answers, and I then summarized a pipeline of how to draw the fixed-axes image. It applied both to show function and savefig function.
before you plot:
fig = pylab.figure()
ax = fig.gca()
ax.set_autoscale_on(False)
This is to request an ax which is subplot(1,1,1).
During the plot:
ax.plot('You plot argument') # Put inside your argument, like ax.plot(x,y,label='test')
ax.axis('The list of range') # Put in side your range [xmin,xmax,ymin,ymax], like ax.axis([-5,5,-5,200])
After the plot:
To show the image :
fig.show()
To save the figure :
fig.savefig('the name of your figure')
I find out that put axis at the front of the code won't work even though I have set autoscale_on to False.
I used this code to create a series of animation. And below is the example of combing multiple fixed axes images into an animation.
Try putting the call to axis after all plotting commands.

Categories

Resources