12
votes

This is related to Simon's question on changing default ColorData in Mathematica. While the solutions all addressed the issue of changing ColorData in line plots, I didn't quite find the discussion helpful in changing the ColorFunction/ColorData in ContourPlot/ArrayPlot/Plot3D, etc.

TLDR: Is there a way to get mma to use custom colors in ArrayPlot/ContourPlot/etc.


Consider the following example plot of the function sin(x^2+y^3) that I created in MATLAB:

enter image description here

Now doing the same in mma as:

xMax = 3; yMax = 3;
img = Transpose@
   Table[Sin[y ^3 + x^2], {x, -xMax, xMax, 0.01}, {y, -yMax, yMax, 
     0.01}];
plot = ArrayPlot[img, ColorFunction -> ColorData["Rainbow"], 
   AspectRatio -> 1, 
   FrameTicks -> {FindDivisions[{0, (img // Dimensions // First) - 1},
       4], FindDivisions[{0, (img // Dimensions // Last) - 1}, 4], 
     None, None}, 
   DataReversed -> 
    True] /. (FrameTicks -> {x_, 
      y_}) :> (FrameTicks -> {x /. {a_?NumericQ, b_Integer} :> {a, 
         2 xMax (b/((img // Dimensions // First) - 1) - 1/2)}, 
      y /. {a_?NumericQ, b_Integer} :> {a, 
         2 yMax (b/((img // Dimensions // Last) - 1) - 1/2)}})

I get the following plot:

enter image description here

I prefer the rich, bright colors in MATLAB to mma's pastel/dull colors. How do I get mma to use these colors, if I have the RGB values of the colormap from MATLAB?

You can download the RGB values of the default colormap in MATLAB, and import it into mma as

cMap = Transpose@Import["path-to-colorMapJet.mat", {"HDF5", 
      "Datasets", "cMap"}];

cMap is a 64x3 array of values between 0 and 1.

Just to give you some background, here's some relevant text from the MathWorks documentation on colormap

A colormap is an m-by-3 matrix of real numbers between 0.0 and 1.0. Each row is an RGB vector that defines one color. The kth row of the colormap defines the kth color, where map(k,:) = [r(k) g(k) b(k)]) specifies the intensity of red, green, and blue.

Here map=cMap, and m=64.

I tried poking at ColorDataFunction, and I see that the ColorData format is similar to the colormap. However, I'm not sure how to get ArrayPlot to use it (and presumably it ought to be the same for other plot functions).


Also, since my exercise here is purely to reach a level of comfort in mma, similar to what I have in MATLAB, I'd appreciate comments and suggestions on improving my code. Specifically, I'm not too satisfied with my hack of a way to "fix" the FrameTicks... surely there must be a nicer/easier way to do it.

2
Have you tried using ColorData["LightTemperatureMap"] or ColorData["TemperatureMap"] instead of "Rainbow"? These produce a much brighter plot.sakra
There are graduate advisors older than yoda?sakra

2 Answers

20
votes

Replace your ColorData["Rainbow"] with this one:

Function[Blend[RGBColor @@@ cMap, Slot[1]]]

and you get this:

enter image description here


As to your second question, you can do it this way:

xMax = 3; yMax = 3;
img = Transpose@
   Table[Sin[y^3 + x^2], {x, -xMax, xMax, 0.01}, {y, -yMax, yMax, 
     0.01}];
plot = ArrayPlot[img, 
  ColorFunction -> Function[Blend[RGBColor @@@ cMap, Slot[1]]], 
  AspectRatio -> 1, FrameTicks -> Automatic, 
  DataRange -> {{-xMax, xMax}, {-yMax, yMax}}, DataReversed -> True]

enter image description here

but why don't you use DensityPlot?

DensityPlot[Sin[y^3 + x^2], {x, -xMax, xMax}, {y, -yMax, yMax}, 
 ColorFunction -> Function[Blend[RGBColor @@@ cMap, Slot[1]]], 
 PlotPoints -> 300]

enter image description here


EDIT
Note that in the second plot the y-range labeling is reversed. That's because it takes the DataReversed setting into account. ArrayPlot plots the rows of the arrays in the same order as they appear when the array's content is printed on screen. So the first row is plotted on top and the last row is plotted at the bottom. High row values correspond to low y-values and vice versa. DataReversed->True corrects for this phenomenon, but in this case it also 'corrects' the y values. A workaround is to fill the array starting from high y-values going down to the lower ones. In that case you don't need DataReversed:

xMax = 3; yMax = 3;
img = Transpose@
   Table[Sin[y^3 + x^2], {x, -xMax, xMax, 0.01}, {y, 
     yMax, -yMax, -0.01}];
plot = ArrayPlot[img, 
  ColorFunction -> Function[Blend[RGBColor @@@ cMap, Slot[1]]], 
  AspectRatio -> 1, FrameTicks -> Automatic, 
  DataRange -> {{-xMax, xMax}, {-yMax, yMax}}]

enter image description here

19
votes

(I hope this isn't too late an addendum.)

As it turns out, one doesn't even need to keep the entire set of sixty-four RGBColor[] directives around for the purpose of using with Blend[] A clue that this is certainly the case is afforded by ListPlot[]s of the columns of cMap:

{rr, gg, bb} = Transpose[Rationalize[cMap]];
GraphicsGrid[{MapThread[
   ListPlot[#1, DataRange -> {0, 1}, Frame -> True, 
     GridLines -> {{1/9, 23/63, 13/21, 55/63}, None}, 
     PlotLabel -> #2] &, {{rr, gg, bb}, {"Red", "Green", "Blue"}}]}]

LisPlot[]s of RGB components of MATLAB's jet colormap

and we see that implicitly, the functions representing these components are piecewise linear. Since Blend[] necessarily does linear interpolation between colors, if we can find those colors that correspond to "corners" in the piecewise linear graphs, we can eliminate all the other colors in between those corners (since Blend[] will do the interpolation for us), and thus potentially have to carry around only, say, seven as opposed to sixty-four colors.

From reading the code given above, you'll note that I already found those transition points for you (hint: check the setting for GridLines). Further hints on what those colors might be are furnished by the documentation for colormap():

jet ranges from blue to red, and passes through the colors cyan, yellow, and orange.

Could it be? Let's check:

cols = RGBColor @@@ Rationalize[cMap];
Position[cols, #][[1, 1]] & /@ {Blue, Cyan, Yellow, 
  Orange // Rationalize, Red}
{8, 24, 40, 48, 56}

This just gives the positions of the colors within the array cols, but we can rescale things to correspond to the argument range expected of a colormap:

(# - 1)/(Length[cols] - 1) & /@ %
{1/9, 23/63, 13/21, 47/63, 55/63}

and those are precisely where the breakpoints of the piecewise linear functions corresponding to RGB components of the colormap are. That is five colors; to ensure a smooth interpolation, we add the first and last colors as well to this list,

cols[[{1, Length[cols]}]]
{RGBColor[0, 0, 9/16], RGBColor[1/2, 0, 0]}

paring the original cols list to a total of seven. Since 7/64 is approximately 11%, this is a pretty big savings.

Thus the color function we seek is

jet[u_?NumericQ] := Blend[
        {{0, RGBColor[0, 0, 9/16]}, {1/9, Blue}, {23/63, Cyan}, {13/21, Yellow},
         {47/63, Orange}, {55/63, Red}, {1, RGBColor[1/2, 0, 0]}}, 
                          u] /; 0 <= u <= 1

We make two comparisons to verify jet[]. Here's a gradient plot comparing the ColorFunctions jet and Blend[cols, #]&:

GraphicsGrid[{{
   Graphics[Raster[{Range[100]/100}, ColorFunction -> (Blend[cols, #] &)], 
    AspectRatio -> .2, ImagePadding -> None, PlotLabel -> "Full", 
    PlotRangePadding -> None], 
   Graphics[Raster[{Range[100]/100}, ColorFunction -> jet], 
    AspectRatio -> .2, ImagePadding -> None, 
    PlotLabel -> "Compressed", PlotRangePadding -> None]}}]

color gradient comparison of jet and explicit 64-color Blend

and here's a mechanical verification that the 64 colors in cols are nicely reproduced:

Rationalize[Table[jet[k/63], {k, 0, 63}]] === cols
True

You can now use jet[] as a ColorFunction for any plotting function that supports it. Enjoy!