title: How to get something like Matplotlib's symlog scale in ggplot or lattice?
body: <p>For very heavy-tailed data of both positive and negative sign, I sometimes like to see all the data on a plot without hiding structure in the unit interval.</p>
  
  <p>When plotting with Matplotlib in Python, I can achieve this by selecting a <a href="http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.set_xscale" rel="nofollow noreferrer">symlog scale</a>, which uses a logarithmic transform outside some interval, and linear plotting inside it.</p>
  
  <p>Previously in R I have constructed similar behavior by transforming the data with an <a href="http://mathworld.wolfram.com/InverseHyperbolicSine.html" rel="nofollow noreferrer">arcsinh</a> on a one-off basis.  However, tick labels and the like are very tricky to do right (see below).
  <img src="https://i.stack.imgur.com/KWygr.png" alt="enter image description here"></p>
  
  <p>Now, I am faced with a bunch of data where the subsetting in <a href="http://www.statmethods.net/advgraphs/trellis.html" rel="nofollow noreferrer">lattice</a> or <a href="http://docs.ggplot2.org/current/" rel="nofollow noreferrer">ggplot</a> would be highly convenient.  I don't want to use Matplotlib because of the subsetting, but I sure am missing <em>symlog</em>!</p>
  
  <h2>Edit:</h2>
  
  <p>I see that <a href="https://stackoverflow.com/questions/11053899/how-to-get-a-reversed-log10-scale-in-ggplot2">ggplot uses a package called scales</a>, which solves a lot of this problem (if it works).  Automatically choosing tick mark and label placing still looks pretty hard to do nicely though.  Some combination of <code>log_breaks</code> and <code>cbreaks</code> perhaps?</p>
  
  <h2>Edit 2:</h2>
  
  <p>The following code is not too bad</p>
  
  <pre><code>sinh.scaled &lt;- function(x,scale=1){ sinh(x)*scale }
  asinh.scaled &lt;- function(x,scale=1) { asinh(x/scale) }
  
  
  
  asinh_breaks &lt;- function (n = 5, scale = 1, base=10) 
  {
      function(x) {
          log_breaks.callable &lt;- log_breaks(n=n,base=base)
          rng &lt;- rng &lt;- range(x, na.rm = TRUE)
          minx &lt;- floor(rng[1])
          maxx &lt;- ceiling(rng[2])
          if (maxx == minx) 
              return(sinh.scaled(minx, scale=scale))
          big.vals &lt;- 0
          if (minx &lt; (-scale)) {
              big.vals = big.vals + 1
          }
          if (maxx&gt;scale) {
              big.vals = big.vals + 1
          }
          brk &lt;- c()
          if (minx &lt; (-scale)) {
              rbrk &lt;- log_breaks.callable(  c(-min(maxx,-scale), -minx ) )
              rbrk &lt;- -rev(rbrk)
              brk &lt;- c(brk,rbrk)
          }
          if ( !(minx&gt;scale | maxx&lt;(-scale))  ) {
              rng &lt;- c(max(minx,-scale), min(maxx,scale))
              minc &lt;- floor(rng[1])
              maxc &lt;- ceiling(rng[2])
              by &lt;- floor((maxc - minc)/(n-big.vals)) + 1
              cb &lt;- seq(minc, maxc, by = by)
              brk &lt;- c(brk,cb)
          } 
          if (maxx&gt;scale) {
              brk &lt;- c(brk,log_breaks.callable( c(max(minx,scale), maxx )))
          }
  
          brk
  
      }
  }
  
  asinh_trans &lt;- function(scale = 1) {
      trans &lt;- function(x) asinh.scaled(x, scale)
      inv &lt;- function(x) sinh.scaled(x, scale)
      trans_new(paste0("asinh-", format(scale)), trans, inv, 
                asinh_breaks(scale = scale), 
                domain = c(-Inf, Inf))
  }
  </code></pre>