# Tick LocatorsΒΆ

When you create a figure in Toyplot, you begin by creating a
`canvas`

, add
`coordinate systems`

, and add data to the
coordinate systems in the form of marks. The coordinate system axes map
data from its *domain* to a *range* on the canvas, and generate *ticks*
in domain-space with *tick labels* as an integral part of the process.

To generate tick locations and tick labels, individual coordinate system
axes delegate to the *tick locator* classes in the
`toyplot.locator`

module. Each tick locator class is responsible
for generating a collection of tick locations from the range of values
in an axis domain, and there are several different classes available
that implement different strategies for generating “good” tick
locations. If you don’t specify any tick locators when creating axes,
sensible defaults will be chosen for you. For example:

```
import numpy
x = numpy.arange(20)
y = numpy.linspace(0, 1, len(x)) ** 2
```

```
import toyplot
canvas, axes, mark = toyplot.plot(x, y, width=300)
```

Note that the X and Y axes in this plot have sensible ticks that use
round numbers. In this case the algorithm for identifying “good” tick
values is provided by Toyplot’s default
`toyplot.locator.Extended`

locator.

However, notice that the X axis has a tick at the value \(20\), even
though the domain of the data values is \([0, 19]\). Toyplot always
expands the visible domain to include every tick generated by a locator.
If you need to change this behavior, there are several approaches:
first, you can configure `toyplot.locator.Extended`

to never
generate ticks outside the data domain:

```
canvas, axes, mark = toyplot.plot(x, y, width=300)
axes.x.ticks.locator = toyplot.locator.Extended(only_inside=True)
```

Notice that this can have the side effect of altering the number and
spacing of ticks. Let’s say instead that we prefer to always have ticks
that include the exact minimum and maximum data domain values, and
evenly divide the rest of the domain. In this case, we can override the
default choice of locator with the `toyplot.locator.Uniform`

tick locator:

```
canvas, axes, mark = toyplot.plot(x, y, width=300)
axes.x.ticks.locator = toyplot.locator.Uniform(count=5)
```

A third alternative is to use explicit tick locators to explicitly specify the ticks to generate for an axis. Explicit tick locators are discussed in detail below.

In the meantime, we can also override the default formatting string used to generate the locator labels:

```
canvas, axes, mark = toyplot.plot(x, y, width=300)
axes.x.ticks.locator = toyplot.locator.Uniform(count=5, format="{:.2f}")
```

Anytime you use log scale axes in a plot, Toyplot automatically uses the
`toyplot.locator.Log`

locator to provide ticks that are
evenly-spaced in the logarithmic domain:

```
canvas, axes, mark = toyplot.plot(x, y, xscale="log10", width=300)
```

If you don’t like the “superscript” notation that the Log locator produces, you could replace it with your own locator and custom format:

```
canvas, axes, mark = toyplot.plot(x, y, xscale="log10", width=300)
axes.x.ticks.locator = toyplot.locator.Log(base=10, format="{base}^{exponent}")
```

Or even display raw tick values:

```
canvas, axes, mark = toyplot.plot(x, y, xscale="log2", width=300)
axes.x.ticks.locator = toyplot.locator.Log(base=2, format="{:.0f}")
```

Although you might not think of Table Coordinates as needing
tick locators, when you use `toyplot.matrix()`

or
`toyplot.canvas.Canvas.matrix()`

to visualize a matrix, it
generates a table visualization that uses
`toyplot.locator.Integer`

locators to generate row and column
labels:

```
numpy.random.seed(1234)
canvas, table = toyplot.matrix(numpy.random.random((5, 5)), width=300)
```

By default the Integer locator generates a tick/label for every integer
in the range \([0, N)\) ... as you visualize larger matrices, you’ll
find that a label for every row and column becomes crowded, in which
case you can override the default `step`

parameter to space-out the
labels:

```
canvas, table = toyplot.matrix(numpy.random.random((50, 50)), width=400, step=5)
```

## Explicit LocatorsΒΆ

For the ultimate flexibility in positioning tick locations and labels,
you can use the `toyplot.locator.Explicit`

locator. With it,
you can specify an explicit set of labels, and a set of \([0, N)\)
integer locations will be created to match. This is particularly useful
if you are working with categorical data:

```
fruits = ["Apples", "Oranges", "Kiwi", "Miracle Fruit", "Durian"]
counts = [452, 347, 67, 21, 5]
canvas, axes, mark = toyplot.bars(counts, width=400, height=300)
axes.x.ticks.locator = toyplot.locator.Explicit(labels=fruits)
```

Note that in the above example the implicit \([0, N)\) tick locations match the implicit \([0, N)\) X coordinates that are generated for each bar when you don’t supply any X coordinates of your own. This is by design!

You can also use Explicit locators with a list of tick locations, and a set of tick labels will be generated using a format string. For example:

```
x = numpy.linspace(0, 2 * numpy.pi)
y = numpy.sin(x)
locations=[0, numpy.pi/2, numpy.pi, 3*numpy.pi/2, 2*numpy.pi]
canvas, axes, mark = toyplot.plot(x, y, width=500, height=300)
axes.x.ticks.locator = toyplot.locator.Explicit(locations=locations, format="{:.2f}")
```

Finally, you can supply both locations and labels to an Explicit locator:

```
labels = ["0", u"\u03c0 / 2", u"\u03c0", u"3\u03c0 / 2", u"2\u03c0"]
canvas, axes, mark = toyplot.plot(x, y, width=500, height=300)
axes.x.ticks.locator = toyplot.locator.Explicit(locations=locations, labels=labels)
```

## Timestamp LocatorsΒΆ

Toyplot includes the `toyplot.locator.Timestamp`

locator which
can be used to provide human-consumable date-time labels when your
data’s domain is *timestamps* (seconds since the Unix epoch, i.e.
midnight, January 1st, 1970, UTC). As an example, let’s load some
real-world data containing date-time information:

```
import toyplot.data
data = toyplot.data.read_csv("commute-obd.csv")
data[:6]
```

Datetime | Name | Value | Units |
---|---|---|---|

2014-05-01T14:08:53.587607Z | Status Since DTC Cleared | 0011110100110 | |

2014-05-01T14:08:53.656972Z | Fuel System Status | 0000 | |

2014-05-01T14:08:53.726403Z | Calculated Load Value | 0.0 | % |

2014-05-01T14:08:53.734393Z | Coolant Temperature | 13 | C |

2014-05-01T14:08:53.804349Z | Short Term Fuel Trim | 0.0 | % |

2014-05-01T14:08:53.873862Z | Long Term Fuel Trim | -3.125 | % |

Our first step will be to convert the string datetimes from the file into true numeric timestamps. Note that for this example we’re using Arrow, a library that improves upon the builtin datetime functionality in Python. Note that Arrow is used in the timestamp locator implementation, so it must already be installed for the following examples to work:

```
import arrow
timestamps = numpy.array([arrow.get(datetime).timestamp for datetime in data["Datetime"]])
```

Now, we can plot the data using the timestamps as our independent variable:

```
observations = numpy.logical_and(data["Name"] == "Vehicle Speed", data["Value"] != "NODATA")
x = timestamps[observations]
y = data["Value"][observations]
```

```
canvas, axes, mark = toyplot.plot(
x, y, label="Vehicle Speed", xlabel="Time", ylabel="km/h",
width=600, height=300)
```

As you would expect, the timestamps make very unfriendly tick labels. Let’s use the timestamp locator instead:

```
canvas, axes, mark = toyplot.plot(
x, y, label="Vehicle Speed", xlabel="Time", ylabel="km/h",
width=600, height=300)
axes.x.ticks.show = True
axes.x.ticks.locator = toyplot.locator.Timestamp()
```

By default, the timestamp locator chooses a “good” time interval based
on the data domain - for this data, it chose to create ticks at five
minute intervals. In addition, the locator also chooses a default format
based on the interval - in this case, `month/day hour:minute`

. This is
a significant improvement over raw timestamps, but before we continue,
we need to address timezone issues.

As alluded to above, in Toyplot **all timestamps must always be UTC
timestamps, without exception** ... that is to say that the numeric
values to be displayed by the timestamp locator must be the number of
seconds since Midnight, January 1st, 1970, UTC time. Our current example
data already contained UTC datetimes (note the “Z” timezone in the data
file), so no special conversion was necessary. If for some reason you’re
working with datetimes that aren’t UTC, you’ll need to take their
timezone into consideration when converting them to timestamps for
display by Toyplot ... and as an aside: you need to *run, not walk* to
anyone collecting data using local timestamps, and make them stop!

Since all Toyplot timestamps are UTC, the times displayed by the timestamp locator are also UTC by default. If you prefer to display times using a specific timezone, you can specify it when creating the locator.

**An Important Note on Timezone Names**

The arrow library (and thus Toyplot) relies on the underlying operating system for its timezone information. The following examples use “US/Mountain” to identify a timezone, and should work on most posix-like systems including Linux and OSX. Unfortunately, Windows operating systems use a different naming scheme; if you are using Windows, you will need to substitute a different name, such as “US Mountain Standard Time”.

```
canvas, axes, mark = toyplot.plot(
x, y, label="Vehicle Speed", xlabel="Time (US/Mountain)", ylabel="km/h",
width=600, height=300)
axes.x.ticks.show = True
axes.x.ticks.locator = toyplot.locator.Timestamp(timezone="US/Mountain")
```

Note that the hour in this data changed from 14:00 to 8:00, which makes more sense, since this data came from a morning commute. You could also specify a timezone of “utc” (the default) to explicitly document in the code that no timezone conversion is taking place. Finally, you may use “local” to automatically display the data using whichever timezone is local to the host running the code ... but be careful with this option, since it means that the the content of a figure could change based on where your laptop was when you generated it!

With timezones out of the way, let’s focus on the labels. The current
set is a little crowded, so let’s look at different ways to clean things
up. First, we can use the `count`

attribute to request that the
locator choose an interval that produces a specific number of ticks:

```
canvas, axes, mark = toyplot.plot(
x, y, label="Vehicle Speed", xlabel="Time (US/Mountain)", ylabel="km/h",
width=600, height=300)
axes.x.ticks.show = True
axes.x.ticks.locator = toyplot.locator.Timestamp(timezone="US/Mountain", count=5)
```

Note that in most cases the locator won’t produce the exact number of ticks requested (we got lucky in this case), it will choose an interval that produces the closest match.

Alternatively, we might choose to accept the default tick count and alter the tick format, to save space:

```
canvas, axes, mark = toyplot.plot(
x, y, label="Vehicle Speed", xlabel="Time (US/Mountain)", ylabel="km/h",
width=600, height=300)
axes.x.ticks.show = True
axes.x.ticks.locator = toyplot.locator.Timestamp(
timezone="US/Mountain", format="{0:HH}:{0:mm}")
```

The individual tick values are passed to the format function as
`arrow.arrow.Arrow`

objects, so you can use any of the arrow
attributes and formatting tokens in the format string, as we’ve done
here.

Or, we might force a specific interval if it had special meaning for our domain, such as the number of seven-minute workouts we could have completed during our commute:

```
canvas, axes, mark = toyplot.plot(
x, y, label="Vehicle Speed", xlabel="Time (US/Mountain)", ylabel="km/h",
width=600, height=300)
axes.x.ticks.show = True
axes.x.ticks.locator = toyplot.locator.Timestamp(
timezone="US/Mountain", interval=(7, "minutes"))
```