I would like to be able to add footnote text similar to the following in matplotlib:
The following code will create a plot with similar text
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
fig, ax = plt.subplots(figsize = (5, 8))
n = 10
np.random.seed(1)
_ = ax.scatter(np.random.randint(0, 10, n), np.random.randint(0, 10, n), s=500)
x = 0
y = 1
_ = ax.text(
x, y, "hello this is some text at the bottom of the plot", fontsize=15, color="#555"
)
Which looks as:
However, if the data changes then the above won't adjust, such as:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
fig, ax = plt.subplots(figsize=(5, 8))
n = 10
np.random.seed(2)
_ = ax.scatter(np.random.randint(0, 10, n), np.random.randint(0, 10, n), s=500)
x = 0
y = 1
_ = ax.text(
x, y, "hello this is some text at the bottom of the plot", fontsize=15, color="#555"
)
I have seen this question/answer, and this just says how to plot text at a particular x,y coordinate. I specifically want to be able to set a footnote though, not plot at a particular x,y, so the solution should be dynamic.
Also, use of the OOP interface is preferred as mentioned in the docs.
Note - there seems to be issues with the current suggestion when using fig.tight_layout()
You should try plotting the text relative to the subplot and not relative to the points in the subplot using transform=ax.transAxes. You should also set the alignment so that the text starts based on the location you want. The can play around with the point location.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
fig, ax = plt.subplots(figsize=(5, 8))
n = 10
np.random.seed(2)
_ = ax.scatter(np.random.randint(0, 10, n), np.random.randint(0, 10, n), s=500)
x = 0
y = -.07
ax.text(x, y, "hello this is some text at the bottom of the plot", fontsize=15,
horizontalalignment='left',verticalalignment='top', transform=ax.transAxes)
plt.show()
Related
I want to adjust colobar scale from my current figure1 to the desired figure2 !!
My colorbar scale range is -1 to 1, but I want it in exponential form and for that I tried levels = np.linspace(-100e-2,100e-2) as well, but it also doesn't give the desired scale2
import xarray as xr
import numpy as np
import matplotlib.pyplot as plt
ds = xr.open_dataset('PL_Era_Tkt.nc')
wp = ds.w.mean(dim=['longitude','latitude']).plot.contourf(x='time',cmap='RdBu',add_colorbar=False,extend='both')
wpcb = plt.colorbar(wp)
wpcb.set_label(label='Pa.s${^{-1}}$',size=13)
plt.gca().invert_yaxis()
plt.title('Vertical Velocity',size=15)
My current scale
My desired scale
Since the data is not presented, I added normalized color bars with the data from the graph sample here. I think the color bar scales will also be in log format with this setup. Please note that the data used is not large, so I have not been able to confirm this.
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.ticker as ticker
import numpy as np
plt.style.use('seaborn-white')
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
fig, ax = plt.subplots()
ax.contourf(X, Y, Z, 20, cmap='RdGy')
cmap = mpl.cm.RdGy
norm = mpl.colors.Normalize(vmin=-1, vmax=1.0)
fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),
ax=ax, orientation='vertical', label='Some Units', extend='both', ticks=ticker.LogLocator())
plt.show()
I would like to generate a series of histogram shown below:
The above visualization was done in tensorflow but I'd like to reproduce the same visualization on matplotlib.
EDIT:
Using plt.fill_between suggested by #SpghttCd, I have the following code:
colors=cm.OrRd_r(np.linspace(.2, .6, 10))
plt.figure()
x = np.arange(100)
for i in range(10):
y = np.random.rand(100)
plt.fill_between(x, y + 10-i, 10-i,
facecolor=colors[i]
edgecolor='w')
plt.show()
This works great, but is it possible to use histogram instead of a continuous curve?
EDIT:
joypy based approach, like mentioned in the comment of october:
import pandas as pd
import joypy
import numpy as np
df = pd.DataFrame()
for i in range(0, 400, 20):
df[i] = np.random.normal(i/410*5, size=30)
joypy.joyplot(df, overlap=2, colormap=cm.OrRd_r, linecolor='w', linewidth=.5)
for finer control of colors, you can define a color gradient function which accepts a fractional index and start and stop color tuples:
def color_gradient(x=0.0, start=(0, 0, 0), stop=(1, 1, 1)):
r = np.interp(x, [0, 1], [start[0], stop[0]])
g = np.interp(x, [0, 1], [start[1], stop[1]])
b = np.interp(x, [0, 1], [start[2], stop[2]])
return (r, g, b)
Usage:
joypy.joyplot(df, overlap=2, colormap=lambda x: color_gradient(x, start=(.78, .25, .09), stop=(1.0, .64, .44)), linecolor='w', linewidth=.5)
Examples with different start and stop tuples:
original answer:
You could iterate over your dataarrays you'd like to plot with plt.fill_between, setting colors to some gradient and the line color to white:
creating some sample data:
import numpy as np
t = np.linspace(-1.6, 1.6, 11)
y = np.cos(t)**2
y2 = lambda : y + np.random.random(len(y))/5-.1
plot the series:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
colors = cm.OrRd_r(np.linspace(.2, .6, 10))
plt.figure()
for i in range(10):
plt.fill_between(t+i, y2()+10-i/10, 10-i/10, facecolor = colors[i], edgecolor='w')
If you want it to have more optimized towards your example you should perhaps consider providing some sample data.
EDIT:
As I commented below, I'm not quite sure if I understand what you want - or if you want the best for your task. Therefore here a code which plots besides your approach in your edit two smples of how to present a bunch of histograms in a way that they are better comparable:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
N = 10
np.random.seed(42)
colors=cm.OrRd_r(np.linspace(.2, .6, N))
fig1 = plt.figure()
x = np.arange(100)
for i in range(10):
y = np.random.rand(100)
plt.fill_between(x, y + 10-i, 10-i,
facecolor=colors[i],
edgecolor='w')
data = np.random.binomial(20, .3, (N, 100))
fig2, axs = plt.subplots(N, figsize=(10, 6))
for i, d in enumerate(data):
axs[i].hist(d, range(20), color=colors[i], label=str(i))
fig2.legend(loc='upper center', ncol=5)
fig3, ax = plt.subplots(figsize=(10, 6))
ax.hist(data.T, range(20), color=colors, label=[str(i) for i in range(N)])
fig3.legend(loc='upper center', ncol=5)
This leads to the following plots:
your plot from your edit:
N histograms in N subplots:
N histograms side by side in one plot:
I'm plotting some function in matplotlib. But I want to change the usual x and y coordinates. For example I plot y=sin(x) in [-pi, pi]. But the x-axis shows 1, 2, 3,... in this way whereas I want x: -pi, 0, pi,... Is it possible?
My Code
import matplotlib as mpl
mpl.rc('text', usetex = True)
mpl.rc('font', family = 'serif')
import matplotlib.pyplot as plt
import numpy as np
plt.gca().set_aspect('equal', adjustable='box')
plt.style.use(['ggplot','dark_background'])
x = np.arange(-np.pi,np.pi,0.001)
y = np.sin(x)
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.plot(x,y, label='$y=\sin x$')
plt.legend()
plt.show()
Output
How to change the marks on the axes coordinates? Thank you.
Yes, you can have custom tick marks on the axis, and set them equally spaced; for this you need to set the tick marks as a sequence, together with the values associated:
import matplotlib as mpl
mpl.rc('text', usetex = True)
mpl.rc('font', family = 'serif')
import matplotlib.pyplot as plt
import numpy as np
plt.gca().set_aspect('equal', adjustable='box')
plt.style.use(['ggplot','dark_background'])
x = np.arange(-np.pi,np.pi,0.001)
y = np.sin(x)
# the following two sequences contain the values and their assigned tick markers
xx = [-np.pi + idx*np.pi/4 for idx in range(10)]
xx_t = ['$-\\pi$', '$\\frac{-3\\pi}{4}$', '$\\frac{-\\pi}{2}$', '$\\frac{-\\pi}{4}$', '0',
'$\\frac{\\pi}{4}$', '$\\frac{\\pi}{2}$', '$\\frac{3\\pi}{4}$', '$\\pi$']
plt.xticks(xx, xx_t) # <-- the mapping happens here
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.plot(x,y, label='$y=\sin x$')
plt.legend()
plt.show()
Here you can display up to whichever range of pi you want to. Just add the following lines to your code after plt.plot
xlabs = [r'%d$\pi$'%i if i!=0 else 0 for i in range(-2, 3, 1)]
xpos = np.linspace(-2*np.pi, 2*np.pi, 5)
plt.xticks(xpos, xlabs)
Output
I'm trying to get a heatmap using seaborn in python. Unfortunately it is not using scientific notation even though the numbers are very large. I was wondering if there's any simple way to convert to scientific notation or any other reasonable format. Here's a piece of code that shows the problem:
import seaborn as sns
import numpy as np
C_vals = np.logspace(3, 10, 8)
g_vals = np.logspace(-6, 2, 9)
score = np.random.rand(len(g_vals), len(C_vals))
sns.heatmap(score, xticklabels=C_vals, yticklabels=g_vals)
The resulting plot is the following
The heatmap allows to create its labels from the input to the xticklabels/yticklabels command. Those are then put along the axes, so there is no numeric format to change their appearance.
An option is to format the labels prior to supplying them to the heatmap. To this end a matplotlib ScalarFormatter can be (mis)used, which allows to automatically generate a MathText string from a float number. The following would be an example:
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import seaborn as sns
import numpy as np
C_vals = np.logspace(3, 10, 8)
g_vals = np.logspace(-6, 2, 9)
score = np.random.rand(len(g_vals),len(C_vals))
tick = ticker.ScalarFormatter(useOffset=False, useMathText=True)
tick.set_powerlimits((0,0))
tc = [u"${}$".format(tick.format_data(x)) for x in C_vals]
tg = [u"${}$".format(tick.format_data(x)) for x in g_vals]
sns.heatmap(score, xticklabels=tc, yticklabels=tg)
plt.show()
If you can bear to do w/o sns.heatmap, its perhaps more natural to do this with pcolormesh
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
C_vals = np.logspace(3, 10, 8)
g_vals = np.logspace(-6, 2, 9)
score = np.random.rand(len(g_vals),len(C_vals))
fig, ax = plt.subplots()
ax.pcolormesh(C_vals, g_vals, score)
ax.set_yscale('log')
ax.set_xscale('log')
plt.show()
As pointed out below, pcolormesh doesn't centre the same way. Further, it actually drops a level. While I have a PR in to change that behaviour, here is a workaround. I admit at this point, its not much more elegant than messing w/ the heatmap output.
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
C_vals = np.logspace(3, 10, 8)
g_vals = np.logspace(-6, 2, 9)
# make bracketing:
def midpointext(x):
return np.hstack(( 1.5 * x[0] - 0.5 * x[1],
x[:-1] + 0.5 * np.diff(x),
1.5 * x[-1] - 0.5 * x[-2]))
newC = np.log10(C_vals)
newC = midpointext(newC)
newC = 10**newC
newg = np.log10(g_vals)
newg = midpointext(newg)
newg = 10**newg
score = np.random.rand(len(g_vals),len(C_vals))
fig, ax = plt.subplots()
ax.pcolormesh(newC, newg, score)
ax.set_yscale('log')
ax.set_xscale('log')
plt.show()
I am trying to make a histgram over a specific range but the matplotlib.pyplot.hist() function keeps cropping the range to the bins with entries in them. A toy example:
import numpy as np
import matplotlib.pyplot as plt
x = np.random.uniform(-100,100,1000)
nbins = 100
xmin = -500
xmax = 500
fig = plt.figure();
ax = fig.add_subplot(1, 1, 1)
ax.hist(x, bins=nbins,range=[xmin,xmax])
plt.show()
Gives a plot with a range [-100,100]. Why is the range not [-500,500] as specified?
(I am using the Enthought Canopy 1.4 and sorry but I do not have a high enough rep to post an image of the plot.)
Actually, it works if you specify with range an interval shorter than [-100, 100]. For example, this work :
import numpy as np
import matplotlib.pyplot as plt
x = np.random.uniform(-100, 100, 1000)
plt.hist(x, bins=30, range=(-50, 50))
plt.show()
If you want to plot the histogram on a range larger than [x.min(), x.max()] you can change xlim propertie of the plot.
import numpy as np
import matplotlib.pyplot as plt
x = np.random.uniform(-100, 100, 1000)
plt.hist(x, bins=30)
plt.xlim(-500, 500)
plt.show()
the following code is for making the same y axis limit on two subplots
f ,ax = plt.subplots(1,2,figsize = (30, 13),gridspec_kw={'width_ratios': [5, 1]})
df.plot(ax = ax[0], linewidth = 2.5)
ylim = [df['min_return'].min()*1.1,df['max_return'].max()*1.1]
ax[0].set_ylim(ylim)
ax[1].hist(data,normed =1, bins = num_bin, color = 'yellow' ,alpha = 1)
ax[1].set_ylim(ylim)