Skip to main content

pybenford

Professional-grade Benford's Law analysis toolkit for forensic accounting, auditing, and fraud detection.

PyPI version Python versions License: MIT Tests Coverage

Why pybenford?

Existing Benford's Law packages on PyPI cover first-digit chi-square and not much else. Most are unmaintained. pybenford implements the complete Nigrini forensic accounting workflow from Benford's Law: Applications for Forensic Accounting, Auditing, and Fraud Detection (Nigrini, 2012):

  • MAD conformity classification with Nigrini's empirical thresholds (close, acceptable, marginally acceptable, nonconformity)
  • Distortion factor model for detecting overstatement vs. understatement
  • Second-order test on differences of sorted values
  • Summation test with uniform 1/90 expectation
  • Mantissa arc test (Alexander, 2009) with L-squared statistic
  • Number duplication analysis
  • All standard digit tests: first, second, third, first-two, first-three, last-two
  • Z-statistic with Fleiss continuity correction, chi-square, Kolmogorov-Smirnov
  • Publication-quality matplotlib visualizations
  • Pure NumPy internals, no pandas dependency

Installation

pip install pybenford             # analysis (numpy + scipy)
pip install "pybenford[plot]"     # analysis + matplotlib visualizations

Requires Python >= 3.10.

Breaking change in 0.2.0: matplotlib is no longer a required dependency. If you use the six plot_* functions, install the [plot] extra. On an analysis-only install, accessing a plot function — including via from pybenford import *, which resolves every exported name — raises an ImportError pointing at pip install "pybenford[plot]".

Quick Start

from pybenford import BenfordAnalysis

analysis = BenfordAnalysis(data)  # list, numpy array, or pandas/polars Series
result = analysis.first_digit()
print(result)

Every result object has a formatted print() output. The two reports below are produced from the 3,144 county-level records (SUMLEV == "050") of the US Census 2025 county population estimates, by this exact code:

import csv

from pybenford import BenfordAnalysis

with open("co-est2025-alldata.csv", encoding="latin-1", newline="") as f:
    data = [
        float(row["POPESTIMATE2025"])
        for row in csv.DictReader(f)
        if row["SUMLEV"].strip() == "050"
    ]

print(BenfordAnalysis(data).first_digit())

Reproducibility note: the census dataset ships in the GitHub repository, not in the PyPI package. Download examples/data/co-est2025-alldata.csv (raw file) or clone the repo.

=======================================================
  First Digit Test  (n=3,144  alpha=0.05)
=======================================================
 Digit   Count   Observed   Expected   Z-Score   Sig
     1    943   29.99%    30.10%      0.11
     2    588   18.70%    17.61%      1.59
     3    382   12.15%    12.49%      0.56
     4    295    9.38%     9.69%      0.55
     5    250    7.95%     7.92%      0.04
     6    191    6.08%     6.69%      1.35
     7    175    5.57%     5.80%      0.52
     8    169    5.38%     5.12%      0.62
     9    151    4.80%     4.58%      0.57
-------------------------------------------------------
 MAD:        0.0036 — Close Conformity
 Chi-Square: 5.6231  (critical: 15.5073) — Pass
 KS:         0.0098  (critical: 0.0242)  — Pass
=======================================================

For tests with many digit bins (first-two, first-three), the display shows only flagged digits instead of all 90 or 900 rows. On the same data:

print(BenfordAnalysis(data).first_two_digits())
=======================================================
  First Two Digits Test  (n=3,144  alpha=0.05)
=======================================================
 Flagged Digits (6 of 90):
 Digit   Count   Observed   Expected   Z-Score
    35     23    0.73%     1.22%      2.43  *
    49     16    0.51%     0.88%      2.12  *
    66     33    1.05%     0.65%      2.65  *
    70     29    0.92%     0.62%      2.08  *
    75     28    0.89%     0.58%      2.22  *
    76      8    0.25%     0.57%      2.22  *
-------------------------------------------------------
 MAD:        0.0016 — Acceptable Conformity
 Chi-Square: 111.1969  (critical: 112.0220) — Pass
 KS:         0.0118  (critical: 0.0242)  — Pass
=======================================================

All results are also accessible programmatically:

result = analysis.first_digit()

result.mad                     # 0.0034
result.mad_conformity          # "close"
result.chi_square              # 4.6922
result.chi_square_significant  # False
result.ks_statistic            # 0.0083
result.ks_critical             # 0.0240
result.z_scores                # array of per-digit Z-scores
result.significant_flags       # bool array of flagged digits
result.observed                # array of observed proportions
result.expected                # array of expected Benford proportions
result.digits                  # array of digit labels
result.counts                  # array of raw counts
result.n                       # number of records analyzed
result.alpha                   # significance level used
result.test_name               # e.g. "First Digit Test"

Demo Notebook

A complete walkthrough of every test and visualization is available in examples/demo.ipynb. It runs against US Census county population data and shows the output of all 11 tests, 6 plot functions, and programmatic result access.

Data Preparation

analysis = BenfordAnalysis(
    data,                        # list, array, or Series of numbers
    sign_filter="positive",      # "all", "positive", or "negative"
    min_abs_value=10.0,          # exclude small values (optional)
    drop_zero=True,              # exclude zeros (default: True)
)

print(analysis.profile)          # data profile per Nigrini Ch. 4

sign_filter separates income from expense items for independent analysis. min_abs_value excludes values below a minimum magnitude, since very small numbers distort digit distributions.

Visualization

Plot functions return (Figure, Axes) with no side effects.

from pybenford.visualization import plot_digit_test, plot_mantissa_arc

result = analysis.first_two_digits()
fig, ax = plot_digit_test(result, show_confidence=True)
fig.savefig("first_two_digits.png", dpi=150)

arc = analysis.mantissa_arc()
fig, ax = plot_mantissa_arc(arc)

from pybenford.visualization import plot_z_scores
fig, ax = plot_z_scores(result, critical_value=1.96)

Available Tests

Method Description Reference
first_digit() First significant digit (1-9) Nigrini Ch. 5
second_digit() Second significant digit (0-9) Nigrini Ch. 5
third_digit() Third significant digit (0-9) Nigrini Ch. 5
first_two_digits() First two digits (10-99) Nigrini Ch. 5
first_three_digits() First three digits (100-999) Nigrini Ch. 5
last_two_digits() Last two digits (00-99), uniform expected Nigrini Ch. 5
second_order() Digit test on sorted differences Nigrini Ch. 6
summation() Sum proportions vs. uniform 1/90 Nigrini Ch. 5
distortion_factor() Overstatement/understatement detection Nigrini Ch. 6
mantissa_arc() Uniformity of mantissas on unit circle Nigrini Ch. 7
number_duplication() Most frequently duplicated values Nigrini Ch. 5

Statistical Measures

Each digit test result includes:

  • Z-statistic per digit bin (Fleiss continuity correction)
  • Chi-square goodness-of-fit with critical value
  • Kolmogorov-Smirnov statistic with critical value
  • MAD (Mean Absolute Deviation) with Nigrini's conformity classification
  • Per-bin significance flags at configurable alpha

MAD Conformity Thresholds

MAD is the preferred conformity measure because chi-square and KS become overly sensitive with large datasets (N > 25,000), rejecting near-perfect conformity. MAD is sample-size independent.

Test Close Acceptable Marginal Nonconformity
First digit < 0.006 < 0.012 < 0.015 >= 0.015
Second digit < 0.008 < 0.010 < 0.012 >= 0.012
First two digits < 0.0012 < 0.0018 < 0.0022 >= 0.0022
First three digits < 0.00036 < 0.00044 < 0.00050 >= 0.00050

References

  • Nigrini, M.J. (2012). Benford's Law: Applications for Forensic Accounting, Auditing, and Fraud Detection. Wiley.
  • Miller, S.J. (2015). Benford's Law: Theory and Applications. Princeton University Press.
  • Kossovsky, A.E. (2014). Benford's Law: Theory, the General Law of Relative Quantities, and Forensic Fraud Detection Applications. World Scientific.

License

MIT License. See LICENSE for details.

Metadata

Release files for pybenford 0.2.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 pybenford 0.2.0
File Size Uploaded Provenance
pybenford-0.2.0.tar.gz 81.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pybenford 0.2.0
File Interpreter ABI Platform
pybenford-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 115.7 kB

Release files / pybenford-0.2.0.tar.gz

Download URL pybenford-0.2.0.tar.gz
Size 81.0 kB
Tags Source
SHA-256 checksum
How to use checksums
fe22fc1cbff2f067f4ad7dbace2fd9c0e1e7316de43a8b1e9b306a51a9ff6f77
BLAKE2b-256 checksum
How to use checksums
5a0ec5121977eca13d520b264f2b062a5c2b367b9deb73c0e58a6f0d2493a8c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Release files / pybenford-0.2.0-py3-none-any.whl

Download URL pybenford-0.2.0-py3-none-any.whl
Size 34.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
99d1c9212b2b086e183721db4bc6621b4b642d10ca2cee70ecd58e060085d3b4
BLAKE2b-256 checksum
How to use checksums
d503cbf70767a16b2ec53ecc40c466a9e5d50b8a8ce79cd10e8fa9040a5720d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.13

Security for pybenford 0.2.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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