physically stretch plot in horizontal direction in python - 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

Related

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

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.

Matplotlib set subplot axis size iteratively

There are several related questions (here, here, and here), but the suggested solutions don't work in my case.
I'm creating subplots iteratively, so I don't know ahead of time the width of each one (it gets calculated AFTER plt.subplots() is called), which means I can't set the size of each subplot when I initially create them.
I would like to set the size of the subplot x axis after it has already been created.
Imagine something like:
items = [A,B,C] #this could have any number of items in it
f,ax = plt.subplots(len(items),1, figsize=(10,10)) #figsize is arbitrary and could be anything
for i in range(len(items)):
#calculate x and y data for current item
#calculate width of x axis for current item
plt.sca(ax[i])
cax = plt.gca()
cax.plot(x,y)
#here is where I would like to set the x axis size
#something like cax.set_xlim(), but for the size, not the limit
Note 1: The units don't matter, but the relative size does, so it could be size in pixels, or centimeters, or even a ratio calculated based on the relative widths.
Note 2: The width of the x axis is NOT related in this case to the x limit, so I can't just set the x limit and expect the axis to scale correctly.
Also, I'm trying to keep this code short, since it's to be shared with people unfamiliar with Python, so if the only solution involves adding a bunch of lines, it's not worth it and I'll live with incorrectly scaled axes. This is an aesthetic preference but not a requirement.
Thanks!
EDIT: Here's what I'm aiming for
You can create a new GridSpec specifying the height_ratios and then updating each axs position:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
# create figure
f, ax = plt.subplots(3, 1, figsize=(10,10))
# plot some data
ax[0].plot([1, 2, 3])
ax[1].plot([1, 0, 1])
ax[2].plot([1, 2, 20])
# adjust subplot sizes
gs = GridSpec(3, 1, height_ratios=[5, 2, 1])
for i in range(3):
ax[i].set_position(gs[i].get_position(f))
plt.show()
I asked a similar question before here. The use case was slightly different, but it might still be helpful.
Surely now you got the answer or this problem is deprecated but if someone else is searching, I solved this problem using "Bbox". The idea is something like this:
from matplotlib.transforms import Bbox
fig, ax = plt.subplots(3,1, figsize = (11,15))
ax[0].set_position(Bbox([[0.125, 0.6579411764705883], [0.745, 0.88]]))
ax[2].set_position(Bbox([[0.125, 0.125], [0.745, 0.34705882352941175]]))
For more information, check https://matplotlib.org/api/transformations.html#matplotlib.transforms.Bbox

Matplotlib axes autoscale does not work with after twinx()

I have found that I can not get axes autoscale to work on the 1st axes after creating a second axes using twinx. Is this expected?
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = 0.05 * x**2
y2 = -1 *y1
fig, axL = plt.subplots() # Make Left Axes
axR = axL.twinx() # Make Left Axes
axL.plot(x, y1, 'g-') # Plot on Left
axL.grid()
axL.autoscale(enable=True, axis=u'both', tight=False)
plt.show()
# Do some stuff then later plot on axR
When I run the above code it autoscales in the y-direction correctly on the left axes (0 to 5) but changes the X-Axis scale to +/- 0.06 instead of the correct 0 to 10. However, once axR is no longer blank and something is plotted on axR it behaves as I would expect.
This is only an example as I first came across this issue in more complicated PyQT4 GUI that allows the user to create multiple subplots & left/right combinations. Since the user is the one manually controlling the plot creation order it is possible for the above situation to present itself.
Is there a way for autoscale to work with a blank twinx right axes. Or is the Xlimit just going to have to be manually set?
FYI, I am using Python 3.4 as part of Anaconda v2.0.1 with Matplotlib v1.3.1
Thanks.
This is merely a workaround than a proper solution or explanation.
Simply add an invisible point in the right axes so it is not completely empty:
axR.plot(0, 0, visible=False)
You have to make sure though, that the invisible point lies within the ranges of the data that you plot in axL. E.g.:
axR.plot(np.mean(x),np.mean(y1),visible=False)
As for an explanation (I'm guessing):
axR.dataLim is [-np.inf, np.inf] initially. The union of axR.dataLim and axL.dataLim still gives [-np.inf, np.inf] which is then collapsed to [0,0].
EDIT: This was fixed recently (here). Upgrading to matplotlib v1.4.* should solve the problem.

ValueError when using matplotlib tight_layout()

Ok, this is my first time asking a question on here, so please be patient with me ;-)
I'm trying to create a series of subplots (with two y-axes each) in a figure using matplotlib and then saving that figure. I'm using GridSpec to create a grid for the subplots and realised that they're overlapping a little, which I don't want. So I'm trying to use tight_layout() to sort this out, which according to the matplotlib documentation should work just fine. Simplifying things a bit, my code looks something like this:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
fig = plt.figure(num=None, facecolor='w', edgecolor='k')
grid = gridspec.GridSpec(2, numRows)
# numRows comes from the number of subplots required
# then I loop over all the data files I'm importing and create a subplot with two y-axes each time
ax1 = fig.add_subplot(grid[column, row])
# now I do all sorts of stuff with ax1...
ax2 = ax1.twinx()
# again doing some stuff here
After the loop for data processing is done and I have created all the subplots, I eventually end with
fig.tight_layout()
fig.savefig(str(location))
As far as I can work out, this should work, however when calling tight_layout(), I get a ValueError from the function self.subplotpars: left cannot be >= right. My question is: How do I figure out what's causing this error and how do I fix it?
I've had this error before, and I have a solution that worked for me. I'm not sure if it will work for you though. In matplotlib, the command
plt.fig.subplots_adjust()
can be used to sort of stretch the plot. The left and bottom stretch more the smaller the number gets, while the top and right stretch more the greater the number is. So if left is greater than or equal to the right, or bottom is greater than or equal to the top, than the graph would kind of flip over. I adjusted my command to look like this:
fig = plt.figure()
fig.subplots_adjust(bottom = 0)
fig.subplots_adjust(top = 1)
fig.subplots_adjust(right = 1)
fig.subplots_adjust(left = 0)
Then you can fill in your own numbers to adjust this, as long as you keep the left and bottom smaller. I hope this fixes your problem.

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