3
$\begingroup$

I have a plot and I want to format it for journal paper. I just realized how hard to format a plot for academic purposes using MMA.

For example:

I want this plot Plot[{Sin[x], Cos[x]}, {x, 0, 4}] to be like this:

enter image description here

I tried using Mesh like this:

p=Plot[{Sin[x], Cos[x]}, {x, 0, 4}, 
 Mesh -> {Range[0, 4, 0.5], Range[0, 4, 1]}, 
 MeshStyle -> {{Directive[Red, PointSize[0.02]]}, {Directive[Green, 
     PointSize[0.02]]}}, PlotLegends -> {"Sin", "Cos"}]

but of course no luck.

I had to use long way like this:

pl = ListLinePlot[Table[{x, #}, {x, 0, 4, 0.5}] & /@ {Sin[x], Cos[x]},
     PlotMarkers -> {Automatic, 15}, 
    PlotLegends -> {"Sin", "Cos"}] /. Line[x_] -> Sequence[];
Show[Plot[{Sin[x], Cos[x]}, {x, 0, 4}], pl]

My questions are:

1-Is there any easy and direct way to do this( I suppose this should be an issue for MMA as a lot of people use it for their academic work)

2-If you note in the plot p, the Mesh and MeshStyle can not be used for each plot separately when more than one functions are in the Plot. They just used the first element in the RHS of the option Rule. Any idea about this?

$\endgroup$
5
$\begingroup$

You can change the legend using the LegendMarkers option to an explicitly constructed LineLegend. The points are a bit of a hack, but you can always explicitly create them as an Epilog collection of points. I couldn't work out which colour scheme is the default in version 10, so I used the first indexed colour scheme, which replicates the default styles used in previous versions of Mathematica.

p = Plot[{Sin[x], Cos[x]}, {x, 0, 4}, PlotStyle -> ColorData[1], 
  Epilog -> {PointSize[0.02], ColorData[1, 1], 
    Point[{#, Sin[#]}] & /@ Range[0.5, 3.5, 0.5], ColorData[1, 2], 
    Point[{#, Cos[#]}] & /@ Range[0.5, 3.5, 0.5]}, 
  PlotLegends -> LineLegend[{"Sin", "Cos"}, 
    LegendMarkers -> 
     Graphics[{Thick , EdgeForm[] , Line[{{-1, 0}, {1, 0}}], 
       Disk[]}]]]

enter image description here

EDIT

I just realised you wanted little squares for the second curve. The 0.03 offset is hard-coded, which means that AspectRatio will affect the look. You could probably do something clever with Scaled and Inset instead.

p = Plot[{Sin[x], Cos[x]}, {x, 0, 4}, PlotStyle -> ColorData[1], 
  Epilog -> {PointSize[0.02], ColorData[1, 1], 
    Point[{#, Sin[#]}] & /@ Range[0.5, 3.5, 0.5], ColorData[1, 2], 
    Rectangle[{# - 0.03, Cos[#] - 0.03}, {# + 0.03, 
        Cos[#] + 0.03}] & /@ Range[0.5, 3.5, 0.5]}, 
  PlotLegends -> 
   LineLegend[{"Sin", "Cos"}, 
    LegendMarkers -> {Graphics[{Thick, EdgeForm[], 
        Line[{{-1, 0}, {1, 0}}], Disk[]}], 
      Graphics[{Thick, EdgeForm[], Rectangle[]}]}]]

enter image description here

$\endgroup$
  • $\begingroup$ Any help with the colors?: mathematica.stackexchange.com/questions/66923/… $\endgroup$ – Michael E2 Apr 23 '15 at 11:40
  • $\begingroup$ @MichaelE2 Yes, but isn't it odd that the default colors don't resolve to an existing scheme? $\endgroup$ – Verbeia Apr 23 '15 at 11:43
  • $\begingroup$ Actually, it's ColorData[97], but I thought the PlotTheme approach might be more stable going forward (an unwarranted assumption, perhaps). $\endgroup$ – Michael E2 Apr 23 '15 at 11:56
4
$\begingroup$

Most of the desired plot can be achieved using only options Mesh and MeshFunctions.

Plot[{Sin[x], Cos[x]}, {x, -0.01, 4.01}, 
 MeshFunctions -> {If[Abs[Sin[#] - #2] < .001 && Abs[Cos[#] - #2] > .0001, 
                      Sin[# 2 Pi] + 1, Sin[# Pi] - 1] &}, 
 Mesh -> {{{1, Directive[Red, PointSize[0.02]]}, 
          {-1, Directive[Green, PointSize[0.02]]}}}, 
 PlotLegends -> {"Sin", "Cos"}]

Mathematica graphics

It works as follows.

  • To place points at the ends of the line, expand the plotting range slightly, from {x, -0.00, 4.00} to {x, -0.01, 4.01}.
  • To place points regularly, choose a mesh function like Sin[# 2 Pi]. If irregularly spaced points are desired, use a polynomial with zeros at the desired locations. Note that only continuous functions work in general.
  • To distinguish between curves, use If (or Which for more than two curves) with a criterion that #2 is equal to f[#1], where f is the definition of the curve.
  • To specify point color and other graphics directives, include them in Mesh.
  • To add the point to the PlotLegend (not done here), use the LegendMarkers procedure in the Answer by Verbeia, although some adjustments of color may be needed.

Note that this solution does not accommodate markers other than points, because Mesh accepts graphics directives but not graphics objects.

$\endgroup$
2
$\begingroup$

1. I doubt there is an easier way than what you did. One can wrap it up in a function:

ClearAll[markerMesh];
SetAttributes[markerMesh, HoldAll]; 
markerMesh[Plot[fns_List, {x_, x1_, x2_}, opts : OptionsPattern[]], 
  markerOpts : OptionsPattern[]] := Show[
  Plot[fns, {x, x1, x2}, Evaluate@FilterRules[{opts}, Except[{Mesh}]]],
  ListPlot[MapThread[
    Table[{x, #},
     {x, Replace[#2, n_Integer :> Rescale[N@Range[1, n], {0, n + 1}, {x1, x2}]]}] &,
    {fns, 
     PadRight[{}, Length[fns], 
      Replace[OptionValue[Plot, {opts}, Mesh], n_Integer :> {n}]]}], 
   FilterRules[{opts}, Options[ListPlot]], 
   FilterRules[{markerOpts}, Except[{PlotLegends}]]],
  ListLinePlot[{{}, {}}, FilterRules[{opts}, Options[ListPlot]],
   markerOpts, PlotLegends -> fns]
  ]

Examples:

markerMesh[
 Plot[{Sin[x], Cos[x]}, {x, 0, 4}, Mesh -> {Range[0, 4, 0.5], Range[0, 4, 1]}],
 PlotMarkers -> {Automatic, 15}, PlotLegends -> {"Sin", "Cos"}]

Mathematica graphics

markerMesh[
 Plot[{Sin[x], Cos[x]}, {x, 0, 4}, Mesh -> {5, 11}, PlotStyle -> {Red, Blue}],
  PlotMarkers -> {Automatic, 25}]

Mathematica graphics

2. Forcing Mesh to do it is more difficult. Here is one way, via a cute use of TagSetDelayed, but it relies on the internal structure of the graphics generated by Plot. A few versions down the line from now (V10.1), it might not work. It uses a dummy "directive" foo, whose definition causes a rewrite of the way the mesh points are stored in the graphics.

ClearAll[foo];
foo /: {foo[x___], Point[p_]} := {Text[Style[x], #] & /@ p}

x1 = 0; x2 = 4; dx = 10^-6;
meshstyles = {
   foo["\[FilledCircle]", ColorData[97, 1], 15],
   foo["\[FilledSquare]", ColorData[97, 2], 15]};
Plot[{Sin[x], Cos[x]}, {x, x1 - dx, x2 + dx},
 MeshFunctions -> {
   If[Abs[Sin[#1] - #2] < 0.0001, #1, x1 - dx] &,
   If[Abs[Cos[#1] - #2] < 0.0001, #1, x1 - dx] &},
 Mesh -> {Range[0, 4, 0.5], Range[0, 4, 1]},
 MeshStyle -> meshstyles,
 PlotLegends -> 
  LineLegend[{"Sin", "Cos"}, 
   LegendMarkers -> (Graphics[{#, Point[{{0, 0}}]}] & /@ meshstyles)]]

Mathematica graphics

This also could be wrapped up in a function, but it hardly seems worth it given the solution in 1. The fudge-factor dx to get the endpoints to work correctly is also annoying.

$\endgroup$
  • $\begingroup$ +1 for using the mesh approach. I have two notes: 1-if you increase the Marks size (in the Mesh approach) the legends get distorted. 2-for the first approach the line is missing in the legends. $\endgroup$ – Algohi Apr 23 '15 at 16:48
  • $\begingroup$ @Algohi Thanks. 1. It's not clear to me that it's worth pursuing the mesh approach to perfection. 2. I was oblivious or indifferent to the difference in the legends. Thanks for pointing it out. ListLinePlot wants to put in extra points, when the points' x coordinates do not match; hence, the combination of it and ListPlot $\endgroup$ – Michael E2 Apr 23 '15 at 17:43

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.