The solutions provided by @FJCC will work quite well, especially if you are plotting this sort of thing as a one-off visualization. If, however, you find yourself doing many of these types of plots, you might want to think first about employing the transformation abilities of the `scale_y_`

and `scale_x_`

functions, then later you can craft your own to fine tune the results.

### First our data:

```
library(ggplot2)
df <- data.frame(a = 1:4, b = c(5, 32, 256, 4781))
```

### Using `trans = `

We can choose a transformation function for our axis using the `trans`

argument. Per the help file `?scale_y_continuous`

we have access to several pre-made transformations,

```
Built-in transformations include "asn", "atanh", "boxcox", "date", "exp",
"hms", "identity", "log", "log10", "log1p", "log2", "logit", "modulus",
"probability", "probit", "pseudo_log", "reciprocal", "reverse", "sqrt" and "time".
```

and in your case using `trans = "log"`

works quite well,

```
ggplot(df, aes(a, b)) +
geom_point() +
scale_y_continuous(trans = "log")
```

**NOTE:** If you used `trans = "log10" this results in a plot identical to using `

scale_y_log10()` as in the second example from FJCC.

```
ggplot(df, aes(a, b)) +
geom_point() +
scale_y_continuous(trans = "log10")
```

### Having more control

Now that we've seen we can set a transformation on the `y`

scale, we can extend that and make our own. We do that with the `trans_new()`

function in the `scales`

package.

The usage of `trans_new()`

looks like this,

```
trans_new(name,
transform,
inverse,
breaks = extended_breaks(),
minor_breaks = regular_minor_breaks(),
format = format_format(),
domain = c(-Inf, Inf))
```

#### Custom Breaks Fucntion

Since we are interested in where the breaks fall, we can consider writing a custom `breaks`

function.

```
# We will use the default breaks function from trans_new()
# as a template to build our new breaks function
scales::extended_breaks
#> function (n = 5, ...)
#> {
#> n_default <- n
#> function(x, n = n_default) {
#> x <- x[is.finite(x)]
#> if (length(x) == 0) {
#> return(numeric())
#> }
#> rng <- range(x)
#> labeling::extended(rng[1], rng[2], n, ...)
#> }
#> }
#> <bytecode: 0x000000001a7e6298>
#> <environment: namespace:scales>
```

```
# the idea is to,
# compute the log of our response
# find attractive break points
# resolve the values back to their original scale
logexp_breaks <- function (n = 5, sigdig = 2, ...) {
n_default <- n
function (x, n = n_default) {
x <- x[is.finite(x)]
if (length(x) == 0) {
return(numeric())
}
rng <- range(log(x))
breaks <- labeling::extended(rng[1],
rng[2],
n,
...)
signif(exp(breaks), sigdig)
}
}
```

#### Custom Transformation Function

Now that we have the breaks function written which our transformation function will use we can start on the transformation.

We'll base our custom transformation function on log_trans(), so let's look at the code before we get started.

```
#
scales::log_trans
#> function (base = exp(1))
#> {
#> force(base)
#> trans <- function(x) log(x, base)
#> inv <- function(x) base^x
#> trans_new(paste0("log-", format(base)), trans, inv, log_breaks(base = base),
#> domain = c(1e-100, Inf))
#> }
#> <bytecode: 0x0000000019bdac98>
#> <environment: namespace:scales>
```

Now, in exploring, I thought it would be good to allow our transformer to take some arguments and send them along to our breaks function. We could have sent some args to the actual transformation and inverse as seen in the `log_trans()`

example, but I wanted to keep this simple_-ish_, so we'll just let `elm()`

accept the three dots and pass them straight on through to `logexp_breaks()`

. The parameters of interest for the breaks function will be `n`

(a rough guide for how many major breaks to make), `sigdig`

(the number of significant digits to set our breaks at), and `w`

(the weights to pass on to the labeling function which does the heavy lifting about where to set the breaks).

**NOTE:** The weights are a bit of a mystery to me. The help file `?labeling::extended`

has this to say about them,

```
w weights applied to the four optimization components (simplicity,
coverage, density, and legibility)
```

You should play around with them to find a balance you like (or just trust the default).

```
elm <- function(...) {
scales::trans_new("elm",
"log",
"exp",
logexp_breaks(...))
}
```

#### Putting it into practice.

Now that we've done all that, we can start to put it into practice and see what we get.

```
ggplot(df, aes(a, b)) +
geom_point() +
scale_y_continuous(trans = elm(n = 10, sigdig = 1, w = c(0.01, 0.1, 0.7, 0.05)))
```

```
ggplot(df, aes(a, b)) +
geom_point() +
scale_y_continuous(trans = elm(n = 4, sigdig = 2))
```

^{Created on 2020-09-01 by the reprex package (v0.3.0)}