12

This is my first question on any stack-exchange site and I'm also very new to using Mathematica software so please excuse/correct me if I mess up.

I would like to take a list of functions of a single variable {$f_1, f_2, f_3, \ldots, f_n$}, and plot the minimum value that each of these functions takes on at any point over an interval $[x_0, x_f]$.

So far this much is not so difficult (or has not been the difficult part for me to figure out). However, I would like for each of the intervals over which any function $f_i$ is the minimum to correspond to it's own color (like how Plot usually attributes different colors to different curves when you plot multiple functions) and further I'd like to include a legend that clearly denotes which function from the list corresponds to which color.

I can do the first part fairly easily using the following:

Z[x_] = Min[x, x^2, x^3, x^4, x^5]
Plot[Z[x], {x, 0, 2}, PlotLegends -> "Expressions"]

Plotting the minimum value of a list of multiple functions

I would like something that would color the segment of the curve where $x^5$ is the minimum function in the list one color and the segment where $x$ is the minimum function in another color and denote the color representations on the legend on the right.

I can plot each of the functions separately with the color-coding system like so:

Plot[{x, x^2, x^3, x^4, x^5}, {x, 0, 2}, PlotLegends -> "Expressions"]

Plotting multiple functions with color-coding and legend

And in both cases the " PlotLegends -> "Expressions" " flag/option (not sure what the terminology is for Mathematica functions) does nicely generate a legend as I'd like it.

But I don't really have a sense of how to combine the two.

Thanks in advance for the help, and while I didn't find any other questions that might help answer (or partially answer) this question please correct me if I missed any.

Moodragonx
  • 173
  • 1
  • 9
  • An idea for a solution I would want to use but don't know how to implement is generating a list of {g1, g2, ..., gn} defined so that g_i = f_i when f_i is the minimum of all the functions and is undefined otherwise. Then plotting all these functions would be (I suspect) sufficient by the means as the second attempt above. – Moodragonx May 16 '14 at 15:48

4 Answers4

9

New method

Inspired by your self-answer we can automate things as follows:

prep[fn_][a__] := If[# == fn[a], #] & /@ {a}

Now:

Plot[prep[Min][x, x^2, x^3, x^4, x^5], {x, 0, 2},
 BaseStyle -> {14, Thick},
 Frame -> True,
 PlotStyle -> {Red, Orange, Yellow, Green, Blue},
 Evaluated -> True
]

enter image description here

Although untested I presume this will work with PlotLegends. Note:

  • prep is somewhat generalized so that it can work with other functions
  • Evaluated -> True is used rather than Evaluate to keep x localized to the Plot
  • This method doesn't work in version 9.0.0 due to a bug. (47981)

Old methods

It may surprise you to learn that Mathematica internally splits the Line on discontinuities (when using the default value Automatic for the Exclusions option), which allows you do use the post-processing method shown here (bottom), e.g.:

f[x_] := Min[x, x^2, x^3, x^4, x^5]

Module[{i = 1},
 Plot[f[x], {x, 0, 2}, PlotStyle -> Thick]
  /. x_Line :> {ColorData[1][i++], x}
]

enter image description here

Or Simon Woods's splitstyle:

splitstyle[styles__] := 
  Module[{st = Directive /@ {styles}}, {{Last[st = RotateLeft @ st], #}} &];

Plot[f[x], {x, 0, 2}, PlotStyle -> splitstyle[Red, Green], BaseStyle -> Thick]

enter image description here

Notes

I don't have the PlotLegends option in version 7, which I use, therefore I cannot test that aspect of the question. Pardon me for not mentioning that directly.

You asked for an explanation of these methods. They are similar, yet work different. Both rely on the operation of the Exclusions mechanism of Plot. When a discontinuity is found a new Line primitive is created within the Graphics expression that is produced by Plot. The first method works by replacing (see ReplaceAll) each Line expression with a {style, Line} pair. (style is drawn from an arbitrarily selected ColorData scheme.)

The second method relies on a clever construction and the behavior of the PlotStyle option when it is given a function as its value. The function generated by splitstyle uses this method to cycle between given styles (not used in this example, but useful elsewhere). It also produces a {style, Line[ . . . ]} pair, and this expression is inserted by Plot itself rather than with post-processing.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Thank you for the prompt response.

    This doesn't seem to address the generation of a legend request here, however. Will the PlotLegends option used in my question still work to handle this? Further, being new to mathematica the syntax or methods used here are a little lost on me. I'll read up on this but further explanation of how this works would be awesome and whether I should have a reason to choose one method over the other would be appreciated as well.

    – Moodragonx May 16 '14 at 15:27
  • @Moodragonx You're welcome. – Mr.Wizard May 16 '14 at 15:28
  • @Moodragonx More notes added. Sorry I can't help with the PlotLegends option, at least not directly. – Mr.Wizard May 16 '14 at 15:41
  • Thanks for the clarification. Just thought I'd confirm that PlotLegends -> "Expressions" doesn't provide a separate legend entry for each section of the curve (which I expected since it is still only a single function f[x] being plotted). – Moodragonx May 16 '14 at 15:45
  • @Moodragonx Unsurprising, really. Let me think about how to approach that problem, at least with the tools I have. – Mr.Wizard May 16 '14 at 15:47
  • Hmm, your "new method" just gives me a blank plot. (Mathematica 9 on Mac OS X.) –  May 16 '14 at 16:25
  • @Rahul Well bollocks. Does Piecewise work for you? I can update my answer to use that instead, though it's a little more verbose. – Mr.Wizard May 16 '14 at 16:26
  • On my machine Plot seems to give up as soon as any of the functions are Undefined everywhere. So I don't think Piecewise would help. That's why I had to use RegionFunction in my answer. –  May 16 '14 at 16:27
  • @Rahul Please try my "New method" with this: prep[fn_][a__] := Piecewise[{{#, # == fn[a]}}, Indeterminate] & /@ {a} – Mr.Wizard May 16 '14 at 16:29
  • No, that doesn't help either. –  May 16 '14 at 16:32
  • Removing the "Indeterminate" parameter in Piecewise (and thus letting the default value of the function settle at 0 where you can not see it over the axes bars) seems to fix the error on my end. Also adding PlotRange -> All seems necessary. – Moodragonx May 16 '14 at 16:36
  • @Mr.Wizard, i.e. Your middle definition of prep[fn_][a__] := Piecewise[{{#, # == fn[a]}}] & /@ {a}, seems to work for me (when specifying the PlotRange). – Moodragonx May 16 '14 at 16:39
  • @Moodragonx Apparently the changed how both If and Indeterminate behave. I guess you are supposed to use ConditionalExpression now. I'll stick that in my answer too. – Mr.Wizard May 16 '14 at 16:40
  • @Moodragonx Would you please see if the new "for version 8" code works as expected? I wish they hadn't changed things... – Mr.Wizard May 16 '14 at 16:41
  • @Rahul You too, please, if you have time. – Mr.Wizard May 16 '14 at 16:41
  • @Mr.Wizard Hmm... neither version of prep currently work for me (I am on Mathematica 9.0 if that is elucidating), but the form I cited before does. – Moodragonx May 16 '14 at 16:42
  • @Mr.Wizard: I see the same behaviour as Moodragonx's last two comments, presumably because we're on the same version of Mathematica. However, instead of the curves stopping they drop to zero: http://i.stack.imgur.com/J5AIb.png –  May 16 '14 at 16:47
  • @RahulNarain Ah, yes I removed the BaseStyle -> {14, Thick}, to conceal the fact that the other functions are still being plotted at 0. Perhaps not the best solution since there are times when the x-axis won't align with y=0, and just that it seems inelligent. But using Indeterminate seems to force no other functions to be plotted on those intervals. – Moodragonx May 16 '14 at 16:53
  • 1
    Interesting news: Both definitions work fine on Mathematica 9.0.1.0 on Linux. My previous comments were on Mathematica 9.0.0.0 on Mac. –  May 16 '14 at 17:23
  • @Rahul A little late but interesting nevertheless. Please post any further observations here. Thanks. :-) – Mr.Wizard May 16 '14 at 17:30
  • @Moodragonx Apparently the problem is a bug in version 9.0.0. Can you confirm that is what you are using? – Mr.Wizard May 16 '14 at 17:38
  • @Mr.Wizard I am currently away from a machine with Mathematica on it and will not have access until the next semester begins in the fall. In the meantime I'm not really sure what to pick as a "best answer" as I've not yet seen if your method works with a legend for a non-buggy version of Mathematica. – Moodragonx May 26 '14 at 20:48
  • @Moodragonx You can wait to Accept an answer if you like. Also, should it not work with the built-in legend system you can still build a legend from Graphics primitives as Jens did here. Anyway, I hope to see you back here this autumn. – Mr.Wizard May 26 '14 at 21:30
7

Simple solution using RegionFunction:

f[x_] := {x, x^2, x^3, x^4, x^5};
Plot[Evaluate@f[x], {x, 0, 2}, PlotLegends -> "Expressions", 
 RegionFunction -> Function[{x, y}, y == Min[f[x]]], PlotPoints -> 20, 
 PlotRange -> All, PlotStyle -> ColorData[35, "ColorList"]]

enter image description here

One limitation is that you get overshooting unless you fiddle with the number of PlotPoints.

The only reason I changed the PlotStyle is because the first and fifth colours are hard to tell apart in the default colour scheme.

6
flist = Table[BesselJ[n, x], {n, 4}];
pieces = Table[ConditionalExpression[f, f == Min[flist]], {f, flist}]; (* thanks Rahul *)
pltstyls = Join[#, Directive[{#, Thickness[.01], Dashed}] & /@ #] &[
            ColorData[1, "ColorList"][[;; Length@flist]]];
lgndlbls = Join[#, StringJoin["piece ", #] &/@ (ToString /@ #)]&[TraditionalForm /@ flist];


Plot[Evaluate@Join[flist, pieces], {x, 0, 10},
     Filling -> Thread[Range[Length@flist] -> Axis], ImageSize -> 500,
     PlotStyle -> pltstyls, PlotLegends -> lgndlbls]

enter image description here

Update: Generalizing to arbitrary list of functions flist and functionals on flist:

 foo = Module[{pieces =Table[ConditionalExpression[f, f == #[#2]], {f, #2}], 
        styles = Join[#, Directive[{#, Thickness[.01], Dashed}] & /@ #] &[
                    #3[[;; Length@#2]]],
       lgndlbls =  Join[#, StringJoin["piece ", #] & /@ (ToString /@ #)] &[
                    TraditionalForm /@ #2]},
 Plot[Evaluate@Join[#2, pieces], {x, 0, 10}, 
        Filling -> Thread[Range[Length@#2] -> Axis], ImageSize -> 500, 
        PlotStyle -> styles, PlotLegends -> lgndlbls]] &;

 foo[RankedMin[#, 2] &, flist, ColorData[1, "ColorList"]]

enter image description here

kglr
  • 394,356
  • 18
  • 477
  • 896
  • This is an interesting solution. If you get the chance, some explanation of what's at work here would be nice. I've also been having some trouble generalizing this to other functions I might use (different lists with different numbers of functions) some help using this solution would be appreciated :). – Moodragonx May 16 '14 at 17:15
  • 1
    Suggestion: define pieces simply as Table[ConditionalExpression[f, f == Min[flist]], {f, flist}] :) Also note that this approach suffers from the same problems that plague Mr.Wizard's "New Method" on Mathematica 9. –  May 16 '14 at 17:20
  • @RahulNarain, thank you. Updated with your suggestion. Re the version/OS related issues, this seems to work ok on MMA version 9.0.1 on Windows 7. – kglr May 16 '14 at 20:48
5

I have somewhat devised a solution that does what I'm looking for but requires manually generating a list of functions that I'd like to avoid.

Z[x_] = Min[x, x^2, x^3, x^4, x^5]
g1[x_] = Piecewise[{{x, x == Z[x]}}]
g2[x_] = Piecewise[{{x^2, x^2 == Z[x]}}]
g3[x_] = Piecewise[{{x^3, x^3 == Z[x]}}]
g4[x_] = Piecewise[{{x^4, x^4 == Z[x]}}]
g5[x_] = Piecewise[{{x^5, x^5 == Z[x]}}]
Plot[{g1[x], g2[x], g3[x], g4[x], g5[x]}, {x, 0, 2}, PlotRange -> All, PlotLegends -> "Expressions"]

Produces the following output

Somewhat Desired Result

Some way to automate this would be much more preferred. In general I might want to err on the side of using Mr.Wizard's solution for my use case as I intend to do this repeated for lists of many functions at a time.

Further it might be nice for the expressions in the legend to include the original definitions of the functions $f$, but this is something that can be adjust manually fairly easily.

Moodragonx
  • 173
  • 1
  • 9
  • 1
    Does the problem you just reported also affect my "New method" on your system? You can try adding the option PlotRange -> All to yours to see if that corrects it. – Mr.Wizard May 16 '14 at 16:25
  • Okay I submit to having completely novice knowledge of how to plot in Mathematica. PlotRange -> All does in fact fix my "error". – Moodragonx May 16 '14 at 16:29
  • Please review the present version of my answer. I included three different definitions for prep; could you try each of them and report which, if any, work for you? – Mr.Wizard May 16 '14 at 16:36