3
$\begingroup$

I want to replicate as closely as possible the x-axis Ticks specifications generated automatically either by Plot or by LogLinearPlot.

Furthermore, I need to do this without a front end 1.

I've tried to roll my own, but generating good tick marks programmatically is tricky.

The other approach I've tried (described below) fails when executed without a front end.


$Version:

"10.4.1 for Linux x86 (64-bit) (April 11, 2016)"

Background / tl;dr

The code below shows how I've produced the desired x-tick specs before. Unfortunately, this code needs a front end to run.

(NB: it is safe to assume that the symbol $plottingFunction below evaluates either to Plot or to LogLinearPlot.)

getTicks[xrange : {a_, b_}] :=
  (
      dummyPlot = $plottingFunction[
                      Null
                    , {$, a, b}
                    , PlotRange -> {xrange, {0, 1}}
                    , Ticks -> {Automatic, None}
                  ]
    ; absOps = AbsoluteOptions[dummyPlot] (* triggers FrontEndObject::notavail *)
    ; ticks = Ticks /. absOps
    ; (Drop[#, -1] & /@ First[ticks])
  ) /; a < b

Basically, getTicks "generates" x-axis tick specs by ripping them out of a dummy plot. (The actual code I use is a bit more complicated. FWIW, I give the full code at the end of this post.)

Unfortunately, function above appears to be reason for why a Mathematica script of mine hangs indefinitely when I run it on a headless server. (The same script runs fine under X11, on my desktop.)

In fact, on the headless server, the call to AbsoluteOptions in this function results, indirectly2, in the warning

FrontEndObject::notavail: A front end is not available; certain operations 
require a front end.

I've described this problem in an earlier post.


Here's the context of the code shown earlier.

(* makeGetTicks takes a plotting function (e.g. `Plot`) as argument,
   and returns another function. *)

makeGetTicks = Function[

   $plottingFunction

 , Module[
    {  
       $
     , getTicks
     , dummyPlot
     , absOps
     , ticks
    },

      getTicks[xrange : {a_, b_}] :=
        (
            dummyPlot = $plottingFunction[
                                            Null
                                          , {$, a, b}
                                          , PlotRange -> {xrange, {0, 1}}
                                          , Ticks -> {Automatic, None}
                                         ]
          ; absOps = AbsoluteOptions[dummyPlot]
          ; ticks = Ticks /. absOps
          ; (Drop[#, -1] & /@ First[ticks])
        ) /; a < b

    ; getTicks[xrange : {a_, b_}] := {}

    ; getTicks

   ]];

Then, I use makeGetTicks to define two other functions:

getLinearTicks = makeGetTicks[Plot];
getLogTicks    = makeGetTicks[LogLinearPlot];

Note that these functions would be more appropriately named as makeGetXTicks, getLinearXTicks, getLogXTicks.


Here's the relevant portion of the call stack (as reported by Stack[_]) at the time the FrontEndObject::notavail warning is triggered (numbering added):

1   absOps$469 = AbsoluteOptions[dummyPlot$469]
2   Module[{System`Dump`plrng$, System`Dump`opts$, System`Dump`names$, System`Dump`vals$, System`Dump`asp$, System`Dump`axes$, System`Dump`x$}, System`Dump`x$ = -Graphics- /. {HoldPattern[Axes -> {System`Dump`a_, System`Dump`a_}] :> Axes -> System`Dump`a, HoldPattern[Frame -> {{System`Dump`a_, System`Dump`a_}, {System`Dump`a_, System`Dump`a_}}] :> Frame -> System`Dump`a}; System`Dump`plrng$ = PlotRange[System`Dump`x$]; System`Dump`names$ = First /@ Options[Head[System`Dump`x$]]; System`Dump`opts$ = Flatten[If[Length[System`Dump`x$] == 0, {}, Drop[List @@ System`Dump`x$, 1]]]; System`Dump`opts$ = Select[System`Dump`opts$, Head[#1] == RuleDelayed || Head[#1] == Rule & ]; System`Dump`opts$ = Join[System`Dump`opts$, Options[Head[System`Dump`x$]]]; System`Dump`opts$ = System`Dump`CheckGopt[System`Dump`x$, System`Dump`opts$, System`Dump`plrng$]; System`Dump`opts$ = Prepend[System`Dump`opts$, PlotRange -> System`Dump`plrng$]; System`Dump`axes$ = FullAxes[System`Dump`x$]; If[ListQ[System`Dump`axes$], System`Dump`opts$ = Join[System`Dump`axes$, System`Dump`opts$]]; System`Dump`opts$ = Join[System`Dump`GetMesh[System`Dump`x$, System`Dump`opts$], System`Dump`opts$]; System`Dump`vals$ = System`Dump`names$ /. System`Dump`opts$; System`Dump`opts$ = Transpose[{System`Dump`names$, System`Dump`vals$}]; System`Dump`opts$ = Apply[{#1, If[MemberQ[{AxesLabel, FrameLabel, PlotLabel}, #1], #2, N[#2]]} & , System`Dump`opts$, {1}]; (Rule @@ #1 & ) /@ System`Dump`opts$]
3   System`Dump`x$38165 = -Graphics- /. {HoldPattern[Axes -> {System`Dump`a_, System`Dump`a_}] :> Axes -> System`Dump`a, HoldPattern[Frame -> {{System`Dump`a_, System`Dump`a_}, {System`Dump`a_, System`Dump`a_}}] :> Frame -> System`Dump`a}; System`Dump`plrng$38165 = PlotRange[System`Dump`x$38165]; System`Dump`names$38165 = First /@ Options[Head[System`Dump`x$38165]]; System`Dump`opts$38165 = Flatten[If[Length[System`Dump`x$38165] == 0, {}, Drop[List @@ System`Dump`x$38165, 1]]]; System`Dump`opts$38165 = Select[System`Dump`opts$38165, Head[#1] == RuleDelayed || Head[#1] == Rule & ]; System`Dump`opts$38165 = Join[System`Dump`opts$38165, Options[Head[System`Dump`x$38165]]]; System`Dump`opts$38165 = System`Dump`CheckGopt[System`Dump`x$38165, System`Dump`opts$38165, System`Dump`plrng$38165]; System`Dump`opts$38165 = Prepend[System`Dump`opts$38165, PlotRange -> System`Dump`plrng$38165]; System`Dump`axes$38165 = FullAxes[System`Dump`x$38165]; If[ListQ[System`Dump`axes$38165], System`Dump`opts$38165 = Join[System`Dump`axes$38165, System`Dump`opts$38165]]; System`Dump`opts$38165 = Join[System`Dump`GetMesh[System`Dump`x$38165, System`Dump`opts$38165], System`Dump`opts$38165]; System`Dump`vals$38165 = System`Dump`names$38165 /. System`Dump`opts$38165; System`Dump`opts$38165 = Transpose[{System`Dump`names$38165, System`Dump`vals$38165}]; System`Dump`opts$38165 = Apply[{#1, If[MemberQ[{AxesLabel, FrameLabel, PlotLabel}, #1], #2, N[#2]]} & , System`Dump`opts$38165, {1}]; (Rule @@ #1 & ) /@ Sy
4   System`Dump`axes$38165 = FullAxes[System`Dump`x$38165]
5   FullAxes[-Graphics-]
6   ConvertToPostScript[Cell[BoxData[FormBox[RowBox[{-, 500}], TraditionalForm]], {}, ShowCellBracket -> False, PageWidth :> Infinity]]

1 The code is supposed to run on a headless server. I'm aware of the technique of using Xvfb to run Mathematica on a headless server, but unfortunately, but I don't have the user permissions required to run Xvfb on the headless server I need to run this script on.

2 According to the value returned by Stack[_], the immediate trigger for the warning is a call to ConvertToPostScript.

$\endgroup$
  • $\begingroup$ Is using Xvnc an option? I don't think it requires any special user privileges. $\endgroup$ – ilian Dec 13 '16 at 2:41
3
$\begingroup$

Starting from version 10 plot-generating functions use Charting`ScaledTicks and Charting`ScaledFrameTicks for generating ticks specifications. These functions are defined in the Kernel, so you don't need FrontEnd. In this answer I provide usage examples for all types of linear and log-plots.

For example the following generates FrameTicks specification identical to what LogLinearPlot generates (Most is added to fix the bug described in the linked answer):

Clear[logLinearFrameTicks]
logLinearFrameTicks[{xmin_, xmax_}, {ymin_, ymax_}] := 
{{Most /@ Charting`ScaledTicks[{Identity, Identity}][ymin, ymax], 
  Most /@ Charting`ScaledFrameTicks[{Identity, Identity}][ymin, ymax]}, 
 {Most /@ Charting`ScaledTicks[{Log, Exp}][xmin, xmax], 
  Most /@ Charting`ScaledFrameTicks[{Log, Exp}][xmin, xmax]}};

logLinearFrameTicks[{1, 10}, {-10, 1}] // Short
{{{{2.,2,{0.01,0.}},{4.,4,{0.01,0.}},{6.,6,{0.01,0.}},<<16>>,{9.,,{0.005,0.}},{9.5,,{0.005,0.}}},{<<1>>}},{<<1>>}}
$\endgroup$
  • 1
    $\begingroup$ I think you get notified of edits, but just in case: I edited your code to swap xmin ↔ymin, xmax ↔ymax. (As I understand it, the ordering of frame ticks is {{left, right}, {bottom, top}}.) $\endgroup$ – kjo Mar 2 '17 at 12:27
3
$\begingroup$

AbsoluteOptions seems to be poorly maintained. One alternative would be to use CustomTicks and this code to extract the ticks:

<< "CustomTicks`"
dummyPlot = 
  Block[{x, xmin = 0, xmax = 4}, 
   Plot[Null, {x, xmin, xmax}, PlotRange -> {{xmin, xmax}, {0, 1}}, 
    Ticks -> {LinTicks, None}]];
ticks=Ticks /. List @@ dummyPlot[[2]];

Just replace xmin and xmax with your required range.

Edit: Of course, you can also just use the result of LinTicks[xmin,xmax], which is identical to the result provided above. You can then apply your further code to strip the line style column of the ticks table:

Drop[#, -1] & /@ LinTicks[xmin, xmax]

I believe that is what your function getTicks should return.

$\endgroup$

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.