Skip to main content

Differentially private publication pipeline for aggregate group statistics with k-anonymity gating and budget accounting

Project description

dp-group-stats

A lightweight Python library for publishing differentially private aggregate statistics over small groups, with k-anonymity gating and privacy budget accounting.

PyPI version Python 3.10+ License: MIT

What this solves

When publishing aggregate statistics (means, sums) for small professional groups on a recurring schedule, you need:

  1. Differential privacy — calibrated noise so individual contributions can't be reverse-engineered
  2. K-anonymity gating — suppress groups that are too small to publish safely
  3. Budget accounting — track cumulative privacy spend per user across groups and time
  4. Graceful degradation — when budget runs low, statistics get noisier instead of disappearing

Existing DP libraries are either massive frameworks (OpenDP, TensorFlow Privacy) or bare mechanism implementations. dp-group-stats is the pipeline layer in between: configuration, noise, publication policy, and budget tracking in one focused package with zero dependencies.

Install

pip install dp-group-stats

Requirements: Python 3.10+ — no dependencies beyond the standard library.

Quick start

from datetime import date
from dp_group_stats import (
    DPGroupStatsConfig,
    InMemoryPrivacyLedger,
    PublicationStatus,
    compute_adaptive_epsilon,
    get_period_bounds,
    get_publication_status,
    laplace_noise,
    laplace_ci_half_width,
)

# 1. Configure the pipeline
config = DPGroupStatsConfig(
    period_type="weekly",
    annual_epsilon_cap=150.0,
)

# 2. Check publication eligibility
status = get_publication_status(
    was_active=False,
    consecutive_eligible=3,
    consecutive_ineligible=0,
    activation_weeks=config.release_policy.activation_weeks,
    deactivation_grace_weeks=config.release_policy.deactivation_grace_weeks,
)
assert status == PublicationStatus.published

# 3. Clip contributions and add noise
raw_actual_hours = [42.0, 55.0, 38.0, 61.0, 200.0, 44.0]
clipped = [config.bounds.clip_actual(h) for h in raw_actual_hours]
true_sum = sum(clipped)

noisy_sum = true_sum + laplace_noise(
    epsilon=config.epsilon_split.actual_sum,
    sensitivity=config.bounds.actual_sensitivity,
)
noisy_mean = noisy_sum / len(clipped)

# 4. Compute confidence intervals
ci_half, n_display = laplace_ci_half_width(
    epsilon=config.epsilon_split.actual_sum,
    sensitivity=config.bounds.actual_sensitivity,
    n_users=len(clipped),
)
print(f"Mean: {noisy_mean:.1f}h [±{ci_half:.1f}h 90% CI] (~{n_display} reporters)")

# 5. Track budget
ledger = InMemoryPrivacyLedger()
for user_id in ["u1", "u2", "u3", "u4", "u5", "u6"]:
    ledger.record(
        user_id=user_id,
        family="state_specialty",
        cell_key="DEU/BE/cardiology",
        period_start=date(2026, 3, 16),
        epsilon=config.epsilon_split.total,
    )

summary = ledger.budget_summary(annual_cap=config.annual_epsilon_cap)
print(f"Worst-case user: {summary['worst_case_spent']:.1f}ε of {summary['annual_cap']}ε cap")

Components

Configuration (dp_group_stats.config)

  • ContributionBounds — clipping bounds that limit sensitivity (default: planned [0, 80h], actual [0, 120h])
  • EpsilonSplit — how per-cell epsilon is divided across quantities (default: 0.2 planned, 0.8 actual)
  • ReleasePolicyConfig — k-anonymity threshold, dominance rule, activation/deactivation streaks
  • DPGroupStatsConfig — top-level config with budget validation at construction time

Mechanisms (dp_group_stats.mechanisms)

  • laplace_noise() — Laplace mechanism with injectable RNG for deterministic testing
  • laplace_ci_half_width() — confidence intervals with rounded group size to avoid leaking exact n

Policy (dp_group_stats.policy)

Publication state machine: suppressed → warming_up → published → cooling_down → suppressed

  • Cells must pass k_min and dominance checks for N consecutive periods before publishing
  • Grace period before suppression (noise continues during cooldown to prevent information leakage)

Accounting (dp_group_stats.accounting)

  • PrivacyLedger (Protocol) — pluggable interface for budget tracking
  • InMemoryPrivacyLedger — reference implementation for testing/simulation
  • EpsilonLedger — lightweight cell-only tracker
  • compute_adaptive_epsilon() — graceful degradation that scales epsilon down as the annual cap approaches

Bring your own storage backend by implementing the PrivacyLedger protocol (e.g., SQL-backed for production).

Periods (dp_group_stats.periods)

  • get_period_bounds() — weekly/biweekly/monthly period boundaries
  • period_before() / compute_period_index() — temporal navigation

Design decisions

This library makes specific choices documented in the design specs:

  • Substitution neighboring relation: DP protects contribution values, not user presence. Counts are public; only sums are noised.
  • Per-user budget accounting: the privacy guarantee lives with users, not cells. A user appearing in multiple release families accumulates epsilon across all of them.
  • Time-based degradation: adaptive epsilon is computed from elapsed time and configured spending, not from actual per-user data (which would itself leak information).
  • K-anonymity as policy, not proof: k_min and dominance rules are publication safeguards layered on top of DP, not substitutes for it.

Developed for

This library was extracted from Open Working Hours, a privacy-first platform for healthcare workers to track and report working hours. The design was validated through 200+ simulation scenarios covering parameter selection for workforce statistics.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dp_group_stats-0.1.2.tar.gz (49.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dp_group_stats-0.1.2-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file dp_group_stats-0.1.2.tar.gz.

File metadata

  • Download URL: dp_group_stats-0.1.2.tar.gz
  • Upload date:
  • Size: 49.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for dp_group_stats-0.1.2.tar.gz
Algorithm Hash digest
SHA256 2a3f0cb77e57c13e1af0799819d2436c0f9e84a6d00dc939da635e0ffcb10678
MD5 619e1b4812c8826f4e0bde0b137d72fa
BLAKE2b-256 156485467ead584ad79fe1dded07ae0b937c21c5205be24bde2bdc45705284aa

See more details on using hashes here.

File details

Details for the file dp_group_stats-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: dp_group_stats-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for dp_group_stats-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d99be5aa1060d80c06d6cf939af21f9806fbc4294439072f487b69edb822c24e
MD5 1a463e297b7d9dd39fe417d80c194a35
BLAKE2b-256 230b309efa413514243e2ae2eaa90d9a20307bc0027f60baeb63d98501d20108

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page