# PyDP: differentially private statistics from Python, backed by a C++ core

> PyDP is OpenMined's Python wrapper around Google's Differential Privacy library, exposing bounded statistics such as BoundedMean and Percentile with an explicit epsilon and a Laplace noise mechanism, with the C++ extension built through Bazel and shipped as platform wheels. It suits a data scientist who needs a private aggregate rather than a private model, and its noise mechanisms are limited to one.

**OpenMined/PyDP** — The Python Differential Privacy Library. Built on top of: https://github.com/google/differential-privacy

- Repository: https://github.com/OpenMined/PyDP
- Stars: 551 · Forks: 142
- Language: Python
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/openmined-pydp

## What the library is for, and the problem it is aimed at

PyDP is the Python Differential Privacy Library, built on top of Google's differential-privacy project.

The motivation in the project documentation is a specific and familiar one. In a data-driven world, researchers and data scientists use machine learning to create better models, and those models often handle sensitive or personal data, which can cause privacy issues. Some AI models can memorize details about the data they were trained on and could potentially leak those details later.

To help measure sensitive data leakage and reduce the possibility of it happening, there is a mathematical framework called differential privacy, and PyDP is OpenMined's Python wrapper for Google's implementation of it, created in 2020.

The library provides a set of epsilon-differentially private algorithms, which can be used to produce aggregate statistics over numeric data sets containing private or sensitive information. So with PyDP you can control the privacy guarantee and accuracy of your model written in Python.

That word aggregate is the important one, and it sets the scope precisely. PyDP computes statistics, not models. A bounded mean, a count above a threshold, a percentile, a median: these are the outputs, and the privacy guarantee is attached to each query rather than to a training run. That is a much narrower and much more tractable proposition than differentially private machine learning, and it is the one this library delivers.

The algorithmic list is the feature list, and it is worth reading as a catalogue of the queries you can ask privately: BoundedMean, BoundedSum, Max, Count Above, Percentile, Min and Median.

The project is Apache-2.0 licensed, the primary language is Python, the default branch is dev, and the recent releases are v1.1.5 from 2026-05-11, v1.1.5.rc11 from 2026-03-27 and 1.1.5rc10 from 2026-03-24, with the last push to the repository on 2026-05-11.

## Installing it, and the two facts about the package you should know

Installation is from PyPI:

```bash
pip install python-dp
```

and the documentation notes that if you have pip3 separately for Python 3.x, you use pip3 install python-dp.

The package name is worth flagging, because it is not the same as the import name. The PyPI distribution is python-dp while the module you import is pydp, which is the kind of small mismatch that costs a confused new user ten minutes.

There are two further facts about the package that the documentation states plainly, and both belong in an adoption decision.

The first is the noise mechanism. All the computation methods mentioned above use Laplace noise only, and the documentation says other noise mechanisms will be added soon.

That is a real constraint rather than an implementation detail. Laplace noise is the right mechanism for bounded queries and it is what the bounded statistics here need, but a composition of many queries under a fixed budget is usually handled with the Gaussian mechanism and its privacy loss accounting, and a library that only offers Laplace pushes that accounting onto you. For a single aggregate, Laplace is entirely adequate. For a pipeline of dozens of queries, the absence of a Gaussian mechanism is the thing to plan around.

The second is the platform and interpreter support. The library is compatible with all three types of operating system, Linux, macOS and Windows, and it uses Python 3.x. The package metadata is more specific than the documentation on the interpreter, with the Poetry configuration declaring python = "^3.11", so a project on an older Python 3 will not install the current release.

The package is also a compiled extension rather than pure Python, which is why the wheels are platform specific and why the build story is as elaborate as it is.

## The BoundedMean example, and what the API makes you decide

The documented example is the one to start with, and it is short.

```python
# Import PyDP
import pydp as dp
# Import the Bounded Mean algorithm
from pydp.algorithms.laplacian import BoundedMean

# Calculate the Bounded Mean
# Basic Structure: `BoundedMean(epsilon: float, lower_bound: Union[int, float, None], upper_bound: Union[int, float, None])`
# `epsilon`: a Double, between 0 and 1, denoting the privacy threshold,
#            measures the acceptable loss of privacy (with 0 meaning no loss is acceptable)
x = BoundedMean(epsilon=0.6, lower_bound=1, upper_bound=10)

# If the lower and upper bounds are not specified,
# PyDP automatically calculates these bounds
# x = BoundedMean(epsilon: float)
x = BoundedMean(0.6)

# Calculate the result
# Currently supported data types are integers and floats
# Future versions will support additional data types
# (Refer to https://github.com/OpenMined/PyDP/blob/dev/examples/carrots.py)
x.quick_result(input_data: list)
```

Three decisions are visible in that code, and each of them is a privacy decision rather than a programming one.

The first is epsilon, which is a double between 0 and 1 denoting the privacy threshold and measuring the acceptable loss of privacy, with 0 meaning no loss is acceptable. That convention is worth pausing on, because it is not the convention in most of the differential privacy literature, where epsilon is a small positive number and smaller means stronger privacy, and where values like 0.6 would be an enormous budget. Here the parameter is described as an acceptable loss of privacy on a scale where 1 appears to be the ceiling, so a higher number is more privacy and a lower number is more accuracy. If you carry an epsilon from a paper into this library, or the other way round, the two numbers mean opposite things.

The second is the bounds, and the API makes them explicit because they are what the mechanism is defined over. A bounded mean without bounds is not computable, and supplying the lower and upper bound is a statement about the data: values outside the range are clipped, which introduces bias, and the tighter the bounds the less bias and the more accuracy you keep.

The third is that the bounds can be omitted, in which case PyDP automatically calculates them. That is convenient and it is also a decision made for you, since the automatic calculation has to look at the data, and a bound derived from the data you are publishing is a bound that leaks a little information about the data.

The result comes from quick_result, which takes a list, and the currently supported data types are integers and floats.

## The build: a C++ core, Bazel, and a pinned upstream commit

The repository is a hybrid project, and understanding that explains most of the tooling.

The Python package wraps a native extension, _pydp.so, which in turn wraps the C++ implementation from Google's differential-privacy repository. The Python layer is in src/ and the native binding sources are in src/bindings/, and the C++ is formatted with clang-format using the repository's own .clang-format file, with a run-clang-format.py script to apply it.

The build system for the native side is Bazel, and the repository carries a WORKSPACE file and a .bazelrc. The Dockerfile shows the full chain: a Python slim base image, an apt install of the toolchain including gcc, g++, clang-format, build-essential and pkg-config, a download of Bazelisk version v1.8.1, and then a clone of the upstream Google repository with a checkout of a specific commit held in the DP_SHA environment variable, 5e7cf28bf55ebac52fc65419364388c33ebc01a4.

Pinning that commit is the detail that matters. The upstream library moves, and a pinned commit means the published PyDP wheel contains a known version of the C++ algorithms rather than whatever was at the head of the branch on the day the wheel was built.

The Docker build also removes the Java directories from the upstream clone and patches the upstream BUILD file with sed to strip the @com_google_cc_differential_privacy prefix, so that the C++ targets can be built without the Java toolchain present. That is the kind of small surgery that a vendored dependency always needs and that nobody enjoys.

The setup.py tells the other half of the story. On Linux the custom build_ext command runs build_PyDP.sh and copies the resulting _pydp.so into the wheel directory, and the comment explains why Linux is special: wheel building is performed inside manylinux Docker images with the help of cibuildwheel, and building with manylinux ensures that PyDP can work on old Linux versions. On Windows and Mac, setup.py is not used for wheel building and the build is performed from GitHub Actions files instead.

The Makefile drives the developer workflow with Poetry, and its targets are the ones you would expect: a build target that installs the Poetry environment and runs build_PyDP.sh, clean targets for build artefacts, Python bytecode and test output, and separate style targets for Python and C++, with black for the former and clang-format with the file style for the latter.

## Where PyDP is the wrong tool

The first limit is the noise mechanism, and it is the one that decides whether the library fits your workload. Everything here uses Laplace noise, and a program that runs many queries against the same dataset, which is the normal case for anything reporting several statistics over one table, needs a composed privacy account, and the standard tool for that is the Gaussian mechanism. With Laplace only, you are either doing a small number of queries or handling the accounting yourself.

The second is the scale of the guarantee. A bounded mean is a private statistic. A model trained on private data is not what this library does, and the documentation's framing is consistent about that, with the mention of models memorising training details serving as the motivation rather than the deliverable. A team whose requirement is a privately trained model is looking for a different project.

The third is the data type restriction. The currently supported data types are integers and floats, and the documentation says future versions will support additional data types. That is a normal limitation for a numeric statistics library, and it does mean a project whose data is text, dates or categories has to map it onto numbers first, and the mapping is part of the privacy analysis rather than a detail.

The fourth is the build and the platform story. The package ships platform wheels, so most users never compile anything, but a user on an unusual platform, an unsupported Python version or a locked-down environment with no compiler is looking at a Dockerfile with Bazelisk, a pinned upstream commit and a sed patch against an upstream BUILD file. The dependency on the upstream C++ library at a pinned commit also means the algorithms PyDP ships are the ones as of that commit, and updating them is a rebuild rather than a pull.

And the fifth is the attack model, which the documentation handles by deferring to the underlying Google library's attack model document for the assumptions and requirements needed to use PyDP in a safe way. That is the right pointer, and it is also the most important document for anyone deploying this. A differential privacy guarantee is relative to an attacker model, and the library's own documentation says to go and read it.

## Conclusion

PyDP is worth reaching for if you need a single differentially private aggregate rather than a private model, because the algorithms are the ones from Google's vetted library rather than a reimplementation, because the interface makes epsilon a constructor argument so the privacy budget cannot be lost in a configuration file, and because the bounds are computed for you when you do not supply them. It is the wrong tool if you need Gaussian or other noise mechanisms today, and it is the wrong tool for private model training. Before you adopt it, check three things: that you read the attack model, because the documentation points at the underlying Google library's attack model for the assumptions and requirements needed to use PyDP safely, and the privacy guarantee is meaningless without knowing the attacker model it assumes; that you understand the epsilon convention, since the parameter is a double between 0 and 1 denoting the privacy threshold and measuring the acceptable loss of privacy with 0 meaning no loss is acceptable, which is a different convention from the epsilon in most differential privacy literature; and that you check the interpreter, because the package metadata requires Python 3.11 or newer even though the documentation describes the library as Python 3.x.

## FAQ

### How do I install PyDP and compute a private statistic?

Install it with pip install python-dp, or pip3 install python-dp if pip3 is separate, then import the algorithm from pydp.algorithms.laplacian. A bounded mean is constructed with an epsilon and optional bounds, for example BoundedMean(epsilon=0.6, lower_bound=1, upper_bound=10), and the result comes from quick_result with a list of integers or floats.

### What does epsilon mean in PyDP?

It is a double between 0 and 1 denoting the privacy threshold, measuring the acceptable loss of privacy, with 0 meaning no loss is acceptable. The example in the documentation uses 0.6, so on this scale a larger value means a larger acceptable privacy loss and therefore more accuracy, which is the opposite of the epsilon convention in most of the differential privacy literature.

### Which algorithms does PyDP provide and which noise mechanism do they use?

The documented set includes BoundedMean, BoundedSum, Max, Count Above, Percentile, Min and Median, and all of the computation methods use Laplace noise only, with the documentation noting that other noise mechanisms will be added soon. The library is compatible with Linux, macOS and Windows, and the package metadata requires Python 3.11 or newer.

## Sources

- [Issues](https://github.com/OpenMined/PyDP/issues)
- [License: Apache-2.0](https://github.com/OpenMined/PyDP/blob/dev/LICENSE)
- [OpenMined/PyDP on GitHub](https://github.com/OpenMined/PyDP)
- [README](https://github.com/OpenMined/PyDP/blob/dev/README.md)
- [Releases](https://github.com/OpenMined/PyDP/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/openmined-pydp
