---
name: pxmake
description: >
  Create, read, modify, and save PX-files (PC-Axis) with the pxmake R package
  from Statistics Greenland. Use this skill whenever the user mentions
  "pxmake", "px object", "PX-file", "PC-Axis", ".px file", ".pxk", "micro
  files", "px_save", any `px_*` function, PX keywords like STUB, HEADING,
  TIMEVAL, ELIMINATION, CODES, VALUES, or wants to build PX-files for a PxWeb
  StatBank in R — including converting data frames, parquet or Excel metadata
  workbooks to PX, adding totals, multilingual (da/en/kl) metadata, or
  .vs/.agg classification files. Prefer this skill over writing raw PX text
  by hand.
compatibility: >
  Requires an R environment with pxmake for execution. The instructions can still
  be used for guidance without R. If the current agent requires this skill to be
  installed or imported and it is unavailable, tell the user that it must be added
  before claiming to use it.
metadata:
  package_version: "0.21.0 (CRAN)"
---

# pxmake — Make PX-files in R

pxmake creates and modifies PX-files (PC-Axis format, used by PxWeb StatBanks).
Every PX keyword has a same-named modifying function (`TITLE` → `px_title()`),
so metadata manipulation is pipeable and human-readable. Developed by
Statistics Greenland (Johan Ejstrud & Lars Pedersen). On CRAN:
`install.packages('pxmake')`.

**The core model:** everything revolves around a **px object** — an R
representation of a complete PX-file (data table + all metadata). The
workflow is always:

```r
library(pxmake)
x <- px(input)          # 1. create px object
x <- x |> px_title(…)   # 2. modify with px_* functions (each returns a NEW object)
px_save(x, "out.px")    # 3. save
```

Modifying functions never mutate in place — reassign or pipe.

## Step 1: Create a px object — `px()`

```r
px(input = NULL, data = NULL, validate = TRUE)
```

`input` accepts, auto-detected:

- Path to a **PX-file** (or a direct-download URL of one, e.g. a StatBank
  saved-query URL like `https://bank.stat.gl/sq/{uuid}`)
- Path to an **Excel metadata workbook** — combine with `data =` (data frame
  or `.rds`/`.parquet` path) to supply the data table separately
- A **data frame** — minimal valid metadata is generated automatically
- Path to `.rds` or `.parquet` (or a parquet URL)
- Path to a **`.pxk`** file (metadata-only PX-file, supported since 0.21.0)
- `NULL` — empty px object

`validate = FALSE` skips validity checks — important speed-up for large
files; run `px_validate(x)` manually afterwards.

Built-in example datasets: `population_gl`, `greenlanders`.

## Step 2: Modify

Each function called **without** `value` returns the current setting
(getter); **with** `value` it returns a modified px object (setter);
with `value = NULL` it removes the keyword.

Table-level keywords take a character scalar; multilingual values take a
data frame with a `language` column; variable- and value-level keywords take
data frames with `variable-code` / `code` columns. Exact column requirements
per function: **read `references/functions.md`**.

The most-used structural functions:

| Function | Effect |
|---|---|
| `px_stub(x, vars)` / `px_heading(x, vars)` | Set STUB/HEADING (row/column variables) |
| `px_figures(x, var)` | Set the figures (cell-value) variable; previous one becomes STUB. Exactly one figures variable exists |
| `px_timeval(x, var)` | Declare the time variable (TIMEVAL) |
| `px_data(x, df)` | Get/replace the data table; `px_data(x, labels = TRUE)` returns VALUES instead of CODES (or a language code for a specific language) |
| `px_add_totals(x, vars)` | Add sum level to variables; name is 'Total' unless `px_elimination()` is set, then the elimination code is used. `na_rm = TRUE` default |
| `px_elimination(x, value)` | Character → all STUB+HEADING vars; data frame (`variable-code`, `elimination`) → per variable |
| `px_values(x, df)` | Value texts: df with `values` + `variable-code`/`code`/`language` |
| `px_order(x, df)` | Value ordering: df with `order` + `variable-code`/`code` |
| `px_validate(x)` | Run validation checks manually |

**CODES and VARIABLECODE have no modifying function by design** — they are
determined automatically from the data table's columns and values. To change
codes, change the data (e.g. `dplyr::mutate` before `px()`, or via
`px_data()`), not the metadata.

## Step 3: Save — `px_save()`

```r
px_save(x, path, save_data = TRUE, data_path = NULL)
```

The **file extension of `path` decides the format**:

| Extension | Output |
|---|---|
| `.px` | PX-file (encoding controlled by `px_codepage()`, e.g. `"UTF-8"`) |
| `.pxk` | Metadata-only PX-file |
| `.xlsx` | Excel metadata workbook (`save_data = FALSE` drops the Data sheet) |
| `.R` | R script that recreates the px object |

`data_path` (`.rds`/`.parquet`) externalises the data table for `.xlsx`/`.R`
targets — the standard pattern for keeping metadata in Excel and data in
parquet.

`px_micro(x, out_dir)` is the only other function that writes PX-files: it
splits one px object into one small PX-file per non-HEADING variable
(counts of each variable), for publishing metadata about microdata. See
`references/multilingual-and-micro.md`.

## Typical recipes

**Data frame → finished PX-file:**

```r
px(mydf) |>
  px_timeval("year") |>
  px_heading("year") |>
  px_stub(c("municipality", "gender")) |>
  px_title("Population 1. January") |>
  px_contents("Population") |>
  px_units("persons") |>
  px_decimals("0") |>
  px_matrix("BEXSTB") |>
  px_codepage("UTF-8") |>
  px_save("bexstb.px")
```

**Read → patch → save (batch pattern):**

```r
for (f in list.files("in", pattern = "\\.px$", full.names = TRUE)) {
  px(f, validate = FALSE) |>
    px_source("Statistics Greenland") |>
    px_save(file.path("out", basename(f)))
}
```

**Multilingual (da/en/kl):** set `px_language()` (main) and
`px_languages()` first, then pass data frames with a `language` column to any
language-dependent keyword function. Details and pitfalls in
`references/multilingual-and-micro.md`.

**Classifications (.vs/.agg):** `px_classification()` builds a
classification from a data frame or existing `.vs`/`.agg` files;
`px_save_classification()` writes them. See `references/classifications.md`.

## Pitfalls — never

- Never edit PX metadata as raw text when a `px_*` function exists — encoding
  and quoting errors are exactly what pxmake prevents.
- Never try to set CODES or VARIABLECODE via metadata — change the data.
- Never assume `px_data()` returns characters — since 0.21.0 data table
  variables are **factors** (halves memory for large objects). Use
  `as.character()` before string operations.
- Never rely on a `sort` argument to `px_data()` — removed in 0.21.0; the
  data table is always returned sorted.
- Never skip `px_codepage("UTF-8")` when the file must hold Greenlandic or
  Danish characters and downstream tooling expects UTF-8 — old PX files
  often carry legacy codepages.
- Never validate huge objects repeatedly in a pipe — pass `validate = FALSE`
  to each modifier and run `px_validate()` once at the end.
- Don't confuse `px()`'s Excel workbook input (metadata workbook, pxmake's
  own layout) with an arbitrary spreadsheet of data — an ordinary data sheet
  should go in as a data frame instead.

## Reference files

- `references/functions.md` — every keyword↔function pair (59 modifying
  functions + 6 extra functions), argument shapes, getter/setter/remove
  semantics. Read when you need an exact function name or its data-frame
  columns.
- `references/classifications.md` — value sets and aggregations (.vs/.agg):
  `px_classification()`, `px_save_classification()`, supported types.
- `references/multilingual-and-micro.md` — multilingual files
  (`px_language`/`px_languages`, language-dependent keywords) and micro
  files (`px_micro`, `keyword_values`, parallelisation with future/furrr).

## Version notes (0.21.0, CRAN)

- Data table stored as factors (was characters).
- `.pxk` read/write support.
- `sort` argument removed from `px_data()`.
- DATA block reading accepts tab, colon and semicolon separators.
- pxmake follows the PX-file format specification 2013 from Statistics
  Sweden.
