# Log axis

> A logarithmic scale for values that span several orders of magnitude, with ticks at the powers of ten, minor grid lines and straight trend lines for exponential growth.

An app that grew from 120 users to 850,000 makes a dull chart on a linear scale: a flat line along the bottom for four years, then a wall. On a log scale every tenfold step is the same height, so the first hundred users get as much room as the last hundred thousand, and steady growth looks like a straight line.

`logAxis()` is an axis type, like `autoScaleAxis()`. It goes in the `type` of an axis:

**Users per year** (runs in the browser):

`src/main.ts`:

```ts
import 'chartist/styles.css';
import { lineChart } from 'chartist';
import { logAxis } from '@chartist-js/essentials';

// Users of an app that grew from 120 to 850,000 in six years. On a log axis
// each tenfold step is the same height, so the early years stay readable.
lineChart(
  '#chart',
  {
    labels: ['2019', '2020', '2021', '2022', '2023', '2024'],
    series: [[120, 980, 4100, 36000, 210000, 850000]]
  },
  {
    axisY: {
      type: logAxis(),
      offset: 64,
      labelTransform: value => value.toLocaleString('en-US')
    }
  }
);
```

`index.html`:

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script type="module" src="/src/main.ts"></script>
  </head>
  <body>
    <div id="chart" style="height: 50vh"></div>
  </body>
</html>
```

Use it when the values span several orders of magnitude, or when what matters is the rate of growth rather than the amount: a line that's straight on a log axis grows by the same factor in every step.

## Linear or log

Moore's law says the transistors on a chip double about every two years. Here are fifteen processors from the Intel 4004 in 1971 to the Apple M1 Ultra in 2022, from 2,250 transistors to 114 billion. Switch the Y axis to a linear scale and back, and watch the points move:

**Moore's law** (runs in the browser):

`src/main.ts`:

```ts
import 'chartist/styles.css';
import './style.css';
import { autoScaleAxis, lineChart } from 'chartist';
import { liveDomRenderer, smilAnimations } from 'chartist/live';
import { logAxis } from '@chartist-js/essentials';
import { processors } from './processors';

// Moore's law: the transistors on a chip double about every two years. On
// a linear axis that's a flat line and a wall at the end; on a log axis
// with minor grid lines, a straight climb from 2,250 to 114 billion.
const compact = new Intl.NumberFormat('en-US', { notation: 'compact' });
const log = logAxis({ minorTicks: true });
const linear = autoScaleAxis();

const chart = lineChart(
  '#chart',
  {
    series: [processors.map(({ name, x, y }) => ({ x, y, meta: name }))]
  },
  {
    showLine: false,
    // Room for the last X label, which starts at its tick.
    chartPadding: { right: 32 },
    axisX: { type: autoScaleAxis(), onlyInteger: true },
    axisY: {
      type: log,
      offset: 48,
      labelTransform: value => compact.format(Number(value))
    },
    // The points move from one scale to the other.
    renderer: liveDomRenderer({ animations: smilAnimations() })
  }
);

const button = document.querySelector<HTMLButtonElement>('#scale')!;
let onLog = true;
button.addEventListener('click', () => {
  onLog = !onLog;
  chart.setOptions({ axisY: { type: onLog ? log : linear } });
  button.textContent = `Show on a ${onLog ? 'linear' : 'log'} scale`;
});
```

`src/processors.ts`:

```ts
// Transistors per processor, as Wikipedia's "Transistor count" list gives
// them: from Intel's 4004 to Apple's M1 Ultra.
export const processors = [
  { name: 'Intel 4004', x: 1971, y: 2_250 },
  { name: 'Intel 8080', x: 1974, y: 6_000 },
  { name: 'Intel 8086', x: 1978, y: 29_000 },
  { name: 'Intel 80286', x: 1982, y: 134_000 },
  { name: 'Intel 80386', x: 1985, y: 275_000 },
  { name: 'Intel 80486', x: 1989, y: 1_180_235 },
  { name: 'Pentium', x: 1993, y: 3_100_000 },
  { name: 'Pentium II', x: 1997, y: 7_500_000 },
  { name: 'Pentium III', x: 1999, y: 9_500_000 },
  { name: 'Pentium 4', x: 2000, y: 42_000_000 },
  { name: 'Core 2 Duo', x: 2006, y: 291_000_000 },
  { name: 'Core i7', x: 2008, y: 731_000_000 },
  { name: 'Apple A7', x: 2013, y: 1_000_000_000 },
  { name: 'Apple M1', x: 2020, y: 16_000_000_000 },
  { name: 'Apple M1 Ultra', x: 2022, y: 114_000_000_000 }
];
```

`src/style.css`:

```css
#chart {
  height: 50vh;
}

.controls {
  margin: 0.5em 0 0;
}
```

`index.html`:

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script type="module" src="/src/main.ts"></script>
  </head>
  <body>
    <div id="chart"></div>
    <p class="controls">
      <button type="button" id="scale">Show on a linear scale</button>
    </p>
  </body>
</html>
```

On the linear scale, thirteen of the fifteen processors sit on the bottom edge: next to 114 billion, a billion is a rounding error. On the log scale every chip gets its place, and the straight climb is the doubling itself. Switching is `setOptions({ axisY: { type } })`, and [`liveDomRenderer()`](https://chartist.dev/docs/live-updates/) moves the points from one scale to the other.

## Range and ticks

Without `low` and `high`, the axis runs from the power of ten at or below the smallest value to the power at or above the largest: 120 to 850,000 becomes 100 to 1,000,000. The ticks sit at the powers in between, and when they'd come closer than `scaleMinSpace` (20 px by default), the axis keeps every second or third. The axis's own options work as on the core's axes:

```ts
import { lineChart } from 'chartist';
import { logAxis } from '@chartist-js/essentials';

lineChart(
  '#chart',
  { labels: ['A', 'B', 'C'], series: [[12, 180, 2400]] },
  {
    axisY: {
      type: logAxis(),
      // The range exactly, without rounding to powers.
      low: 10,
      high: 4000,
      // Ticks by hand, at their places on the log scale.
      ticks: [10, 25, 63, 160, 400, 1000, 2500]
    }
  }
);
```

When the range is narrower than a power, say from 20 to 80, the ticks are the whole numbers between: 20, 30, 40 and so on.

`base` changes the base of the logarithm. `logAxis({ base: 2 })` puts the ticks at 1, 2, 4, 8, 16, which suits values that double, such as memory sizes.

## Minor grid lines

`minorTicks: true` draws lighter grid lines without labels between the ticks: at 2, 3, … 9 times each power, the way log paper has them. When the axis shows only every second power, the ones it leaves out get the minor lines instead. Moore's law above has them on its Y axis only. On a log-log chart, both axes are logarithmic, and both can have them:

**Kleiber’s law** (runs in the browser):

`src/main.ts`:

```ts
import 'chartist/styles.css';
import '@chartist-js/essentials/styles.css';
import { axisGrid, axisLabels, cartesianAxes, chart, line } from 'chartist';
import { logAxis, trendLine } from '@chartist-js/essentials';

// Body mass (kg) and resting metabolic rate (W) from a mouse to an
// elephant. The rate grows with the mass to the power of about 3/4
// (Kleiber's law): on two log axes that's a straight line, and the trend
// line fitted to the logarithms finds it.
const animals = [
  { x: 0.02, y: 0.17, meta: 'Mouse' },
  { x: 0.3, y: 1.45, meta: 'Rat' },
  { x: 4, y: 9.1, meta: 'Cat' },
  { x: 30, y: 46, meta: 'Dog' },
  { x: 70, y: 80, meta: 'Human' },
  { x: 600, y: 395, meta: 'Horse' },
  { x: 4000, y: 1800, meta: 'Elephant' }
];

chart('#chart', {
  data: { series: [animals] },
  axes: cartesianAxes([
    { id: 'mass', side: 'bottom', type: logAxis({ minorTicks: true }) },
    { id: 'rate', side: 'left', type: logAxis({ minorTicks: true }) }
  ]),
  layers: [axisGrid(), trendLine(), line({ showLine: false }), axisLabels()],
  // Room for the last X label, which starts at its tick.
  padding: { right: 32 }
});
```

`index.html`:

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script type="module" src="/src/main.ts"></script>
  </head>
  <body>
    <div id="chart" style="height: 50vh"></div>
  </body>
</html>
```

They're grid lines with the class `ct-grid-minor` next to `ct-grid`, and Chartist's stylesheet styles them with three custom properties:

```css
.ct-chart {
  --ct-grid-minor-color: rgba(0, 0, 0, 0.05);
  --ct-grid-minor-width: 1px;
  --ct-grid-minor-dasharray: 1px 3px;
}
```

## Labels

The labels show the tick values. Long numbers read better with separators, and `labelTransform` formats them as on any axis. Give the axis an `offset` wide enough for the longest label:

```ts
import { lineChart } from 'chartist';
import { logAxis } from '@chartist-js/essentials';

lineChart(
  '#chart',
  { labels: ['2022', '2023', '2024'], series: [[3200, 41000, 870000]] },
  {
    axisY: {
      type: logAxis(),
      offset: 64,
      labelTransform: value => value.toLocaleString('en-US')
    }
  }
);
```

## Zero and negative values

A log scale has no place for zero: you can halve a value forever and never reach it. A zero in your data doesn't break the chart, though. Zero and negative values are left out of the range and drawn at the start of the axis, on its bottom edge. A bar for zero has no length.

That's what you want for a day with no sales, but it can read as "very small" for a missing measurement. Use `null` where a value is missing: it's a gap in the line, as on every axis.

## Bars

Bars grow from the start of the axis, so on a log axis their length shows the value's power of ten rather than the value: a bar for 1,000 is three times as long as one for 10 when the axis starts at 1. Set `low` to say where the bars start. Stacked bars work, but stacking adds values while a log axis compares factors, so the segments' lengths don't add up; grouped bars or lines read better.

## Trend lines

A [`trendLine()`](https://chartist.dev/packages/essentials/trend-lines/) on a log axis is fitted to the logarithms of the values, so it's straight on the chart and follows exponential growth on a log Y axis. On two log axes, as in the log-log chart above, it finds a power law: the animals' metabolic rate grows with their mass to the power of about ¾, and the trend line's `slope` says so.

```ts
import { lineChart } from 'chartist';
import { logAxis, trendLine } from '@chartist-js/essentials';

lineChart(
  '#chart',
  {
    labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
    series: [[30, 75, 210, 480, 1300]]
  },
  {
    axisY: { type: logAxis() },
    layers: { above: [trendLine()] }
  }
);
```

The trend line's context has its `slope` in powers of the base per step: a slope of 0.4 on a base 10 axis means the values grow about 2.5 times (10<sup>0.4</sup>) a day.

## Next to a linear axis

In a [composed chart](https://chartist.dev/docs/composed-charts/), a log axis is one axis among others. Each axis fits its range to the series of the layers drawn on it:

```ts
import {
  autoScaleAxis,
  axisGrid,
  axisLabels,
  bar,
  cartesianAxes,
  chart,
  line,
  stepAxis
} from 'chartist';
import { logAxis } from '@chartist-js/essentials';

chart('#chart', {
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr'],
    series: [
      { name: 'posts', data: [3, 5, 4, 6] },
      { name: 'reach', data: [240, 3100, 18000, 95000] }
    ]
  },
  axes: cartesianAxes([
    { id: 'month', side: 'bottom', type: stepAxis() },
    { id: 'posts', side: 'left', type: autoScaleAxis(), low: 0 },
    { id: 'reach', side: 'right', type: logAxis(), offset: 50 }
  ]),
  layers: [
    axisGrid({ axes: ['month', 'posts'] }),
    bar({ series: ['posts'], y: 'posts' }),
    line({ series: ['reach'], y: 'reach', align: 'center' }),
    axisLabels()
  ],
  className: 'ct-chart-bar'
});
```

## Coming from chartist-logaxis

The `chartist-logaxis` plugin patched 1.x's auto-scale axis with `scale: 'log10'`. With `logAxis()`, change the axis type: `type: logAxis()` instead of `type: Chartist.AutoScaleAxis, scale: 'log10'`, and `logAxis({ minorTicks: true })` instead of `showMinorGrid: true`. The minor grid lines keep the class `ct-grid-minor`, so your CSS for them still applies. Zeros no longer throw an error.
