Upper limit with upper error - python

Is it possibile to have an upper limit (with the down arrow) with the point centered in the the best value and, at the same time, the upper error?
Something like this:
I'm trying with:
import numpy as np
import matplotlib.pyplot as plt
x = np.array([10, 15, 20, 25, 30, 35])
x_el = np.array([1, 1, 2, 25, 1, 2, 1])
x_eu = np.array([1, 1, 2, 1, 1, 2, 1])
y = np.array([29, 15, 9, 10, 25, 14])
y_el = np.array([1, 1, 2, 1, 1, 2, 1])
y_eu = np.array([11,1,2,1,1,2,1])
fig, ax = plt.subplots()
for i in range(len(x)):
if (x[i] - x_el[i]) == 0:
el = 0
ax.errorbar(x[i], y[i], yerr=[[y_el[i]], [y_eu[i]]], xerr=[[el],[x_eu[i]]],
c='b', capsize=2, elinewidth=1, marker='o',
xuplims=True)
else:
ax.errorbar(x[i], y[i], yerr=[[y_el[i]], [y_eu[i]]], xerr=[[x_el[i]], [x_eu[i]]],
c='b', capsize=2, elinewidth=1, marker='o')
But this is the result:
The point number 4 has neither the uplim nor the upper error.

The short answer is yes, but you have to plot the upper limits and the error bars separately. Let's start by plotting your normal error bars properly. You can do this without looping if your data is in a numpy array already:
import numpy as np
import matplotlib.pyplot as plt
x = np.array([10, 15, 20, 25, 30, 35])
x_el = np.array([1, 1, 2, 25, 1, 2])
x_eu = np.array([1, 1, 2, 1, 1, 2])
y = np.array([29, 15, 9, 10, 25, 14])
y_el = np.array([1, 1, 2, 1, 1, 2])
y_eu = np.array([11, 1, 2, 1, 1, 2])
fig, ax = plt.subplots()
mask = (x != x_el)
ax.errorbar(x, y, yerr=[y_el, y_eu], xerr=[x_el * mask, x_eu],
c='b', capsize=2, elinewidth=1, marker='o', linestyle='none')
Notice that I trimmed the error bar arrays down to the same size as x, which allows me to compute the mask using the != operator. Since you are interested in having all the error bars besides the one in x_el, I multiply by the mask. The mask is a boolean, and any error bar that is masked out will just be set to zero that way. All the other bars get plotted properly at this point:
Now you can use the same mask (but inverted) to plot the upper limits:
ax.errorbar(x[~mask], y[~mask], xerr=x_el[~mask],
c='b', capsize=2, elinewidth=1, marker='o', linestyle='none',
xuplims=True)
The result is
If you were not interested in having an obscenely long arrow that stretches to zero, you can shorten it to whatever size you like:
ax.errorbar(x[~mask], y[~mask], xerr=1,
c='b', capsize=2, elinewidth=1, marker='o', linestyle='none',
xuplims=True)
Alternative
You could even get pretty close with a single plotting call, since xuplims accepts an array of booleans. However, anywhere it is True will eliminate the right bar:
mask = (x == x_el)
ax.errorbar(x, y, yerr=[y_el, y_eu], xerr=[x_el, x_eu],
c='b', capsize=2, elinewidth=1, marker='o', linestyle='none',
xuplims=mask)
You end up having to fill in the right bars in this case:
ax.errorbar(x[mask], y[mask], xerr=[np.zeros_like(x_eu)[mask], x_eu[mask]],
c='b', capsize=2, elinewidth=1, marker='o', linestyle='none')

Related

How to properly set labels in contourf subplots?

I am trying to get rid of these purple points on the picture below. Here is my code:
p_values = [0., 0.05, 0.25, 0.5, 1, 1.5, 2, 5, 10, np.inf]
xx, yy = np.meshgrid(np.linspace(-3, 3, num = 101),
np.linspace(-3, 3, num = 101))
fig, axes = plt.subplots(ncols = (len(p_values) + 1) // 2,
nrows = 2, figsize = (16, 7))
for p, ax in zip(p_values, axes.flat):
### BEGIN Solution (do not delete this comment)
z = np.linalg.norm([xx, yy], ord = p, axis = 0)
ax.contourf(yy, xx, z, 25, cmap = 'coolwarm')
ax.contour(yy, xx, z, [1], colors = 'fuchsia', linewidths = 3)
ax.set_title(f'p = {p}')
ax.legend([f'$x: |x|_{{{p}}} = 1$']);
### END Solution (do not delete this comment)
plt.show()
Which parameters should be specified in ax.legend() in order to plot the graph clear.
You could create the legend using an explicit handle. In this case the fuchsia colored line is stored as the last element of ax.collections. Creating the legend with only labels, when there were no "handles with labels" set, could be the cause of the weird purple dots.
import matplotlib.pyplot as plt
import numpy as np
p_values = [0., 0.05, 0.25, 0.5, 1, 1.5, 2, 5, 10, np.inf]
xx, yy = np.meshgrid(np.linspace(-3, 3, num=101),
np.linspace(-3, 3, num=101))
fig, axes = plt.subplots(ncols=(len(p_values) + 1) // 2,
nrows=2, figsize=(16, 7))
cmap = plt.get_cmap('magma').copy()
cmap.set_extremes(over='green', under='black', bad='turquoise')
for p, ax in zip(p_values, axes.flat):
### BEGIN Solution (do not delete this comment)
z = np.linalg.norm([xx, yy], ord=p, axis=0)
cnt = ax.contourf(yy, xx, z, 25, cmap='coolwarm')
ax.contour(yy, xx, z, [1], colors='fuchsia', linewidths=3)
ax.set_title(f'p = {p}')
ax.legend(handles=[ax.collections[-1]], labels=[f'$x: |x|_{{{p}}} = 1$'])
plt.colorbar(cnt, ax=ax)
### END Solution (do not delete this comment)
plt.tight_layout()
plt.show()

Prevent axes from cutting off dots in matplotlib scatter plots

import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color='red')
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
If one was to plot this data, the dots on the axes are partially cut off. Is there a way to prevent this (i.e. can the dots be plotted on top of the axes)?
Setting the clip_on attribute to False allows you to go beyond the axes, but by default the axes will be on top. For example, the script
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color="red", clip_on=False)
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
Yields the following.
Note that the axes "cut through" the dots. If you want the dots to go on top of the axes/labels, you need to change the default zorder. For example, the script
x = [1, 2, 3, 4]
y = [0, 1, 7, 2]
plt.scatter(x, y, color="red", clip_on=False, zorder = 10)
plt.title('number of iterations')
plt.xlim([1, 4])
plt.ylim([1, 8])
yields
Note: any zorder value 3 or greater will work here.
Set clip_on to False:
plt.scatter(x, y, color='red', clip_on=False)

Got more ylabels than specified by set_yticklabel() [duplicate]

Here is a simple plot:
1) How to disable the ticks?
2) How to reduce their number?
Here is a sample code:
from pylab import *
import numpy as np
x = [5e-05, 5e-06, 5e-07, 5e-08, 5e-09, 5e-10]
y = [-13, 14, 100, 120, 105, 93]
def myfunc(x,p):
sl,yt,yb,ec=p
y = yb + (yt-yb)/(1+np.power(10, sl*(np.log10(x)-np.log10(ec))))
return y
xp = np.power(10, np.linspace(np.log10(min(x)/10), np.log10(max(x)*10), 100))
pxp=myfunc(xp, [1,100,0,1e-6])
subplot(111,axisbg="#dfdfdf")
plt.plot(x, y, '.', xp, pxp, 'g-', linewidth=1)
plt.xscale('log')
plt.grid(True,ls="-", linewidth=0.4, color="#ffffff", alpha=0.5)
plt.draw()
plt.show()
Which produces:
plt.minorticks_off()
Turns em off!
To change the number of them/position them, you can use the subsx parameter. like this:
plt.xscale('log', subsx=[2, 3, 4, 5, 6, 7, 8, 9])
From the docs:
subsx/subsy: Where to place the subticks between each major tick.
Should be a sequence of integers. For example, in a log10 scale: [2,
3, 4, 5, 6, 7, 8, 9]
will place 8 logarithmically spaced minor ticks between each major
tick.
Calling plt.minorticks_off() will apply this to the current axis. (The function is actually a wrapper to gca().minorticks_off().)
You can also apply this to an individual axis in the same way:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.minorticks_off()
from pylab import *
import numpy as np
x = [5e-05, 5e-06, 5e-07, 5e-08, 5e-09, 5e-10]
y = [-13, 14, 100, 120, 105, 93]
def myfunc(x,p):
sl,yt,yb,ec=p
y = yb + (yt-yb)/(1+np.power(10, sl*(np.log10(x)-np.log10(ec))))
return y
xp = np.power(10, np.linspace(np.log10(min(x)/10), np.log10(max(x)*10), 100))
pxp=myfunc(xp, [1,100,0,1e-6])
ax=subplot(111,axisbg="#dfdfdf")
plt.plot(x, y, '.', xp, pxp, 'g-', linewidth=1)
plt.xscale('log')
plt.grid(True,ls="-", linewidth=0.4, color="#ffffff", alpha=0.5)
plt.minorticks_off() # turns off minor ticks
plt.draw()
plt.show()

How to plot text below xticks (using x-coords) in matplotlib when x-axis are dates?

I'm trying to plot arbitrary text below the x tick marks in a matplotlib figure (see example figure below). I'm using dates as the x-axis and, for instance, I want to display counts of some variable associated with each date.
In the example below I use the relative positions from 0-1 within the figure for the x-coordinate of where the text should be positioned. However I've just guessed these relative values (using trial and error) and so I would like to know how would one plot text below the x-ticks using the actual positions of the x-data instead of using these 0-1 relative scaling that's the default in figtext?
import numpy as np
import matplotlib.pyplot as plt
import datetime
x = [datetime.datetime(2010, 12, 1, 0, 0),
datetime.datetime(2011, 1, 1, 0, 0),
datetime.datetime(2011, 5, 1, 1, 0)]
y = [4, 9, 2]
fig, ax = plt.subplots()
ax.bar(x, y, width = 20)
xticks = ax.get_xticks()
xtick_rel_position = np.linspace(0.13, 0.81, len(xticks)) # <- these are just guessed
counts = np.random.randint(0, 25, len(xticks))
for i, xpos in enumerate(xtick_rel_position):
plt.figtext(xpos, 0.028, "Below tick\nlabel "+str(i),
size = 6, ha = 'center')
plt.figtext(xpos, 0.005, "Count: "+str(counts[i]),
size = 6, ha = 'center')
# For better aesthetics
ax.yaxis.set_visible(False)
plt.show()
I've tried including transform = ax.transAxes with the actual x coordinates from ax.get_xticks() but this doesn't change anything.
Use ax.text for text positions relative to axis positions rather than fig positions:
import matplotlib.pyplot as plt
import datetime
import numpy as np
x = [datetime.datetime(2010, 12, 1, 0, 0),
datetime.datetime(2011, 1, 1, 0, 0),
datetime.datetime(2011, 5, 1, 1, 0)]
y = [4, 9, 2]
fig, ax = plt.subplots()
ax.bar(x, y, width = 20, align='center')
counts = np.random.randint(0, 25, len(ax.get_xticks()))
for i, xpos in enumerate(ax.get_xticks()):
ax.text(xpos,-1, "Below tick\nlabel "+str(i),
size = 6, ha = 'center')
ax.text(xpos, -1.25, "Count: "+str(counts[i]),
size = 6, ha = 'center')
You can use the date2num function to do the conversion, and a DataFormatter to display the dates correctly. Finally get_xticks is used to get the locations of the ticks for the text to be accurately displayed underneath:
import numpy as np
import matplotlib.pyplot as plt
import datetime
import matplotlib
x = [datetime.datetime(2010, 12, 1, 0, 0),
datetime.datetime(2011, 1, 1, 0, 0),
datetime.datetime(2011, 5, 1, 1, 0)]
y = [4, 9, 2]
hfmt = matplotlib.dates.DateFormatter('%b %Y')
months = matplotlib.dates.MonthLocator(range(1, 13), bymonthday=1, interval=1)
xs = matplotlib.dates.date2num(x)
fig, ax = plt.subplots()
ax.bar(xs, y, width=20)
ax.xaxis.set_major_locator(months)
ax.xaxis.set_major_formatter(hfmt)
ax.yaxis.set_visible(False)
for i, x in enumerate(ax.get_xticks()):
plt.text(x, -1.3, "Below tick\nlabel {}\nCount x".format(i), size=7, ha='center')
fig.subplots_adjust(bottom=0.15) # Add space at bottom
plt.show()
This would display:

Matplotlib: Getting different colors in data lines with error bars

I am trying to draw two data lines with error bars, each having the same color as the data line. However, I get another thin line with a color I have not specified in each data line when I add an error bar.
Also, I would like to make the caps of the error bars thicker but the option capthick is not valid here.
Could anybody please help me fix these issues?
This is my code.
import matplotlib.pyplot as plt
from pylab import *
import numpy as np
xaxis = [1, 2, 3]
mean1 = [1,2,3.6]
se1 = [0.2, 0.5, 0.9]
mean2 = [10, 29, 14]
se2 = [3, 4, 2]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlabel('X', fontsize = 16)
ax.set_ylabel('Y', fontsize = 16)
ax.axis([0, 5, 0, 35])
ax.plot(xaxis, mean1, 'r--', linewidth = 4)
ax.errorbar(xaxis, mean1, yerr = se1, ecolor = 'r', elinewidth = 2, capsize = 5)
ax.plot(xaxis, mean2, 'b--', linewidth = 4)
ax.errorbar(xaxis, mean2, yerr = se2, ecolor = 'b', elinewidth = 2, capsize = 5)
plt.show()
The extra thin line is coming from the errorbar() call.
errorbar will draw a line too, what you're doing is changing the colour of the error bars, but not the actual lines (hence it using the standard matplotlib first two colours, blue and green.
it's all in the documentaion, here.
To achieve what you want, you only need to use the errorbar() function;
This does what you want i think, maybe jsut tweak the numbers a bit.
import matplotlib.pyplot as plt
from pylab import *
import numpy as np
xaxis = [1, 2, 3]
mean1 = [1,2,3.6]
se1 = [0.2, 0.5, 0.9]
mean2 = [10, 29, 14]
se2 = [3, 4, 2]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlabel('X', fontsize = 16)
ax.set_ylabel('Y', fontsize = 16)
ax.axis([0, 5, 0, 35])
linestyle = {"linestyle":"--", "linewidth":4, "markeredgewidth":5, "elinewidth":5, "capsize":10}
ax.errorbar(xaxis, mean1, yerr = se1, color="r", **linestyle)
ax.errorbar(xaxis, mean2, yerr = se2, color="b", **linestyle)
plt.show()
I put the common line style arguments into a dict which gets unpacked.

Categories