Set Nan Colour In Mayavi - python

I have lots of data, interpolated with separate functions, which contains gaps (NaN) values. I would like to plot these planes of data as images in MayaVi using imshow(), which I have done with some success.
To avoid the complexity of the data, consider displaying the array:
import numpy as np
from mayavi.mlab import *
grid_z0 = np.array([[1,2,3],[4,np.nan,6],[7,8,9]])
imshow(grid_z0, interpolate = False)
Snapshot without transparency:
In reality, I would like the gaps in my data to be transparent. (i.e. the dark red 128,0,0 square in the middle would be see through).
I'm aware that editing colormaps isn't really a thing in MayaVi (as it is in matplotlib), but I can see 'NaN color' options in the MayaVi pipeline, and documentation like this show that editing the color options is a possibility.
However, I'm stuck to see why the NaN values come out as (128,0,0) RGB, and what I can do to make them transparent.

More thinking, reading and fiddling:
img = imshow(grid_z0)
img.module_manager.scalar_lut_manager.lut.nan_color = 0, 0, 0, 0
img.update_pipeline()

Related

How can I plot only particular values in xarray?

I am using data from cdasws to plot dynamic spectra. I am following the example found here https://cdaweb.gsfc.nasa.gov/WebServices/REST/jupyter/CdasWsExample.html
This is my code which I have modified to obtain a dynamic spectra for STEREO.
from cdasws import CdasWs
from cdasws.datarepresentation import DataRepresentation
import matplotlib.pyplot as plt
cdas = CdasWs()
import numpy as np
datasets = cdas.get_datasets(observatoryGroup='STEREO')
for index, dataset in enumerate(datasets):
print(dataset['Id'], dataset['Label'])
variables = cdas.get_variables('STEREO_LEVEL2_SWAVES')
for variable_1 in variables:
print(variable_1['Name'], variable_1['LongDescription'])
data = cdas.get_data('STEREO_LEVEL2_SWAVES', ['avg_intens_ahead'],
'2020-07-11T02:00:00Z', '2020-07-11T03:00:00Z',
dataRepresentation = DataRepresentation.XARRAY)[1]
print(data)
plt.figure(figsize = (15,7))
# plt.ylim(100,1000)
plt.xticks(fontsize=18)
plt.yticks(fontsize=18)
plt.yscale('log')
sorted_data.transpose().plot()
plt.xlabel("Time",size=18)
plt.ylabel("Frequency (kHz)",size=18)
plt.show()
Using this code gives a plot that looks something like this,
My question is, is there anyway of plotting this spectrum only for a particular frequency? For example, I want to plot just the intensity values at 636 kHz, is there any way I can do that?
Any help is greatly appreciated, I dont understand xarray, I have never worked with it before.
Edit -
Using the command,
data_stereo.avg_intens_ahead.loc[:,625].plot()
generates a plot that looks like,
While this is useful, what I needed is;
for the dynamic spectrum, if i choose a particular frequency like 600khz, can it display something like this (i have just added white boxes to clarify what i mean) -
If you still want the plot to be 2D, but to include a subset of your data along one of the dimensions, you can provide an array of indices or a slice object. For example:
data_stereo.avg_intens_ahead.sel(
frequency=[625]
).plot()
Or
# include a 10% band on either side
data_stereo.avg_intens_ahead.sel(
frequency=slice(625*0.9, 625*1.1)
).plot()
Alternatively, if you would actually like your plot to show white space outside this selected area, you could mask your data with where:
data_stereo.avg_intens_ahead.where(
data_stereo.frequency==625
).plot()

How to edit a color scheme?

I am using Altair for Python and my current heatmap code uses a redyellowblue color scheme (A) that uses yellow as the middle color. I am trying to edit this color scheme in order to achieve the scheme on (B), which the only difference is replacing yellow with white as the middle color. Does anyone have any idea on how to achieve that in Altair?
The color scheme on (B) was created in R, by using the RdYlBu color pallete (the one with 11 colors) and overwrite the middle (6th color) with white. Then, they increased the number of colors in the pallete to 99, to make the fade look more fluid.
My current code (A):
color=alt.Color('Spline_WW_Diff_Trend:Q', scale=alt.Scale(scheme='redyellowblue',reverse=True, domain=[-3.57,2.270], domainMid=0, clamp=True), legend=alt.Legend(title="Trend"))
I have tried manually setting up the colors using range but got an odd result. I've also used a condition to override the color for the value 0, but it wasn't satisfactory because the numbers neighboring 0 should have a white(ish) color.
You probably want interpolate='rgb' when defining your own range.Using the interpolate property for the color scale you can define one of the interpolation methods as is defined by d3-interpolate, https://github.com/d3/d3-interpolate#color-spaces.
The default value for interpolate is hcl, which is not always what you want. Observe the changes in color interpolation once you change the interpolation methods with a fixed range/domain:
import altair as alt
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.arange(-10, 10)})
def charter(method):
return alt.Chart(df, title=method).mark_rect().encode(
x=alt.X('x:O',title=None),
color=alt.Color('x:Q',
scale=alt.Scale(
domain=[-10,-5,0,5,9],
range=['red','orange','white','lightblue','darkblue'],
interpolate=method
),
legend=alt.Legend(direction='horizontal', orient='top', title=None)
)
)
methods = ['hcl', 'rgb', 'hsl', 'hsl-long', 'lab', 'hcl-long', 'cubehelix', 'cubehelix-long']
alt.vconcat(*[charter(method) for method in methods]).resolve_scale(color='independent')

Discretize or bin LAB colorspace in 2 dimensions

I have a lab colorspace
And I want to "bin" the colorspace in a grid of 10x10 squares.
So the first bin might be (-110,-110) to (-100,-100) then the next one might be (-100,-110) to (-90,-100) and so on. These bins could be bin 1 and bin 2
I have seen np.digitize() but it appears that you have to pass it 1-dimensional bins.
A rudimentary approach that I have tried is this:
for fn in filenames:
image = color.rgb2lab(io.imread(fn))
ab = image[:,:,1:]
width,height,d = ab.shape
reshaped_ab = np.reshape(ab,(width*height,d))
print reshaped_ab.shape
images.append(reshaped_ab)
all_abs = np.vstack(images)
all_abs = shuffle(all_abs,random_state=0)
sns
df = pd.DataFrame(all_abs[:3000],columns=["a","b"])
top_a,top_b = df.max()
bottom_a,bottom_b = df.min()
range_a = top_a-bottom_a
range_b = top_b-bottom_b
corner_a = bottom_a
corner_b = bottom_b
bins = []
for i in xrange(int(range_a/10)):
for j in xrange(int(range_b/10)):
bins.append([corner_a,corner_b,corner_a+10,corner_b+10])
corner_b = bottom_b+10
corner_a = corner_a+10
but the "bins" that results seem kinda sketchy. For one thing there are many empty bins as the color space does have values in a square arrangement and that code pretty much just boxes off from the max and min values. Additionally, the rounding might cause issues. I am wondering if there is a better way to do this? I have heard of color histograms which count the values in each "bin". I don't need the values but the bins are I think what I am looking for here.
Ideally the bins would be an object that each have a label. So I could do bins.indices[0] and it would return the bounding box I gave it. Then also I could bin each observation, like if a new color was color = [15.342,-6.534], color.bin would return 15 or the 15th bin.
I realize this is a lot to ask for, but I think it must be a somewhat common need for people working with color spaces. So is there any python module or tool that can accomplish what I'm asking? How would you approach this? thanks!
Use the standard numpy 2D-histogram function: numpy.histogram2d:
import numpy as np
# a and b are arrays representing your color points
H, a_edges, b_edges = np.histogram2d(a, b, bins=10)
If you want to discard the empty bins, you'd have to do some work from here. But I don't see why you'd want that, because assigning future colors to existing nonempty bins will be much more work if they are not on a rectangular grid.
You are probably trying to repeat what Richard Zhang did in "Colorful Image Colorization" research: http://richzhang.github.io/colorization/
Here, author himself discuss this problem: https://github.com/richzhang/colorization/issues/23
Fortunately Zhang provides .npy file, that contains those quantized values. It is under: https://github.com/richzhang/colorization/blob/master/resources/pts_in_hull.npy
The only thing, you have to do now, is to load this file in your python script:
import numpy as np
pts_in_hull = np.load("pts_in_hull.npy")
It is numpy array of shape 313x2 containing values from your image.
I know this answer comes few years too late, but maybe it will help someone else.

Empty figures with basemap

I am trying to use model output on flows in a tidal basin. The model uses a curvilinear grid. My first task is to just plot one component of the velocity of the highest water layer. I wrote a little bit of code based on the question under the name: Matplotlib Streamplot for Unevenly (curvilinear) Grid.
Now as far as I can see, I didn't change anything essential except for the numbers in comparison to the earlier metioned question, but the figures remain empty. I put the code and some numbers below.
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
Lat = np.array([[ 30.40098833, 30.40103752, 30.40108727, 30.40113704],
[ 30.40140046, 30.40145021, 30.40149997, 30.40154973],
[ 30.40186559, 30.40191478, 30.40196453, 30.4020143 ],
[ 30.40239781, 30.402447, 30.40249676, 30.40254652]])
Lon = np.array([[-86.51729818, -86.51794126, -86.5185871, -86.51923603],
[-86.51725858, -86.51790149, -86.51854717, -86.51919595],
[-86.51721383, -86.51785659, -86.51850228, -86.51915089],
[-86.51716242, -86.51780518, -86.51845087, -86.51909948]])
Xvel = np.array([[ 0.0325774, -0.02811189, -0.04972513, -0.07736091],
[ 0.00592685, -0.00043959, -0.00735147, -0.05015078],
[-0.03365543, -0.03183309, -0.03701356, -0.07232581],
[-0.09578606, -0.10139448, -0.11220678, -0.13221299]])
plt.ion()
fig,(ax1) = plt.subplots(1,1)
m = Basemap(llcrnrlon=Lon.min(),llcrnrlat=Lat.min(),
urcrnrlon=Lon.max(), urcrnrlat=Lat.max(),
projection='merc',resolution='i',ax=ax1)
m.contourf(Lat,Lon,Xvel,latlon=True)
m.drawcoastlines()
m.drawrivers()
m.plot(Lat,Lon,'-k',alpha=0.3,latlon=True)
m.plot(Lat.T,Lon.T,'-k',alpha=0.3,latlon=True)
Could someone tell me what it is that causes the plots to remain empty?
I have another question regarding the use of Basemap: My datasheet also contains a lot of NaN's (gridpoints with no information). I was wondering how I can let Basemap know that I just don't have any information on these positions and that I don't want any plotting there. In the current code it causes an 'Points of LinearRing do not form a closed linestring' error.
Regarding the second part of your question (since Ajean appears to have solved the first half), the standard way to tell Matplotlib (and hence Basemap) to not plot data is to create a masked array. Lets say your Xvel contained NaNs, then to plot it you would do
import numpy.ma as ma
m.contourf(Lon, Lat, ma.masked_invalid(Xvel), latlon=True)
the function ma.masked_invalid, as its name implies, masks all invalid (i.e., NaN) values, so that they're not plotted.

matplotlib - How to add pixel size legend?

import scipy as sp
import scipy.misc
lena = sp.misc.lena()
plt.imshow2(lena)
What I'd like is then to add a bar indicative of distance. ie suppose this was an actual image captured with a camera and I knew that each pixel corresponds to 1cm. I would want to add a bar that is 10 x 100 pixels and add some text that says 1m above the bar. Is there a simple way to do this?
thank you
In the example bellow I made a simple solution of your problem. It should not be too hard to extend this to cover a more general case. Hardest thing to get right here is the pos_tuple.
Since pos_tuple represents the upper left corner of Rectangle you have to subtract the length of the bar itself and then still leave some padding, otherwise it will be plotted at the very edge of the graph and look ugly. So a more general pos_tuple would look something like
pos_tuple = (np.shape(lena)[0]-m2pix(1)-padding_right,
np.shape(lena)[1]-m2pix(0.1)-padding_bottom)
This whole thing could also be adapted into a neat function add_image_scale that would take in your figure and spit out a figure which has the scale "glued" on. m2pix could also be generalized to receive a scale instead of hardcoding it.
import scipy as sp
import scipy.misc
import numpy as np
lena = sp.misc.lena()
def m2pix(pix): #it takes a 100 pix to make a meter
return 100*pix
pos_tuple = (np.shape(lena)[0]-100-12, np.shape(lena)[1]-10-2)
rect = plt.Rectangle( pos_tuple, m2pix(1), m2pix(0.1))
plt.imshow2(lena)
plt.gca().add_patch(rect)
plt.show()
As far as adding text goes, you can use the annotations or text which are both very easy to use.

Categories