Nonlinear colormap for data that diverges

I have data that goes through divergences where the Z value goes from approximately -4000 to 4000 very rapidly in certain regions, but the vast majority of the data is near zero. In the picture below, to be able to see what is going on I had to scale most of the color values to near zero to properly see what is happening. But in doing so, most of the colors are in the tiny sliver near zero and the red and blue take up the rest. I would like to nonlinearly scale the colorbar to be able to see all of the colors being used and greatly shrink the red and blue color part of the bar, with leaving the raw data as is if possible. It would also be nice if the colorbar ticks would vary nonlinear as well, instead of being forced to have a set number between consecutive ticks.
%Simple Model of divergent data
X = linspace(-2,2,501) + 0.002;
Y = linspace(-2,2,501) + 0.002;
%Function with Positive and Negative Divergence, and most values near 0
Z = zeros(501,501);
for i=1:length(X)
for j=1:length(Y)
Z(i,j) = 1/((X(i) - 1)^2 + (Y(j) - 1)^2) - 1/((X(i) + 1)^2 + (Y(j) + 1)^2) + 5*(cos(X(i))+sin(Y(i)));
end
end
%Setting the colormap
factor = 0.3;
cmap = jet(1000);
map = 999*rescale(1000./(1+exp(-factor*([1:1000]-500))));
cmap2 = cmap(1+fix(map),:);
figure('Position',[200,100,1500,730]);
ax=axes;
[X,Y] = meshgrid(X,Y);
surf(X,Y,Z,'linestyle','none');
a = colorbar;
set(ax,'colormap',cmap2)
view([0,90])

 Accepted Answer

How about something like this:
%Simple Model of divergent data
X = linspace(-2,2,801) + 0.002;
Y = linspace(-2,2,501) + 0.002;
%Function with Positive and Negative Divergence, and most values near 0
Z = 1./((X - 1).^2 + (Y.' - 1).^2) - 1./((X + 1).^2 + (Y.' + 1).^2) + 5*(cos(X)+sin(Y.'));
Z_scaled = sign(Z).*log10(1+abs(Z));
figure('Position',[200,100,1500,730]);
surf(X,Y,Z_scaled,'LineStyle','none')
view(2)
colormap(jet(1000))
cb = colorbar();
tl = [-10.^(5:-1:1) 0 10.^(1:5)];
cb.Ticks = sign(tl).*log10(1+abs(tl));
cb.TickLabels = tl;
Edited to use Z_scaled = sign(Z).*log10(1+abs(Z)); instead of Z_scaled = sign(Z).*log10(abs(Z));

6 Comments

Thanks for your response. This doesn't quite work, as I dont want the values near 0 to be enhanced due to the log10 making very small numbers large. However having Z_scaled = sign(Z).*log10(1+abs(Z)) works farily well, and is definitely an improvement over what I was doing. But is there any way to have the colorbar values not be logarithmic scaling, and instead a different nonlinear function? I would like to have more color variation focused on the small values, as let's say for all values above 200 and below -200 I would like them to be the same color, because as they diverge so rapidly dedicating colors to the high values is a waste. Currently with your response, for the values I care about (let's say between -10 and 10) there isn't much visibility for how they are changing, and I would like to allocate more of the color range to the values I am more interested in. Having a logarithmic scale would also be fine if there is still a way to allocate more colors to values near 0, and away from extreme values.
How about something like this?
%Simple Model of divergent data
X = linspace(-2,2,801) + 0.002;
Y = linspace(-2,2,501) + 0.002;
%Function with Positive and Negative Divergence, and most values near 0
Z = 1./((X - 1).^2 + (Y.' - 1).^2) - 1./((X + 1).^2 + (Y.' + 1).^2) + 5*(cos(X)+sin(Y.'));
Z_scaled = scaleZ(Z);
figure('Position',[200,100,1500,730]);
surf(X,Y,Z_scaled,'LineStyle','none')
view(2)
colormap(jet(1000))
cb = colorbar();
clim(scaleZ([-200 200]))
tl = [1 2 3 4 6 8 10 20 30 40 50 200];
tl = [-tl(end:-1:1) 0 tl];
cb.Ticks = scaleZ(tl);
cb.TickLabels = tl;
function out = scaleZ(in)
out = sign(in).*log10(1+abs(in));
end
You can change the scaleZ function to use whatever you want, and you can adjust the colorbar ticks/tick-labels as well by changing tl.
Is it possible to keep the original limits while still having more colors focused on small values?
%Simple Model of divergent data
X = linspace(-2,2,801) + 0.002;
Y = linspace(-2,2,501) + 0.002;
%Function with Positive and Negative Divergence, and most values near 0
Z = 1./((X - 1).^2 + (Y.' - 1).^2) - 1./((X + 1).^2 + (Y.' + 1).^2) + 5*(cos(X)+sin(Y.'));
Z_scaled = scaleZ(Z);
figure('Position',[200,100,1500,730]);
surf(X,Y,Z_scaled,'LineStyle','none')
view(2)
max_Z_val = 200;
n_colors = 1000;
cmap = jet(n_colors);
n_new = n_colors*max(abs(clim()))/scaleZ(max_Z_val);
n_add = round((n_new-n_colors)/2);
cmap = cmap([ones(1,n_add) 1:end end*ones(1,n_add)],:);
colormap(cmap)
cb = colorbar();
tl = [1 5 10 25 50 200 1e3 1e4 1e5];
tl = [-tl(end:-1:1) 0 tl];
cb.Ticks = scaleZ(tl);
cb.TickLabels = tl;
function out = scaleZ(in)
out = sign(in).*log10(1+abs(in));
end
Yes, this is exactly what I wanted! Thank you so very much for all your help!
You're welcome!

Sign in to comment.

More Answers (1)

Would something like this work for you?
% CALCULATE MESHGRID
xa = linspace(-2,2,501) + 0.002;
ya = linspace(-2,2,501) + 0.002;
[X,Y] = meshgrid(xa,ya);
% CALCULATE Z
Z = 1./((X - 1).^2 + (Y - 1).^2) - 1./((X + 1).^2 + (Y + 1).^2) + 5*(cos(X)+sin(Y));
% SCALE Z
ZS = sign(Z).*abs(log10(Z));
surf(X,Y,ZS)
axis equal tight
shading interp
colorbar;
view([ 0 90 ])

3 Comments

I would like the limits of the colorbar to still be the same as they were originally, so not really. It would be best if there was just a way to scale the colorbar values nonlinearly with keeping the plot coloring the same. I also tried scaling with a signedlog10 (sign(Z).*(log10(1+abs(Z)/))) previously, and the scaling doesn't work properly for that either. Thanks for the response though.
Something like this, then?
Unforunately that doesn't work for negative values, but I would like to have a nonlinearity that isn't log based, as I couldn't get any log scaling to work correctly.

Sign in to comment.

Categories

Products

Release

R2022a

Asked:

on 10 Feb 2024

Commented:

on 11 Feb 2024

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!