I have a collection of (sparse) data that has temperature measurements. With a heatmap, areas that have more observations show a higher value because the heatmap accumulates the values.
Is there a way to get more of an average as opposed to a sum? But also with the feel of gaussian filtering. If no data is in a region, a 0 value would be preferred (which would be transparent).
If you would like to gaussian filter, see ndimage.gaussian_filter
Here's an example:
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage
fig = plt.figure()
# Random example data with some values set to 0
im = np.random.random((10, 10))
im[im < 0.3] = 0
# Smooth image
smoothed_im = scipy.ndimage.filters.gaussian_filter(im, sigma=1)
im[im == 0] = None
plt.imshow(im, interpolation = "nearest")
plt.title("Original image")
plt.colorbar()
plt.figure()
plt.imshow(smoothed_im, interpolation = "nearest")
plt.title("Smoothed image")
plt.colorbar()
# Blank elements that were originally 0
smoothed_im[np.isnan(im)] = None
plt.figure()
plt.imshow(smoothed_im, interpolation = "nearest")
plt.title("Smoothed image with original zeros blanked")
plt.colorbar()
This produces:
Related
I used the following code to get the 3D depth projection of the shown 2 images. I need the max and minimum depth values, and the x and y coordinates of these max and min depth values.
Is there a function/method from which I can get this information? Even if it will be using a library other than matplotlib.
import cv2
import numpy as np
import math
import scipy.ndimage as ndimage
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
image2=cv2.imread('D:/Post_Grad/STDF/iPython_notebooks/2228.jpg')
image2 = image2[:,:,1] # get the first channel
rows, cols = image2.shape
x, y= np.meshgrid(range(cols), range(rows)[::-1])
blurred = ndimage.gaussian_filter(image2,(5, 5))
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(221)
ax.imshow(image2, cmap='gray')
ax = fig.add_subplot(222, projection='3d')
ax.elev= 5
f1=ax.plot_surface(x,y,image2, cmap=cm.jet)
ax = fig.add_subplot(223)
ax.imshow(blurred, cmap='gray')
ax = fig.add_subplot(224, projection='3d')
ax.elev= 5
f2=ax.plot_surface(x,y,blurred, cmap=cm.jet)
plt.show()
max depth and min depth are just maximum and minimum pixel values of image. And you can easily find the values via np.max(image2),np.min(image2) etc..
Also coordinates can be found via a simple function
def getCoord(image,val):
coords = []
for i in range(image.shape[0]):
for j in range(image.shape[1]):
if image[i][j] == val:
coords.append([i,j])
return coords
so getCoord(image2,np.max(image2)) will return all highest pixel coordinates in image2 (it can be more than 1) , getCoord(blurred,np.min(blurred)) will return all lowest pixel coordinates in blurred etc..
I'm trying to plot a series of frequency spectra in a 3D space using PolyCollection. My goal is to set "facecolors" as a gradient, i.e., the higher the magnitude, the lighter the color.
Please see this image for reference (I am not looking for the fancy design, just the gradients).
I tried to use the cmap argument of the PollyCollection, but I was unsuccessful.
I came this far with the following code adapted from here:
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
from mpl_toolkits.mplot3d import axes3d
import numpy as np
from scipy.ndimage import gaussian_filter1d
def plot_poly(magnitudes):
freq_data = np.arange(magnitudes.shape[0])[:,None]*np.ones(magnitudes.shape[1])[None,:]
mag_data = magnitudes
rad_data = np.linspace(1,magnitudes.shape[1],magnitudes.shape[1])
verts = []
for irad in range(len(rad_data)):
xs = np.concatenate([[freq_data[0,irad]], freq_data[:,irad], [freq_data[-1,irad]]])
ys = np.concatenate([[0],mag_data[:,irad],[0]])
verts.append(list(zip(xs, ys)))
poly = PolyCollection(verts, edgecolor='white', linewidths=0.5, cmap='Greys')
poly.set_alpha(.7)
fig = plt.figure(figsize=(24, 16))
ax = fig.add_subplot(111, projection='3d', proj_type = 'ortho')
ax.add_collection3d(poly, zs=rad_data, zdir='y')
ax.set_xlim3d(freq_data.min(), freq_data.max())
ax.set_xlabel('Frequency')
ax.set_ylim3d(rad_data.min(), rad_data.max())
ax.set_ylabel('Measurement')
ax.set_zlabel('Magnitude')
# Remove gray panes and axis grid
ax.xaxis.pane.fill = False
ax.xaxis.pane.set_edgecolor('white')
ax.yaxis.pane.fill = False
ax.yaxis.pane.set_edgecolor('white')
ax.zaxis.pane.fill = False
ax.zaxis.pane.set_edgecolor('white')
ax.view_init(50,-60)
plt.show()
sample_data = np.random.rand(2205, 4)
sample_data = gaussian_filter1d(sample_data, sigma=10, axis=0) # Just to smoothe the curves
plot_poly(sample_data)
Besides the missing gradients I am happy with the output of the code above.
I am trying to plot a RGBA image with a colorbar representing color values.
The RGBA image is generated from raw data, transforming the 2d data array into a 6d-array with x, y, [R, G, B and A] according to the color input. E.g. 'green' will make it fill just the G channel with the values from the 2d-array, leaving R and B = 0 and A = 255. Like this:
All solutions I found would apply a color map or limit the vmin and vmax of the colorbar but what I need is a colorbar that goes from pitch black to the brightest color present in the image. E.g. if I have an image in shades of purple, the color bar should go from 0 to 'full' purple with only shades of purple in it. The closest solution I found was this (https://pelson.github.io/2013/working_with_colors_in_matplotlib/), but it doesn't fit a "general" solution.
An image I'm getting is given below.
import numpy as np
from ImgMath import colorize
import matplotlib.pyplot as plt
import Mapping
data = Mapping.getpeakmap('Au')
# data shape is (10,13) and len(data) is 10
norm_data = data/data.max()*255
color_data = colorize(norm_data,'green')
# color_data shape is (10,13,4) and len(color_data) is 10
fig, ax = plt.subplots()
im = plt.imshow(color_data)
fig.colorbar(im)
plt.show()
You could map your data with a custom, all-green, colormap
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
# input 2D array
data = np.random.randint(0,255, size=(10,13))
z = np.zeros(256)
colors = np.linspace(0,1,256)
alpha = np.ones(256)
#create colormap
greencolors = np.c_[z,colors,z,alpha]
cmap = ListedColormap(greencolors)
im = plt.imshow(data/255., cmap=cmap, vmin=0, vmax=1)
plt.colorbar(im)
plt.show()
Some background
I have a 2-d array in the shape of (50,50), the data value are range from -40 ~ 40.
But I want to plot the data in three data range[<0], [0,20], [>20]
Then, I need to generate a colormap corresponding to the three section.
I have some thought now
## ratio is the original 2-d array
binlabel = np.zeros_like(ratio)
binlabel[ratio<0] = 1
binlabel[(ratio>0)&(ratio<20)] = 2
binlabel[ratio>20] = 3
def discrete_cmap(N, base_cmap=None):
base = plt.cm.get_cmap(base_cmap)
color_list = base(np.linspace(0, 1, N))
cmap_name = base.name + str(N)
return base.from_list(cmap_name, color_list, N)
fig = plt.figure()
ax = plt.gca()
plt.pcolormesh(binlabel, cmap = discrete_cmap(3, 'jet'))
divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="4%", pad=0.45)
cbar = plt.colorbar(ratio_plot, cax=cax, orientation="horizontal")
labels = [1.35,2,2.65]
loc = labels
cbar.set_ticks(loc)
cbar.ax.set_xticklabels(['< 0', '0~20', '>20'])
Is there any better approach? Any advice would be appreciate.
There are various answers to other questions using ListedColormap and BoundaryNorm, but here's an alternative. I've ignored the placement of your colorbar, as that's not relevant to your question.
You can replace your binlabel calculation with a call to np.digitize() and replace your discrete_cmap() function by using the lut argument to get_cmap(). Also, I find it easier to place the color bounds at .5 midpoints between the indexes rather than scale to awkward fractions of odd numbers:
import matplotlib.colors as mcol
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
ratio = np.random.random((50,50)) * 50.0 - 20.0
fig2, ax2 = plt.subplots(figsize=(5,5))
# Turn the data into an array of N bin indexes (i.e., 0, 1 and 2).
bounds = [0,20]
iratio = np.digitize(ratio.flat,bounds).reshape(ratio.shape)
# Create a colormap containing N colors and a Normalizer that defines where
# the boundaries of the colors should be relative to the indexes (i.e., -0.5,
# 0.5, 1.5, 2.5).
cmap = cm.get_cmap("jet",lut=len(bounds)+1)
cmap_bounds = np.arange(len(bounds)+2) - 0.5
norm = mcol.BoundaryNorm(cmap_bounds,cmap.N)
# Plot using the colormap and the Normalizer.
ratio_plot = plt.pcolormesh(iratio,cmap=cmap,norm=norm)
cbar = plt.colorbar(ratio_plot,ticks=[0,1,2],orientation="horizontal")
cbar.set_ticklabels(["< 0","0~20",">20"])
I want to plot a true/false or active/deactive binary data similar to the following picture:
The horizontal axis is time and the vertical axis is some entities(Here some sensors) which is active(white) or deactive(black). How can I plot such a graphs using pyplot.
I searched to find the name of these graphs but I couldn't find it.
What you are looking for is imshow:
import matplotlib.pyplot as plt
import numpy as np
# get some data with true # probability 80 %
data = np.random.random((20, 500)) > .2
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(data, aspect='auto', cmap=plt.cm.gray, interpolation='nearest')
Then you will just have to get the Y labels from somewhere.
It seems that the image in your question has some interpolation in the image. Let us set a few more things:
import matplotlib.pyplot as plt
import numpy as np
# create a bit more realistic-looking data
# - looks complicated, but just has a constant switch-off and switch-on probabilities
# per column
# - the result is a 20 x 500 array of booleans
p_switchon = 0.02
p_switchoff = 0.05
data = np.empty((20,500), dtype='bool')
data[:,0] = np.random.random(20) < .2
for c in range(1, 500):
r = np.random.random(20)
data[data[:,c-1],c] = (r > p_switchoff)[data[:,c-1]]
data[-data[:,c-1],c] = (r < p_switchon)[-data[:,c-1]]
# create some labels
labels = [ "label_{0:d}".format(i) for i in range(20) ]
# this is the real plotting part
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(data, aspect='auto', cmap=plt.cm.gray)
ax.set_yticks(np.arange(len(labels)))
ax.set_yticklabels(labels)
creates
However, the interpolation is not necessarily a good thing here. To make the different rows easier to separate, one might use colors:
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
# create a bit more realistic-looking data
# - looks complicated, but just has a constant switch-off and switch-on probabilities
# per column
# - the result is a 20 x 500 array of booleans
p_switchon = 0.02
p_switchoff = 0.05
data = np.empty((20,500), dtype='bool')
data[:,0] = np.random.random(20) < .2
for c in range(1, 500):
r = np.random.random(20)
data[data[:,c-1],c] = (r > p_switchoff)[data[:,c-1]]
data[-data[:,c-1],c] = (r < p_switchon)[-data[:,c-1]]
# create some labels
labels = [ "label_{0:d}".format(i) for i in range(20) ]
# create a color map with random colors
colmap = matplotlib.colors.ListedColormap(np.random.random((21,3)))
colmap.colors[0] = [0,0,0]
# create some colorful data:
data_color = (1 + np.arange(data.shape[0]))[:, None] * data
# this is the real plotting part
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(data_color, aspect='auto', cmap=colmap, interpolation='nearest')
ax.set_yticks(np.arange(len(labels)))
ax.set_yticklabels(labels)
creates
Of course, you will want to use something less strange as the coloring scheme, but that is really up to your artistic views. Here the trick is that all True elements on row n have value n+1 and, and all False elements are 0 in data_color. This makes it possible to create a color map. Naturally, if you want a cyclic color map with two or three colors, just use the modulus of data_color in imshow by, e.g. data_color % 3.