I am trying to plot rectangles inside a circle and color each rectangle based on their value. Each rectangle depicts the failure rate at that position. Initially I have divided the range of values into 5 intervals of 20 limits each and assigned a fixed color as below example.
{'0-20':'Yellow', '21-40':'Orange', '41-60':'Coral', '61-80':'Red', '81-100':'Black'}
Later, I scrapped the idea and went with the 'plasma' cmap from matplotlib.colors.cmaps. It ideally colored the rectangle in the shades of yellow to purple. However, it misses the small data point values to show on the plot. I am looking for the flexibility of changing the range of intervals.
from matplotlib import cm
plasma = cm.get_cmap('plasma', 30)
Ideally I want something as below. If the max value of a rectangle is 92 and min value is 0. I want to divide the range into 6 intervals and plot them based on the interval . Attached is the color bar I am looking for.
Is there a way to achieve this in matplotlib? Kindly help.
Edit:
Adding few more details.
I am not looking for the fixed color , rather I am looking for gradient which intensifies from lower limit to upper limit of the range in each interval.For example in the attached picture all value between 0 to 15.33 have the color intensifying from yellow to red.
I agree with ImportanceOfBeingErnest's comment that single colors in a legend or a regular colorbar might be helpful. Here is an example of how the former could be created using colorbar tick labelling:
# import modules
import numpy as np
import matplotlib.pyplot as plt
# select colormap
cmap = plt.cm.get_cmap('plasma')
# define bins
bins = [0, 15.333, 30.666, 46, 61.333, 76.666, 92]
# create dummy array for heatmap
imag = np.reshape(np.linspace(0, 92, 50), (10, -1))
# prepare tick positions
pairs = list(zip(bins[:-1], bins[1:]))
labs = ['']*len(bins) + ['%05.2f ≤ x < %05.2f' % pair for pair in pairs]
bins = bins + [0.5*sum(pair) for pair in pairs]
plt.imshow(imag, cmap=cmap, aspect='auto', origin='lower')
# plot colorbar
cbar = plt.colorbar(ticks=bins)
cbar.ax.set_yticklabels(labs)
plt.tight_layout()
Related
I am trying to plot a scatter plot where each point in the scatter plot should correspond to a particular shade of a given color of my choice. The mpl documentation states that if I set something like:
color = '0.7'
it gives me a shade of grey with that scaled intensity of 0.7. I am reading the intensity of the colours from an array with values between 0 and 1 and each value corresponds to the intensity of that point in the scatter plot. My code below is as follows:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import math
tsne_embeddings = np.load("tsne_embeddings.npy")
labels = np.load('labels.npy')
weights = np.load('weights.npy')
# Scale the weights from 0 to 1
max_weight = max(weights)
min_weight = min(weights)
weights = (weights - min_weight)/(max_weight - min_weight)
print(tsne_embeddings.shape)
x = list(tsne_embeddings[:,0])
y = list(tsne_embeddings[:,1])
labels = list(labels)
weights = np.round(weights,decimals=2)
weights = (np.exp(weights) - 1)/(np.exp(1) - 1)
weights = list(weights)
print(min(weights),max(weights))
for i, shade in enumerate(weights):
plt.scatter(x[i],y[i],color=shade,marker = '+')
plt.show()
I am scaling those weights exponentially hoping for a better variation.
So, essentially, my questions are:
How do I change the color to say shades of blue or red or green as opposed to just greyscale?
Is the approach which I follow for greyscale correct?
Thank you!
To make your approach work for shades of grey, you need to convert the value to a string, so plt.scatter(..., color=str(shade)).
The more standard way of working with matplotlib would be to directly use the weights, without rescaling them to the range 0 to 1, use a colormap, and calling scatter directly with the arrays. The weights go into the c= parameter. For grey values this would be plt.scatter(x, y, c=weights, cmap='Greys', marker='+'). An additional feature of matplotlib is that with this information it can automatically create a colorbar mapping the grey values to the corresponding weight. If only one scatter plot is created, plt.colorbar() without parameters will show this colorbar.
Similar colormaps exist of 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds', ... See the official doc with a complete list. If the range between light and dark goes the wrong way, appending '_r' to the name will use the opposite color range (so, 'Greys' goes from white to black, while 'Greys_r' goes from black to white).
Here is a working example using the values from 1 to 10 for the three arrays:
from matplotlib import pyplot as plt
import numpy as np
x = np.arange(1, 11)
y = np.arange(1, 11)
weights = np.arange(1, 11)
plt.scatter(x, y, c=weights, cmap='Greys', marker='+')
plt.colorbar()
plt.show()
You can use colormaps in python to generate different shades of blue green etc.
https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html
I am using Blues color map here
import matplotlib.pyplot as plt
import matplotlib as mpl
norm = mpl.colors.Normalize(vmin=min(weights), vmax=max(weights))
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=mpl.cm.Blues)
for i, xi in enumerate(x):
plt.scatter(x[i],y[i],color=cmap.to_rgba(i+1),marker = '+')
plt.show()
I would like to plot contourf with (lat,depth,temp) and then have similar spacing as in the figure below (the temperature vary more near the surface then at depth, so I want to emphasized this region).
My depth array is not uniform (i.e. depth = [5,15,...,4975,5185,...]. I want to have such non-uniform vertical spacing.
I would like to show yticks = [10,100,500,1000,1500,2000,3000,4000,5000], and depth array does not have those exact values.
z = np.arange(0,50) # I want uniform spacing
pos = ([0,2,5,10,15,20,30,40,48]) # I want some yticks (not all of them)
ax=plt.contourf(lat,z,temp) # temp is a variable with dimensions (lat,depth)
plt.colorbar()
plt.gca().yaxis.set_ticks(pos) # Set some yticks, not all of them
plt.yticks(z[pos],depth[pos].astype(int)) # Replace the dummy values of z-array by something meaningful
plt.gca().invert_yaxis()
plt.grid(linestyle=':')
plt.gca().set(ylabel='depth (m)',xlabel='Latitude')'''
Potential Temperature of the Atlantic Ocean:
Per the matplotlib docs on yticks, you can specify the labels you want to use. In your case, if you want to show the labels [10,100,500,1000,1500,2000,3000,4000,5000] you can simply pass that list as the second argument in plt.yticks(), like so
plt.yticks(z[pos], [10,100,500,1000,1500,2000,3000,4000,5000])
and it will display the yticks accordingly. The issue arises in the specification of the positions - since the depth array does not have points corresponding exactly to the desired ytick values you will need to interpolate in order to find the exact position at which to place the labels. Unless the approximate positions specified in pos are already sufficient, in which case the above suffices.
If the depth data are not uniformly spaced then you can use numpy.interp to perform the interpolation, as shown below
import matplotlib.pyplot as plt
import numpy as np
# Create some depth data that is not uniformly spaced over [0, 5500]
depth = [(np.random.random() - 0.5)*25 + ii for ii in np.linspace(0, 5500, 50)]
lat = np.linspace(-75, 75, 50)
z = np.linspace(0,50, 50)
yticks = [10,100,500,1000,1500,2000,3000,4000,5000]
# Interpolate depths to get z-positions
pos = np.interp(yticks, depth, z)
temp = np.outer(lat, z) # Arbitrarily populate temp for demonstration
ax = plt.contourf(lat,z,temp)
plt.colorbar()
plt.gca().yaxis.set_ticks(pos)
plt.yticks(pos,yticks) # Place yticks at interpolated z-positions
plt.gca().invert_yaxis()
plt.grid(linestyle=':')
plt.gca().set(ylabel='Depth (m)',xlabel='Latitude')
plt.show()
This will find the exact positions where the yticks would fall if the depth array had data at those positions and place them accordingly as shown below.
How do I change a colormap color scheme to show the same color beyond a point.
E.g. here's my colormap:
import palettable
cmap = palettable.colorbrewer.sequential.YlGn_9.mpl_colormap
If I use this colormap to plot a range from 0 to 100, how can I modify the color map such that beyond 50, it changes to the color red?
You could create the colormap for the given range (0 →100) by stacking two different colormaps on top of each other as shown:
Illustration:
import numpy as np
import matplotlib.pyplot as plt
import palettable
import matplotlib.colors as mcolors
# Set random seed
np.random.seed(42)
# Create random values of shape 10x10
data = np.random.rand(10,10) * 100
# Given colormap which takes values from 0→50
colors1 = palettable.colorbrewer.sequential.YlGn_9.mpl_colormap(np.linspace(0, 1, 256))
# Red colormap which takes values from 50→100
colors2 = plt.cm.Reds(np.linspace(0, 1, 256))
# stacking the 2 arrays row-wise
colors = np.vstack((colors1, colors2))
# generating a smoothly-varying LinearSegmentedColormap
cmap = mcolors.LinearSegmentedColormap.from_list('colormap', colors)
plt.pcolor(data, cmap=cmap)
plt.colorbar()
# setting the lower and upper limits of the colorbar
plt.clim(0, 100)
plt.show()
Incase you want the upper portion to be of the same color and not spread over the length of the colormap, you could make the following modification:
colors2 = plt.cm.Reds(np.linspace(1, 1, 256))
cmap.set_over("red")
And you may wanna use one of the norm functions to set your specific bounds. If using imshow, you can also set the parameter vmin=50 to make that your top value.
You can create a new colormap from an existing colormap using:
newcmap = cmap.from_list('newcmap',list(map(cmap,range(50))), N=50)
This new map uses the last value from the colormap for colors over 50. To make the last color red, we can just append red to the last color in the list that defines the colormap.
newcmap = cmap.from_list('newcmap',list(map(cmap,range(50)))+[(1,0,0,1)], N=51)
import palettable
from matplotlib import pyplot as plt
cmap = palettable.colorbrewer.sequential.YlGn_9.mpl_colormap
newcmap = cmap.from_list('newcmap',list(map(cmap,range(50))), N=50)
for x in range(80):
plt.bar(x,1, width=1, edgecolor='none',facecolor=newcmap(x))
plt.show()
newcmap = cmap.from_list('newcmap',list(map(cmap,range(50)))+[(1,0,0,1)], N=51)
for x in range(80):
plt.bar(x,1, width=1, edgecolor='none',facecolor=newcmap(x))
plt.show()
You can access the colors with:
cmap_dict = cmap._segmentdata
which yields a dictionary. By indexing it with:
red = cmap_dict["red"]
green= cmap_dict["green"]
blue = cmap_dict["blue"]
alpha = cmap_dict["alpha"]
Now you can add a color from the list like this:
red .append(red [1])
recombine them into a dictionary with the 4 keys like:
cmap_dict_new["red"] = red
and create a new colormap with:
new_cmap = palettable.palette.ListedColormap(cmap_dict_new)
I don't think you should change the colormap, but rather the object using the colormap. I asked a similar question not so long ago: change color for first level of contourf, and I took the answer from here: Python matplotlib change default color for values exceeding colorbar range
If you use contours in your plot for example, you should do something like this:
cs = pyplot.contourf(x,y,z, cmap=your_cmap)
cs.cmap.set_over('r') # Change color to red
cs.set_clim(0, 50) # Set the limit beyond which everything is red
cb = pyplot.colorbar(cs) # Plot the colorbar (if needed)
I want to plot the RGB histograms of an image using numpy.histogram.
(See my function draw_histogram below)
It works well for a regular range of [0, 255] :
import numpy as np
import matplotlib.pyplot as plt
im = plt.imread('Bulbasaur.jpeg')
draw_histogram(im, minimum=0., maximum=255.)
What I want to do :
I expect the images I use to have out of range values. Sometimes they will be out of range, sometimes not. I want to use the RGB histogram to analyse how bad the values are out of range.
Let's say I expect the values to be at worst in the interval [-512, 512]. I still want the histogram to display the in-range intensities at the right spot, and leave blank the unpopulated range sections. For example, if I draw the histogram of Bulbasaur.jpeg again but with range [-512, 512], I expect to see the same histogram but contracted along the "x" axis (between the two dashed lines in the histogram below).
The problem :
When I try to draw the histogram for an unregular range, something goes wrong :
import numpy as np
import matplotlib.pyplot as plt
im = plt.imread('Bulbasaur.jpeg')
draw_histogram(im, minimum=-512., maximum=512.)
My code for draw_histogram() :
def draw_histogram(im, minimum, maximum):
fig = plt.figure()
color = ('r','g','b')
for i, col in enumerate(color):
hist, bins = np.histogram(im[:, :, i], int(maximum-minimum), (minimum, maximum))
plt.plot(hist, color=col)
plt.xlim([int(minimum), int(maximum)])
# Draw vertical lines to easily locate the 'regular range'
plt.axvline(x=0, color='k', linestyle='dashed')
plt.axvline(x=255, color='k', linestyle='dashed')
plt.savefig('Histogram_Bulbasaur.png')
plt.close(fig)
return 0
Question
Does anyone know a way of properly drawing RGB histogram with unregular ranges?
You should pass x values to 'plt.plot'
I changed:
plt.plot(hist, color=col)
to this:
plt.plot(np.arange(minimum,maximum),hist, color=col)
With this change, the graph began to appear normally. Essentially, plt.plot was trying to start plotting the y-values you gave it from np.hist starting at 0. This works when your expected range starts at 0, but when you want to include negative numbers, plt.plot shouldn't start at 0, rather, it should start at minimum, so using np.range to manually assign x values fixes the problem.
I have a bar chart of N bars I built with python matplotlib.pyplot.bar and I would like to give each bar a different color following a shaded off:
for example if N = 4, I would like: yellow, orange, light red, dark red. I would like to stay in the yellow-red tonalities.
But I want the shade to take N as a parameter.
Can you help me please?
I suggest you use the color maps to accomplish what you want. There are a lot of color maps available, pick your favourite or make your own.
import matplotlib.pyplot as plt
import numpy as np
# number of bars
N = 20
# plot a random bar graph
fig = plt.figure()
ax = fig.add_subplot(111)
bars = ax.bar(np.arange(N), np.random.random(N))
# change the colors according to the color map 'jet'
for i, b in enumerate(bars):
b.set_color(plt.cm.jet(1. * i / (N - 1)))
This creates:
So, there are two points:
when the bar graph is drawn, bar returns a collection of bars
the color of each bar is changed by giving the desired color map an argument between 0..1 (here leftmost is 0 and rightmost 1, but you may pick only a small part of the color map if you want)
Defining your own color maps is easy, as well. Or if you only want to have a linear transition between two colors, you may skip using the color map altogether and make your own calculations. set_color expects to receive a color name or a 3 (RGB) or 4 (RGBA) element iterable.