How do I use axvfill with a boolean series - python

I have a boolean time series that I want to use to determine the parts of the plot that should be shaded.
Currently I have:
ax1.fill_between(data.index, r_min, r_max, where=data['USREC']==True, alpha=0.2)
where, r_min and r_max are just the min and max of the y-axis.
But the fill_between doesn't fill all the way to the top and bottom of the plot because, so I wanted to use axvspan() instead.
Is there any easy way to do this given axvspan only takes coordinates? So the only way I can think of is to group all the dates that are next to each other and are True, then take the first and last of those dates and pass them into axvspan.
Thank you

You can still use fill_between, if you like. However instead of specifying the y-coordinates in data coordinates (for which it is not a priori clear, how large they need to be) you can specify them in axes coorinates. This can be achieved using a transform, where the x part is in data coordinates and the y part is in axes coordinates. The xaxis transform is such a transform. (This is not very surprising since the xaxis is always independent of the ycoorinates.) So
ax.fill_between(data.index, 0,1, where=data['USREC'], transform=ax.get_xaxis_transform())
would do the job.
Here is a complete example:
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
x = np.linspace(0,100,350)
y = np.cumsum(np.random.normal(size=len(x)))
bo = np.zeros(len(y))
bo[y>5] = 1
fig, ax = plt.subplots()
ax.fill_between(x, 0, 1, where=bo, alpha=0.4, transform=ax.get_xaxis_transform())
plt.plot(x,y)
plt.show()

Related

Horizontal line to infinity on one side only in matplotlib

I'd like to plot a line that goes to infinity, but starting from a finite point. For simplicity, let's say that the line can be horizontal. I would like to plot a line from (0, 0) to (inf, 0).
Using hlines:
>>> fig, ax = plt.subplots()
>>> ax.hlines(0, 0, np.inf)
.../python3.8/site-packages/matplotlib/axes/_base.py:2480: UserWarning: Warning: converting a masked element to nan.
xys = np.asarray(xys)
The result is an empty plot.
axhline has a starting parameter, but it is in axis coordinates rather than data. Similar problem for axline. Is there a way to plot a (horizontal) line with one end in data coordinates and the other at infinity?
The motivation behind this is that I'd like to be able to plot some cumulative probabilities without setting data past the last bin to zero, as here: Matplotlib cumulative histogram - vertical line placement bug or misinterpretation?. Rather than simply ending the histogram, I'd like to be able to extend the line from the last bin to infinity at y=1.0.
There's no built-in function for this, but you can re-draw the line to the axis limit on each change of the x limits.
From Axes:
The events you can connect to are 'xlim_changed' and 'ylim_changed'
and the callback will be called with func(ax) where ax is the Axes
instance.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
def hline_to_inf(ax, x, y):
line = ax.hlines(0, 0, ax.get_xlim()[1])
ax.callbacks.connect('xlim_changed',
lambda ax: line.set_paths([[[x, y], [ax.get_xlim()[1], y]]]))
hline_to_inf(ax, 0, 0)
plt.show()
Part of the issue is that normal plotting methods apply the same transform to the input data. What is required here is to apply a data transform to the start point, and a blended transform to the endpoint. It seems that there may be an answer using existing tools with ConnectionPatch, as explained in the Annotations Guide. The idea is to make the left point use data coordinates and the right point have a blended transform with x in axes coordinates and y in data.
from matplotlib import pyplot as plt
from matplotlib.patches import ConnectionPatch
fig, ax = plt.subplots()
line, = ax.plot([1, 2], [1, 2])
ax.add_artist(ConnectionPatch([2, 2], [1, 2], coordsA=ax.transData, coordsB=ax.get_yaxis_transform(), color=line.get_color(), linewidth=line.get_linewidth(), clip_on=True))
Turning on clipping is necessary, otherwise you could end up with artifacts that look like this:

matplotlib: manually change yaxis values to differ from the actual value (NOT: change ticks!) [duplicate]

I am trying to plot a data and function with matplotlib 2.0 under python 2.7.
The x values of the function are evolving with time and the x is first decreasing to a certain value, than increasing again.
If the function is plotted against time, it shows function like this plot of data against time
I need the same x axis evolution for plotting against real x values. Unfortunately as the x values are the same for both parts before and after, both values are mixed together. This gives me the wrong data plot:
In this example it means I need the x-axis to start on value 2.4 and decrease to 1.0 than again increase to 2.4. I swear I found before that this is possible, but unfortunately I can't find a trace about that again.
A matplotlib axis is by default linearly increasing. More importantly, there must be an injective mapping of the number line to the axis units. So changing the data range is not really an option (at least when the aim is to keep things simple).
It would hence be good to keep the original numbers and only change the ticks and ticklabels on the axis. E.g. you could use a FuncFormatter to map the original numbers to
np.abs(x-tp)+tp
where tp would be the turning point.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker
x = np.linspace(-10,20,151)
y = np.exp(-(x-5)**2/19.)
plt.plot(x,y)
tp = 5
fmt = lambda x,pos:"{:g}".format(np.abs(x-tp)+tp)
plt.gca().xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(fmt))
plt.show()
One option would be to use two axes, and plot your two timespans separately on each axes.
for instance, if you have the following data:
myX = np.linspace(1,2.4,100)
myY1 = -1*myX
myY2 = -0.5*myX-0.5
plt.plot(myX,myY, c='b')
plt.plot(myX,myY2, c='g')
you can instead create two subplots with a shared y-axis and no space between the two axes, plot each time span independently, and finally, adjust the limits of one of your x-axis to reverse the order of the points
fig, (ax1,ax2) = plt.subplots(1,2, gridspec_kw={'wspace':0}, sharey=True)
ax1.plot(myX,myY1, c='b')
ax2.plot(myX,myY2, c='g')
ax1.set_xlim((2.4,1))
ax2.set_xlim((1,2.4))

Single column heat map in python

My goal is to have a single column heat map, but for some reason to code I normally use for heat maps doesn't work with if I'm not using a 2-D array.
vec1 = np.asarray([1,2,3,4,5])
fig, ax = plt.subplots()
plt.imshow(vec1, cmap='jet')
I know it's weird to show I single column vector as a heat map, but it's a nice visual for my purposes. I just want a column of colored squares that I can label along the y-axis to show a ranked list of things to people.
You could use the library Seaborn to do this. In Seaborn you can identify specific columns to plot. In this case that'd be your array. The following should accomplish what you're wanting
vec1 = np.asarray([1,2,3,4,5])
fig, ax = plt.subplots()
seaborn.heatmap([vec1])
Then you'll just have to do your formatting on that heatmap as you would in pyplotlib.
http://seaborn.pydata.org/generated/seaborn.heatmap.html
Starting from the previous answer, I've come up with an approach which uses both Seaborn and Matplotlib's transform to do what pavlov requested within its comment (that is, swapping axis in a heatmap even though Seaborn does not have an orientation parameter).
Let's start from the previous answer:
vec1 = np.asarray([1,2,3,4,5])
sns = heatmap([vec1])
plt.show()
Using heatmap on a single vector yields to the following result:
Ok, let's swap the x-axis with the y-axis. To do that, we can use an Affine2D transform, applying a rotation of 90 degrees.
from matplotlib import transforms
tr = transforms.Affine2D().rotate_deg(90)
Let's also reshape the initial array to make it resemble a column vector:
vec2 = vec1.reshape(vec1.shape[0], 1)
Now we can plot the heatmap and force Matplotlib to perform an affine transform:
sns.heatmap(vec2)
plt.show(tr)
The resulting plot is:
Now, if we want to force each row to be a square, we can simply use the square=True parameter:
sns.heatmap(vec2, square=True)
plt.show(tr)
This is the final result:
Hope it helps!

Scale colormap for contour and contourf

I'm trying to plot the contour map of a given function f(x,y), but since the functions output scales really fast, I'm losing a lot of information for lower values of x and y. I found on the forums to work that out using vmax=vmax, it actually worked, but only when plotted for a specific limit of x and y and levels of the colormap.
Say I have this plot:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
u = np.linspace(-2,2,1000)
x,y = np.meshgrid(u,u)
z = (1-x)**2+100*(y-x**2)**2
cont = plt.contour(x,y,z,500,colors='black',linewidths=.3)
cont = plt.contourf(x,y,z,500,cmap="jet",vmax=100)
plt.colorbar(cont)
plt.show
I want to uncover whats beyond the axis limits keeping the same scale, but if I change de x and y limits to -3 and 3 I get:
See how I lost most of my levels since my max value for the function at these limits are much higher. A work around to this problem is to increase the levels to 1000, but that takes a lot of computational time.
Is there a way to plot only the contour levels that I need? That is, between 0 and 100.
An example of a desired output would be:
With the white space being the continuation of the plot without resizing the levels.
The code I'm using is the one given after the first image.
There are a few possible ideas here. The one I very much prefer is a logarithmic representation of the data. An example would be
from matplotlib import ticker
fig = plt.figure(1)
cont1 = plt.contourf(x,y,z,cmap="jet",locator=ticker.LogLocator(numticks=10))
plt.colorbar(cont1)
plt.show()
fig = plt.figure(2)
cont2 = plt.contourf(x,y,np.log10(z),100,cmap="jet")
plt.colorbar(cont2)
plt.show()
The first example uses matplotlibs LogLocator functions. The second one just directly computes the logarithm of the data and plots that normally.
The third example just caps all data above 100.
fig = plt.figure(3)
zcapped = z.copy()
zcapped[zcapped>100]=100
cont3 = plt.contourf(x,y,zcapped,100,cmap="jet")
cbar = plt.colorbar(cont3)
plt.show()

Creating two x-axes for a line-plot in matplotlib with unknown transform function between scales

Using matplotlib, two x-axes for 1 line plot can easily be obtained using twiny().
If the transform between the two x-scales can be described by a function, the corresponding ticks can be set by applying this transform function.
(this is described here: How to add a second x-axis in matplotlib)
How can I achieve this, if the transform function between the scales is unknown?
Edit:
Imagine the following situation:
You have 2 thermometers, both measuring the temperature. Thermometer 1 is measuring in °C and thermometer 2 in an imaginary unit, lets call it °D. Basically, what you know is that with increasing °C °D is increasing as well. Additionally, both thermometers have some degree of inaccuracy.
Both thermometers measure the same physical quantity, hence I should be able to represent them with a single line and two scales. However, in contrast to plotting tempoeratures in °C vs. K or °F, the transformation between the scales is unknown.
This means for example I have:
import numpy as np
from matplotlib import pyplot as plt
temp1 = np.sort(np.random.uniform(size=21))
temp2 = np.sort(np.random.uniform(low=-20, high=20, size=21))
y = np.linspace(0,1,21, endpoint=True)
A transform function between temp1 and temp2 is existent, but unknow. Y, however, is the same.
Additionally, I know that temp1 and y are confined to the range (0,1)
Now we may plot like this:
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.set_aspect('equal')
ax2 = plt.twiny(ax1)
ax1.plot(x1,y, 'k-')
ax2.plot(x2,y, 'r:')
ax1.set_xlabel(r'1st x-axis')
ax2.set_xlabel(r'2nd x-axis')
ax1.set_xlim([0,1])
ax1.set_ylim([0,1])
fig.savefig('dual_x_faulty.png', format='png')
This leads to the following plot:
You can see that both curves are not the same, and the plot is not square (as it would be without twinning the y axis).
So, here is what I want (and can't achieve on my own):
Plotting a 3d-array (temp1, temp2, y) in a 2d line plot by having two x-axes
Matplotlib shoud 'automagically' set the ticks of temp2 such, that the curves (temp1, y) and (temp2, y) are congruent
Is there a workaround?
Thanks for your help!

Categories

Resources