Metadata-Version: 2.4
Name: gog
Version: 0.0.1
Summary: The Grammar of Graphics — one engine, spoken from Python
Project-URL: Homepage, https://psychometrician.github.io/gog-book/
Project-URL: Documentation, https://psychometrician.github.io/gog-book/
Author-email: psychometrician <psychometrician@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Requires-Python: >=3.9
Provides-Extra: test
Requires-Dist: pytest; extra == 'test'
Description-Content-Type: text/markdown

# gog for Python

The Grammar of Graphics, spoken from Python. A plot is a sentence — a table, a
mark, and the channels that bind the mark to the table's columns:

```python
from gog import *

gapminder = {"gdp": [...], "life": [...], "continent": [...]}

(data(gapminder) + point + x(col.gdp, scale="log") + y(col.life)
 + color(col.continent) + size(col.population)).save("life.svg")
```

The same sentence in R is `data(gapminder) + point + x(gdp) + y(life) +
color(continent) + size(population)`. Both build the same specification and hand
it to the same engine, so the plots are identical to the byte. Nothing about
*what a plot means* lives in this package — the grammar, the legality rules and
the diagnostics are all `gog-core`'s. Read the manual in `book/`; it is written
in R, and every sentence in it translates by the rule below.

## Installing

**The wheel carries the engine.** `gog` is a bridge to a Rust binary, and a
package that installs but cannot draw is no package at all, so a release wheel
ships the `gog-cli` built for its own platform. Installing needs no Rust
toolchain and no checkout:

```bash
pip install gog          # once the wheels are published
```

The wheels are built by `.github/workflows/python-wheels.yml`, one per platform
(macOS arm64 and x86_64, manylinux x86_64 and aarch64, Windows x64), and each
job proves the wheel draws in a fresh environment before uploading it.

To build one yourself, or to work from a checkout:

```bash
cargo build --release -p gog-cli          # the engine
uv build --wheel py-pkg/gog               # a wheel carrying it
python3 -c "import sys; sys.path.insert(0, 'py-pkg/gog'); import gog"   # or just use the source
```

The engine is looked for in this order: `GOG_CLI_PATH`, the copy inside the
installed package, `gog-cli` on `PATH`, then a local `target/release` build.

## The two Python-specific things

**A column is `col.name`.** Python has no bare names, and in this grammar a
plain string is a *value* (`style(color="tomato")`, `title("…")`). The accessor
is what keeps "which column?" and "which value?" apart, so `x("gdp")` is refused
and told what to write. Use `col["life exp"]` for a name Python cannot spell as
an identifier.

**`from gog import *` shadows five builtins** — `bin`, `sum`, `min`, `max` and
`range` are transforms here, the same way the R package masks `base::range`. A
module that needs both can `import gog` and write `gog.point`, or take the
builtin back with `from builtins import sum`.

## Tables

A table is a dict of columns, or anything with `.columns` and `df[name]` —
pandas and polars work without being imported, and without being required:

```python
data({"x": [1.0, 2.0], "y": [3.0, 4.0]})        # stdlib only
data(pandas.read_csv("gapminder.csv"))          # duck-typed
```

Numbers become positions, text becomes categories, `datetime`/`date` become a
calendar axis, a pandas `Categorical` carries its declared order, and `None`/
`NaN` is a missing value the engine drops with a message. A column that mixes
numbers and text is refused, because a column is one type.

## Tests

```bash
python3 py-pkg/gog/tests/test_basic.py            # from the repo root
python3 py-pkg/gog/tests/book_parity/run.py       # every sentence the manual teaches
```

The second one is the real check: it takes all 481 sentences from the book's 47
chapters, says each in Python, and compares against what R got. 379 byte-identical
plots, 88 word-identical refusals, and no disagreements. See
[`tests/book_parity/README.md`](tests/book_parity/README.md) — including the
complete list of rewrites an R sentence needs, which is the honest measure of how
far the two spellings are apart.

## License

Apache License 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE), which ship
inside the package, so a copy taken from a registry carries them too.
