# Aggregation: Mean#

Any constructors that have not completed the proof-writing and vetting process may still be accessed if you opt-in to “contrib”. Please contact us if you are interested in proof-writing. Thank you!

```
[51]:
```

```
from opendp.mod import enable_features
enable_features("contrib")
```

## Known Dataset Size#

The much easier case to consider is when the dataset size is known:

```
[52]:
```

```
from opendp.transformations import make_sized_bounded_mean
sb_mean_trans = make_sized_bounded_mean(size=10, bounds=(0., 10.))
sb_mean_trans([5.] * 10)
```

```
[52]:
```

```
5.0
```

The sensitivity of this transformation is the same as in `make_sized_bounded_sum`

, but divided by `size`

.

That is, \(map(d_{in}) = (d_{in} // 2) \cdot max(|L|, U) / size\), where \(//\) denotes integer division with truncation.

```
[53]:
```

```
# since we are in the bounded-DP model, d_in should be a multiple of 2,
# because it takes one removal and one addition to change one record
sb_mean_trans.map(2)
```

```
[53]:
```

```
1.0000000000000169
```

Note that this operation does not divide by the length of the input data, it divides by the size parameter passed to the constructor. As in any other context, it is expected that the data passed into the function is a member of the input domain, so no promises of privacy or correctness are guaranteed when the data is not in the input domain. In particular, the function may give a result with no error message.

```
[54]:
```

```
sb_mean_trans = make_sized_bounded_mean(size=10, bounds=(0., 10.))
sb_mean_trans([5.])
```

```
[54]:
```

```
0.5
```

## Unknown Dataset Size#

There are several approaches for releasing the mean when the dataset size is unknown.

The first approach is to use the resize transformation. You can separately release an estimate for the dataset size, and then preprocess the dataset with a resize transformation.

```
[55]:
```

```
from opendp.transformations import make_count, part_clamp, make_resize
from opendp.measurements import part_base_discrete_laplace, part_base_laplace
from opendp.domains import vector_domain, atom_domain
from opendp.metrics import symmetric_distance
data = [5.] * 10
bounds = (0., 10.)
input_domain = vector_domain(atom_domain(T=float))
input_metric = symmetric_distance()
# (where TIA stands for Atomic Input Type)
count_meas = make_count(TIA=float) >> part_base_discrete_laplace(1.)
dp_count = count_meas(data)
mean_meas = (
(input_domain, input_metric) >>
part_clamp(bounds) >>
make_resize(dp_count, atom_domain(bounds), constant=5.) >>
make_sized_bounded_mean(dp_count, bounds) >>
part_base_laplace(1.)
)
mean_meas(data)
```

```
[55]:
```

```
5.239477071130359
```

The total privacy expenditure is the composition of the `count_meas`

and `mean_meas`

releases.

```
[56]:
```

```
from opendp.combinators import make_basic_composition
make_basic_composition([count_meas, mean_meas]).map(1)
```

```
[56]:
```

```
2.000000000000017
```

Another approach is to compute the DP sum and DP count, and then postprocess the output.

```
[59]:
```

```
from opendp.transformations import make_bounded_sum
dp_sum = (
(input_domain, input_metric) >>
part_clamp(bounds) >>
make_bounded_sum(bounds) >>
part_base_laplace(10.)
)
dp_count = make_count(TIA=float) >> part_base_discrete_laplace(1.)
dp_fraction_meas = make_basic_composition([dp_sum, dp_count])
dp_sum, dp_count = dp_fraction_meas(data)
print("dp mean:", dp_sum / dp_count)
print("epsilon:", dp_fraction_meas.map(1))
```

```
dp mean: 4.4093953568039455
epsilon: 2.000000009313226
```

The same approaches are valid for the variance estimator. The Unknown Dataset Size notebook goes into greater detail on the tradeoffs of these approaches.