0
$\begingroup$

I'm trying to understand the idea and (possibly) replicate myself how Mathematica generate ticks. For a linear scale it is simple enough: one can use FindDivisions function, and this will give the same (or very close to) what Mathematica places on axes in Plot. But what to do in case of LogPlot? For a large range Mathematica can produce something like {10,50,100,500,1000}, but for smaller range this may be almost linear range. Is there some analog of FindDivision for log scale?

Up

An example: LogPlot[x, {x, 12, 167}, Frame -> True].

enter image description here

Ticks above 100 and below 20 can confuse: for usual log scale they should be read as 200 and 10 respectively. But not in this case: here they are 150 and 15. Very confusing.

Add

To see clearly what are confusing ticks:

Log10[{12, 167}]
FindDivisions[%,6]
Part[%,{1,-1}]
(* {1, 2.4} *)

LogPlot[x, {x, 12, 167}, Frame -> True, PlotRange -> 10^{1., 2.4}]

enter image description here

There are additional ticks in {10, 20} and {100, 200}.

$\endgroup$
2
  • $\begingroup$ What is x? Given as it is, the code you have included in the text (which is better placed into a code-block for readability, among other things) would not produce the figure you show. Can you, please, be a bit more verbose and indicate what you have used & seek to use to generate the figure? Additionally, it seems the ticks go by 10s below 100, then by 100s above it, as one would expect. $\endgroup$ Mar 1 at 2:40
  • $\begingroup$ I edited code, of course that should be LogPlot command. You are right about expectation on ticks. But in this example the lower point is 12, so the tick below 20 can not be 10, it is 15. The same is for tick above 100: upper point is 167, hence the tick is not 200, it is 150. That is confusion. $\endgroup$
    – LittleCat1
    Mar 1 at 3:59

1 Answer 1

1
$\begingroup$

Not so clear from your code what you aim for. See the excellent answers at The implementation of "SignedLog" Scaling Function of `Plot`.

The bottom line is you can translate either way using

signedLogCoords[{x_, y_}] := {
  If[signedLogX, Sign[x] Log10[Abs[x] + 1], x],
  If[signedLogY, Sign[y] Log10[Abs[y] + 1], y]
  }
signedLogCoordsInv[{x_, y_}] := {
  If[signedLogX, Sign[x] ((10^Abs[x]) - 1), x],
  If[signedLogY, Sign[y] ((10^Abs[y]) - 1), y]

You precede your graphs with signedLogX=True if you want the x coordinate to be transformed, and analogously for the y coordinate.

Ticks take the form of Ticks->{{{xtickvalue,xticklabel},...},{{ytickvalue,yticklabel},...}}. You can add a tick specification or add graphics primitives before the graph (under the graph's output) by using Prolog->{graphics primitives} or after (on top of) it by using Epilog->{...}.

Edit pursuant to explanation:

Since you want to adjust the ticks automatically, you want to play around with the function FindDivisions. A reasonable attempt to produce "pretty" divisions may be:

logFindDivisions1[min_, max_] := Module[{sm, big, zeros, eigh},
  Flatten@{zeros = (Floor@Log10@max) - 1;
    eigh = 8 10^zeros;
    big = eigh Floor[max/eigh];
    If[# > min, If[! IntegerQ@#, N@#, #], 
       Nothing] & /@ (big/(2^Range[0, 8]))}]

It takes the maximum, finds the next largest 8 10^x and gives its half, quarter, etc., provided they are greater than the minimum.

A somewhat more complex version may also start from 16, 32, or 64 10^x:

logFindDivisions2[min_, max_] := 
 Module[{sm, big, zeros, first, foundation},
  Flatten@{
    {first, zeros} = RealDigits[max];
    first = 
     If[first[[2]] != 0, FromDigits[first[[1 ;; 2]]], first[[1]]];
    foundation = Which[
      first >= 80, 80,
      first >= 64, 64,
      first >= 32, 32,
      first >= 16, 16,
      True, 8];
    If[foundation > 8, --zeros];
    big = foundation 10^--zeros;
    If[# > min, If[! IntegerQ@#, N@#, #], 
       Nothing] & /@ (big/(2^Range[0, 8]))}]

The difference is that the latter prevents having the large division be a clumsy multiple of eight, like 72, which is clumsy because after 36 and 18 it enters the territory of clumsy divisions (9, 4.5, ...).

In[196]:= divs = {.12, .75};
logFindDivisions1 @@ divs
logFindDivisions2 @@ divs

(*Out[]= {0.72, 0.36, 0.18}*)
(*Out[]= {0.64, 0.32, 0.16}*)
$\endgroup$
3
  • $\begingroup$ Sorry, but I don't want to transform coordinates. The question is very simple: given a range how to compute ticks position and labels as Mathematica does in its LogPlot? Have you seen added picture? How and why Mathematica computed those ticks at 15 and 150? $\endgroup$
    – LittleCat1
    Feb 28 at 4:21
  • $\begingroup$ @LittleCat1 It is considered good form to upvote helpful answers and to accept as answers those that answer your question. $\endgroup$
    – Nicholas G
    Mar 3 at 1:20
  • $\begingroup$ Sorry, your answer can not be considered as general one. Take another range of x and you get "strange" labels of ticks, not so nice as in your answer. $\endgroup$
    – LittleCat1
    Mar 3 at 3:01

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