Plotting with errorbars in Python - python

I have a list, the form is [[(x1, y1), (x2, y2), (x3, y3)], [...], [...]]
[[(10.0, -1.0), (7.0, 0.05889647076017157), (13.0, 0.47096776983628086)], [(10.5, -1.0), (13.0, 0.07080679131269396), (7.5, 0.16547229577841294)], [(11.0, -1.0), (8.0, 0.27471205881135075), (13.5, 0.682988382311833)]]
I would like to extract the first index in tuples from the list.
For example, the list above would be -> [[(10.0), (7.0), (13.0)], [(10.5), (13.0), (7.5)], [(11.0), (8.0), (13.5)]] (The form: [[(x1), (x2), (x3)], [(x4), (x5), (x6)], [(x7), (x8), (x9)]]
and then turn into a plot with errorbars. (The first value in the tuples would be the main values, and other two values would be errors)
This is what I am trying to get:
How can I do this? I can't find any similar example online.

You can get the list out in this way:
vals = [[i[0] for i in tup] for tup in lst ]
vals
[[10.0, 7.0, 13.0], [10.5, 13.0, 7.5], [11.0, 8.0, 13.5]]
To plot, it's easier to have it in a np matrix, and sorted because the error bar function needs the length as input and not the coordinate:
import numpy as np
import matplotlib.pyplot as plt
vals = np.sort(np.array(vals))
vals[:,[0,2]] = vals[:,[0,2]] - vals[:,1].reshape(-1,1)
vals
array([[-3. , 10. , 3. ],
[-3. , 10.5, 2.5],
[-3. , 11. , 2.5]])
fig, ax = plt.subplots(1, 1)
ax.errorbar(vals[:,1], vals[:,1], yerr=[-vals[:,0],vals[:,2]], fmt='o')
plt.show()

You can try
import matplotlib.pyplot as plt
x = [[(10.0, -1.0), (7.0, 0.05889647076017157), (13.0, 0.47096776983628086)], [(10.5, -1.0), (13.0, 0.07080679131269396), (7.5, 0.16547229577841294)], [(11.0, -1.0), (8.0, 0.27471205881135075), (13.5, 0.682988382311833)]]
plt.boxplot([[j[0] for j in i] for i in x])

Related

Matplotlib Imshow Showing the Wrong Colours on Update

I'm trying to make a heatmap over time, but I think matplotlib is messing with the plot colours.
My code is based on the heat equation, I think the specs are not important, the main thing is that I am creating a 3D array and plotting a slice from that array (a 2D matrix), setting which slice I plot using the matplotlib widget Slider.
The important part of the code is this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.colors import LogNorm
def update(val):
newdata = mat[:,:,int(val)]
plot.set_data(newdata)
plt.title(f'{val}')
plt.draw()
def init_plot():
global plot
fig, ax = plt.subplots()
flukacolours = [(1.0, 1.0, 1.0), (0.9, 0.6, 0.9), (1.0, 0.4, 1.0), (0.9, 0.0, 1.0), (0.7, 0.0, 1.0), (0.5, 0.0, 0.8), (0.0, 0.0, 0.8),
(0.0, 0.0, 1.0), (0.0, 0.6, 1.0), (0.0, 0.8, 1.0), (0.0, 0.7, 0.5), (0.0, 0.9, 0.2), (0.5, 1.0, 0.0), (0.8, 1.0, 0.0),
(1.0, 1.0, 0.0), (1.0, 0.8, 0.0), (1.0, 0.5, 0.0), (1.0, 0.0, 0.0), (0.8, 0.0, 0.0), (0.6, 0.0, 0.0), (0.0, 0.0, 0.0)]
cmap_name = 'fluka'
cm = colors.LinearSegmentedColormap.from_list(cmap_name, flukacolours, N=30)
plot = plt.imshow(mat[:,:,0], cmap=cm, norm=LogNorm(vmin=mat.min(), vmax=mat.max()), aspect='auto')
ax = plot.axes
cbar = plt.colorbar(plot, ax=ax)
plt.subplots_adjust(left=0.10, bottom=0.15, right=1, top=0.9)
axfreq = plt.axes([0.10, 0.02, 0.8, 0.03])
freq_slider = Slider(ax=axfreq, label='Slice', valmin=0, valmax=mat.shape[2], valinit=0, valstep=1, orientation='horizontal')
freq_slider.on_changed(update)
plt.show()
if __name__ == "__main__":
mat = crazy_function() # This function returns a 3D np.array
init_plot()
The problem is seen in some slices of the plot, where the colours just... break. In the images below I am showing the differences between 3 consecutive slices. At this point, I thought the problem was in my crazy_function(), but then I noticed the graph value that appears in the upper right corner when you place the cursor inside the chart.
Trying to place the cursor at the same maximum point for each plot, the 36th slice is showing a green tint, which would mean a value in the order 10⁻¹⁶ (as shown in colorbar), but the cursor value shows 7x10⁻⁸, which is the right value of the array that matplotlib is not showing correctly.
.
I think the problem might be my custom colour scale, or more likely the absurdly large scale of the colorbar. Because changing the scale vmin and vmax in the plt.imshow, the colour break tends to decrease and even stop. Which is not a problem, I even prefer a shorter scale to visualize the data, but I was really curious about the cause of this problem.
If you know the answer, I'd love to know. In case it matters, my current version of matplotlib is 3.5.1.

How to unpack list of lists into x and y values for plotting a polygon?

I have a list of points that
import matplotlib.pyplot as plt
polygons = [[(5.0, 0.05), (10.0, 0.05), (10.0, -0.05), (5.0, -0.05)],
[(0.0, 0.05), (5.0, 0.05), (5.0, -0.05), (0.0, -0.05)]]
coord = []
for item in polygons:
coord.extend(item)
coord.append(coord[0]) #repeat the first point to create a 'closed loop'
xs, ys = zip(*coord) #create lists of x and y values
plt.figure()
plt.fill(xs,ys,'k')
plt.ylim(-1, 1)
plt.xlim(0, 10)
plt.show()
And get the following picture:
which is wrong the plot should look like this, two rectangles next to each other:
How can I loop through the list in a correct way and assign the values to corresponding x and y to plot the correct image?
It's a problem of points ordering. A possible approach is to sort your coordinates using topological sort (please install networkx to do so):
import networkx as nx
G = nx.DiGraph()
for polygon in polygons:
G.add_edges_from(zip(polygon[:-1], polygon[1:]))
coord = list(nx.topological_sort(G))
coord.append(coord[0])
And now you can plot coord.
This worked , but the problem is that the plot is not uniform. There is a boundary between the first polygon and the second one. Now the question is how to fill uniformely so there is no boundary.
import matplotlib.pyplot as plt
polygons = [[(5.0, 0.05), (10.0, 0.05), (10.0, -0.05), (5.0, -0.05)],
[(0.0, 0.05), (5.0, 0.05), (5.0, -0.05), (0.0, -0.05)]]
for item in polygons:
xs, ys = zip(*item)
plt.fill(xs,ys, 'k')
plt.show()

In matplotlib how do I fill between two curves defined by two different sets of arrays?

I have two curves defined by two sets of arrays: (x1, y1) and (x2, y2) and I want to fill between them with polygons. All arrays are the same length but x1 and x2 contain different values.
plt.fill_between(x, y1, y2) requires that both curves share the same x-array.
How do I do something like fill_between(x1, y1, x2, y2)?
For instance if:
x1 = np.array([1.0, 2.0, 3.0, 4.0, 5.0]) and y1 = np.array([3.0, 2.0, 3.0, 2.0, 3.0]) define the first curve
and
x2 = np.array([1.5, 2.5 ,3.5 ,4.5 , 5.5]) and y2 = np.array([5.0, 6.0, 7.0, 8.0, 9.0]) define the second.
How can I fill colour between curves (x1, y1) and (x2, y2) using four polygons (the left and right boundaries need not be vertical)?
To clarify, the four polygons (A,B,C,D) would have coordinates:
A: [(1.0, 3.0), (1.5, 5.0), (2.5, 6.0), (2.0, 2.0)]
B: [(2.0, 2.0), (2.5, 6.0), (3.5, 7.0), (3.0, 3.0)]
C: [(3.0, 3.0), (3.5, 7.0), (4.5, 8.0), (4.0, 2.0)]
D: [(4.0, 2.0), (4.5, 8.0), (5.5, 9.0), (5.0, 3.0)]
I'm struggling to interpret your question unambiguously, but I think you just want to use fill, something like:
import numpy as np
import matplotlib.pyplot as plt
x1 = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
y1 = np.array([3.0, 2.0, 3.0, 2.0, 3.0])
x2 = np.array([1.5, 2.5, 3.5, 4.5, 5.5])
y2 = np.array([5.0, 6.0, 7.0, 8.0, 9.0])
plt.plot(x1, y1, 'o')
plt.plot(x2, y2, 'x')
plt.fill(
np.append(x1, x2[::-1]),
np.append(y1, y2[::-1]),
)
would give you
You can use polygonal patches do draw quadrilaterals filling the space between the two curves — the only tricky point is the generation of the 5 points that define the polygon but (ab)using zip it can be done... also you need to know how to place the polygons on the plot, but it's easy when you know of matplotlib.collections.PatchCollection and ax.add_collection
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
x1 = np.linspace(0,6,21) ; y1 = np.sin(x1)
x2 = x1+0.28 ; y2 = np.cos(x2)
fig, ax = plt.subplots()
ax.plot(x1, y1, x2, y2)
patches = [Polygon(poly) for poly in (
[p0,p1,p2,p3,p0] for p0,p1,p2,p3 in
zip(zip(x1,y1),zip(x1[1:],y1[1:]),zip(x2[1:],y2[1:]),zip(x2,y2)))
ax.add_collection(PatchCollection(patches, alpha=0.6))
As you can see, it's not perfect but maybe it's good enough...

Matplotlib Line3DCollection for time-varying colors

I'm attempting to plot 3D line trajectories that evolve over time, and I would like the colors to change to show that passage of time (e.g. from light blue to dark blue). However, there is a distinct lack of tutorials for using matplotlib's Line3DCollection; this is the closest I could find, but all I'm getting is a white line.
Here's my code.
import matplotlib.pyplot as plot
from mpl_toolkits.mplot3d.axes3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Line3DCollection
import numpy as np
# X has shape (3, n)
c = np.linspace(0, 1., num = X.shape[1])[::-1]
a = np.ones(shape = c.shape[0])
r = zip(a, c, c, a) # an attempt to make red vary from light to dark
# r, which contains n tuples of the form (r,g,b,a), looks something like this:
# [(1.0, 1.0, 1.0, 1.0),
# (1.0, 0.99998283232330165, 0.99998283232330165, 1.0),
# (1.0, 0.9999656646466033, 0.9999656646466033, 1.0),
# (1.0, 0.99994849696990495, 0.99994849696990495, 1.0),
# ...,
# (1.0, 1.7167676698312416e-05, 1.7167676698312416e-05, 1.0),
# (1.0, 0.0, 0.0, 1.0)]
fig = plot.figure()
ax = fig.gca(projection = '3d')
points = np.array([X[0], X[1], X[2]]).T.reshape(-1, 1, 3)
segs = np.concatenate([points[:-1], points[1:]], axis = 1)
lc = Line3DCollection(segs, colors = r)
ax.add_collection3d(lc)
ax.set_xlim(-0.45, 0.45)
ax.set_ylim(-0.4, 0.5)
ax.set_zlim(-0.45, 0.45)
plot.show()
However, here's what I get:
Just a bunch of white line segments, no shift in the color. What am I doing wrong? Thanks!
Your code works just fine, here's a bit of a sample. Basically, this is your code with a custom X set.
fig = plot.figure();
ax = fig.gca(projection = '3d')
X = [(0,0,0,1,0),(0,0,1,0,0),(0,1,0,0,0)]
points = np.array([X[0], X[1], X[2]]).T.reshape(-1, 1, 3)
r = [(1.0, 1.0, 1.0, 1.0), (1.0, 0.75, 0.75, 1.0), (1.0, 0.5, 0.5, 1.0), (1.0, 0.25, 0.25, 1.0), (1.0, 0.0, 0.0, 1.0)];
segs = np.concatenate([points[:-1], points[1:]], axis = 1)
ax.add_collection(Line3DCollection(segs,colors=list(r)))
plot.show()
And the plot looks like this:
Wow, so it turns out the problem was that X was actually not of shape (3, n), but rather something like (3, n^10), but I was only plotting n points, hence the color appeared to never change (and why r seems to have extremely small intervals...there were something like 58,000 points when I was plotting only 250).
So yes, it was a bug. Sorry about that; it works fine now.

Customize colorbar in matplotlib

I have a 2D array that I'm plotting with imshow and I would like to have costums colors depending on the value of each pixel of my array. I'll explain it with an example.
from pylab import *
from numpy import *
img = ones((5,5))
img[1][1] = 2
imshow(img,interpolation='nearest');colorbar()
If you ran this code you would see a red square in a blue background. The red square corresponds to the pixel [1][1] in img, while the other pixel are colored blue because they have a value of 1. What if I want the red square to be colored with a custom color?
Or more generally, if I have a 2D array like img in the example, how can I color pixel with the same value with a color I can choose.
I have found this page that explains how to generate a custom colorbar but that's not useful: http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps
That link you sent has the following:
But, what if I think those colormaps are ugly? Well, just make your
own using matplotlib.colors.LinearSegmentedColormap. First, create a
script that will map the range (0,1) to values in the RGB spectrum. In
this dictionary, you will have a series of tuples for each color
'red', 'green', and 'blue'. The first elements in each of these color
series needs to be ordered from 0 to 1, with arbitrary spacing
inbetween. Now, consider (0.5, 1.0, 0.7) in the 'red' series below.
This tuple says that at 0.5 in the range from (0,1) , interpolate from
below to 1.0, and above from 0.7. Often, the second two values in each
tuple will be the same, but using diferent values is helpful for
putting breaks in your colormap. This is easier understand than might
sound, as demonstrated by this simple script:
1 from pylab import *
2 cdict = {'red': ((0.0, 0.0, 0.0),
3 (0.5, 1.0, 0.7),
4 (1.0, 1.0, 1.0)),
5 'green': ((0.0, 0.0, 0.0),
6 (0.5, 1.0, 0.0),
7 (1.0, 1.0, 1.0)),
8 'blue': ((0.0, 0.0, 0.0),
9 (0.5, 1.0, 0.0),
10 (1.0, 0.5, 1.0))}
11 my_cmap = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict,256)
12 pcolor(rand(10,10),cmap=my_cmap)
13 colorbar()
Isn't this exactly what you want?
Here's an example of how to do it with the image you provided:
import matplotlib
from matplotlib import pyplot as plt
from pylab import *
img = ones((5,5))
img[1][1] = 2
cdict = {'red': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.7),
(1.0, 1.0, 1.0)),
'green': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.0),
(1.0, 1.0, 1.0)),
'blue': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.0),
(1.0, 0.5, 1.0))}
my_cmap = matplotlib.colors.LinearSegmentedColormap('my_colormap',cdict,256)
plt.pcolor(img,cmap=my_cmap)
plt.colorbar()
plt.show()
Also, if you really want to map a number to a colour you can use discrete_cmap as specified in that example you linked to, here's the example method the scipy documentation provides:
def discrete_cmap(N=8):
"""create a colormap with N (N<15) discrete colors and register it"""
# define individual colors as hex values
cpool = [ '#bd2309', '#bbb12d', '#1480fa', '#14fa2f', '#000000',
'#faf214', '#2edfea', '#ea2ec4', '#ea2e40', '#cdcdcd',
'#577a4d', '#2e46c0', '#f59422', '#219774', '#8086d9' ]
cmap3 = col.ListedColormap(cpool[0:N], 'indexed')
cm.register_cmap(cmap=cmap3)

Categories