Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I would like to generate a ListPlot with the color for each point in the plot corresponding to a particular value (not associated with the position in the plot). I'd then like to add a legend indicating what the color means.

I'm currently solving the first part of the problem by essentially generating a separate ListPlot for each data point and then assigning a color to that ListPlot. Here's a toy example:

n = 5000;
pos = RandomVariate[NormalDistribution[0, 2], {n, 2}];
altitude = Norm /@ pos;
ListPlot[{#} & /@ pos, 
 PlotStyle -> ((Blend[{{Min[altitude], Yellow}, {Max[altitude], 
         Red}}, #] &) /@ altitude), AspectRatio -> 1]

enter image description here

So my questions are:

(1) Is this the only way to generate such a ListPlot (it does the job but it seems inelegant and I suspect it's inefficient, though that's not a big concern for my application)?

(2) Is there an easy way to generate a legend which indicates the value of the color (i.e., a gradient bar which shows the color scale)?

share|improve this question

3 Answers 3

up vote 22 down vote accepted

In this case I would use Point for plotting the points. For example

n = 5000;
pos = RandomVariate[NormalDistribution[0, 2], {n, 2}];
altitude = Norm /@ pos;
colorf = Blend[{{Min[altitude], Yellow}, {Max[altitude], Red}}, #] &

pl = Graphics[MapThread[{colorf[#1], Point[#2]} &, {altitude, pos}], 
  Axes -> True, AspectRatio -> 1]

As for plotting legends, that's a reoccurring issue in Mathematica. There is a package called PlotLegends` which you could try but it is not very user friendly and the legends it produces are quite ugly IMHO. I find that it's often faster to just create a legend by hand. For example, this is a function I use for creating legends with contour plots:

plotLegend[{min_, max_}, n_, col_] := 
 Graphics[MapIndexed[{{col[#1], 
      Rectangle[{0, #2[[1]] - 1}, {1, #2[[1]]}]}, {Black, 
      Text[NumberForm[N@#1, {4, 2}], {4, #2[[1]] - .5}, {1, 0}]}} &, 
   Rescale[Range[n], {1, n}, {min, max}]],
  Frame -> True, FrameTicks -> None, PlotRangePadding -> .5]

Here, n is the number of subdivisions and col is the colour function. You could combine the legend with the original plot using Grid, e.g.

leg = plotLegend[Through[{Min, Max}[altitude]], 20, colorf];
Grid[{{Show[pl, ImageSize -> {Automatic, 300}], 
  Show[leg, ImageSize -> {Automatic, 250}]}}]

Mathematica graphics

share|improve this answer
8  
The numbers on the color legend aren't really nice. You could use FindDivisions to get a nice set, like so FindDivisions[Through[{Min, Max}[altitude]], 10] –  Sjoerd C. de Vries Feb 4 '12 at 18:20
    
Very helpful! Many thanks to you and the others who posted answers and suggestions!!! –  David Skulsky Feb 5 '12 at 12:42

ListLinePlot (or alternately ListPlot with Joined->True) accepts a ColorFunction, which you can use to color your points. The lines can be later converted to points as:

colorFun = Function[{x, y}, Blend[{{Min[altitude], Yellow}, {Max[altitude], Red}}, 
    Norm[{x, y}]]];    
ListLinePlot[pos, ColorFunction -> colorFun, AspectRatio -> 1, 
      ColorFunctionScaling -> False] /. Line -> Point

Then using Heike's plot legends and Sjoerd's suggestion to use FindDivisions, you get:

enter image description here

share|improve this answer
    
+1 this gets the same faster rendering via VertexColors that my code does, but is arguably neater. I still like Inset for the legend; consider combining them. –  Mr.Wizard Feb 4 '12 at 21:29
    
@Spartacus Thanks. I will, sometime later in the day. I'm off for now. –  rm -rf Feb 4 '12 at 21:59

As usual Heike has a fine method, but I can tighten it up.

This will render quite a bit faster, it will use built-in color functions more easily, and it will IMO interactively rescale better.

Data:

n = 5000;
pos = RandomReal[NormalDistribution[0, 2], {n, 2}];
altitude = Norm /@ pos;
colorf = Blend[{Yellow, Red}, #] &;

Legend function (modified):

plotLegend[{min_, max_}, n_, col_] :=
 Graphics[
  {{col[(# - 1)/(n - 1)], Rectangle[{0, # - 1}, {1, #}]},
   {Black, Text[
      NumberForm[Rescale[#, {1, n}, {min, max}], {4, 2}],
      {3, # - .5}, {1, 0}]}
  } & /@ Range@n,
  Frame -> True, FrameTicks -> None, PlotRangePadding -> .5]

Plot:

Graphics[{
  Point[pos, VertexColors -> colorf /@ Rescale@altitude],
  Inset[plotLegend[{Min@#, Max@#}& @ altitude, 20, colorf],
    Scaled[{1, 1/2}],
    ImageScaled[{-0.1, 1/2}],
    Scaled[{1/4, 0.9}]]
 }, Axes -> True]

Mathematica graphics

It's easy to use other gradients. With colorf = ColorData["Rainbow"]:

Mathematica graphics

share|improve this answer
1  
As with Heike's solution, yours has an ugly set of numbers. No professional typesetter/layouter would use these. Use FindDivision for a better set of numbers. –  Sjoerd C. de Vries Feb 5 '12 at 22:14
    
@Sjoerd I only noticed this comment now. Okay, I'll improve this later. –  Mr.Wizard Feb 11 '12 at 0:29
    
The plotting here is tremendously faster than the accepted answer, FYI. –  Guillochon Mar 20 at 15:34
    
@Guillochon Thanks for noticing. :-) –  Mr.Wizard Mar 20 at 17:57
    
@Sjoerd I see I never did address your criticism. Sorry. This time I won't claim that I will, but perhaps; we'll see. –  Mr.Wizard Mar 20 at 17:58

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.