I have two graphs to where both have the same x-axis, but with different y-axis scalings.
The plot with regular axes is the data with a trend line depicting a decay while the y semi-log scaling depicts the accuracy of the fit.
fig1 = plt.figure(figsize=(15,6))
ax1 = fig1.add_subplot(111)
# Plot of the decay model
ax1.plot(FreqTime1,DecayCount1, '.', color='mediumaquamarine')
# Plot of the optimized fit
ax1.plot(x1, y1M, '-k', label='Fitting Function: $f(t) = %.3f e^{%.3f\t} \
%+.3f$' % (aR1,kR1,bR1))
ax1.set_xlabel('Time (sec)')
ax1.set_ylabel('Count')
ax1.set_title('Run 1 of Cesium-137 Decay')
# Allows me to change scales
# ax1.set_yscale('log')
ax1.legend(bbox_to_anchor=(1.0, 1.0), prop={'size':15}, fancybox=True, shadow=True)
Now, i'm trying to figure out to implement both close together like the examples supplied by this link
http://matplotlib.org/examples/pylab_examples/subplots_demo.html
In particular, this one
When looking at the code for the example, i'm a bit confused on how to implant 3 things:
1) Scaling the axes differently
2) Keeping the figure size the same for the exponential decay graph but having a the line graph have a smaller y size and same x size.
For example:
3) Keeping the label of the function to appear in just only the decay graph.
Any help would be most appreciated.
Look at the code and comments in it:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import gridspec
# Simple data to display in various forms
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig = plt.figure()
# set height ratios for subplots
gs = gridspec.GridSpec(2, 1, height_ratios=[2, 1])
# the first subplot
ax0 = plt.subplot(gs[0])
# log scale for axis Y of the first subplot
ax0.set_yscale("log")
line0, = ax0.plot(x, y, color='r')
# the second subplot
# shared axis X
ax1 = plt.subplot(gs[1], sharex = ax0)
line1, = ax1.plot(x, y, color='b', linestyle='--')
plt.setp(ax0.get_xticklabels(), visible=False)
# remove last tick label for the second subplot
yticks = ax1.yaxis.get_major_ticks()
yticks[-1].label1.set_visible(False)
# put legend on first subplot
ax0.legend((line0, line1), ('red line', 'blue line'), loc='lower left')
# remove vertical gap between subplots
plt.subplots_adjust(hspace=.0)
plt.show()
Here is my solution:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 400)
y = np.sin(x ** 2)
fig, (ax1,ax2) = plt.subplots(nrows=2, sharex=True, subplot_kw=dict(frameon=False)) # frameon=False removes frames
plt.subplots_adjust(hspace=.0)
ax1.grid()
ax2.grid()
ax1.plot(x, y, color='r')
ax2.plot(x, y, color='b', linestyle='--')
One more option is seaborn.FacetGrid but this requires Seaborn and Pandas libraries.
Here are some adaptions to show how the code could work to add a combined legend when plotting a pandas dataframe. ax=ax0 can be used to plot on a given ax and ax0.get_legend_handles_labels() gets the information for the legend.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
dates = pd.date_range('20210101', periods=100, freq='D')
df0 = pd.DataFrame({'x': np.random.normal(0.1, 1, 100).cumsum(),
'y': np.random.normal(0.3, 1, 100).cumsum()}, index=dates)
df1 = pd.DataFrame({'z': np.random.normal(0.2, 1, 100).cumsum()}, index=dates)
fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True, gridspec_kw={'height_ratios': [2, 1], 'hspace': 0})
df0.plot(ax=ax0, color=['dodgerblue', 'crimson'], legend=False)
df1.plot(ax=ax1, color='limegreen', legend=False)
# put legend on first subplot
handles0, labels0 = ax0.get_legend_handles_labels()
handles1, labels1 = ax1.get_legend_handles_labels()
ax0.legend(handles=handles0 + handles1, labels=labels0 + labels1)
# remove last tick label for the second subplot
yticks = ax1.get_yticklabels()
yticks[-1].set_visible(False)
plt.tight_layout()
plt.show()
Related
I am currently making a plot on matplotlib, which looks like below.
The code for which is:
fig, ax1 = plt.subplots(figsize=(20,5))
ax2 = ax1.twinx()
# plt.subplots_adjust(top=1.4)
ax2.fill_between(dryhydro_df['Time'],dryhydro_df['Flow [m³/s]'],0,facecolor='lightgrey')
ax2.set_ylim([0,10])
AB = ax2.fill_between(dryhydro_df['Time'],[12]*len(dryhydro_df['Time']),9.25,facecolor=colors[0],alpha=0.5,clip_on=False)
ab = ax2.scatter(presence_df['Datetime'][presence_df['AB']==True],[9.5]*sum(presence_df['AB']==True),marker='X',color='black')
# tidal heights
ax1.plot(tide_df['Time'],tide_df['Tide'],color='dimgrey')
I want the blue shaded region and black scatter to be above the plot. I can move the elements above the plot by using clip_on=False but I think I need to extend the space above the plot to do visualise it. Is there a way to do this? Mock-up of what I need is below:
You can use clip_on=False to draw outside the main plot. To position the elements, an xaxis transform helps. That way, x-values can be used in the x direction, while the y-direction uses "axes coordinates". ax.transAxes() uses "axes coordinates" for both directions.
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
dates = pd.date_range('2018-07-01', '2018-07-31', freq='H')
xs = dates.to_numpy().astype(float)
ys = np.sin(xs * .091) * (np.sin(xs * .023) ** 2 + 1)
fig, ax1 = plt.subplots(figsize=(20, 5))
ax1.plot(dates, ys)
ax1.scatter(np.random.choice(dates, 10), np.repeat(1.05, 10), s=20, marker='*', transform=ax1.get_xaxis_transform(),
clip_on=False)
ax1.plot([0, 1], [1.05, 1.05], color='steelblue', lw=20, alpha=0.2, transform=ax1.transAxes, clip_on=False)
plt.tight_layout() # fit labels etc. nicely
plt.subplots_adjust(top=0.9) # make room for the additional elements
plt.show()
I have the following sub figures and sub plots in matplotlib"
Sub figure 1 > ax1
Sub figure 2 > Sub plot 1 > ax2
> Sub plot 2 > ax3
The MWE is given below. The problem with the present MWE is that for numbers of different magnitudes on y-axes; the alignment between ax1, ax2, and ax3 are broken as shown in the green box of the figure.
Setting the contstrained_layout to False I can get the alignments right, but messes the spacings. So I need the constrained_layout set to True, but need to get the alignments of ax1, ax2, ax3 right. Are there any methods that I am missing to fix this alignment?
MWE
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 100, 100)
y = x ** 2
y2 = x ** 10
figure = plt.figure(figsize=(10, 8), constrained_layout=True)
figure.clf()
subfigs = figure.subfigures(2, 1, height_ratios=[1, 1], hspace=0.05, wspace=0.05)
plots = subfigs[0].subplots()
ax1 = plt.gca()
ax1.plot(x, y2)
sub_plot = subfigs[1].subplots(2,1)
ax2 = sub_plot[0]
ax2.plot(x, y)
ax3 = sub_plot[1]
ax3.plot(x, y)
plt.show()
The point of a subfigure is to make the subfigures independent. If you want axes spines to line up, then you need to keep the axes in the same figure:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 100, 100)
y = x ** 2
y2 = x ** 10
figure = plt.figure(figsize=(5, 4), constrained_layout=True)
ax1, ax2, ax3 = figure.subplots(3, 1, gridspec_kw={'height_ratios': [2, 1, 1]})
ax1.plot(x, y2)
ax2.plot(x, y)
ax3.plot(x, y)
plt.show()
Note that you can set the height_ratios and the width_ratios using the gridspec_kw argument.
For more explanation, you can see: https://matplotlib.org/stable/tutorials/intermediate/arranging_axes.html
I'm trying to create a horizontal bar chart, with dual x axes. The 2 axes are very different in scale, 1 set goes from something like -5 to 15 (positive and negative value), the other set is more like 100 to 500 (all positive values).
When I plot this, I'd like to align the 2 axes so zero shows at the same position, and only the negative values are to the left of this. Currently the set with all positive values starts at the far left, and the set with positive and negative starts in the middle of the overall plot.
I found the align_yaxis example, but I'm struggling to align the x axes.
Matplotlib bar charts: Aligning two different y axes to zero
Here is an example of what I'm working on with simple test data. Any ideas/suggestions? thanks
import pandas as pd
import matplotlib.pyplot as plt
d = {'col1':['Test 1','Test 2','Test 3','Test 4'],'col 2':[1.4,-3,1.3,5],'Col3':[900,750,878,920]}
df = pd.DataFrame(data=d)
fig = plt.figure() # Create matplotlib figure
ax = fig.add_subplot(111) # Create matplotlib axes
ax2 = ax.twiny() # Create another axes that shares the same y-axis as ax.
width = 0.4
df['col 2'].plot(kind='barh', color='darkblue', ax=ax, width=width, position=1,fontsize =4, figsize=(3.0, 5.0))
df['Col3'].plot(kind='barh', color='orange', ax=ax2, width=width, position=0, fontsize =4, figsize=(3.0, 5.0))
ax.set_yticklabels(df.col1)
ax.set_xlabel('Positive and Neg',color='darkblue')
ax2.set_xlabel('Positive Only',color='orange')
ax.invert_yaxis()
plt.show()
I followed the link from a question and eventually ended up at this answer : https://stackoverflow.com/a/10482477/5907969
The answer has a function to align the y-axes and I have modified the same to align x-axes as follows:
def align_xaxis(ax1, v1, ax2, v2):
"""adjust ax2 xlimit so that v2 in ax2 is aligned to v1 in ax1"""
x1, _ = ax1.transData.transform((v1, 0))
x2, _ = ax2.transData.transform((v2, 0))
inv = ax2.transData.inverted()
dx, _ = inv.transform((0, 0)) - inv.transform((x1-x2, 0))
minx, maxx = ax2.get_xlim()
ax2.set_xlim(minx+dx, maxx+dx)
And then use it within the code as follows:
import pandas as pd
import matplotlib.pyplot as plt
d = {'col1':['Test 1','Test 2','Test 3','Test 4'],'col 2' [1.4,-3,1.3,5],'Col3':[900,750,878,920]}
df = pd.DataFrame(data=d)
fig = plt.figure() # Create matplotlib figure
ax = fig.add_subplot(111) # Create matplotlib axes
ax2 = ax.twiny() # Create another axes that shares the same y-axis as ax.
width = 0.4
df['col 2'].plot(kind='barh', color='darkblue', ax=ax, width=width, position=1,fontsize =4, figsize=(3.0, 5.0))
df['Col3'].plot(kind='barh', color='orange', ax=ax2, width=width, position=0, fontsize =4, figsize=(3.0, 5.0))
ax.set_yticklabels(df.col1)
ax.set_xlabel('Positive and Neg',color='darkblue')
ax2.set_xlabel('Positive Only',color='orange')
align_xaxis(ax,0,ax2,0)
ax.invert_yaxis()
plt.show()
This will give you what you're looking for
I plot using two y-axis, on the left and the right of a matplotlib figure and use zorder to control the position of the plots. I need to define the zorder across axes in the same figure.
Problem
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10,10,0.01)
fig, ax1 = plt.subplots( 1, 1, figsize=(9,3) )
ax1.plot( x, np.sin(x), color='red', linewidth=10, zorder=1 )
ax2 = ax1.twinx()
ax2.plot( x, x, color='blue', linewidth=10, zorder=-1)
In the previous diagram, I would expect the blue line to appear behind the red plot.
How do I control the zorder when using twin axes?
I am using:
python: 3.4.3 + numpy: 1.11.0 + matplotlib: 1.5.1
This should work
ax1.set_zorder(ax2.get_zorder()+1)
ax1.patch.set_visible(False)
the following codes works
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker as tick
x = np.arange(-10,10,0.01)
plt.figure(figsize=(10, 5))
fig = plt.subplot(111)
"""be attention to here. it's fig.plot, not ax1.plot
if you write ax1.plot, then it does not work.
"""
fig.plot(x, x, color ='blue', linewidth =10)
ax2 = fig.twinx()
ax2.plot(x, np.sin(x), color='red', linewidth =10)
"""
It looks like the two axes have separate z-stacks.
The axes are z-ordered with the most recent axis on top
"""
fig.set_zorder(ax2.get_zorder()+1)
fig.patch.set_visible(False)
plt.show()
It looks like the two axes have separate z-stacks. The axes are z-ordered with the most recent axis on top, so you need to move the curve you want on top to the last axis you create:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-10,10,0.01)
fig, ax1 = plt.subplots( 1, 1, figsize=(9,3) )
ax1.plot( x, x, color='blue', linewidth=10 )
ax2 = ax1.twinx()
ax2.plot( x, np.sin(x), color='red', linewidth=10 )
I would like to ask how to produce a plot similar to that in the figure below? Basically, how to have x-axis at the top of the figure. Thanks
Image from: http://oceanographyclay1987.blogspot.com/2010/10/light-attenuation-in-ocean.html
Use
ax.xaxis.set_ticks_position("top")
For example,
import numpy as np
import matplotlib.pyplot as plt
numdata = 100
t = np.linspace(0, 100, numdata)
y = 1/t**(1/2.0)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.xaxis.set_ticks_position('top')
ax.yaxis.grid(linestyle = '-', color = 'gray')
ax.invert_yaxis()
ax.plot(t, y, 'g-', linewidth = 1.5)
plt.show()