I am trying to have two inter-depedent x-axis in a matplotlib imshow() plot. I have bottom x-axis as the radius squared and I want the top as just the radius. I have tried so far:
ax8 = ax7.twiny()
ax8._sharex = ax7
fmtr = FuncFormatter(lambda x,pos: np.sqrt(x) )
ax8.xaxis.set_major_formatter(fmtr)
ax8.set_xlabel("Radius [m]")
where ax7 is the y-axis and the bottom x-axis (or radius squared). Instead of getting the sqrt (x_bottom) as the ticks at the top I just get a range from 0 to 1. How can I fix this?
Thanks a lot in advance.
You're misunderstanding what twiny does. It makes a completely independent x-axis with a shared y-axis.
What you want to do is have a different formatter with a linked axis (i.e. sharing the axis limits but nothing else).
The simple way to do this is to manually set the axis limits for the twinned axis:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
fig, ax1 = plt.subplots()
ax1.plot(range(10))
ax2 = ax1.twiny()
formatter = FuncFormatter(lambda x, pos: '{:0.2f}'.format(np.sqrt(x)))
ax2.xaxis.set_major_formatter(formatter)
ax2.set_xlim(ax1.get_xlim())
plt.show()
However, as soon as you zoom or interact with the plot, you'll notice that the axes are unlinked.
You could add an axes in the same position with both shared x and y axes, but then the tick formatters are shared, as well.
Therefore, the easiest way to do this is using a parasite axes.
As a quick example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
from mpl_toolkits.axes_grid1.parasite_axes import SubplotHost
fig = plt.figure()
ax1 = SubplotHost(fig, 1,1,1)
fig.add_subplot(ax1)
ax2 = ax1.twin()
ax1.plot(range(10))
formatter = FuncFormatter(lambda x, pos: '{:0.2f}'.format(np.sqrt(x)))
ax2.xaxis.set_major_formatter(formatter)
plt.show()
Both this and the previous plot will look identical at first. The difference will become apparent when you interact (e.g. zoom/pan) with the plot.
Related
I want to draw multiple bar plots with the same y-scale, and so I need the y-scale to be consistent.
For this, I tried using ylim() after yscale()
plt.yscale("log")
plt.ylim(top=2000)
plt.show()
However, python keeps autoscaling the intermittent values depending on my data.
Is there a way to fix this?
overlayed graphs
import numpy as np
import matplotlib.pyplot as plt
xaxis = np.arange(10)
yaxis = np.random.rand(10)*100
fig = plt.subplots(figsize =(10, 7))
plt.bar(xaxis, yaxis, width=0.8, align='center', color='y')
# show graph
plt.yscale("log")
plt.ylim(top=2000)
plt.show()
You can set the y-axis tick labels manually. See yticks for an example. In your case, you will have to do this for each plot to have consistent axes.
What I want to achieve with Python 3.6 is something like this :
Obviously made in paint and missing some ticks on the xAxis. Is something like this possible? Essentially, can I control exactly where to plot a histogram (and with what orientation)?
I specifically want them to be on the same axes just like the figure above and not on separate axes or subplots.
fig = plt.figure()
ax2Handler = fig.gca()
ax2Handler.scatter(np.array(np.arange(0,len(xData),1)), xData)
ax2Handler.hist(xData,bins=60,orientation='horizontal',normed=True)
This and other approaches (of inverting the axes) gave me no results. xData is loaded from a panda dataframe.
# This also doesn't work as intended
fig = plt.figure()
axHistHandler = fig.gca()
axScatterHandler = fig.gca()
axHistHandler.invert_xaxis()
axHistHandler.hist(xData,orientation='horizontal')
axScatterHandler.scatter(np.array(np.arange(0,len(xData),1)), xData)
A. using two axes
There is simply no reason not to use two different axes. The plot from the question can easily be reproduced with two different axes:
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
xData = np.random.rand(1000)
fig,(ax,ax2)= plt.subplots(ncols=2, sharey=True)
fig.subplots_adjust(wspace=0)
ax2.scatter(np.linspace(0,1,len(xData)), xData, s=9)
ax.hist(xData,bins=60,orientation='horizontal',normed=True)
ax.invert_xaxis()
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax2.tick_params(axis="y", left=0)
plt.show()
B. using a single axes
Just for the sake of answering the question: In order to plot both in the same axes, one can shift the bars by their length towards the left, effectively giving a mirrored histogram.
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
xData = np.random.rand(1000)
fig,ax= plt.subplots(ncols=1)
fig.subplots_adjust(wspace=0)
ax.scatter(np.linspace(0,1,len(xData)), xData, s=9)
xlim1 = ax.get_xlim()
_,__,bars = ax.hist(xData,bins=60,orientation='horizontal',normed=True)
for bar in bars:
bar.set_x(-bar.get_width())
xlim2 = ax.get_xlim()
ax.set_xlim(-xlim2[1],xlim1[1])
plt.show()
You might be interested in seaborn jointplots:
# Import and fake data
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(2,1000)
# actual plot
jg = sns.jointplot(data[0], data[1], marginal_kws={"bins":100})
jg.ax_marg_x.set_visible(False) # remove the top axis
plt.subplots_adjust(top=1.15) # fill the empty space
produces this:
See more examples of bivariate distribution representations, available in Seaborn.
(Heavily edited:)
In python matplotlib, I want to plot y against x with two xscales, the lower one with linear ticks and the upper one with logarithmic ticks.
The lower x values are an arbitrary function of the upper ones (in this case the mapping is func(x)=np.log10(1.0+x)). Corollary: The upper x tick positions are the same arbitrary function of the lower ones.
The positions of the data points and the tick positions for both axes must be decoupled.
I want the upper axis's logarithmic tick positions and labels to be as tidy as possible.
What is the best way to produce such a plot?
Related: http://matplotlib.1069221.n5.nabble.com/Two-y-axis-with-twinx-only-one-of-them-logscale-td18255.html
Similar (but unanswered) question?: Matplotlib: how to set ticks of twinned axis in log plot
Could be useful: https://stackoverflow.com/a/29592508/1021819
You may find Axes.twiny() and Axes.semilogx() useful.
import numpy as np
import matplotlib.pyplot as plt
fig, ax1 = plt.subplots()
x = np.arange(0.01, 10.0, 0.01) # x-axis range
y = np.sin(2*np.pi*x) # simulated signal to plot
ax1.plot(x, y, color="r") # regular plot (red)
ax1.set_xlabel('x')
ax2 = ax1.twiny() # ax1 and ax2 share y-axis
ax2.semilogx(x, y, color="b") # semilog plot (blue)
ax2.set_xlabel('semilogx')
plt.show()
Here is an attempt at an answer after speaking to a few people and with thanks to #BusyBeaver.
I agree the question was ill-posed and will amend it to clarify (help welcome!).
I do think this is a useful one to have written down on stackoverflow.
Code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator
# Necessary functions
def tick_function(x):
"""Specify tick format"""
return ["%2.f" % i for i in x]
def func(x):
"""This can be anything you like"""
funcx=np.log10(1.0+x)
return funcx
z=np.linspace(0.0,4.0,20)
np.random.seed(seed=1234)
y=np.random.normal(10.0,1.0,len(z))
# Set up the plot
fig,ax1 = subplots()
ax1.xaxis.set_minor_locator(AutoMinorLocator())
ax1.yaxis.set_minor_locator(AutoMinorLocator())
# Set up the second axis
ax2 = ax1.twiny()
# The tick positions can be at arbitrary positions
zticks=np.arange(z[0],z[-1]+1)
ax2.set_xticks(func(zticks))
ax2.set_xticklabels(tick_function(zticks))
ax2.set_xlim(func(z[0]),func(z[-1]))
ax1.set_ylim(5.0,15.0)
ax1.set_xlabel(r'$\log_{10}\left(1+z\right)$')
ax2.set_xlabel(r'$z$')
ax1.set_ylabel('amplitude/arb. units')
plt.tick_params(axis='both',which = 'major', labelsize=8, width=2)
plt.tick_params(axis='both',which = 'minor', labelsize=8, width=1)
_=ax1.plot(func(z),y,'k.')
plt.savefig('lnopz2.png')
I am not sure how to control the upper ax2 minor ticks (e.g. every 0.5).
I have questions related to creating a simple lineplot in Python with mplot3D where the area under the plot is filled. I am using Python 2.7.5 on RedHatEnterprise 7.2, matplotlib 1.2.0 and numpy 1.7.2.
Using the code below, I am able to generate a line plot. This is displayed as expected with the beginning / end of the plot set by the limits of the imported data set.
I am then trying to fill the area between the line plot and -0.1 using the answer given by Bart from Plotting a series of 2D plots projected in 3D in a perspectival way. This works, however, the filled area is continued beyond the limits of the data set. This is also the case when running the example from the link.
This screen shot shows the plot generated with filled area extending beyond the set axis limits.
How do I achieve that the filled area is only the range of the data set or the axis limits whichever is smaller?
How do I add a legend for those plots onto the figure?
Code as follows:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
x,y = genfromtxt("data.dat",unpack=True)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
ax.plot(x,y,1,zdir="y",label="line plot")
ax.legend()
ax.set_xlim3d(852.353,852.359)
ax.set_zlim3d(-0.1,5)
ax.set_ylim3d(0,2)
ax.get_xaxis().get_major_formatter().set_useOffset(False)
plt.show()
I don't know how to put fill_between working the way you want it to, but I can provide an alternative using a 3D polygon:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection # New import
#x,y = genfromtxt("data.dat",unpack=True)
# Generated some random data
w = 3
x,y = np.arange(100), np.random.randint(0,100+w,100)
y = np.array([y[i-w:i+w].mean() for i in range(3,100+w)])
z = np.zeros(x.shape)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
verts = [(x[i],z[i],y[i]) for i in range(len(x))] + [(x.max(),0,0),(x.min(),0,0)]
ax.add_collection3d(Poly3DCollection([verts],color='orange')) # Add a polygon instead of fill_between
ax.plot(x,z,y,label="line plot")
ax.legend()
ax.set_ylim(-1,1)
plt.show()
The code above generates some random data. Builds vertices from it and plots a polygon with those vertices. This will give you the plot you wish (but does not use fill_between). The result is:
How can I turn the minor ticks only on y axis on a linear vs linear plot?
When I use the function minor_ticks_on to turn minor ticks on, they appear on both x and y axis.
Nevermind, I figured it out.
ax.tick_params(axis='x', which='minor', bottom=False)
Here's another way I found in the matplotlib documentation:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.ticker import MultipleLocator
a = np.arange(100)
ml = MultipleLocator(5)
plt.plot(a)
plt.axes().yaxis.set_minor_locator(ml)
plt.show()
This will place minor ticks on only the y-axis, since minor ticks are off by default.
To clarify the procedure of #emad's answer, the steps to show minor ticks at default locations are:
Turn on minor ticks for an axes object, so locations are initialized as Matplotlib sees fit.
Turn off minor ticks that are not desired.
A minimal example:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
plt.plot([1,2])
# Currently, there are no minor ticks,
# so trying to make them visible would have no effect
ax.yaxis.get_ticklocs(minor=True) # []
# Initialize minor ticks
ax.minorticks_on()
# Now minor ticks exist and are turned on for both axes
# Turn off x-axis minor ticks
ax.xaxis.set_tick_params(which='minor', bottom=False)
Alternative Method
Alternatively, we can get minor ticks at default locations using AutoMinorLocator:
import matplotlib.pyplot as plt
import matplotlib.ticker as tck
fig, ax = plt.subplots()
plt.plot([1,2])
ax.yaxis.set_minor_locator(tck.AutoMinorLocator())
Result
Either way, the resulting plot has minor ticks on the y-axis only.
To set minor ticks at custom locations:
ax.set_xticks([0, 10, 20, 30], minor=True)
Also, if you only want minor ticks on the actual y-axis, rather than on both the left and right-hand sides of the graph, you can follow the plt.axes().yaxis.set_minor_locator(ml) with plt.axes().yaxis.set_tick_params(which='minor', right = 'off'), like so:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.ticker import MultipleLocator
a = np.arange(100)
ml = MultipleLocator(5)
plt.plot(a)
plt.axes().yaxis.set_minor_locator(ml)
plt.axes().yaxis.set_tick_params(which='minor', right = 'off')
plt.show()
The following snippets should help:
from matplotlib.ticker import MultipleLocator
ax.xaxis.set_minor_locator(MultipleLocator(#))
ax.yaxis.set_minor_locator(MultipleLocator(#))
# refers to the desired interval between minor ticks.