|
Occupation Generator 3.0.1
Employment status and occupation by country, age, sex and education for C++23
|
This guide covers every feature of the occupation-generator library. For a quick overview, see the README. For the data and the model, see Data and Method. For the full API reference, run doxygen Doxyfile from the repository root and open doc/api/html/index.html.
Copy dasmig/occupationgen.hpp, dasmig/core.hpp and the dasmig/core/ directory into your include path, keeping them together. Supply resources/ next to the program, or define DASMIG_OCCUPATION_EMBED to compile the lite tier in.
instance() loads the lite tier on first use (or full if there is no lite), from the compiled-in copy or from the first of resources/, ../resources, occupation-generator/resources and ../occupation-generator/resources that has it.
Each load replaces the data and every layer loaded before. The tiers have the same measured data and differ in the catalogue: lite has 95 jobs, full 320 (see Data and Method).
Every call takes an occupation_request; every field may be omitted. These are the only generation overloads, and choices are never stored as instance defaults:
The old no-argument, country-only and seed-only overloads have been removed. Use a request (including {} for the defaults) first, followed by a seed when deterministic output is needed. Data loading and layers remain instance-level configuration.
A country chosen at random is uniform over the countries loaded; the data has no population column. Pass a country chosen by country-generator for a population-weighted world.
A result converts to std::string (the job label, or the status when there is no job) and streams with <<.
get_occupation(request, seed) gives the same result for the same seed, request and data, on every platform.get_occupation(request) draws a seed from the instance's entropy-seeded engine. Instance seed() / unseed() controls have been removed; deterministic calls always take an explicit seed.v.seed() is the seed a result was generated from: get_occupation(request, v.seed()) replays it..education forced gives the same sex and status.For a deterministic sequence, keep the seed source with the caller:
The shipped data is a base layer. Files with the same columns as the shipped tables can be loaded on top of it to add rows, change them or remove them, without editing the shipped files. The tables and their keys:
Table (ocg::table) | File | Key |
|---|---|---|
status | status.tsv | country_code, sex, band |
inactivity | inactivity.tsv | band |
education | education.tsv | country_code, sex, band |
isco | isco.tsv | code |
shares | shares.tsv | country_code, sex, code |
factors | factors.tsv | country_code, major, dimension, value |
occupations | occupations.tsv | key |
weight, so a job added to a group takes a share of what the group already has. Give a new group (isco.tsv) rows in shares.tsv, or it is never drawn.15-19 or 65+ (no upper limit). The bands of status.tsv and education.tsv need not match the shipped ones; a person is looked up by age in the rows of their country and sex.unresolved(), so removing a group needs no other change.dasmig::core::tsv_error with the file, line and column, and the data stays as it was.layers(table) lists what is loaded: source, mode, rows added, replaced and removed, and warnings such as unknown columns.load(...) replaces the base and drops every layer.Every result records the seed and the data it came from:
to_json() gives the result as JSON, the form the WebAssembly layer and the game consume. Seeds are strings because JavaScript numbers cannot hold every 64-bit value. to_json(writer) writes into an existing dasmig::core::json_writer, for composing larger documents.
education and job are null when there are none.
Define DASMIG_OCCUPATION_EMBED before including the header to compile the lite tier into the program. load(dataset::lite) and instance() then use it and need no files. The compiled-in data has the same data_hash() as the file, so seeds replay the same either way. The header is about 2 MB.
dasmig/occupationgen_data.hpp is generated from resources/lite by make embed; CI checks it is up to date.
Each ocg instance is independent. Concurrent calls to get_occupation(request) on the same instance require external synchronisation (e.g. a std::mutex).
Every seeded getter has batch forms: get_occupation_batch return a std::vector, and for_each_occupation streams the items to a sink in index order without holding the batch.
i is exactly get_occupation(request, core::item_seed(batch_seed, first + i)). A range can be generated directly (first = 1000 starts at item 1000), two batches that follow each other equal one, and the output is the same for any threads (0 means the hardware count). Without a batch_seed one is drawn like the single getter draws its own.core::batch_options has count, first, threads and unique. The request describes one item; the options describe the batch..unique = core::unique_rule::by<occupation>(key) regenerates an item whose key already appeared in the batch, with a deterministic retry seed, and throws std::runtime_error naming the item when the attempts run out.core::jsonl_writer writes one to_json() per line, core::csv_writer flattens each result's JSON into dotted columns. Any callable (std::uint64_t index, occupation&) works.| Exception | When |
|---|---|
std::runtime_error | Any generation call before data is loaded |
std::invalid_argument | An unknown country code; a country with no status row for the person's sex and age; no job open to the person (a country with no shares for the sex, or a job age limit that excludes everyone); with no country, no country has status rows for both sexes |
std::invalid_argument | extend() of a file whose name is not a table's |
dasmig::core::tsv_error | A malformed table file or layer (message gives file, line and column); the data is unchanged |