.. _constructors: Constructors ============ In OpenDP, Measurements and Transformations are created by calling constructor functions. The majority of the library's interface consists of these `make_*` constructors, like :py:func:`make_clamp ` or :py:func:`make_base_laplace `. Because Measurements and Transformations are themselves like functions (they can be invoked on an input and return an output), you can think of constructors as higher-order functions: You call them to produce another function that you will then feed data. Constructors are organized into Transformations, Measurements and Combinators: .. toctree:: transformations measurements combinators Let's demonstrate with a few examples! In this example, we use a constructor to build a clamp transformation: .. testsetup:: from opendp.mod import enable_features enable_features('contrib') .. doctest:: >>> from opendp.transformations import make_clamp >>> clamper = make_clamp(bounds=(0, 10)) ... >>> # invoke the function with some data >>> clamper([-1, 0, 3, 5, 10, 20]) [0, 0, 3, 5, 10, 10] The input metric and output metric are implicitly `SymmetricDistance`. We can use the stability map on this transformation to ask a hypothetical question: If neigboring datasets differ by as much as `d_in`, then how much can neigboring datasets differ after running this transformation (`d_out`)? .. doctest:: >>> # map an input distance to an output distance >>> clamper.map(d_in = 1) 1 We know the clamp transformation is 1-stable, so `d_out = 1 * d_in`. Similarly, we can check if the transformation is (`d_in`, `d_out`)-close (that is, `map(d_in) <= d_out` ) by checking the stability relation: .. doctest:: >>> # check if clamper is (d_in, d_out)-close >>> clamper.check(d_in = 1, d_out = 1) True We can use the `>>` shorthand to chain multiple transformations (internally uses the `make_chain_tt `_ combinator). .. doctest:: >>> from opendp.transformations import make_cast_default ... >>> # build another transformation that casts, and fills nulls with a default value (0) >>> caster = make_cast_default(TIA=str, TOA=int) ... >>> # construct a new transformation such that preprocessor(x) = clamper(caster(x)) >>> preprocessor = caster >> clamper ... >>> # invoke the chained transformation >>> preprocessor(["1", "2", "3", "20", "a"]) [1, 2, 3, 10, 0] >>> # since both are 1-stable transformations, then the map remains trivial >>> preprocessor.map(d_in=2) 2 In this simplified example with the :py:func:`opendp.measurements.make_base_discrete_laplace` constructor, we assume the data was properly preprocessed and aggregated such that the sensitivity (by absolute distance) is at most 1. .. doctest:: >>> from opendp.measurements import make_base_discrete_laplace ... >>> # call the constructor to produce the measurement `base_dl` >>> base_dl = make_base_discrete_laplace(scale=1.0) ... >>> # investigate the privacy relation >>> absolute_distance = 1 >>> base_dl.map(d_in=absolute_distance) # returns epsilon 1.0 >>> # feed some data/invoke the measurement as a function >>> aggregated = 5 >>> release = base_dl(aggregated) As you can see, constructor functions are the gateway to building differentially private analyses in OpenDP. The next sections are a tour of the available constructor functions.