# agate is a data analysis library that favours readable code

> agate is a MIT licensed Python library for analysing tabular data, presented as an alternative to numpy and pandas that optimises for humans, with a heavy dependency set for dates and locales. It suits journalists and analysts who want typed tables without a dataframe API, and it is a poor fit for large numeric workloads.

**wireservice/agate** — A Python data analysis library that is optimized for humans instead of machines.

- Repository: https://github.com/wireservice/agate
- Website: https://agate.readthedocs.io
- Stars: 1,199 · Forks: 166
- Language: Python
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/wireservice-agate

## A library that optimises for the person reading the code

agate states its purpose in a single line: it is a Python data analysis library that is optimised for humans instead of machines. The contrast drawn in the project documentation is with numpy and pandas, and the claim is that agate solves real-world problems with readable code. That is a different goal from the one a dataframe library pursues, where the priority is fast column operations over large arrays and where the API is tuned for that.

The audience is visible in the repository itself. The project is maintained under the wireservice organisation, and the tree contains exonerations.py, a script with the shape of a newsroom investigation tool, alongside charts.py and a set of example data files under examples. The library was previously known as journalism, which is a small clue about where it came from: newsroom data work, where the file on disk is often strange and the person reading the script is not a data engineer.

The project is MIT licensed and the current version is 1.14.2. It supports Python 3.10 through 3.14 on both CPython and PyPy, and the packaging metadata marks it as production stable, intended for developers and for science and research, with a framework classifier for IPython. The documentation lives on Read the Docs and the source is on GitHub, and the last push to the repository was on 2026-09-21.

## Type inference is where a messy file becomes a table

The mechanism that distinguishes agate from a generic CSV reader is type inference. A file arrives, its columns are examined, and each column is given a type that the rest of the code can rely on, so a date column behaves like dates and a number column behaves like numbers, instead of everything arriving as text and being converted by hand in every script that touches the file.

The dependency list explains how much of that inference depends on date handling. Babel, isodate, parsedatetime, pytimeparse and, on Windows, tzdata are all in the runtime requirements, alongside python-slugify and leather. A parser list that long means the library is making a serious attempt at interpreting whatever a real file contains rather than rejecting the first surprise.

The parsedatetime dependency also carries a version constraint with a reason attached. The metadata requires parsedatetime at 2.1 or newer and explicitly excludes 2.5, with a comment pointing at an upstream pull request and an agate issue about a KeyError on the letter s. That is the shape of a library that has been broken by a transitive dependency and has pinned around it.

The API surface is not described in the project documentation, so the honest description of the object model is at the level the project files support: agate works with tables of rows, and the examples directory contains directories named tableset and realdata, which indicates that both multi-table and real-world sample data are part of the tested surface.

## The fixture directory is the honest specification

When a library's promise is that it handles real files, the test fixtures tell you what real means to its authors. The examples directory is a catalogue of awkwardness. There are files for Latin-1 text and for UTF-16 in both byte orders, a file with CRLF line endings, a file whose dialect has to be sniffed, a file with no header row, a file with a quoted non-numeric value, a directory named testfixed holding fixed-width data together with a converted CSV and a schema file, and a set of JSON fixtures including keyed, nested, mixed and ambiguous ones.

Read as a list, that is a specification of the problem agate exists to solve: a spreadsheet exported by one tool, a hand-edited file, a legacy fixed-width extract, a JSON feed with an awkward shape. None of these are hypothetical, and the naming shows which of them broke something at least once.

The same directory holds the positive cases as well, test.csv, test.json, an empty.csv and a tableset of multiple files, which is what a test suite for a table library needs on both sides. The broader repository carries a benchmarks directory, which is an unusual thing to keep for a library that argues for readability, and it suggests the performance question was considered rather than avoided.

## Installing agate and getting to a table

agate is published on PyPI, so a project that wants it adds it to its environment from PyPI:

From there the workflow the repository documents is the one in example.py and in the tutorial notebook, tutorial.ipynb, both of which sit at the top of the tree next to the example notebook variant. The documentation site is agate.rtfd.org, and the examples directory provides the input files those scripts work against, which means you can follow the tutorial without assembling a dataset of your own first.

Two paths through the tree support that. example.py is the plain script version and example.py.ipynb is the notebook version of the same content, so a reader can work in whichever fits. The examples/realdata directory suggests the tutorial also expects to be run against messier inputs than the synthetic ones, which is the point of the library: the interesting behaviour is what happens when the file is not clean.

A note on the environment. The runtime dependencies are declared in pyproject.toml with lower bounds rather than exact pins, and the optional test extra adds coverage, cssselect, lxml and PyICU, with a comment that CI is not configured to install PyICU on macOS and Windows. If you are running the test suite on those platforms, that extra is the part that will not install cleanly, and it is a dependency of the test tooling rather than of the library itself.

## Why the dependency list reads like a calendar library

The clearest signal about agate's priorities is that its runtime requirements are dominated by date, locale and text handling, and contain no numerical stack at all. There is no numpy in the list, no pandas, no scipy. A library that competes with pandas for the same job and does not depend on numpy is making a deliberate statement about the size of the data it expects to handle.

That is the trade. agate gives you typed, human-readable tables for a file of records, where a date column is a date and a number column is a number, with the type decisions made once at load time rather than at every operation. What it does not give you is the vectorised, array-oriented computation that a numeric workload needs, and there is no indication in the dependency set that it is trying to.

The dependencies also hint at the two things the library is careful about. Localisation comes through Babel, which is what lets a number or a date be formatted for a reader rather than for a machine, and that is consistent with the claim about being optimised for humans. The date parsers are plural because the formats encountered in the wild are plural, and the slugify dependency points at the other end of the pipeline, where a table's text is turned into something addressable, which is what a chart or a published story needs.

## Where pandas and numpy fit better

The project names the alternative itself, and the difference is worth stating precisely because both libraries are called data analysis tools and solve different problems. pandas and numpy are built for volume and for vectorised computation, where the unit of work is an array of values and the operations are applied across it. agate is built for a table of records where the unit of work is a row or a cell, where the person writing the script wants to read what it does.

A few concrete consequences follow. In a pandas workflow, a column that arrives as text is converted with an explicit call at the point of use, and the reader of the code has to notice that. In an agate workflow the conversion happens at load time and the column carries its type from then on, so the type errors surface early and the script below reads as plain Python. For a report with a few thousand rows, that difference in readability is the whole argument. For a ten million row file with numeric operations across columns, the same choice makes agate the wrong tool, and no amount of readable code changes that.

The ecosystem is the second difference, and agate cannot match it. A team that already has pandas, plotting helpers and a community of answers to their questions gets none of that by switching. The case for agate is strongest in a small team or a teaching context where the code is read by people who do not use pandas, and weakest in an organisation with an existing pandas-based stack.

## Versioning without GitHub releases

The release history is a little unusual. The repository has no GitHub releases at all, and the version lives in pyproject.toml, where the project section declares 1.14.2 and the build system uses setuptools 61 or newer. The change record is CHANGELOG.rst, and the contributors are recorded in AUTHORS.rst, both of which are the older Python packaging conventions rather than the newer automated release tooling.

The maintenance signals are consistent with a mature, slow-moving project rather than an abandoned one. The last push was on 2026-09-21, the metadata marks the development status as production stable, the CI badge and the codecov coverage badge are both in the readme, and a pre-commit configuration sits in the repository for formatting and linting on commit. The coverage badge in particular suggests that the test suite is the thing the maintainers watch, which fits a library whose value is in the edge cases of its fixture directory.

Licensing is MIT, with the text in COPYING and the same identifier recorded in the project metadata and in the licence classifiers, so an organisation can use agate in a commercial pipeline without a licence review. The remaining cost of adopting it is not legal but practical: the dependency set is broad for a table library, and the parsedatetime exclusion in particular shows that a transitive dependency can break the install for reasons that have nothing to do with your code.

## Conclusion

agate fits analysts, journalists and educators who want a table of typed values from a messy file and prefer plain Python expressions over a dataframe API, and who work on data small enough to read. It does not fit a workload built around large arrays, vectorised operations or the wider pandas ecosystem. Verify first which column types agate infers for your file, since a single ambiguous value can change a column's type, and check that the declared dependency requirements in pyproject.toml still support the Python version you run.

## FAQ

### What is agate and how is it different from pandas?

agate is a Python data analysis library that the project describes as optimised for humans instead of machines, and it names numpy and pandas as the alternatives it offers. It focuses on readable code over typed tables rather than on array-oriented computation over large data.

### How do I install agate?

The package is published on PyPI, so pip install agate adds it to your environment. The project requires Python 3.10 or newer and supports CPython and PyPy, and the documentation is hosted on Read the Docs.

### What kinds of files can agate read?

The example fixtures in the repository cover CSV files in Latin-1 and UTF-16 encodings, CRLF line endings, sniffed dialects, files without a header, quoted non-numeric values, fixed-width data and several JSON shapes including keyed, nested and ambiguous ones.

### What are the runtime dependencies of agate?

The declared requirements are Babel, isodate, leather, parsedatetime, python-slugify, pytimeparse and, on Windows, tzdata. parsedatetime is required at 2.1 or newer with version 2.5 explicitly excluded because of a KeyError issue tracked upstream.

## Sources

- [Issues](https://github.com/wireservice/agate/issues)
- [License: MIT](https://github.com/wireservice/agate/blob/master/LICENSE)
- [Project website](https://agate.readthedocs.io)
- [README](https://github.com/wireservice/agate/blob/master/README.md)
- [wireservice/agate on GitHub](https://github.com/wireservice/agate)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/wireservice-agate
