Let's assume I have two numpy arrays (The ones I present are just examples):
import numpy as np
A = np.arange(144).reshape((12, 12))
np.random.shuffle(A)
B = np.ones((12,12))
B[0:10:4,:] = None
I want to plot A using imshow:
import matplotlib.pyplot as mplt
mplt.imshow(A, cmap = mplt.gray())
and overlay B so that the None areas are fully transparent and the one areas have an alpha of (e.g. alpha = 0.3.).
I already tried using something along the lines of:
mplt.imshow(B, cmap = mplt.get_cmap('Reds), alpha = 0.3)
but that does not work. Also tried to use masked arrays to create B, but cannot get my head around it. Any suggestions?
Thanks
EDIT:
I ended up using
my_red_cmap = mplt.cm.Reds
my_red_cmap.set_under(color="white", alpha="0")
which works like a charm (I tested Bill's solution as well, which also works perfectly).
If instead of None you use 0's for the transparent colors, you can take your favorite matplotlib colormap and add a transparent color at the beginning of it:
my_red_cmap = mplt.cm.Reds
my_red_cmap.set_under(color="white", alpha="0")
then you can just plot the array B with a global alpha of 0.3 whatever you want, using your custom color map, which will use a transparent white as its first value.
You can do the following:
import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
x = np.arange(100).reshape(10, 10)
y = np.arange(-50, 150, 2).reshape(10, 10)
y[y<x] = -100 # Set bad values
cmap1 = cm.gray
cmap2 = cm.Reds
cmap2.set_under((1, 1, 1, 0))
params = {'interpolation': 'nearest'}
plt.imshow(x, cmap=cmap1, **params)
plt.show()
plt.imshow(y, cmap=cmap2, **params)
plt.show()
plt.imshow(x, cmap=cmap1, **params)
plt.imshow(y, cmap=cmap2, vmin=0, **params) # vmin > -100
plt.show()
Related
I am trying to display a contourf plot as you can see below in the code snippet on a Folium map.
I can see the filled contour plot just fine. Also, I added a color bar with exact same colors using branca at this line:
bmap = branca.colormap.LinearColormap(colorl, vmin=levs[0],
vmax=levs[-1]).to_step(len(levs),index=levs)
geojsonf = geojsoncontour.contourf_to_geojson(
contourf=pcontf,
min_angle_deg=3.0,
ndigits=5,
stroke_width=1,
fill_opacity=0.9)
As you can see in the output image, colors don't match.
I suspect opacity I use for the contour plot might play a role here but changing opacity does not make it better.
I also tried making Circle markers (not shown here) with the same colors but still no luck. I cannot get pyplot colors to match.
Any suggestion is greatly appreciated. Also is there a better way to accomplish the same task? I basically have a 2D NumPy array with values ranging from -50 to 50on a reprojected lat-lon grid. I need to be able to show the shaded contours and associated values in the bar.
fig = plt.figure(figsize=[10, 15], dpi=None)
ax = fig.subplots()
jet =plt.get_cmap('jet')
clevs= np.array(levs)
cnorm = plt.Normalize(vmin=levs[0],vmax=levs[-1])
clevels = [levs[0]] + list(0.5*(clevs[1:]+clevs[:-1])) + [levs[-1]]
colors=jet(cnorm(clevels))
colorsm = color.ListedColormap(colors)
pcontf = ax.contourf(lons,lats,data,levels=levs,cmap=colorsm)
mapa = folium.Map([np.mean(lats), np.mean(lons)], zoom_start=10,tiles='Stamen Terrain')
colorl = []
for i,val in enumerate(colors):
carr= colors[i-1]
ccol = (carr[1],carr[2],carr[3])
colorl.insert(i,ccol)
bmap = branca.colormap.LinearColormap(colorl, vmin=levs[0],
vmax=levs[-1]).to_step(len(levs),index=levs)
geojsonf = geojsoncontour.contourf_to_geojson(
contourf=pcontf,
min_angle_deg=3.0,
ndigits=5,
stroke_width=1,
fill_opacity=0.9)
folium.GeoJson(
geojsonf,
style_function=lambda x: {
'color': x['properties']['stroke'],
'weight': x['properties']['stroke-width'],
'fillColor': x['properties']['fill'],
'opacity': 0.9,
}).add_to(mapa)
bmap.add_to(mapa)
I believe you have to recreate the Matplotlib colormap in Folium first.
This is how I did it (in the example my values range from 0 to 310, m is the Folium map):
Creating a colormap with Matplotlib:
import matplotlib as mpl
import branca.colormap as cm
import numpy as np
cmap = mpl.cm.get_cmap(name='rainbow',lut=310)
Creating a list with appropriate step size:
index_list = range(0,310,10).tolist()
cmap_list = cmap(index_list).tolist()
Creating a Folium colormap identical to the Matplotlib colormap:
cmap_foliump = cm.LinearColormap(
cmap_list, vmin=0, vmax=310, index=index_list, caption='my colormap')
Adding to Folium map:
m.add_child(cmap_folium)
Make sure index_list is comprised of true integers. If you create it with numpy.arange() just add .astype(int) prior to tolist()
I'm trying to do a quite simple scatter plot with error bars and semilogy scale. What is a little bit different from tutorials I have found is that the color of the scatterplot should trace a different quantity. On one hand, I was able to do a scatterplot with the errorbars with my data, but just with one color. On the other hand, I realized a scatterplot with the right colors, but without the errorbars.
I'm not able to combine the two different things.
Here an example using fake data:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
n=100
Lx_gas = 1e40*np.random.random(n) + 1e37
Tx_gas = np.random.random(n) + 0.5
Lx_plus_error = Lx_gas
Tx_plus_error = Tx_gas/2.
Tx_minus_error = Tx_gas/4.
#actually positive numbers, this is the quantity that should be traced by the
#color, in this example I use random numbers
Lambda = np.random.random(n)
#this is actually different from zero, but I want to be sure that this simple
#code works with the log axis
Lx_minus_error = np.zeros_like(Lx_gas)
#normalize the color, to be between 0 and 1
colors = np.asarray(Lambda)
colors -= colors.min()
colors *= (1./colors.max())
#build the error arrays
Lx_error = [Lx_minus_error, Lx_plus_error]
Tx_error = [Tx_minus_error, Tx_plus_error]
##--------------
##important part of the script
##this works, but all the dots are of the same color
#plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error,fmt='o')
##this is what is should be in terms of colors, but it is without the error bars
#plt.scatter(Tx_gas, Lx_gas, marker='s', c=colors)
##what I tried (and failed)
plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error,\
color=colors, fmt='o')
ax = plt.gca()
ax.set_yscale('log')
plt.show()
I even tried to plot the scatterplot after the errorbar, but for some reason everything plotted on the same window is put in background with respect to the errorplot.
Any ideas?
Thanks!
You can set the color to the LineCollection object returned by the errorbar as described here.
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
n=100
Lx_gas = 1e40*np.random.random(n) + 1e37
Tx_gas = np.random.random(n) + 0.5
Lx_plus_error = Lx_gas
Tx_plus_error = Tx_gas/2.
Tx_minus_error = Tx_gas/4.
#actually positive numbers, this is the quantity that should be traced by the
#color, in this example I use random numbers
Lambda = np.random.random(n)
#this is actually different from zero, but I want to be sure that this simple
#code works with the log axis
Lx_minus_error = np.zeros_like(Lx_gas)
#normalize the color, to be between 0 and 1
colors = np.asarray(Lambda)
colors -= colors.min()
colors *= (1./colors.max())
#build the error arrays
Lx_error = [Lx_minus_error, Lx_plus_error]
Tx_error = [Tx_minus_error, Tx_plus_error]
sct = plt.scatter(Tx_gas, Lx_gas, marker='s', c=colors)
cb = plt.colorbar(sct)
_, __ , errorlinecollection = plt.errorbar(Tx_gas, Lx_gas, xerr = Tx_error,yerr = Lx_error, marker = '', ls = '', zorder = 0)
error_color = sct.to_rgba(colors)
errorlinecollection[0].set_color(error_color)
errorlinecollection[1].set_color(error_color)
ax = plt.gca()
ax.set_yscale('log')
plt.show()
Sometimes, I want to plot discrete value in pcolormesh style.
For example, to represent a 2-d array in the shape of 100x100 which contain int 0~7
data = np.random.randint(8, size=(100,100))
cmap = plt.cm.get_cmap('PiYG', 8)
plt.pcolormesh(data,cmap = cmap,alpha = 0.75)
plt.colorbar()
The figure shows like this:
How to generate the colorbar in legend style. In other word, each color box corresponds to its value(e.g pink colorbox --> 0)
An illustration here(Not fit this example):
Maybe the easiest way is to create corresponding number of Patch instances:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
data = np.random.randint(8, size=(100,100))
cmap = plt.cm.get_cmap('PiYG', 8)
plt.pcolormesh(data,cmap = cmap,alpha = 0.75)
# Set borders in the interval [0, 1]
bound = np.linspace(0, 1, 9)
# Preparing borders for the legend
bound_prep = np.round(bound * 7, 2)
# Creating 8 Patch instances
plt.legend([mpatches.Patch(color=cmap(b)) for b in bound[:-1]],
['{} - {}'.format(bound_prep[i], bound_prep[i+1] - 0.01) for i in range(8)])
I would like to use matplotlibs pcolormesh and mask data (i.e. indicate with a special color not part of the chosen colormap) for more than 3 types of data in the colormap.
As this example shows, it is clear how to do that for three types by using the three functions:
cmap.set_under('yellow')
cmap.set_over('cyan')
cmap.set_bad('blue')
But how can one do this for values that are 'not bad', not under or over a given range, but just deserve special attention e.g. for indicating the 'best values' (which are within a given range) in the data displayed as colormap.
In terms of code: the indices_to_be_masked_with_another_color below shall be colored differently (not using a color from the colormap).
import matplotlib.pyplot as plt
import numpy as np
import copy
np.random.seed(0)
D = np.random.rand(12, 72)
D[4, :] = np.nan
D[6, 6] = np.nan
D[2, :] = np.linspace(0.4, 0.6, D[2, :].size)
D = np.ma.masked_invalid(D)
cmap = copy.copy(plt.get_cmap('bwr'))
cmap.set_bad(color = 'k', alpha = 1.)
cmap.set_under(color = 'cyan')
cmap.set_over(color = 'yellow')
xbin = np.linspace(0, 12, 13)
ybin = np.linspace(-90, 90, 73)
fig = plt.figure()
ax = fig.add_subplot(111)
pl = ax.pcolormesh(xbin, ybin, D.T, cmap = cmap, edgecolors = 'None',
vmin = 0.1, vmax = 0.9)
indices_to_be_masked_with_another_color = np.where(np.abs(D - 0.5) < 0.1)
# what to do now?
plt.show()
All values in the third column should have a special color, e.g. green
You can do it with these lines:
import matplotlib.colors as colors
ind = (np.abs(D.T - 0.5) > 0.1)
new_D = np.ma.masked_array(D.T,mask=ind)
greenmap = colors.ListedColormap(['g'])
ax.pcolormesh(xbin,ybin,new_D,cmap=greenmap,edgecolors='None')
First, you don't need to use np.where, but instead create a new mask for those values that you don't want to be green.
Then you create a listed colormap (of just green) to use with pcolormesh
Finally, you use this masked array and new colormap to plot another pcolormesh on top of your other one.
Is it possible to create a RegularPolyCollection with static sizes?
I'd like to give the size in data units, not in screen units. Just like the offsetts.
The target is to have an image of a camera with 1440 hexagonal Pixels with a diameter of 9.5 mm.
It is possible to achieve this with looping over 1440 Polygons but i was not successfull creating it with a PolyCollection which has big advantages, for creating colormaps etc.
Here is the code i use to plot the 1440 hexagons with static size:
for c, x, y in zip(pixel_color, pixel_x, pixel_y):
ax.add_artist(
RegularPolygon(
xy=(x, y),
numVertices=6,
radius=4.75,
orientation=0.,
facecolor=c,
edgecolor=edgecolor,
linewidth=1.5,
)
)
And this code produces the same but with wrong and not static (in terms of data) sizes:
a = 1/np.sqrt(3) * 9.5
collection = RegularPolyCollection(
numsides=6,
rotation=0.,
sizes=np.ones(1440)*np.pi*a**2, # tarea of the surrounding circle
facecolors=pixel_colors,
edgecolors="g",
linewidth=np.ones(1440)*1.5,
offsets=np.transpose([pixel_x, pixel_y]),
transOffset=self.transData,
)
self.add_collection(collection)
How can I achieve the static sizes of the hexagons with the advantages of having a collection?
I recently had the same problem. The solution is to simply use PatchCollection instead of RegularPolyCollection. The disadvantage is, however, that you have instantiate every single patch manually. Below you'll find a code example that plots 10,000 regular hexagons on a regular grid.
# imports
import matplotlib.pyplot as plt
from matplotlib.patches import RegularPolygon
from matplotlib.collections import PatchCollection
import numpy as np
# set up figure
fig, ax = plt.subplots(1)
# positions
pixel_x, pixel_y = np.indices((100, 100))
pixel_color = np.random.random_sample(30000).reshape(10000, 3)
dx = 4 # horizontal stride
dy = 5 # vertical stride
# set static radius
poly_radius = 2.5
# list to hold patches
patch_list = []
# creat the patches
for c, x, y in zip(pixel_color, pixel_x.flat, pixel_y.flat):
patch_list.append(
RegularPolygon(
xy=(x*dy, y*dy),
numVertices=6,
radius=poly_radius,
orientation=0.,
facecolor=c,
edgecolor='k'
)
)
pc = PatchCollection(patch_list, match_original=True)
ax.add_collection(pc)
ax.axis([-3, 480, -3, 480])
plt.show()
On my machine this code takes about 2.8 seconds to render everything.
If you'd like to use RegularPolyCollection, I've figured out how to set the sizes correctly. The main limitation is that the sizes depend on the axes transform, and so both the axes limits and the figure size need to be locked in before you calculate the sizes.
In the version below, the figure - and axis - also has to be square.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
sin60 = np.sin(np.pi/3)
fig, ax = plt.subplots()
fig.set_size_inches(8, 8)
ax.set_aspect(1)
ax.set_xlim(-1.5*sin60, +1.5*sin60)
ax.set_ylim(-1.5*sin60, +1.5*sin60)
ax.set_frame_on(False)
ax.set_xticks([])
ax.set_yticks([])
coords = [[-1/2, +sin60/2], [+1/2, +sin60/2], [0, -sin60/2]]
radius = .5/sin60
data_to_pixels = ax.transData.get_matrix()[0, 0]
pixels_to_points = 1/fig.get_dpi()*72.
size = np.pi*(data_to_pixels*pixels_to_points*radius)**2
hexes = mpl.collections.RegularPolyCollection(
numsides=6,
sizes=3*(size,),
offsets=coords,
edgecolors=3*('k',),
linewidths=1,
transOffset=ax.transData)
ax.add_collection(hexes)