<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Differentially Private Histograms
Plotting the distribution of ages in Adult
Step1:  We first read in the list of ages in the Adult UCI dataset (the first column).
Step2:  Using Numpy's native histogram function, we can find the distribution of ages, as determined by ten equally-spaced bins calculated by histogram.
Step3:  Using matplotlib.pyplot, we can plot a barchart of the histogram distribution.
Step4:  Differentially private histograms
Using diffprivlib, we can calculate a differentially private version of the histogram. For this example, we use the default settings
Step5:  Privacy Leak
Step6:  Mirroring the behaviour of np.histogram
Step7:  Error
Step8:  Effect of epsilon
Step9:  Deciding on the range parameter
We know from the dataset description that everyone in the dataset is at least 17 years of age. We don't know off-hand what the upper bound is, so for this example we'll set the upper bound to 100. As of 2019, less than 0.005% of the world's population is aged over 100, so this is an appropriate simplification. Values in the dataset above 100 will be excluded from calculations.
An epsilon of 0.1 still preserves the broad structure of the histogram.
Step10:  Error for smaller datasets
Let's repeate the first experiments above with a smaller dataset, this time the Cleveland heart disease dataset from the UCI Repository. This dataset has 303 samples, a small fractin of the Adult dataset processed previously.
Step11:  We first find the histogram distribution using numpy.histogram.
Step12:  And then find the histogram distribution using diffprivlib.histogram, using the defaults as before (with the accompanying warning).
Step13:  And double-check that the bins are the same.
Step14:  We then see that the error this time is 3%, a 100-fold increase in error.
Step15:  Mirroring Numpy's behaviour
We can evaluate diffprivlib.models.histogram without any privacy by setting epsilon = float("inf"). This should give the exact same result as running numpy.histogram.
<ASSISTANT_TASK:>
Python Code:
import numpy as np
from diffprivlib import tools as dp
import matplotlib.pyplot as plt
Explanation: Differentially Private Histograms
Plotting the distribution of ages in Adult
End of explanation
ages_adult = np.loadtxt("https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data",
                        usecols=0, delimiter=", ")
Explanation: We first read in the list of ages in the Adult UCI dataset (the first column).
End of explanation
hist, bins = np.histogram(ages_adult)
hist = hist / hist.sum()
Explanation: Using Numpy's native histogram function, we can find the distribution of ages, as determined by ten equally-spaced bins calculated by histogram.
End of explanation
plt.bar(bins[:-1], hist, width=(bins[1]-bins[0]) * 0.9)
plt.show()
Explanation: Using matplotlib.pyplot, we can plot a barchart of the histogram distribution.
End of explanation
dp_hist, dp_bins = dp.histogram(ages_adult)
dp_hist = dp_hist / dp_hist.sum()
plt.bar(dp_bins[:-1], dp_hist, width=(dp_bins[1] - dp_bins[0]) * 0.9)
plt.show()
Explanation: Differentially private histograms
Using diffprivlib, we can calculate a differentially private version of the histogram. For this example, we use the default settings:
- epsilon is 1.0
- range is not specified, so is calculated by the function on-the-fly. This throws a warning, as it leaks privacy about the data (from dp_bins, we know that there are people in the dataset aged 17 and 90).
End of explanation
dp_bins[0], dp_bins[-1]
Explanation: Privacy Leak: In this setting, we know for sure that at least one person in the dataset is aged 17, and another is aged 90.
End of explanation
np.all(dp_bins == bins)
Explanation: Mirroring the behaviour of np.histogram: We can see that the bins returned by diffprivlib.tools.histogram are identical to those given by numpy.histogram.
End of explanation
print("Total histogram error: %f" % np.abs(hist - dp_hist).sum())
Explanation: Error: We can see very little difference in the values of the histgram. In fact, we see an aggregate absolute error across all bins of the order of 0.01%. This is expected, due to the large size of the dataset (n=48842).
End of explanation
dp_hist, dp_bins = dp.histogram(ages_adult, epsilon=0.001)
dp_hist = dp_hist / dp_hist.sum()
print("Total histogram error: %f" % np.abs(hist - dp_hist).sum())
Explanation: Effect of epsilon: If we decrease epsilon (i.e. increase the privacy guarantee), the error will increase.
End of explanation
dp_hist2, dp_bins2 = dp.histogram(ages_adult, epsilon=0.1, range=(17, 100))
dp_hist2 = dp_hist2 / dp_hist2.sum()
plt.bar(dp_bins2[:-1], dp_hist2, width=(dp_bins2[1] - dp_bins2[0]) * 0.9)
plt.show()
Explanation: Deciding on the range parameter
We know from the dataset description that everyone in the dataset is at least 17 years of age. We don't know off-hand what the upper bound is, so for this example we'll set the upper bound to 100. As of 2019, less than 0.005% of the world's population is aged over 100, so this is an appropriate simplification. Values in the dataset above 100 will be excluded from calculations.
An epsilon of 0.1 still preserves the broad structure of the histogram.
End of explanation
ages_heart = np.loadtxt("https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/processed.cleveland.data",
                        usecols=0, delimiter=",")
Explanation: Error for smaller datasets
Let's repeate the first experiments above with a smaller dataset, this time the Cleveland heart disease dataset from the UCI Repository. This dataset has 303 samples, a small fractin of the Adult dataset processed previously.
End of explanation
heart_hist, heart_bins = np.histogram(ages_heart)
heart_hist = heart_hist / heart_hist.sum()
Explanation: We first find the histogram distribution using numpy.histogram.
End of explanation
dp_heart_hist, dp_heart_bins = dp.histogram(ages_heart)
dp_heart_hist = dp_heart_hist / dp_heart_hist.sum()
Explanation: And then find the histogram distribution using diffprivlib.histogram, using the defaults as before (with the accompanying warning).
End of explanation
np.all(heart_bins == dp_heart_bins)
Explanation: And double-check that the bins are the same.
End of explanation
print("Total histogram error: %f" % np.abs(heart_hist - dp_heart_hist).sum())
Explanation: We then see that the error this time is 3%, a 100-fold increase in error.
End of explanation
heart_hist, _ = np.histogram(ages_heart)
dp_heart_hist, _ = dp.histogram(ages_heart, epsilon=float("inf"))
np.all(heart_hist == dp_heart_hist)
Explanation: Mirroring Numpy's behaviour
We can evaluate diffprivlib.models.histogram without any privacy by setting epsilon = float("inf"). This should give the exact same result as running numpy.histogram.
End of explanation
<END_TASK>