1
$\begingroup$

Suppose I want to make a ListPlot of an XY data set for which all the Y values are integers, with the restriction that the tick labels on the Y axis only take integer values. Make note that it doesn't' always work out that way in the Automatic case.

How can one create a tick label function or another method to do this?

I don't necessarily want to change the Y axis plot range from its Automatic value and I'd rather not plot the data twice -- once to get the plot range and once again to select the tick marks and tick labels according to my specifications -- if I can avoid it because that takes too long.

Similarly, if one of the axes uses a log scale (e.g., ListLogPlot), how can I ensure that it only chooses axis labels that are simple powers of ten rather than a number times a power of ten?

I asked Wolfram Tech Support (WTS) for help finding a solution to this problem, but their offering (set Ticks -> {Automatic, Cases[#, _Integer]}] &[data]) fell short of the mark.

What follows is some test code with example results demonstrating the problem when using Automatic ticks selection and with the solution offered by WTS. Clearly neither one produces an acceptable result in the general case.

Example code:

Module[{min, max, data},
  {min, max} = Sort[RandomChoice[Range[30], 2]];
  data = RandomInteger[{min, max}, 100];
  GraphicsRow[{
     ListPlot[data],
     ListPlot[#, Ticks -> {Automatic, Cases[#, _Integer]}] &[data]},
    ImageSize -> Large]
]

Sample result #1.

enter image description here

Same data being plotted, first the default way, second WTS way.

The ideal general solution would produce a plot looking like the first one. I’m OK with unlabeled tick marks in non-integer locations. I absolutely don’t want non-integer tick labels. The Y axis tick marks and labels in the second plot are just weird looking.

Here’s a second example result:

enter image description here

First plot is fine, second one completely unacceptable.

In my third and final example, both plots are bad but for different reasons.

enter image description here

Not in this particular case, but in some cases the default tick labels are only at non-integer values, which is even worse.

The same test code modified for the log scale case shows that the WTS method doesn’t’ work unless elements of the data set just happen to coincide with all the required powers of 10. Furthermore, in the log scale case I’d like a method that is usable for real number data sets, especially those representing a wide range of floating point numbers from one down to machine precision and that obviously requires negative powers of ten.

$\endgroup$
2
  • $\begingroup$ Have you seen FindDivisions[] already? $\endgroup$ Commented Jun 2, 2020 at 0:29
  • $\begingroup$ Yes. Been playing with it a little. Haven't come up with a completely satisfactory solution yet, however. $\endgroup$
    – jjoIV
    Commented Jun 2, 2020 at 1:22

1 Answer 1

1
$\begingroup$

I'm not certain if this produces the exact output you're looking for in all cases. If it's not quite right, let me know and hopefully either I can adjust it or someone else may come along with a better solution.

Essentially, one of the possibilities for Ticks is to use a function. In this case, #1 is the minimum and #2 is the maximum of where it would like to plot. I'm rounding the minimum down and the maximum up, then using FindDivisions to find approximately 8 divisions near that range that are exactly a multiple of 1.

The idea for the LogPlot is basically the same.

Module[
  {min, max, data}, 
  {min, max} = Sort[RandomChoice[Range[30], 2]];
  data = RandomInteger[{min, max}, 100];
  GraphicsRow[{
    ListPlot[
      data, 
      Ticks -> {Automatic, FindDivisions[{Floor[#1], Ceiling[#2], 1}, 8] &}
    ], 
    ListLogPlot[
      10^data, 
      Ticks -> {Automatic, {10^#, Superscript["10", #]} & /@ 
        FindDivisions[{Floor[Log10[#1]], Ceiling[Log10[#2]], 1}, 
         8] &}
    ]}, 
    ImageSize -> Large
  ]
]

Plot and LogPlot with custom tick marks.

$\endgroup$
2
  • $\begingroup$ Cool! A very workable solution. And given in about 1% of the time it took to get the WTS solution, which was only marginally useful. Thanks! Two comments: 1) I didn't know about the implied min and max axis limit arguments. Makes for nice clean code. I'll be looking in to using that in some other code I have that plots separate sets of data on a single plot but with different Y axis scales on the L and R sides. 2) FindDivisions doesn't work quite as well was Mathematica's internal routines, does it? Even so, I might try some mods to plot minor ticks as well. $\endgroup$
    – jjoIV
    Commented Jun 2, 2020 at 5:30
  • $\begingroup$ @jjoIV I’m not sure, but yeah the internal algorithms are probably more robust. If you’re going to be doing a lot with ticks, I would highly recommend checking out CustomTicks by Mark Caprio. I don’t know about forcing integer values, but it includes a lot of features for customizing ticks that are desperately missing from Mathematica. $\endgroup$
    – MassDefect
    Commented Jun 2, 2020 at 5:49

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.