Custom pcolor in matplotlib - python

I am plotting several heatmaps in matplotlib as shown below.
Here is my loop:
with open(gene_peak) as f:
count = 1
for line in f:
np_array=[]
gene_peak = line.strip().split("\t")
gene_id = gene_peak[0]
peaks = gene_peak[1].split(",")
for peak in peaks:
np_array.append(enhancer_fc[peak])
data, pval = stats.spearmanr(np.transpose(np.array(np_array)))
plt.subplot(4,3,count+1)
# plt.title(gene_id)
plt.pcolor(data, cmap=plt.cm.OrRd, vmin=-1, vmax=1)
plt.gca().invert_yaxis()
plt.gca().set_aspect(aspect='equal', adjustable='box-forced')
plt.xticks([])
plt.yticks([])
print count
count += 1
plt.show()
I am plotting the spearman correlations of different 2D arrays of different dimensions.
Question:
There are correlation values, so they range from -1 to 1. I want to add custom colorbar() such that values above 0.4 starts showing a gradient of red and below -0.4 shows a gradient of blue, such that I show only the points that are more than 0.4 and less than -0.4.
Also I would like to plot only one colorbar() such that the image looks cleaner. Any help would be appreciated, Thanks.

You can define your own discrete colormap using the ListedColorMap from Matplotlib. You can use the colorbar from one of the plots, and place it in position so it represent all of the plots visually. Here is an example with the colours you have given:
from matplotlib import colors
discrete_colors = [(255, 0, 20), (255, 70, 65), (255, 128, 110), (255, 181, 165), (64, 64, 64),
(0, 0, 0), (64, 64, 64), (124, 128, 217), (102, 107, 216), (69, 76, 215), (33, 33, 245)]
discrete_colors = [(r/255., g/255., b/255.) for r, g, b in discrete_colors]
my_colormap = colors.ListedColormap(discrete_colors)
subplot(211)
data = 2 * np.random.rand(10, 10) - 1.0
pcolor(data, cmap=my_colormap, vmin=-1, vmax=1)
subplot(212) # Some other plot
data = 2 * np.random.rand(10, 10) - 1.0
pc = pcolor(data, cmap=my_colormap, vmin=-1, vmax=1)
fig = gcf()
fig.subplots_adjust(right=0.70)
cax = fig.add_axes([0.80, 0.15, 0.05, 0.7])
fig.colorbar(pc, cax=cax)
You might have to adjust the code a bit. I'm using IPython 2.7.

Related

how to create a colormap and a colorbar with discrete values in python?

I want to build a colormap and a colorbar with specific values for a map of accumulated rainfall, as shown in the image:
enter image description here
This is the function that I have that graphs the map, however, it does not work correctly for me, currently it is not graphing the blue color, which goes between the values 1 to 5:
def plot_acumradar(path_plot, name_plot,lon, lat, lon_plot, lat_plot, radaracum):
data_g1 = radaracum
data_g1[data_g1==0] = np.nan
maxlon = -74.4000
minlon = -76.7000
minlat = 5.1000
maxlat = 7.3000
RR = [0, 0, 70, 44,255,255,255,255,128, 255]
GG = [255, 0,220,141,255,200,142, 0, 0, 153]
BB = [255,255, 45, 29, 75, 50, 0, 0,128, 255]
VariableLimits = np.array([1.,5.,10.,20.,30.,40.,50.,65., 80., 100.])
Custom_Color = list(zip(RR, GG,BB))
scale_factor = ((255-0.)/(VariableLimits.max() - VariableLimits.min()))
new_Limits = list(np.array(np.round((VariableLimits-VariableLimits.min())*\
scale_factor/255.,3),dtype = float))
Custom_Color = list(map(lambda x: tuple(ti/255. for ti in x) ,\
Custom_Color))
nueva_tupla = [((new_Limits[i]),Custom_Color[i],) for i in range(len(Custom_Color))]
my_colorbar = clr.LinearSegmentedColormap.from_list('RADAR',nueva_tupla)
norm = clr.BoundaryNorm(VariableLimits, ncolors=256)
print ('Plot imagen')
plt.close()
plt.cla()
plt.clf()
fig = plt.figure(figsize=(5.1,4.9))
fig.subplots_adjust(left = 0.0,right = 1.,top = 0.9, bottom = 0.15, hspace = 0.2,\
wspace = 0.2)
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Mercator(central_longitude=lon.mean(),
min_latitude=min(lat),
max_latitude=max(lat)))
projection = ccrs.PlateCarree()
ax.set_extent([minlon,maxlon,minlat,maxlat], crs=projection)
ax.tick_params(top='off', right='off', bottom ='off', left='off')
pm = ax.pcolormesh(lon_plot, lat_plot, data_g1, transform=projection, cmap = my_colorbar,\
norm = norm)
fig.colorbar(pm, ax=ax, extend='both', orientation='vertical')
plt.savefig(path_plot+name_plot, transparent=True)
and the graph looks like this:
enter image description here
How do I make it exactly the same as the first figure?
The first figure shows 10 colors, so 11 boundaries are needed. The code below temporarily adds an extra boundary, but doesn't display its tick label. cbar.ax.set_title() is used to add text on top of the colorbar. When working with a BoundaryNorm, the ListedColormap can be created without providing tuples.
To set the ticks and their labels at the left of the colorbar, cbar.ax.tick_params can be used. Some extra padding is needed, which can be added via fig.colorbar(..., padding=).
The example code uses a scatterplot to test the colorbar
import matplotlib.pyplot as plt
import matplotlib.colors as clr
import numpy as np
RR = [0, 0, 70, 44, 255, 255, 255, 255, 128, 255]
GG = [255, 0, 220, 141, 255, 200, 142, 0, 0, 153]
BB = [255, 255, 45, 29, 75, 50, 0, 0, 128, 255]
colors = np.c_[RR, GG, BB] / 255
my_colormap = clr.LinearSegmentedColormap.from_list('RADAR', colors)
VariableLimits = np.array([1, 5, 10, 20, 30, 40, 50, 65, 80, 100])
norm = clr.BoundaryNorm(np.append(VariableLimits, 1000), ncolors=256)
fig, ax = plt.subplots()
pm = ax.scatter(np.random.rand(100), np.random.rand(100), c=np.random.uniform(0, 120, 100),
cmap=my_colormap, norm=norm)
cbar = fig.colorbar(pm, ticks=VariableLimits, pad=0.1, ax=ax)
cbar.ax.set_title('(mm)', size=8)
cbar.ax.tick_params(left=True, right=False, labelleft=True, labelright=False)
plt.show()

Color by category in matplotlib using np.where

I'm trying to create a scatter plot with 100 data points and three variables: x value, y value, and category. This information is stored in an ndarray.
I can create the scatter plot, but I don't know how to use a different color for each category. I used the following code for the plot, which seems to work fine (although it's not finished):
def my_plot(data, color_map):
f, ax = plt.subplots()
ax.scatter(data.x, data.y, s = 150, edgecolors = "r")
return f
In my function, color_map is a parameter which refers to a dictionary I created to color the different categories (there are four in total). This is the dictionary:
color_map = {"winter":(15, 28, 75), "spring":(92, 57, 32), "summer":(255, 253, 211), "fall":(174, 12, 12)}
What I would like to do is to somehow integrate this color_map in my function so that each dot in my plot receives a different color.
I think this could be done using np.where to create a mask, but I'm not sure how to proceed...
The color values need to be divided by 255 because matplotlib likes them between 0 and 1.
With this dict you can create an array of colors for the categories:
from matplotlib import pyplot as plt
from matplotlib.lines import Line2D
import pandas as pd
import numpy as np
color_map = {"winter": (15, 28, 75), "spring": (92, 57, 32), "summer": (255, 253, 211), "fall": (174, 12, 12)}
color_map = {key: (r / 255, g / 255, b / 255,) for key, (r, g, b) in color_map.items()}
N = 200
data = pd.DataFrame({'x': np.random.uniform(1, 9, N), 'y': np.random.uniform(1, 5, N),
'cat': np.random.choice([*color_map.keys()], N)})
fig, ax = plt.subplots()
ax.scatter(data.x, data.y, s=150, color=[color_map[c] for c in data.cat], ec='r')
handles = [Line2D([], [], marker='o', ls='', color=col, markeredgecolor='r', label=label)
for label, col in color_map.items()]
plt.legend(handles=handles, bbox_to_anchor=[1.02, 1.02], loc='upper left')
plt.tight_layout()
plt.show()
PS: A similar plot can be generated with seaborn, which also automatically adds the corresponding legend. Note that the current version of matplotlib (3.3.1) has a problem with the hue parameter. Normally you would add it as hue='cat' but in this version a workaround via .to_list is needed.
import seaborn as sns
ax = sns.scatterplot(x='x', y='y', hue=data['cat'].to_list(), s=150, palette=color_map, edgecolor='r', data=data)

How to construct a color map in seaborn from a list of RGB colors?

I would like to start with a list of RGB colors, and from them construct a color map I can use in seaborn plots. I have found several instructions on how to change the default color map, but that's not what I'm looking for. I would like to construct a color map that I can use in the cmap argument of, for instance, the kdeplot command.
Constructing a matplotlib.colors.ListedColormap from a list of colors is fairly trivial. Here is an example using the first 4 colors in the tableau 20 color palette -
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib import cm
# Tableau 20 color palette for demonstration
colors = [(31, 119, 180), (174, 199, 232), (255, 127, 14), (255, 187, 120)]
# Conversion to [0.0 - 1.0] from [0.0 - 255.0]
colors = [(e[0] / 255.0, e[1] / 255.0, e[2] / 255.0) for e in colors]
cmap = ListedColormap(colors)
a = np.outer(np.linspace(0, 1, 20), np.linspace(0, 1, 20))
im = plt.imshow(a, cmap=cmap)
plt.colorbar(im)
plt.show()
However, if you don't already have a gradient in the list of colors (as the above does not) then it might be more useful to use a matplotlib.colors.LinearSegmentedColormap instead. This is a bit more tricky because of the format expected,
[...] segmentdata argument is a dictionary with a set of red, green and blue entries. Each entry should be a list of x, y0, y1 tuples, forming rows in a table [...].
Each row in the table for a given color is a sequence of x, y0, y1 tuples. In each sequence, x must increase monotonically from 0 to 1. For any input value z falling between x[i] and x[i+1], the output value of a given color will be linearly interpolated between y1[i] and y0[i+1]
Such a dictionary can be generated algorithmically by the method in the example below
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
from matplotlib import cm
# Tableau 20 color palette for demonstration
colors = [(31, 119, 180), (174, 199, 232), (255, 127, 14), (255, 187, 120)]
colors = [(e[0] / 255.0, e[1] / 255.0, e[2] / 255.0) for e in colors]
nc = len(colors)
c = np.zeros((3, nc, 3))
rgb = ['red', 'green', 'blue']
for idx, e in enumerate(colors):
for ii in range(3):
c[ii, idx, :] = [float(idx) / float(nc - 1), e[ii], e[ii]]
cdict = dict(zip(rgb, c))
cmap = LinearSegmentedColormap('tab20', cdict)
a = np.outer(np.linspace(0, 1, 20), np.linspace(0, 1, 20))
im = plt.imshow(a, cmap=cmap)
plt.colorbar(im)
plt.show()
Assuming the input list colors has the proper RGB format.

How do I plot stacked histograms side by side in matplotlib?

I'm looking to plot two side-by-side stacked histograms (similar to the example image below) in matplotlib.
I've tried several variations on
bins = np.arange(10)
a1,b1,c1 =plt.hist([arr1,arr2,arr3],bins,stacked=True)
a2,b2,c2 =plt.hist([arr4,arr5,arr6],bins,stacked=True)
But can't seem to avoid getting the second plot to directly overlay the first.
Any ideas on how this could be resolved?
The picture shows a bar chart and not a histogram. I am pointing this out, not only because I am an obnoxious pedant, but also because I believe it could help you find the right tool :-)
Indeed, for your purpose plt.bar is probably a better pick than plt.hist.
Based on Scironic's suggestion, I modified this demonstration example to make stacked bars, like the ones on your figure.
Adding an offset to the position index (first argument in plt.bar()) is what prevents the bars from overlapping each other.
import numpy as np
import matplotlib.pyplot as plt
N = 5
men1 = (130, 90, 70, 64, 55)
men2 = (120, 85, 62, 50, 53)
men3 = (100, 70, 60, 45, 50)
ind = np.arange(N) + .15 # the x locations for the groups
width = 0.35 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(ind, men1, width, color='g')
rects2 = ax.bar(ind, men2, width, color='r')
rects3 = ax.bar(ind, men3, width, color='b')
women4 = (140, 90, 78, 65, 50)
women5 = (130, 80, 70, 60, 45)
women6 = (120, 60, 60, 55, 44)
xtra_space = 0.05
rects2 = ax.bar(ind + width + xtra_space , women1, width, color='orange')
rects2 = ax.bar(ind + width + xtra_space, women2, width, color='cyan')
rects2 = ax.bar(ind + width + xtra_space, women3, width, color='purple')
# add some text for labels, title and axes ticks
ax.set_ylabel('Population, millions')
ax.set_title('Population: Age Structure')
ax.set_xticks(ind+width+xtra_space)
ax.set_xticklabels( ('USA', 'Brazil', 'Russia', 'Japan', 'Mexico') )
plt.show()

Matplotlib: Broken_barh plots with same height

I'm working with broken_barh plots. Is there any way to get a fixed height of a single broken_barh? The image should get bigger vertically, but proportions should stay the same.
Here is a simple example.
import matplotlib.pyplot as plt
import matplotlib as mlp
fig = plt.figure()
ax = fig.add_subplot(111)
broken_barh(self, xranges, yrange, **kwargs)
ax.broken_barh([(110, 30), (150, 10)], (0, 10), facecolors='blue')
ax.broken_barh([(10, 50), (100, 20), (130, 10)] , (10, 10),
facecolors=('red', 'yellow', 'green'))
ax.broken_barh([(50, 30), (85, 10)], (20, 10), facecolors='black')
ax.set_xlim(0,200)
ax.set_xlabel('seconds since start')
ax.set_yticks([0,10,20])
ax.set_yticklabels(['Bill', 'Jim', 'Jeff'])
ax.grid(True)
plt.savefig('broken_barh_example.png', bbox_inches='tight')
plt.show()
If I generate two plots, one with two broken_barh and the other with three, it looks like this:
with 2 broken_barh
http://imageshack.us/a/img195/747/brokenbarhexample2.png
with 3 broken_barh
http://img341.imageshack.us/img341/5650/brokenbarhexamplenoyran.png
The render fits everything into the available space. If you want the size of the figure to grow as you add more rows, you can do it by hand via
fig.set_size_inches(w, h * num_rows, forward=True)
to force a fixed bar height.
(doc)

Categories