1
$\begingroup$

I am aware that this question has already been asked: Providing a color function for ListPointPlot3D. However, that question and its answers did not help. I had better luck with this other, similar question: ListPlot - coloring negative and positive points. My code is taken from the latter.

I have a largish set of points, of which this is a small sample (30):

data = {{41.9752, 51.0084, -0.14}, {46.3064, 55.7369, 0},
        {41.4526, 57.7858, 0}, {37.0897, 9.06416, 0},
        {44.0334, 55.3856, 1098.}, {44.5004, 50.5896, -3778.25},
        {44.6699, 53.0785, 0}, {32.7899, 7.67608, 0},
        {40.6237, 58.6538, 0}, {50.261, 63.6736, 0}, {46.7637, 51.6856, 0},
        {42.2847, 50.6747, 0}, {44.9239, 56.1008, 2619.29}, {32., 8.8501, 0},
        {46.951, 51.1587, -31.16}, {41.8275, 51.7466, -1318.44},
        {41.3532, 50.1727, -105.16}, {36.9869, 12.2926, 0},
        {46.7228, 50.8667, 0}, {37.7692, 12.9168, 0},
        {41.7265, 56.5381, -4748.22}, {45.4683, 51.9324, 5866.5},
        {46.9676, 55.7092, 3596.78}, {47.0195, 56.0355, -2888.82},
        {44.9408, 52.8141, 3537.41}, {40.5041, 52.5762, -785.58},
        {46.3697, 55.1177, 24437.9}, {46.5075, 52.7793, 6381.1},
        {42.1066, 52.3115, 0}, {43.7638, 52.579, -1678.97}}

I want the colour to depend on the third coordinate of each point. If it's above 0 it should be plotted as a green point, while if it's less than or equal to zero it should be blue. The code is:

ListPointPlot3D[data,
 ColorFunction -> 
 Piecewise[{{Green, #[[3]] >= 0}, {Blue, #[[3]] < 0}}] & /@ data,
 PlotRange -> {{7.25, 10.75}, {42, 45.5}, {-50000, 50000}}];

It almost works, meaning that I get just one error message, and the plot does appear, but all the points are the same colour (dark blue). The error message I get is

ListPointPlot3D::cfun: Value of option ColorFunction -> 
(here there is a blue box)
is not a valid color function, or a gradient ColorData entity

I am sorry if this is a really simple error that I can't see, but what am I doing wrong? Oh, I have MMA 10 on a Mac running Yosemite.

Many thanks

$\endgroup$
  • $\begingroup$ Why not separate the data into two sets? $\endgroup$ – Feyre Oct 13 '16 at 16:51
  • $\begingroup$ You were so close; try adding ColorFunction -> (Piecewise[{{Green, #3 >= 0}, {Blue, #3 < 0}}] &), ColorFunctionScaling -> False. $\endgroup$ – J. M. will be back soon Oct 13 '16 at 16:52
  • $\begingroup$ As Feryre suggests, ListPointPlot3D[ GatherBy[data, Last@# <= 0 &] , PlotStyle -> {Green, Blue} ] also works. $\endgroup$ – N.J.Evans Oct 13 '16 at 17:01
  • $\begingroup$ ...but if you really want that route: GatherBy[data, Sign @* Last]. $\endgroup$ – J. M. will be back soon Oct 13 '16 at 17:06
  • $\begingroup$ @J.M., thank you very much! It does work! Wow I think I sort of understand the syntax, that's great. Feyre (and N.J.Evans), thanks, very good question. The reason is that my next step will be to make an animation. The third coordinate will oscillate up and down around zero for each point, and I will have to generate a few hundred plots, I think. So having to filter the data array each time to generate two separate arrays seems like a lot of CPU time. ColorFunction seemed easier. $\endgroup$ – pdini Oct 13 '16 at 17:07
1
$\begingroup$

There appear to be two problems with my syntax:

  1. the function definition is not enclosed in parentheses
  2. I used the Map construct to apply the colour function to each element at the first level of the list data.

In addition, I did not use

ColorFunctionScaling -> False.

which is needed if discrete colours are desired rather than a smooth rainbow gradient.

J.M.'s hint helps understand the parentheses. The fact that the rule 'Operator Input Form' has higher precedence than the ampersand (&) means that the beginning of the function definition, i.e.

Piecewise[{{Green, #[[3]] >= 0}, {Blue, #[[3]] < 0}}],

is bound to ColorFunction before the & is recognised. Hence, the function definion is meaningless. Adding the parentheses ensures that the Function operator (&) is detected and applied first, making the rest an actual function.

The second error is less excusable. By using the Map construct I tried to override the internal workings of ListPointPlot3D (which I do not understand and am not familiar with) whereby the data list was already being fed by ListPointPlot3D to the ColorFunction I defined through the Rule (->). Clearly that wasn't going to work.

Interestingly, the Map operator (/@) has much higher precedence than either the Rule (->) or the Function (&) operators, so this would have probably thrown MMA for a loop.

Second interesting point is that now the error message is clear!

$\endgroup$
  • $\begingroup$ This is mostly correct, except for the part on ColorFunctionScaling; briefly, when set to True, the $z$ values (corresponding to #3 in the ColorFunction) are first scaled so that the minimum is mapped to $0$, and the maximum is mapped to $1$ before the ColorFunction is evaluated on them. With the False setting, no scaling is done, and thus the actual height values are what is passed to the ColorFunction. (IOU one upvote.) $\endgroup$ – J. M. will be back soon Oct 14 '16 at 10:51
  • $\begingroup$ Excellent, thank you J.M.! This part is clear too now. $\endgroup$ – pdini Oct 14 '16 at 12:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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