2

I have a picture which shows the magnitude of some value in terms of color, like this one:

The higher the magnitude, the more it looks red. However there are only a few points at the edge has very high values and most of the points have much lower values. If a colorbar with equal intervals is used the picture just looks blue throughout and the values in most areas cannot be distinguished.

Is it possible to set a colorbar that has, say, exponentially increasing intervals (or others) so that the center part can also show different colors?

rayryeng
  • 102,964
  • 22
  • 184
  • 193
Kelvin S
  • 341
  • 2
  • 4
  • 13
  • 1
    Did you try applying a non-linear transform to your data? How about `log`? – rayryeng Mar 09 '16 at 07:10
  • 1
    @rayryeng I have considered that, but it seems that it would be easier for people to read if only the colorbar is adjusted instead of the data. – Kelvin S Mar 09 '16 at 07:16
  • @KelvinS See the bottom answer here, might help: https://www.mathworks.com/matlabcentral/newsreader/view_thread/152310 – Dan Mar 09 '16 at 07:33
  • [This answer](http://www.mathworks.com/matlabcentral/answers/100066-how-do-i-create-a-logarithmic-scale-colormap-or-colorbar) from MATLAB Central could be useful: It applies a `log10`, rescales the result, and sets the ticks in the colorbar correctly. (Yes, I know, it does adjust the data, not only the colorbar..) – hbaderts Mar 09 '16 at 07:42
  • 1
    I don't think this is an exact duplicate of http://stackoverflow.com/questions/20613841/how-to-generate-a-non-linear-colormap-colorbar as the OP specifies that they don't want to scale their data, only the map. Correct me if I'm wrong. – Dan Mar 09 '16 at 08:31
  • 1
    @Dan I disagree. But it's probably a matter of taste. You lose a lot of information by using a colorbar with a non-linear color distribution. Maintaining a useful colorbar and still get a balanced representation of your data probably requires scaling the data. I'd go for the duplicate option -> matter of taste – Robert Seifert Mar 09 '16 at 08:52
  • 1
    @thewaywewalk Dan is right, readers are usually more comfortable with un-scaled data. The other post (and many others) only offers solutions which requires scaling the data. – Kelvin S Mar 09 '16 at 08:56
  • 2
    @thewaywewalk I agree that yours looks better and is likely more readable. Rather have log incrementing ticklabels that are physically linearly spaced n the screen. I just think it's not a duplicate because the OP asked for something else (albeit in the comments) – Dan Mar 09 '16 at 08:58

1 Answers1

3

Here is one way, it's not quite right but it's close. The idea is to make your own log spaced colour map. I do it by using linear interpolation between log spaced break points. It could probably be improved to use logarithmic interpolation (and maybe have better end cases):

First I simulate some data (open to suggestions for simulating data that better illustrates this example):

M = exp(rand(50)*10)

Then plot it (ignore the figure(2), that's just to make this match the image later)

n = 64

figure(2)
imagesc(M)
colormap(jet(n))
colorbar

now create a log spaced colour map

linMap = jet(n);
breaks = round(0:n/5:n)';
breaks(1) = 1;
cBreaks = linMap(breaks,:);
idx = 2.^(6:-1:1)';
idx(end) = 0; %// this part is hacky
logMap = interp1(((idx)/64)*max(M(:)),flipud(cBreaks),linspace(0,max(M(:)),64)); %// based on http://stackoverflow.com/questions/17230837/how-to-create-a-custom-colormap-programatically

figure(1)
imagesc(M)
colormap(logMap)
colorbar

results in

enter image description here

as you can see, the data remain unchanged and the data inspector still gives you back the same values but the colour bar is pretty much on a log scale now. I'd be interested to see what this looks like on your data.

Dan
  • 45,079
  • 17
  • 88
  • 157
  • I am trying your code with R2014a, there is an error "Subscript indices must either be real positive integers or logicals. Error in Untitled2 (line 13) cBreaks = linMap(breaks,:);" How to solve this problem? Thank you. – Kelvin S Mar 09 '16 at 08:48
  • 1
    @KelvinS what are your values for `n` and `breaks`? You should probably change that `5` to be the value to makes `2^(x+1) = n`. So when `n` is `64` then `x` is `5` as `2^(5+1)` is `64 – Dan Mar 09 '16 at 08:56
  • I am using n=64 and breaks = round(0:n/5:n)'; (exactly your code). I have just found that if the first entry in breaks is 0 it cannot be put in the function linMap. Does it depends on version of Matlab? – Kelvin S Mar 09 '16 at 09:07
  • 1
    @KelvinS oops, left off a line. You're right, it can't have that `0` in it. I just changed it to `1`. It's what I meant by not having great edge conditions. – Dan Mar 09 '16 at 09:16
  • @Kelvin can you link to an image of what the end result was on your data? – Dan Mar 09 '16 at 09:27
  • It becomes like this, https://www.dropbox.com/s/zk6d2n63twtuqdr/bar2.jpg?dl=0 Not completely satisfactory but at least I know the way to set exponential scale bar so that I can fine tune it. – Kelvin S Mar 09 '16 at 09:37
  • 1
    Hmmm so that didn't do much at all. I would increase `n` and then change the number of breakpoints and see if that helps at all – Dan Mar 09 '16 at 09:48