19
$\begingroup$

What's the best (most elegant, shortest, easiest to read) way to add minor grid lines to a plot?

Here is an example:

data = Table[{x, Log@x}, {x, 100}];
ListPlot[data, Frame -> True, GridLines -> Automatic]

what I get

But this gives gridlines only at the position of the major frameticks. I'm looking for an easy way to automatically generate minor grid lines at the positions of minor frameticks (in a style different from the major gridlines).

Here is an example (yes, made in excel)

What I would like to get

I guess I could write half a page of code to generate both the frame ticks and the gridlines, but it seems like an overkill.

I realize this is very similar to this question, but if I have to enter the ranges manually for each plot then I might as well draw my plots in excel.

$\endgroup$
2
  • $\begingroup$ Maybe could use similar methods in this question. $\endgroup$
    – Silvia
    Commented Apr 26, 2013 at 8:43
  • $\begingroup$ Somewhat related: (54613) $\endgroup$
    – Mr.Wizard
    Commented Jul 1, 2015 at 14:57

6 Answers 6

10
$\begingroup$

Not elegant, but at least it's quite short :)

makeGrid[{minX_, maxX_}, {minY_, MaxY_}, {xStep_, yStep_}] := 
 {AbsoluteThickness[.25],
   Table[{If[Mod[x, 10] == 0, Black, LightGray], 
     Line[{{x , -100}, {x , 100}}]}, {x, minX, maxX, xStep}],
   Table[{If[Mod[y, 1] == 0, Black, LightGray], 
     Line[{{-100, y}, {100, y}}]}, {y, minY, MaxY, yStep}]}

ListPlot[data, Frame -> True, 
 Prolog -> makeGrid[{0, 100}, {0, 10}, {10, .1}]]

rules

$\endgroup$
3
  • $\begingroup$ +1 for Prolog. Had a similar one. :) $\endgroup$
    – Silvia
    Commented Apr 26, 2013 at 10:16
  • $\begingroup$ @Silvia so that's where I'd seen it before! :) Still, good practice to code these things up without looking sometimes... $\endgroup$
    – cormullion
    Commented Apr 26, 2013 at 10:26
  • $\begingroup$ Good practice indeed :) $\endgroup$
    – Silvia
    Commented Apr 26, 2013 at 10:35
14
$\begingroup$

This is what came to my mind

myGridDivision[{min_, max_}, {major_, majorStyle_}, {minor_, minorStyle_}] :=
 Function[divisions,
   Join[{#, majorStyle} & /@ divisions[[1]],
    {#, minorStyle} & /@ Complement[Flatten[#[[2]]], #[[1]]] &@ divisions]
   ][FindDivisions[{min, max}, {major, minor}]]

ListPlot[data, Frame -> True,
 FrameTicks -> {{True, False}, {True, False}},
 PlotRange -> {{0, 100}, {0, 5}},
 GridLines -> {
   myGridDivision[{0, 100}, {5, GrayLevel[.5]}, {5, GrayLevel[.8]}],
   myGridDivision[{0, 5}, {5, GrayLevel[.5]}, {5, GrayLevel[.8]}]
   }]

enter image description here

You can always set min/max to far more negative/positive large than the actual range of the plot to avoid manully determine the range.

Unfortunately, for some custom set FrameTicks cases, it seems not possible to auto-determine the FrameTicks setting using AbsoluteOptions[(*graphics*), FrameTicks] after the plot has been generated.. Maybe a home-made frame-ticks generator along with this one would be good.

$\endgroup$
8
$\begingroup$

Update 2: If it is ok to have minor and major gridlines with the same style, then the simplest solution is to use Full as the option setting:

ListPlot[data, Frame -> True, GridLines -> {Full, Full}]

enter image description here

AFAIK, this setting is not documented.

Update: A more convenient approach is to use the function Charting`FindTicks:

ClearAll[gridLinesF2]
gridLinesF2[majorstyle_: Thick, minorstyle_: Thin] := Replace[
   DeleteDuplicatesBy[Charting`FindTicks[{0, 1}, {0, 1}][##][[All, ;; 2]], First], 
     {{a_, ""} :> {a, minorstyle}, {a_, b_} :> {a, majorstyle}}, 1] &

ListPlot[data, Frame -> True, GridLines -> {gridLinesF2[Blue, Green], gridLinesF2[]}]

enter image description here

Original answer:

GridLines option setting can be a function. Using FindDivisions (as in Silvia's answer) without specific values for min and max (i.e., letting FindDivisions use automatically generated min and max values) and using {6, 6} as the second argument we get major and minor GridLines that match automatic ticks.

ClearAll[gridLinesF]
gridLinesF[style1_: Directive[Thickness[.003], GrayLevel[.5]], 
  style2_: Directive[Thin, GrayLevel[.9]], divs_:{6, 6}] := 
 Join[Thread[{#, style1}], 
   Thread[{DeleteCases[DeleteDuplicates[Join @@ #2], Alternatives @@ #], style2}]] & @@
 FindDivisions[{##}, divs] &

Examples:

data = Table[{x, Log@x}, {x, 100}];
ListPlot[data, Frame -> True, GridLines -> {gridLinesF[], gridLinesF[]}]

enter image description here

ListPlot[{RandomInteger[100], RandomReal[127]} + # & /@ data, 
 Frame -> True, GridLines -> {gridLinesF[], gridLinesF[]}]

enter image description here

Graphics[Circle[], Frame -> True, GridLines -> {gridLinesF[Red], gridLinesF[Blue]}]

enter image description here

Graphics[{}, PlotRange -> {{0, 1}, {0, 1}}, Frame -> True, 
 GridLines -> {gridLinesF[Directive[Thickness[.01], Red], Orange], 
   gridLinesF[Directive[Thickness[.01], Blue], Green]}]

enter image description here

$\endgroup$
4
$\begingroup$

I think it is quite useful since it is automatically applicaple:

data = Table[{x, Log@x}, {x, 100}];
plot = ListPlot[data, Frame -> True, GridLines -> Automatic]
{leftTicks, bottomTicks} = 
AbsoluteOptions[plot, FrameTicks][[ 1, 2, {2, 1}]];

we are taking FrameTicks from automatic plot, then we are taking labeled ticks as thicker GridLines and rest as thinner:

horizontalGridLines = 
leftTicks /. {x_?NumericQ, y_String, z__} :> {x, 
 [email protected]} /. {x_?NumericQ, y_?NumericQ, z__} :> {x, Black}

verticalGridLines = 
bottomTicks /. {x_?NumericQ, y_String, z__} :> {x, 
 [email protected]} /. {x_?NumericQ, y_?NumericQ, z__} :> {x, Black}

ListPlot[data, Frame -> True, 
GridLines -> {verticalGridLines, horizontalGridLines}]
$\endgroup$
4
  • $\begingroup$ AbsoluteOptions[plot, FrameTicks] won't work for some kinds of custom setting FrameTicks, say FrameTicks -> {{True, False}, {True, False}}. $\endgroup$
    – Silvia
    Commented Apr 26, 2013 at 9:17
  • $\begingroup$ So do not set it for first plot. It is only to get values we need. But I agree that exact values of FrameTicks given by user in final plot may not match those GridLines in general case. $\endgroup$
    – Kuba
    Commented Apr 26, 2013 at 9:19
  • $\begingroup$ Good call! (+1) $\endgroup$
    – Silvia
    Commented Apr 26, 2013 at 9:22
  • $\begingroup$ Fix for V11.1: Change plot and the line after to plot = ListPlot[data, Frame -> False, GridLines -> Automatic]; {leftTicks, bottomTicks} = AbsoluteOptions[plot, Ticks][[1, 2, {2, 1}]];. Then the rest can stay, or replace the second ListPlot with Show[plot, Frame -> True, GridLines -> {verticalGridLines, horizontalGridLines}]. $\endgroup$
    – Michael E2
    Commented May 10, 2017 at 21:03
2
$\begingroup$

Using elements from Kuba's great answer this is what I came up with:

addMajorMinorGridLines[plot_, majorStyle_, minorStyle_] := 
 Block[{leftTicks, bottomTicks, horizontalGridLines, 
   verticalGridLines},
  {leftTicks, bottomTicks} = AbsoluteOptions[plot, FrameTicks][[1, 2, {2, 1}]];
  horizontalGridLines = 
   leftTicks /. {x_?NumericQ, y_String, z__} :> {x, minorStyle} 
            /. {x_?NumericQ, y_?NumericQ, z__} :> {x, majorStyle};

  verticalGridLines = 
   bottomTicks /. {x_?NumericQ, y_String, z__} :> {x, minorStyle} 
               /. {x_?NumericQ, y_?NumericQ, z__} :> {x, majorStyle};
  Show[plot, GridLines -> {verticalGridLines, horizontalGridLines}]
  ]


plot = ListPlot[data, Frame -> True, GridLines -> Automatic, PlotStyle -> Red];
addMajorMinorGridLines[plot, Directive[Thick, Black], Directive[Thin, [email protected]]]

enter image description here

$\endgroup$
4
  • $\begingroup$ How will you deal with plot with non-integer ticks, say plot = ListPlot[data, Frame -> True, FrameTicks -> {{{-1, 0, 1}, None}, {{0, Pi, 2 Pi, 3 Pi}, None}}]? $\endgroup$
    – Silvia
    Commented Apr 26, 2013 at 10:11
  • $\begingroup$ I tried upvoting this but stackexchange temporarily lost the plot $\endgroup$
    – cormullion
    Commented Apr 26, 2013 at 10:31
  • $\begingroup$ @Silvia Good point. I guess "Not now" is not a good answer? I welcome any suggestions. $\endgroup$
    – Ajasja
    Commented Apr 26, 2013 at 12:30
  • $\begingroup$ @Ajasja I would say re-constructing a ListGridPlot from Graphics and primitives. :) $\endgroup$
    – Silvia
    Commented Apr 26, 2013 at 12:59
0
$\begingroup$

enter image description here

You just need:

GridLines -> {Range[0, 100, 5], Range[0, 5, 0.2]}

data = Table[{x, Log @ x}, {x, 100}];

ListPlot[data,
  Frame -> True,
  GridLines -> {Range[0, 100, 5], Range[0, 5, 0.2]},
  PlotRange -> {{0, 100}, {0, 5}}]
$\endgroup$
1
  • $\begingroup$ The grid lines all have equal weight unlike the image in the OP $\endgroup$
    – Michael E2
    Commented Apr 15, 2018 at 0:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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