Matplotlib: Plotting two graphs within a specified x-axis range - python

Basically I'm in a situation where I want to lock down the starting point of the graph depending on the first graph that's plotted.
e.g. If i do something like this.
import matplotlib.pyplot as plt
plt.plot([7,8,9,10], [1,4,9,16], 'yo')
plt.plot([1,9,11,12], [1,4,9,16], 'ro')
plt.show()
I would like a way to restrict the x-axis to start from 7 so (1,1) from the second plot will be removed.
Is there a way to do this? I could keep track of it myself but just curious if there's something built in to handle this.
Thanks.

Matplotlib offers you two ways:
import matplotlib.pyplot as plt
plt.plot([7,8,9,10], [1,4,9,16], 'yo')
plt.plot([1,9,11,12], [1,4,9,16], 'ro')
plt.xlim(xmin=7)
plt.show()
or the more object-oriented way
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([7,8,9,10], [1,4,9,16], 'yo')
ax.plot([1,9,11,12], [1,4,9,16], 'ro')
ax.set_xlim(xmin=7)
plt.show()
If you don't use IPython, I highly recommend it since you can create the axes object and then type ax.<Tab> and see all of your options. Autocomplete can be a wonderful thing in this case.

You can turn auto-scaling off after the first plot (doc):
ax = plt.gca()
ax.autoscale(enable=False)
which will lock down all of the scales (you can do x and y separately as well).

In short: plt.xlim().
In long:
import matplotlib.pyplot as plt
x1, y1 = ([7,8,9,10], [1,4,9,16])
plt.plot(x1, y1, 'yo')
plt.xlim(min(x1), max(x1))
plt.plot([1,9,11,12], [1,4,9,16], 'ro')
plt.show()

Related

How to add grid graph as a background of one graph plot?

I want to add grid graph as a background of a signal plot generated from the following code?
dff = pd.read_csv("abc.csv")
x = dff['A']
times = np.arange(len(x))/360
plt.subplot(121)
plt.plot(times, x)
plt.tight_layout()
plt.show()
What should I change in this code. Can anyone help me out.Thanks in advance!
To add gridlines to PyPlot plots, you can use the grid() tool:
import matplotlib.pyplot as plt
plt.subplot(121)
plt.plot()
plt.tight_layout()
plt.grid(axis='both')
plt.show()
You can see more about its arguments here.
Note that in matplotlib you might have a better time using figures with the "axes" object, rather than the functional interface. E.g.:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot()
ax.grid()
This allows you to index axs objects like lists, and pass plots as arguments.

How to change the frequency of labeling the x and y axis in matplotlib in python?

I am trying to plot a circle with a grid being shown. I wrote the following script which gives the below picture. However, the labels on the axes are interfering together. How to make the label appear (..,-10,-5,0,5,10,...) KEEPING the grid as it appears in the below figure?. I want to keep the dimension of the grid cell as 1*1 dimension.
I tried to use plt.locator_params(), but the dimension of the grid cell changed and became bigger.
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib.pyplot import figure
R1=28
n=64
t=np.linspace(0, 2*np.pi, n)
x1=R1*np.cos(t)
y1=R1*np.sin(t)
plt.axis("square")
plt.grid(True, which='both', axis='both')
plt.xticks(np.arange(min(x1)-2,max(x1)+2, step=1))
plt.yticks(np.arange(min(y1)-2,max(y1)+2, step=1))
#plt.locator_params(axis='x', nbins=5)
#plt.locator_params(axis='y', nbins=5)
plt.plot(x1,y1)
plt.legend()
plt.show()
Not a matplotlib expert, so there may be a better way to do this, but perhaps like the following:
from matplotlib.ticker import MultipleLocator
...
fig, ax = plt.subplots(figsize=(6, 6))
ax.plot(x1,y1)
ax.xaxis.set_minor_locator(MultipleLocator())
ax.xaxis.set_major_locator(MultipleLocator(5))
ax.yaxis.set_minor_locator(MultipleLocator())
ax.yaxis.set_major_locator(MultipleLocator(5))
ax.grid(True, which='both', axis='both')
plt.show()

How can I ask matplotlib to slightly shift overlapping curves so they don't hide one another?

I am using matplotlib.pyplot to plot several curves on the same graph. Sometimes they have equal values, so the last one hides the others, like this:
import matplotlib.pyplot as plt
plt.figure()
plt.plot([1,2,3,4], label="up")
plt.plot([1,2,3,2.5], label="down")
plt.show()
I would like the curves to be slightly shifted so they don't hide one another, like this:
Is there any proper way to do this, without changing the actual values I am plotting?
Update:
The best answer so far (for my case) is that of theImportanceOfBeingErnest.
However, if I have n curves to plot, instead of just 2, I would have to compute the accumulated offset for each. But given the complex calculations that this answer gets into, I suppose there is no way for matplotlib to do this automatically?
PS: since my values are all multiples of 0.5, the (slight) shifting doesn't risk to create a confusion about the actual value...
I suppose the usual way of translating an artist with a size in points is to use
matplotlib.transforms.offset_copy. Since the default linewidth of lines is 1.5 points, one would translate the curve by approximately that number.
import matplotlib.pyplot as plt
import matplotlib.transforms as mtrans
fig, ax = plt.subplots()
ax.plot([1,2,3,4], label="up")
tr = mtrans.offset_copy(ax.transData, fig=fig, x=0.0, y=-1.5, units='points')
ax.plot([1,2,3,2.5], label="down", transform=tr)
plt.show()
Note that this works well in case the line is spread and without many up- and downs. A more sophisticated solution (but also largely more complicated) is found in In matplotlib, how can I plot a multi-colored line, like a rainbow
You can use alpha attribute.
import matplotlib.pyplot as plt
plt.figure()
plt.plot([1,2,3,4], label="up")
plt.plot([1,2,3,2.5], label="down", alpha=.3)
plt.legend()
plt.show()
Alter the ratio between [0, 1] in order to get best way you wanted. With this way you don't have to change your values.
In order to improve visibility you can add a "linewidth" attribute with "alpha". e.g.
import matplotlib.pyplot as plt
plt.figure()
plt.plot([1,2,3,4], label="up")
plt.plot([1,2,3,2.5], label="down", linewidth=4, alpha=.5)
plt.legend()
plt.show()
Change the values as you like.
I would use a transform that moves the points in the second plot downward slightly (you can also apply it to the first plot to move the points upward):
import matplotlib.pyplot as plt
from matplotlib import transforms
fig, ax = plt.subplots()
transform = transforms.Affine2D().translate(0, -0.03) + ax.transData
ax.plot([1, 2, 3, 4], label="up")
ax.plot([1, 2, 3, 2.5], label="down", transform=transform)
Another option would be to use a dashed line with a darker color that overlaps.
import matplotlib.pyplot as plt
plt.figure()
plt.plot([1,2,3,4], color="red", label="up", lw=2)
plt.plot([1,2,3,2.5], color="black", linestyle=":", label="down", lw=2)
You can simply scale the values
import matplotlib.pyplot as plt
plt.figure()
plt.plot([1,2,3,4], label="up")
plt.plot([x-0.02 for x in [1,2,3,2.5]], label="down")
plt.legend()
plt.show()

Showing a correct legend when doing scatter plot with palette

Stupid way to plot a scatter plot
Suppose I have a data with 3 classes, the following code can give me a perfect graph with a correct legend, in which I plot out data class by class.
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_blobs
import numpy as np
X, y = make_blobs()
X0 = X[y==0]
X1 = X[y==1]
X2 = X[y==2]
ax = plt.subplot(1,1,1)
ax.scatter(X0[:,0],X0[:,1], lw=0, s=40)
ax.scatter(X1[:,0],X1[:,1], lw=0, s=40)
ax.scatter(X2[:,0],X2[:,1], lw=0, s=40)
ax.legend(['0','1','2'])
Better way to plot a scatter plot
However, if I have a dataset with 3000 classes, the above method doesn't work anymore. (You won't expect me to write 3000 line corresponding to each class, right?)
So I come up with the following plotting code.
num_classes = len(set(y))
palette = np.array(sns.color_palette("hls", num_classes))
ax = plt.subplot(1,1,1)
ax.scatter(X[:,0], X[:,1], lw=0, s=40, c=palette[y.astype(np.int)])
ax.legend(['0','1','2'])
This code is perfect, we can plot out all the classes with only 1 line. However, the legend is not showing correctly this time.
Question
How to maintain a correct legend when we plot graphs by using the following?
ax.scatter(X[:,0], X[:,1], lw=0, s=40, c=palette[y.astype(np.int)])
plt.legend() works best when you have multiple "artists" on the plot. That is the case in your first example which is why calling plt.legend(labels) works effortlessly.
If you are worried about writing lots of lines of code then you can take advantage of for loops.
As we can see with this example using 5 classes:
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
import numpy as np
X, y = make_blobs(centers=5)
ax = plt.subplot(1,1,1)
for c in np.unique(y):
ax.scatter(X[y==c,0],X[y==c,1],label=c)
ax.legend()
np.unique() returns a sorted array of the unique elements of y, by looping through these and plotting each class with its own artist plt.legend() can easily provide a legend.
Edit:
You can also assign labels to the plots as you make them which is probably safer.
plt.scatter(..., label=c) followed by plt.legend()
Why not simply do the following?
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_blobs
import numpy as np
X, y = make_blobs()
ngroups = 3
ax = plt.subplot(1, 1, 1)
for i in range(ngroups):
ax.scatter(X[y==i][:,0], X[y==i][:,1], lw=0, s=40, label=i)
ax.legend()

How do I add a colorbar with log data in an image/matrix

I have a 2D matrix I want to plot. The plotting itself works, but I need
a colorbar with it. The figure only makes sense when the data is
log-tranformed. And I want the colorbar show the original values. How
do I do this?
A search provided
A logarithmic colorbar in matplotlib scatter plot
but I cannot make this work.
The code below gives an idea of what I attempt to do. Only the revevant
lines are included (as far as I could see).
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
my_speed=np.ones(shape=(no,no))
fig=plt.figure(2)
ax=fig.add_subplot(1,1,1)
my_speed=np.log10(my_speed)
ax.imshow(my_speed, interpolation='bilinear', cmap=cm.jet)
plt.colorbar() #this does not work
plt.savefig('myspeedplot.png')
plt.close(2)
Thank you for any help
The idea is not to transform your data, but let the visualization do the trick for you.
pylot.imshow[1] has an optional parameter norm that can do the log transformation for you.
my_speed=np.ones(shape=(no,no))
fig = plt.figure(2)
ax = fig.add_subplot(1,1,1)
# my_speed=np.log10(my_speed)
img = ax.imshow(my_speed, interpolation='bilinear', cmap=cm.jet,
norm=mpl.colors.LogNorm())
fig.colorbar(img)
As far as I see, there are two problems with your code.
First, you are trying to have the ticks on colorbar show original values. For this you should not transform the data, but just normalize the plot.
And second, you are using the ax.imshow and this is why the colorbar does not see it. You should use plt.imshow or use im=ax.imshow and then colorbar(im)
Here is a working solution:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
my_speed = np.random.rand(20, 20)
fig = plt.figure(2)
ax = fig.add_subplot(1, 1, 1)
im = ax.imshow(my_speed, interpolation='bilinear',
norm=mpl.colors.LogNorm(),
cmap=plt.cm.jet)
cb = plt.colorbar(im, orientation='vertical')
plt.show()

Categories

Resources