I try to plot some curves with matplotlib using the default gui component and have some trouble to select which of the two y-axes that the mouse over functionality should select. The default case seems to be that ax2 gets selected but I would like to use ax1 instead. Is this possible to fix in some easy way?
This is the code I use at the moment to plot my curves.
Best regards Anders Olme
delta=np.median(np.diff(measurementvalues.measvalues))
myscale=10
myrange=(measurementvalues.lowerlimit - delta*myscale, measurementvalues.upperlimit + delta*myscale)
figure = plt.figure()
ax1 = figure.add_subplot(111)
(n, bins, patches) = ax1.hist(measurementvalues.measvalues, 10, range=myrange, normed=0, facecolor='green', alpha=0.75)
ax2 = ax1.twinx()
mean = np.average(measurementvalues.measvalues)
sigma = np.std(measurementvalues.measvalues)
y = mlab.normpdf(bins, mean, sigma)
ax2.plot(bins, y, 'r-', linewidth=1)
ax1.set_xlabel('Measvlues')
ax2.set_ylabel('Probability')
ax1.set_title(r'$\mathrm{Histogram\ of\ measvalues:}\ \mu=$'+str(mean)+r'$,\ \sigma=$'+str(sigma)+r'$$')
plt.grid(True)
plt.show()
Add the following after calling twinx
ax3 = ax1.figure.add_axes(ax1.get_position(True), sharex=ax1, sharey=ax1,
frameon=False)
ax3.xaxis.set_visible(False)
ax3.yaxis.set_visible(False)
You will also need to change plt.grid(True) to ax1.grid(True)
Related
I just specify the x and y axis limitations but the numbers' order is wrong. how can I fix this?
here is my code:
fig, ax = plt.subplots(figsize=(20,10))
ax.plot(df.finish_price, label="Stock Values", color = 'blue')
plt.ylabel("Price", color='b')
# Generate a new Axes instance, on the twin-X axes (same position)
ax2 = ax.twinx()
ax2.plot(df.sentiment, label= 'Sentiment', color='green')
ax2.tick_params(axis='y', labelcolor='green')
plt.ylim(bottom = -1)
plt.ylim(top=1)
plt.xlabel("Days")
plt.ylabel("Sentiment", color='g')
fig.legend()
plt.show()
and here is the result:
as you can see the numbers' order on the right y-axis is wrong.
I want show three plots with the same data but in different kinds of plots.
The following Code would show them one below the other:
plt.figure()
sns.displot(t['Age'], kind="kde", rug = True)
sns.displot(t['Age'], kind="hist", bins = 25)
sns.displot(t['Age'], kind="ecdf")
plt.show()
With pyplot plots and the none-OO style i could do something like this:
plt.figure(figsize=(12,4))
plt.subplot(131)
plt.hist(t['Age'], bins = 22)
plt.subplot(132)
plt.hist(t['Age'], bins = 33)
plt.subplot(133)
plt.hist(t['Age'], bins = int(t['Age'].max() - t['Age'].min()))
plt.show()
Which looks perfectly nice.
Here you can see
Why cant i just do this?
plt.figure(figsize=(12,4))
plt.subplot(131)
sns.displot(t['Age'], kind="kde", rug = True)
plt.subplot(132)
sns.displot(t['Age'], kind="hist", bins = 25)
plt.subplot(133)
sns.displot(t['Age'], kind="ecdf")
plt.show()
This looks terrible
How it looks... it seems like they are stuck in the left border
Is it possible to do that without this oo notation?
fig, (ax1, ax2, ax3) = plt.subplots(1,3)
sns.displot(..., ax=ax1)
sns.displot(..., ax=ax2)
...
okok, in consequence of a short but very enlightening study of the seaborn api i get to a conclusion:
displot() is very flexible and you can do many kinds of graphs, which have interestingly each its own functions.
you can do something like this:
plt.figure(figsize=(16,4))
plt.subplot(141)
sns.kdeplot(t['Age'])
plt.subplot(142)
sns.histplot(t['Age'])
plt.subplot(143)
sns.ecdfplot(t['Age'])
plt.subplot(144)
sns.rugplot(t['Age'])
plt.show()
to avoid OO-Style.
Result
But in this case its not that easy to get the rugplot andd the kde plot to the same axes. When you try:
plt.figure(figsize=(12,4))
plt.subplot(131)
sns.kdeplot(t['Age'])
plt.subplot(132)
sns.histplot(t['Age'])
plt.subplot(133)
sns.ecdfplot(t['Age'])
plt.subplot(131)
sns.rugplot(t['Age'])
plt.show()
You get this warning:
So you shouldnt use it because of this warning.
If you want to make it look beautiful you need more controol and OO-Style is probably best solution:
fig, axes = plt.subplots(1,3, figsize=(12,4))
sns.kdeplot(t['Age'], ax = axes[0])
sns.histplot(t['Age'], ax = axes[1])
sns.ecdfplot(t['Age'], ax = axes[2])
sns.rugplot(t['Age'], ax = axes[0])
plt.show()
Very nice
I was trying to make a graph with two secondary vertical axis (y-axis), with python matplotlib.
I was using twinx() method, where one of the two new axis is with the default behavior (labels on the right)
and the other with labels on the left, like the example of tan(x) on the figure bellow (created in an specific software).
Is there an easy way to do that? I'm not restricted to use the twinx() method, if there is another way
Here is a way to add two secondary y-axis, one towards the inside:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(.5, 10, 1000)
y1 = np.cos(x)
y2 = np.sin(2 * x)
y3 = np.clip(np.tan(x * .6), -75, 75)
fig, ax1 = plt.subplots()
color = 'dodgerblue'
ax1.set_ylabel('$cos(x)$', color=color)
ax1.plot(x, y1, color=color)
ax1.tick_params(axis='y', labelcolor=color)
ax2 = ax1.twinx()
color = 'limegreen'
ax2.set_ylabel('$sin(2 x)$', color=color)
ax2.plot(x, y2, color=color)
ax2.tick_params(axis="y", labelcolor=color)
ax3 = ax1.twinx()
color = 'crimson'
ax3.set_ylabel('$tan(.6 x)$', color=color, labelpad=-40)
ax3.plot(x, y3, color=color)
ax3.tick_params(axis="y", labelcolor=color, direction="in", pad=-5)
plt.setp(ax3.get_yticklabels(), ha="right")
ax1.set_xlim(0, 12)
fig.tight_layout()
plt.show()
In pyplot, you can change the order of different graphs using the zorder option or by changing the order of the plot() commands. However, when you add an alternative axis via ax2 = twinx(), the new axis will always overlay the old axis (as described in the documentation).
Is it possible to change the order of the axis to move the alternative (twinned) y-axis to background?
In the example below, I would like to display the blue line on top of the histogram:
import numpy as np
import matplotlib.pyplot as plt
import random
# Data
x = np.arange(-3.0, 3.01, 0.1)
y = np.power(x,2)
y2 = 1/np.sqrt(2*np.pi) * np.exp(-y/2)
data = [random.gauss(0.0, 1.0) for i in range(1000)]
# Plot figure
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax2 = ax1.twinx()
ax2.hist(data, bins=40, normed=True, color='g',zorder=0)
ax2.plot(x, y2, color='r', linewidth=2, zorder=2)
ax1.plot(x, y, color='b', linewidth=2, zorder=5)
ax1.set_ylabel("Parabola")
ax2.set_ylabel("Normal distribution")
ax1.yaxis.label.set_color('b')
ax2.yaxis.label.set_color('r')
plt.show()
Edit: For some reason, I am unable to upload the image generated by this code. I will try again later.
You can set the zorder of an axes, ax.set_zorder(). One would then need to remove the background of that axes, such that the axes below is still visible.
ax2 = ax1.twinx()
ax1.set_zorder(10)
ax1.patch.set_visible(False)
The question is bettered explained with examples. Let's say below is a figure I tried to plot:
So the figure region is square in shape, and there are axis labels explaining the meaning of the values. In MATLAB the code is simple and works as expected.
t = linspace(0, 2*pi, 101);
y = sin(t);
figure(1)
h = gcf;
set(h,'PaperUnits','inches');
set(h,'PaperSize',[3.5 3.5]);
plot(t, y)
xlim([0, 2*pi])
ylim([-1.1, 1.1])
xlabel('Time (s)')
ylabel('Voltage (V)')
axis('square')
Now let's work with Python and Matplotlib. The code is below:
from numpy import *
from matplotlib import pyplot as plt
t = linspace(0, 2*pi, 101)
y = sin(t)
plt.figure(figsize = (3.5, 3.5))
plt.plot(t, y)
plt.xlim(0, 2*pi)
plt.ylim(-1.1, 1.1)
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.axis('equal') # square / tight
plt.show()
It does not work, see the first row of the figure below, where I tried three options of the axis command ('equal', 'square' and 'tight'). I wondered if this is due to the order of axis() and the xlim/ylim, they do affect the result, yet still I don't have what I want.
I found this really confusing to understand and frustrating to use. The relative position of the curve and axis seems go haywire. I did extensive research on stackoverflow, but couldn't find the answer. It appears to me adding axis labels would compress the canvas region, but it really should not, because the label is just an addon to a figure, and they should have separate space allocations.
I do not have a computer at hand right now but it seems this answer might work: https://stackoverflow.com/a/7968690/2768172 Another solution might be to add the axis with a fixed size manually with: ax=fig.add_axes(bottom, left, width, height). If width and height are the same the axis should be squared.
It doesn't explain the figures you obtain but here is one way to achieve square axes with the right axes limits. In this example, I just calculate the axes aspect ratio using the x and y range:
plt.figure()
ax = plt.axes()
ax.plot(t, y)
xrange = (0, 2*pi)
yrange = (-1.1, 1.1)
plt.xlim(xrange)
plt.ylim(yrange)
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
ax.set_aspect((xrange[1]-xrange[0])/(yrange[1]-yrange[0]))
plt.show()
Another method is to create a square figure and square axes:
plt.figure(figsize = (5, 5))
ax = plt.axes([0.15, 0.15, 0.7, 0.7])
ax.plot(t, y)
plt.xlim(0, 2*pi)
plt.ylim(-1.1, 1.1)
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.show()