Lesson 09 / 09
Data Ethics and Privacy
Of 18 questions asked of the same subscriber set, 14 can be answered with the fields on hand and 4 have no field; when the boundary rule changes, the number of questions that are answerable but should not be asked rises from 3 to 8 and the reportable questions fall from 11 to 6. As the breakdown deepens the group shrinks: the smallest group is 209 records in the region breakdown and 1 record in the four-field breakdown, and 45 subscribers sit alone in a cell. When the minimum group size threshold is raised from 5 to 10, the suppressed cells rise from 117 to 146 and the records left outside the report from 245 to 431.
Published Updated
In this lesson
- Tells two separate boundaries apart: a question the data cannot answer and a question that is answerable but should not be asked
- Counts by running how the number of unaskable questions moves when the boundary rule changes
- Measures how group size falls as the breakdown deepens and how many records land in a single cell
- Compares the cells suppressed and the records left outside the report at two minimum group size thresholds
Contents
The previous lesson tightened the quality threshold and left 422 rows out of 1,400. Each of those 422 rows belongs to a household. Throughout the course the rows were treated as observations; when a breakdown is thinned enough, the row is no longer an observation but a single person.
This lesson counts two things. The first is how many of the questions that can be asked of the same set are answerable but should not be asked. The second is how many people a breakdown falls down to. What they share is this: neither is read from the data, because that a calculation can be done does not say that it should be done.
In the Detection, Response and Compliance course, what an audit log records was measured. The question here is different: the record has already been taken; what is measured is where the analysis will stop.
- SD19. The set is again the 1,400-row subscriber record and it is fictional. What is counted is not the content of the set but the questions that can be asked of it.
- SD20. Personal data, as a kind, is a record that can be tied to a person. The boundaries are treated here as analysis decisions, and how far the decision moves the number is measured.
- SD21. The three boundary rules are cumulative: no inference that descends to a single person is made, data is not used outside the purpose for which it was collected, and an attribute of a sensitive kind is not produced by inference.
- SD22. The minimum group size threshold is the least number of records a breakdown cell must contain for it to be reported. is given to the model as input.
Answerable but Not to Be Asked
// questions.mjs — 18 questions asked of the same subscriber set are classified on two axes: // can the data answer it, and does the boundary rule allow it to be asked. IT IS A MODEL. // [question, has field, reaches a person, outside purpose, sensitive inference] export const QUESTIONS = [ ["mean consumption per region", 1, 0, 0, 0], ["tariff tier distribution", 1, 0, 0, 0], ["share of subscribers with a field visit", 1, 0, 0, 0], ["satisfaction difference by region", 1, 0, 0, 0], ["mean monthly reading per meter", 1, 0, 0, 0], ["region with the highest reading delay", 1, 0, 0, 0], ["why the subscriber asked for a tariff change", 0, 0, 0, 0], ["how income level affects consumption", 0, 0, 0, 0], ["did the complaining subscriber leave the service", 0, 0, 0, 0], ["did those who saw the promotion consume less", 0, 0, 0, 0], ["which household is empty right now", 1, 1, 0, 0], ["a subscriber's daily living pattern", 1, 1, 0, 0], ["list of lowest-consumption subscribers", 1, 1, 0, 0], ["hand subscriber list to tariff promotion", 1, 0, 1, 0], ["route overdue subscribers to other services", 1, 0, 1, 0], ["evaluate the field team by satisfaction", 1, 0, 1, 0], ["household size and age from consumption", 1, 0, 0, 1], ["maintenance need from night consumption", 1, 0, 0, 1], ].map(([text, field, person, purpose, sensitive]) => ({ text, field, person, purpose, sensitive })); // The boundary rules are cumulative; each is added to the previous set. export const RULE = [ ["R0 no rule", () => false], ["R1 no identity descent", (q) => q.person === 1], ["R2 + purpose tie", (q) => q.person === 1 || q.purpose === 1], ["R3 + sensitive inference", (q) => q.person === 1 || q.purpose === 1 || q.sensitive === 1], ]; if (import.meta.url === `file://${process.argv[1]}`) { const N = QUESTIONS.length, answerable = QUESTIONS.filter((q) => q.field === 1).length; console.log(`${N} questions, same subscriber set; ${answerable} can be answered with the ` + `fields on hand, no field exists for ${N - answerable}`); const y = (h) => String(h[0]).padEnd(26) + h.slice(1).map((v) => String(v).padStart(15)).join(""); console.log("\n" + y(["boundary rule", "unaskable", "reportable", "closed share"])); for (const [name, barrier] of RULE) { const banned = QUESTIONS.filter((q) => q.field === 1 && barrier(q)).length; console.log(y([name, banned, answerable - banned, `${(100 * banned / answerable).toFixed(1)}%`])); } const [, r1] = RULE[1], [, r3] = RULE[3]; console.log(`\nunaskable at R1 ${QUESTIONS.filter((q) => q.field === 1 && r1(q)).length}, ` + `at R3 ${QUESTIONS.filter((q) => q.field === 1 && r3(q)).length}; ` + `the data did not change at all, only the boundary rule did`); console.log(`\ntwo separate boundaries: no field for ${N - answerable} questions (data boundary), ` + `${QUESTIONS.filter((q) => q.field === 1 && r3(q)).length} questions at R3 (analysis boundary)`); const y2 = (h) => String(h[0]).padEnd(46) + h.slice(1).map((v) => String(v).padStart(14)).join(""); console.log("\n" + y2(["questions hitting the analysis boundary", "answerable", "barrier"])); for (const q of QUESTIONS.filter((q) => q.field === 1 && r3(q))) { console.log(y2([q.text, "yes", q.person ? "identity" : q.purpose ? "purpose" : "sensitive"])); } }
18 questions, same subscriber set; 14 can be answered with the fields on hand, no field exists for 4 boundary rule unaskable reportable closed share R0 no rule 0 14 0.0% R1 no identity descent 3 11 21.4% R2 + purpose tie 6 8 42.9% R3 + sensitive inference 8 6 57.1% unaskable at R1 3, at R3 8; the data did not change at all, only the boundary rule did two separate boundaries: no field for 4 questions (data boundary), 8 questions at R3 (analysis boundary) questions hitting the analysis boundary answerable barrier which household is empty right now yes identity a subscriber's daily living pattern yes identity list of lowest-consumption subscribers yes identity hand subscriber list to tariff promotion yes purpose route overdue subscribers to other services yes purpose evaluate the field team by satisfaction yes purpose household size and age from consumption yes sensitive maintenance need from night consumption yes sensitive
The table puts two separate boundaries side by side. Four questions hit the data boundary: the set has no income, intervention, or churn record, and this boundary can be lifted by adding fields. Eight questions hit the analysis boundary, and the last column says “yes” for every one of them: they are answerable. This boundary is not lifted by adding fields, because the barrier is not missing data but the question itself.
The number of questions that cannot be asked is not a constant either. With only the rule against descending to identity in force it is 3, with all three rules together it is 8; the reportable questions fall from 11 to 6, and not a single record in the set changes. An analysis cannot be satisfied with a yes or no to the question “does the data contain personal information”; unless it is written which rules were applied, it is not known how many questions were closed.
The barriers of the eight closed questions differ from one another. Three descend to identity: the question “which household is empty right now” can be answered from a drop in consumption, and its answer points to a single household. Three break the tie to purpose: the records were collected for measurement and billing. Two produce an attribute of a sensitive kind by inference; the set has no age or maintenance-need field, but a guess can be produced from the night consumption pattern. This third group is the quietest, because there is no collected sensitive field — the sensitive attribute is born in the analysis itself.
As the Breakdown Deepens, the Group Shrinks
As long as the rule against descending to identity remains a statement of intent it cannot be measured; to be measurable it has to be tied to a number: how many people are in a breakdown cell.
// breakdown.mjs — group size as the breakdown deepens; the same 1,400-row // subscriber record. IT IS A MODEL, the set is fictional. const SEED = 20260218, RECORD_COUNT = 1400, TARIFF = [10, 25, 40]; const mix = (x) => { x = Math.imul(x ^ (x >>> 16), 2246822507); x = Math.imul(x ^ (x >>> 13), 3266489909); return (x ^ (x >>> 16)) >>> 0; }; const rng = (t) => { let s = mix(t); return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; const pick = (u, w) => { let t = 0; for (let i = 0; i < w.length; i++) { t += w[i]; if (u < t) return i; } return w.length - 1; }; const REGION = [ { name: "north", share: 0.28, base: 21, sat: [0.05, 0.10, 0.25, 0.40, 0.20] }, { name: "south", share: 0.22, base: 17, sat: [0.02, 0.08, 0.30, 0.45, 0.15] }, { name: "east", share: 0.18, base: 26, sat: [0.20, 0.08, 0.05, 0.12, 0.55] }, { name: "west", share: 0.14, base: 14, sat: [0.10, 0.15, 0.25, 0.30, 0.20] }, { name: "central", share: 0.18, base: 23, sat: [0.06, 0.12, 0.28, 0.34, 0.20] }, ]; const RECORDS = Array.from({ length: RECORD_COUNT }, (_, i) => { const r = rng(SEED + i); const b = REGION[pick(r(), REGION.map((x) => x.share))]; const t = Math.max(1.2, b.base * Math.exp((r() + r() + r() - 1.5) * 0.62)); const consumption = Math.round(t * 100) / 100; return { region: b.name, meter_count: pick(r(), [0.62, 0.24, 0.10, 0.04]) + 1, visit_count: pick(r(), [0.38, 0.32, 0.16, 0.08, 0.04, 0.02]), satisfaction: pick(r(), b.sat) + 1, tier: consumption < TARIFF[0] ? 1 : consumption < TARIFF[1] ? 2 : consumption < TARIFF[2] ? 3 : 4 }; }); const FIELDS = [ ["region", (k) => k.region, 5], ["tier", (k) => k.tier, 4], ["meter", (k) => k.meter_count, 4], ["satisfaction", (k) => k.satisfaction, 5], ["visit", (k) => k.visit_count, 6], ]; const cells = (d) => { const m = new Map(); for (const k of RECORDS) { const key = FIELDS.slice(0, d).map(([, f]) => f(k)).join("|"); m.set(key, (m.get(key) ?? 0) + 1); } return [...m.values()]; }; const y = (h) => String(h[0]).padEnd(48) + h.slice(1).map((v) => String(v).padStart(10)).join(""); console.log(`${RECORD_COUNT} subscriber records; the breakdown deepens by one field at each step`); console.log("\n" + y(["breakdown", "possible", "occupied", "smallest", "singleton", "k<5 rows"])); for (let d = 1; d <= FIELDS.length; d++) { const h = cells(d), possible = FIELDS.slice(0, d).reduce((a, [, , n]) => a * n, 1); console.log(y([FIELDS.slice(0, d).map(([a]) => a).join(" x "), possible, h.length, Math.min(...h), h.filter((n) => n === 1).length, h.filter((n) => n < 5).reduce((a, n) => a + n, 0)])); } // Threshold decision: minimum group size k. Cells below k are suppressed. const suppress = (d, k) => { const h = cells(d); const closed = h.filter((n) => n < k); return { cells: h.length, closed: closed.length, rows: closed.reduce((a, n) => a + n, 0) }; }; console.log("\nwhen the minimum group size threshold changes (breakdown: region x tier x meter x satisfaction)"); const y2 = (h) => String(h[0]).padEnd(10) + h.slice(1).map((v) => String(v).padStart(16)).join(""); console.log(y2(["threshold", "occupied cells", "suppressed", "rows outside", "coverage"])); for (const k of [5, 10, 20]) { const o = suppress(4, k); console.log(y2([`k = ${k}`, o.cells, o.closed, o.rows, `${(100 * (RECORD_COUNT - o.rows) / RECORD_COUNT).toFixed(1)}%`])); } const a = suppress(4, 5), b = suppress(4, 10); console.log(`\nwhen k goes from 5 to 10, suppressed cells rise from ${a.closed} to ${b.closed}, ` + `rows left outside the report from ${a.rows} to ${b.rows}`); const deep = cells(5); console.log(`in the finest breakdown ${deep.filter((n) => n === 1).length} subscribers sit alone in ` + `a cell; every number in that cell is the record of one person`);
1400 subscriber records; the breakdown deepens by one field at each step breakdown possible occupied smallest singleton k<5 rows region 5 5 209 0 0 region x tier 20 16 2 0 2 region x tier x meter 80 54 1 4 49 region x tier x meter x satisfaction 400 190 1 45 245 region x tier x meter x satisfaction x visit 2400 502 1 251 694 when the minimum group size threshold changes (breakdown: region x tier x meter x satisfaction) threshold occupied cells suppressed rows outside coverage k = 5 190 117 245 82.5% k = 10 190 146 431 69.2% k = 20 190 172 770 45.0% when k goes from 5 to 10, suppressed cells rise from 117 to 146, rows left outside the report from 245 to 431 in the finest breakdown 251 subscribers sit alone in a cell; every number in that cell is the record of one person
In the single-field breakdown the smallest group is 209 records and there is no singleton cell. With the second field the smallest group falls to 2, with the third the first singleton cells appear, with the fourth 45, and with the fifth 251 subscribers sit alone in a cell. As the breakdown deepens, the number of cells grows by multiplication while the number of records stays fixed; the ratio of the two is the group size, and it falls quickly to 1.
What this means is the following: in the four-field breakdown, the line “mean consumption of subscribers in the north region, in the third tariff tier, with two meters, with a satisfaction score of 1” looks like a summary and is in fact the reading of a single person. The report carries no identity field; even so, for someone who knows those four attributes the line belongs to that person. Descending to identity does not require an identity field.
The last table sets a measurable decision against this. When is chosen, 117 of the 190 cells are suppressed, 245 records are left outside the report, and coverage falls to 82.5%; when is chosen, 146 cells are suppressed, 431 records are left outside, and coverage is 69.2%. Doubling the threshold raises the suppressed cells by 25% but raises the records left outside by 76%, because as the threshold rises, more crowded cells also fall under the limit. is not a technical setting but a decision that writes down a trade-off, and two defensible thresholds produce two different reports. If a breakdown table does not state next to it with which it was suppressed, the reader assumes the suppressed cells do not exist.
Summary
- There are two separate boundaries: four questions hit the data boundary and can be opened by adding fields, and eight questions hit the analysis boundary and are all answerable.
- The number of questions that cannot be asked also depends on a decision: it is 3 with one rule and 8 with three rules together.
- A sensitive attribute is not always a collected field; in two questions it is not in the set and is produced by inference.
- As the breakdown deepens the group shrinks: the smallest group is 209 records in one field and 1 record in four fields, and 45 subscribers sit alone in a cell.
- The minimum group size threshold is a trade-off decision: when goes from 5 to 10, the suppressed cells rise from 117 to 146 and the records left outside from 245 to 431.
Course Wrap-Up
Nine lessons did the same job: produced a number, wrote the decision, changed the decision and printed the difference.
| Lesson | Reported number | Changed decision | Difference |
|---|---|---|---|
| What Is Data Analytics | 3,849 active subscribers | the definition of active changes | 3,548, divergence on 645 records |
| Types of Analytics | 7 answerable questions | the derived field is ignored | 2 questions, 5 of them close |
| Analysis Workflow | 33 step visits | the decision is written at the step where it arises, a preview is added | 8 visits, 1 rework loop |
| Analyst Roles | -5.14% subscribers | the criterion is fee per subscriber | +1.89% |
| Data Types | mean 1.20 visits | the discrete field is read as a distribution | records equal to it 0, 68.3% below |
| Measurement Scales | east ranks first at 3.71 | the satisfaction step coding changes | east ranks fifth at 5.88 |
| Data Sources | 22.42 m³ field survey | the source switches to telemetry | 17.01 m³, 5.41 m³ gap |
| Data Quality Dimensions | 24.70 m³, 1,400 rows | the quality threshold tightens | 20.83 m³, 422 rows |
| Data Ethics and Privacy | 3 questions cannot be asked | the boundary rule widens | 8 questions, 6 of them reportable |
None of the nine rows has a calculation error. Both the left column and the right column of every row are defensible numbers, and the only thing that produces the difference between them is the middle column. This is the rule of the course, and it has been measured nine times: if a number does not state next to it the decision with which it was produced, that number is not a finding but a choice. A number whose decision is not written is counted as unmeasured.
The table itself shows a problem. All the decisions counted in the nine lessons were taken by hand: which definition was chosen, which coding was used, which source was read, which threshold was applied. None of them stands in a written place; the body of the decisions is in the text of these lessons, not in the calculation itself. The question of whether a second person taking the same set will find the same number has no answer, because what that person has is not the list of decisions but only the result. The next course, Analysis with Spreadsheets, takes the work from here: in a setup where the calculation stands in one place and changing an input recalculates all the results, it measures how much of the same decisions stays visible.
To keep your progress and take notes, Log in
My notes
Log in to take notes.