Skip to main content

US synthetic names — Python library

Generate random names for synthetic ID-card tests from a released US-wide SQLite dataset. Supports weighted or uniform token sampling, character limits, initials, multiple given names and surnames, shared given-name gender categories, and birth-year cohorts.

This repository owns generation and reading released data. ds.source.usnames owns the government downloads, database schema, builds, and releases. The two packages can be installed together but neither depends on the other.

Results are plausible approximations. Census demographic groups are not linguistic origins or detailed naming traditions. Unknown origins remain unknown; source-recorded sex is not a person's gender identity. Full-name joint distributions are not observed. See the limitations below.

Install and download the dataset

Python 3.10+. From a checkout of this repository:

python -m pip install -e .
python -m us_names download
python -m us_names generate --format full --count 20 --seed 42

Or install directly from GitHub:

python -m pip install "git+https://github.com/lambdawalker/ds.python.usnames.git"

download defaults to the published dataset-v0.2.0 release and writes data/names.sqlite. It streams the compressed asset, verifies SHA-256 against that release's SHA256SUMS, decompresses it, and validates schema 2 and SQLite integrity before installing the file. Failed downloads leave no destination file. Existing files are never overwritten; use a new output path for upgrades.

python -m us_names download --release dataset-v0.2.0 --output datasets/v0.2.0.sqlite
python -m us_names generate --db datasets/v0.2.0.sqlite --format middle_initial --count 10

Downloading is explicit. Once downloaded, generation is offline and never silently updates the dataset. The wheel contains no database, government downloader, workbook importer, or database builder. Only regex is required beyond Python's standard library.

from us_names import download_dataset
path = download_dataset('datasets/names.sqlite', release='dataset-v0.2.0')

Library version 0.3.0 and dataset release versions are independent. The Dataset, Generator, and Query generation API is retained. Database creation and mutation APIs moved to the producer package us_names_source; the consumer's Dataset is read-only. The us-names-download command now downloads a release rather than government originals.

Python API

from us_names import Dataset, Generator, Query

with Dataset('data/names.sqlite') as dataset:
    generator = Generator(dataset, seed=42)
    query = Query(
        format='double_surname',
        group='hispanic',
        gender='female',
        birth_year_range=(1980, 1989),
        min_length=11,
        max_length=30,
        use_frequency_weights=True,
    )
    result = generator.generate(query)
    print(result['text'])
    print(result['given_names'], result['surnames'])
    print(result['gender'])

Results include the original components, final character length, selected group/gender, published counts, selected gender share, cohort counts when requested, sources, and dataset/generator versions. Origin lists are empty when unknown. The CLI adds seed and result index in JSONL mode. Reproducibility requires the same source snapshot, software/dependency versions, query, seed, and call order. Create a connection/generator per worker. The library opens persistent tables read-only; cohort caches use connection-local temporary tables.

Gender behavior

  • gender='auto' (default) selects one male or female category for the entire generated name.
  • gender='female' or 'male' explicitly selects that category.
  • gender='unrestricted' disables the filter and permits mixed given names.
  • Surnames are not filtered by sex; surname-only queries have no selected gender.
  • min_gender_share=0.05 requires at least 5% of a token's recorded uses to belong to the selected category. This is a configurable compatibility heuristic, not a claim of certainty.
  • Frequency-weighted sampling favors counts for the chosen category. Uniform sampling still enforces the category and threshold.
  • Names without usable sex evidence are excluded from gender-constrained generation. Missing values are not assigned guessed genders.

Without a birth-year filter, Census sex counts are preferred. Where a name has no Census sex record, an explicitly source-labeled SSA all-years aggregate supplies the association. With a year filter, eligibility and frequency use SSA sex counts for that cohort, not overall Census associations.

Ordered compound exceptions: Query(format='full', given_pair=('JOSE','MARIA')) explicitly requests the curated male compound; ('MARIA','JOSE') requests the female compound. These two manually curated exceptions bypass individual gender thresholds only for the requested pair and mark compound_exception=true in the result. They still obey group, year, length and formatting constraints. No probability or population frequency is invented for these pairs; they are never automatically injected into random sampling. Other explicitly requested pairs must pass ordinary shared-gender rules. This is a narrow formatting rule, not an exhaustive compound-name dataset.

CLI examples:

python -m us_names generate --format full --gender female --count 10
python -m us_names generate --format full --gender male --birth-years 1980 1989 --count 10
python -m us_names generate --format full --uniform --count 10
python -m us_names generate --gender unrestricted --group none --format full --count 10
python -m us_names generate --format '{surname}, {given_initial} {middle_initial}' --count 10
python -m us_names generate --jsonl --count 100 --output generated.jsonl

Normalized SQLite schema

Table Purpose and key
names Integer ID, text, role, nullable Census total, stored grapheme length, source ID; unique text/role
name_group_counts Published count per name/group; primary key (name_id, group_id)
name_sex_counts Preferred overall male/female counts and derived shares per name; primary key (name_id, sex)
name_year_sex_counts SSA counts retaining year and recorded sex; primary key (name_id, year, sex)
sources Source IDs, URLs, filenames, SHA-256 hashes and sizes
name_origin_associations Sourced origin labels/evidence, currently unpopulated
metadata Version, source manifest, build time and statistics

Counts are numeric rows with foreign keys and nonnegative checks. Group counts are no longer stored in a JSON field. metadata still uses JSON for descriptive information; JSON is also used for portable exports.

Indexes support role/length, group, sex/share, and year/sex candidate selection. SQLite filters candidate rows before Python loads them; the generator no longer parses every token's group JSON. Source grapheme length can be filtered in SQL. Transform-dependent lengths (ASCII/case conversion) are checked after transformation. Cohort observations are aggregated into an indexed temporary table, reused for that range. Sampling pools and record caches are bounded and instance-local.

share in name_sex_counts is a derived sampling value: count divided by the sum of both source sex counts for that name. It is not a separately observed statistic. Census group and sex totals can differ due to independent disclosure noise; denominators are not mixed.

Formats and length rules

Preset Template
given {given}
surname {surname}
name (default) {given} {surname}
full {given} {middle} {surname}
double_surname {given} {middle} {surname} {surname2}
middle_initial {given} {middle_initial} {surname}
given_initials {given_initial} {middle_initial} {surname}
initials {given_initial} {middle_initial} {surname_initial}
surname_first {surname}, {given} {middle_initial}

Each component supports _initial, including {surname2_initial}. Custom literals/separators are preserved. Two given components and two surname components are supported. The slot middle means a second given component; it does not impose a universal naming tradition. Multiword source components stay intact; their initial is the first visible character. Repeated placeholders use the same sampled component.

Length limits are inclusive. “More than 10 and fewer than 20” means 11–19. Count Unicode extended grapheme clusters, including spaces and punctuation, after casing, ASCII folding and initial formatting. Font width is not modeled. Full components can have separate bounds: component_lengths={'given': (4,8), 'surname': (5,12)}. These apply before replacing a component by an initial.

Defaults: min_length=1, max_length=None, component_lengths={}, use_frequency_weights=True, group='auto', min_group_share=0.05, gender='auto', min_gender_share=0.05, given_pair=None, birth_year_range=None, casing='source', ascii_only=False, initial_period=True, max_attempts=10000.

Use --query query.json for all API options; JSON keys override CLI query options. --group none disables group compatibility. --no-initial-period removes periods. --casing accepts source, upper, lower, title. ASCII folding is lossy character removal, not linguistic transliteration; mechanical title casing may be inappropriate for some names.

NoCandidates means no eligible token pools. SamplingExhausted means rejection sampling found no final-length match within the attempt budget; it does not prove the query impossible. No truncation or silent gender relaxation occurs. Duplicate names/components are possible. Failed CLI batches can leave partial output; existing output files are never overwritten.

Frequency model and limitations

For a specified Census group, eligibility requires the group's count to represent at least min_group_share of that token's national count. Counts are not linguistic-origin probabilities. Census groups are white, black, aian, asian_nhpi, multiracial, hispanic. The first five denote non-Hispanic groups; Asian and Pacific Islander categories are combined and cannot distinguish individual naming traditions.

Without a cohort, weighted given-name sampling uses national count (or selected group count) multiplied by the selected sex share. With a cohort, it uses SSA counts for the selected sex and years, multiplied by the Census group share when enabled. These are approximations combining separate marginal tables; joint group/sex/year counts are not available. Surnames use Census counts.

Auto mode chooses among eligible group/gender combinations using the first component's eligible mass, then samples components within that combination. Uniform mode gives equal probability to eligible tokens within a combination. Overlapping groups and sex associations mean unconditional token probabilities need not be uniform. Use both group=None and gender='unrestricted' for globally uniform token sampling. Final-length rejection conditions the distribution on the requested constraints.

A name's group composition is not its prevalence within the entire group. We preserve source counts rather than inventing population denominators. This release does not establish full-name joint frequencies, middle-name distributions, paired-surname frequencies, or detailed cultural origins. Gender consistency reduces clearly implausible combinations but does not make every name culturally plausible.

Census counts include disclosure noise; SSA suppresses small cells. ALL OTHER NAMES is excluded from tokens and retained only as metadata. SSA covers US births, not all current residents, and cannot account for migration, survival or later name changes. It removes spaces and hyphens; uppercase matching does not restore these or missing accents. Rare or unusual source entries may remain. Some names used by both sexes remain eligible for both categories. Requested year ranges use available published observations only. Names are not guaranteed unique or unlike real names.

Development and dataset contract

python -m unittest discover -s tests -v

Unit tests use tiny schema-2 fixtures and require no network. CI also downloads the pinned release and exercises full-name and cohort generation. Test fixtures are not demographic estimates. openpyxl is not a library dependency.

The producer owns the schema contract. Incompatible schema changes must use a new SQLite user_version and a compatible consumer release. The consumer fails clearly on unsupported schemas. Source URLs, original checksums, source years and dataset statistics are stored in SQLite metadata and the release manifest.

Use python -m us_names info --db data/names.sqlite to inspect a downloaded dataset. python -m us_names export --output tokens.jsonl.gz exports its token records (annual observations remain in SQLite).

Publishing the library

Releases are managed by Release Please. Merge changes into main using Conventional Commit titles (feat:, fix:, or feat!: for breaking changes). Automation opens or updates a release PR containing the version bump and changelog. Review its build run, then merge that PR to create the GitHub release and publish the package to PyPI.

See publishing setup and release operations. PyPI trust still uses publish.yml and environment pypi; no extra token is needed. The package name is us-synthetic-names, and its database remains in the source repository's GitHub Releases.

Metadata

Release files for us-synthetic-names 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for us-synthetic-names 0.3.0
File Size Uploaded
us_synthetic_names-0.3.0.tar.gz 30.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for us-synthetic-names 0.3.0
File Interpreter ABI Platform
us_synthetic_names-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 46.3 kB

Release files / us_synthetic_names-0.3.0.tar.gz

Download URL us_synthetic_names-0.3.0.tar.gz
Size 30.6 kB
Tags Source
SHA-256 checksum
How to use checksums
cb917cc43d50fdbd500b9300b7840a52d77fa25eff5d65d3de2c1e1c1f396d10
BLAKE2b-256 checksum
How to use checksums
4ef7b5c3f066094608f63abfd25293868a5a45df871dab360ec3db59f3ec906d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / us_synthetic_names-0.3.0-py3-none-any.whl

Download URL us_synthetic_names-0.3.0-py3-none-any.whl
Size 15.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc523a7659877b38a11b22f357b66e8302101b94a2671367463184963b98a2a4
BLAKE2b-256 checksum
How to use checksums
27e27b9288b583b48ab2ab84abec4fe6c062e19bf85210374546c4b3025e792a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page