Not all tables are created equal: tidy data, keys, duplicates and tools

After this lesson you will spot the four messy shapes of a table and repair each one, explain why matching identities and finding duplicates works only to a degree of accuracy, say why schema on read makes wrangling the reader's job, and name the tools that record a wrangling job as replayable steps.

Page 1133 of the slides prints a small table, Year | Comedy-Q1 | Thriller-Q1 | Action-Q1 | …, with 2 comedy hits in Q1 2014 and none in Q1 2015. Under it, one question — "Find total comedy movies in all of 2014?" — and the slide's verdict: "Not easy in current form".

Not easy? With only the Q1 columns printed, you can read the answer off the table: 2. But the "…" says that more columns follow — Comedy-Q2, Comedy-Q3, Comedy-Q4, one per category and quarter. Now write the query that stays right as they arrive. You cannot: you would need to know today which columns the table will have tomorrow. The thing you want to filter on, the quarter, is in no cell at all. It is hiding inside the column names.

The same six numbers, laid out differently, make the question one filter and one sum. By the end of the lesson you will know the rule the wide table breaks, the move that repairs it, and why in a big-data system it is you, the reader, who has to make that move — not the database.

Before the table: what kind of value, from which source

Wrangling starts before any table exists, with two questions. What kind of value is this? And should I trust the place it comes from? The slides answer the first with a bunch of balloons (p. 1130).

one bunch, three kinds of value Categorical Color Qualitative Subjective Nice, Good, For birthday Quantitative Discrete 6 balloons Continuous Pressure 15 PSI, 139 m. over sea level
Fig. 1. The types of data of p. 1130 with the slide's own examples; the sketched balloons stand in for its photo. One object carries values of every type, and the type decides the cleaning: you can average a pressure but not a colour, and "6.5 balloons" is a glitch.

Read the bunch as five facts about one object. Its colour is categorical: a label taken from a set of possible values. "Nice, Good, For birthday" are qualitative, and the slide adds "Subjective": an opinion that depends on who gives it. "6 balloons", "Pressure 15 PSI" and "139 m. over sea level" are quantitative. The first is discrete, a count with gaps between its values; the other two are continuous, measurements on a scale limited only by the instrument.

Why sort values at all? Because the type decides which cleaning makes sense. You can average a pressure, not a colour. "6.5 balloons" is a glitch, "15.3 PSI" is not. "Nice" needs text handling, not statistics. And only a quantitative value can sit outside the whiskers of a boxplot, as an outlier (lesson 36).

"Categorical and qualitative are the same thing." In many statistics courses they are: both meant non-numeric variables, nominal or ordinal. The slide separates them — categorical for labels such as a colour, qualitative for subjective judgements such as "Nice". At the exam, reproduce the slide's three groups with its examples.

The second question gets twelve words on p. 1131, the data source selection criteria. The slide prints them in two columns without comment. The table groups them by the question each one asks; the grouping and the questions are ours, the words are the slide's.

The twelve data source selection criteria of p. 1131, grouped.
GroupCriterionThe question it asks
The contentCredibilityIs the source trustworthy?
CompletenessDoes it cover everything you need?
AccuratenessAre its values right?
VerifiabilityCan you check them against something else?
CurrencyIs it up to date?
Getting itAccessibilityCan you get at the data: an API, a file, a format you can read?
CostWhat does it cost?
StorageHow much must you keep, and in what form?
Rules and trustComplianceDoes using it respect the regulations, privacy law first?
Legal issuesDo its terms allow your use — scraping, redistribution?
SecurityCan it be moved and stored safely?
ProvenanceWhere does it come from, through which transformations?

Suppose you need hit counts per film category and quarter, the data of the next section. A chart scraped from a fan site is accessible, free and current, but its credibility and verifiability are low and its legal status is unclear (scraping is lesson 34). An industry feed is credible and verifiable, but it costs money. No source wins on all twelve. The list makes the trade-off explicit before you spend a week cleaning the wrong data.

Currency means up-to-dateness, not money — money is Cost. A closed question can hide exactly that trap.

Observations and variables: the shape of a table

Page 1132 shows the same six numbers in two tables: the student counts of two schools in three years.

year hides in the headers School 2012 2013 2014 Good Samaritans 2321 4550 1293 Percy Grammar 1540 1400 2949 one row, three observations

Before: Column and Row

one column, one variable School Year Student Count Good Samaritans 2012 2321 Good Samaritans 2013 4550 Good Samaritans 2014 1293 Percy Grammar 2012 1540 Percy Grammar 2013 1400 Percy Grammar 2014 2949 one row, one observation

After: Variable and Observation

Fig. 2. The two tables of p. 1132. Before: the variable year has no column — its values are headers (highlighted), so each row holds three observations. After: one column per variable (violet header, the Year column outlined), one row per observation (blue: Good Samaritans in 2013, 4550 students).

The slide labels the first table with the words of a spreadsheet, Column and Row, and the second with the words of the data, Variable and Observation. That difference is the whole lesson.

Variable — a property measured on every unit: school, year, student count. Observation — all the values measured on one unit: one school in one year. Rows and columns are where values sit; observations and variables are what they mean.

In the wide table the variable year has no column. Its values 2012, 2013 and 2014 are column names, and each row carries three observations. In the long table each variable is one column and each row one observation. Same information, different shape — and the shape decides which questions are easy.

Page 1133 makes it worse. Its header Comedy-Q1 glues two values into one name, a category and a quarter, and the "…" promises Comedy-Q2, Thriller-Q2 and more. Here is the hook's question against the wide table and against the long table of the same slide, Category | Quarter | Year | #Hits:

-- wide table: returns 2
-- (the one comedy column of Q1)
SELECT "Comedy-Q1"
FROM hits
WHERE Year = 2014;
-- long table: returns 2
-- (one filter, one sum)
SELECT SUM("#Hits")
FROM hits
WHERE Category = 'Comedy'
  AND Year = 2014;

Both return 2 today. Only the second is still right when Q2 arrives. The first must be rewritten for every new quarter, because what it filters on lives in the column names. The slide's second note, "Very easy to add a new column", says the same from the other side: a new measure — say the number of movies released, which its "% of hit comedy movies" question would need — is one more column in the long table, but a whole block of new columns in the wide one.

The repair has a name. Melt (also called pivot longer) turns the headers that are values into a column, one row per old cell. Its inverse, pivot (pivot wider), spreads the values of a column into headers. In pandas, which p. 1143 lists among the wrangler's tools, the melt and the split of the glued header are two lines; R's tidyr (p. 1149) calls the moves pivot_longer and pivot_wider.

# wide -> long: 6 rows,
# one per (Year, old header)
long = wide.melt(
    id_vars="Year",
    var_name="Header",
    value_name="#Hits")
# split the glued header in two
long[["Category", "Quarter"]] = (
    long["Header"]
    .str.split("-", expand=True))

Page 1134 holds the messiest case, "Variables in both rows and columns". The quarters run across the headers (Q1, Q2, Q3, …) and the rating runs down the rows: an Excellent row and a Good row for each category. One observation, a category in a quarter, is smeared over two rows and several columns. The slide's repair melts the quarters into a Quarter column and lifts the two ratings into two count columns, so that "Each row is complete observation".

Tidy it: melt, split, pivot — and watch the query

The two tables of pp. 1133–1134. Step through the repair and follow the highlighted cells: values sitting in headers, which move down into a column. Under the table, a fixed question and the query that answers it on the current shape. Then let new data arrive — dashed cells are made up, not on the slide — and see which query has to be rewritten.

  1. Hits table at step 1, as printed, and choose "a new quarter". Before you look at the query box: does the query written for Q1 still return the total comedy movies of 2014?
    What you should see

    No. It reads only the column Comedy-Q1 and still returns 2, while the made-up Comedy-Q2 adds 1: the right total is 3, and the query must name the new column — again for every quarter. Go to step 3: the same question is one filter and one sum, and it returns 3 with no change. A query has to be rewritten exactly when new data adds a column.

  2. Keep the new quarter and go to step 2, melted. The query works now — so what is still wrong with this table?
    What you should see

    The Header column holds two values glued together, such as "Comedy-Q2". The query reads the category out of a string with LIKE 'Comedy-%', and a question by quarter would need the same string surgery. Step 3 splits it: each variable in its own column.

  3. Hits table at step 1 again, and choose "a new year 2016" instead. Predict: does the wide table break this time?
    What you should see

    No. Year is a real column of the wide table, so 2016 arrives as a new row (3 rows instead of 2), the answer stays 2 and the query stays right. Only the variables that live in the headers — category and quarter — turn new data into new columns.

  4. Ratings table at step 3, the slide's own repair, and choose "rating Average". Which step needs a rewritten query now: 1, 2 or 3?
    What you should see

    Step 3. Excellent and Good are values of the rating used as column names, so Average arrives as a new column and SUM(Excellent + Good) misses it: it returns Q1 4 · Q2 1 · Q3 7 instead of Q1 6 · Q2 2 · Q3 9. At step 1 the ratings are rows and at step 2 they are cells, so both queries stay right. The slide's shape is a choice made for convenience — the one the next box explains.

Excellent and Good are values. Why may they be column names after the repair?

Because what counts as one observation is a choice. For the slide, one observation is a category in a quarter, measured twice — hits rated excellent and hits rated good. Then Excellent and Good are two variables, and the table is tidy. If one observation is a category in a quarter with one rating, the fully long table Category | Quarter | Rating | #Hits is the tidy one. The deck allows the choice in its lessons learnt: "Shape may depend on convenience of queries" (p. 1140). The price is the one the widget shows: a third rating level means a new column, and every query that names the old ones must change.

A value never belongs in a header: when it sits there, new data means new columns, and every query that names them must be rewritten.

One kind of thing per table

The two remaining messy shapes are about observational units — the kind of thing one row describes. Page 1135 bills two customers. Its first table mixes two kinds of thing, invoices and invoice lines, so every fact about an invoice is repeated on each of its lines.

The flat table of p. 1135. Highlighted: the invoice facts that a second line repeats.
InvoiceBill ToSales %Total($)SKU#ItemQtyUnit Price ($)
1Jim Jones88.03A123Hammer13.55
1Jim Jones88.03Q34Screw Driver22.05
2Mike Z’Kale897.20W23Hair Dryer159.25
2Mike Z’Kale897.20E452Cologne310.25

The slide's verdict is "Normalize to avoid duplication": one table per kind of observation, linked by the key Invoice. It is the normalization of lesson 9, met from the data side.

The same data normalized, as on p. 1135: each invoice fact is stored once.
invoices
InvoiceBill ToSales %Total($)
1Jim Jones88.03
2Mike Z’Kale897.20
invoice lines
InvoiceSKU#ItemQtyUnit Price ($)
1A123Hammer13.55
1Q34Screw Driver22.05
2W23Hair Dryer159.25
2E452Cologne310.25

Why bother, if no number is lost? Because a fact stored twice can disagree with itself. An update that changes one copy — Jim Jones moves, the rate changes — leaves the other behind; lesson 9 calls it an update anomaly. Before reading on, check invoice 1 on the slide: two lines, 1 × 3.55 and 2 × 2.05, a Sales % of 8 and a Total($) of 8.03. Does the total agree with its lines?

Check yourself

No. The lines add up to 1 × 3.55 + 2 × 2.05 = 7.65, and 8 % on top gives 7.65 × 1.08 = 8.26. Invoice 2 follows the rule to the cent: (59.25 + 3 × 10.25) × 1.08 = 90.00 × 1.08 = 97.20. The printed 8.03 is 7.65 × 1.05, a 5 % rate.

The last shape is the opposite case: one kind of observation scattered over many tables, typically one file per period. Page 1136 labels a stack of tables "Multiple Tables Divided by Time" and asks to "Combine all tables accommodating varying formats". Two things make that union work. The formats must be aligned on the way — column order, column names, separators. And the period must become a column: in the union the file names disappear, so a value that lived only in a name ("Q2 2014") must first become a cell.

Q1 2014 combine Category Quarter Year #Hits ComedyQ120142 ThrillerQ120141 ActionQ120140 ComedyQ120142 ThrillerQ120141 ActionQ120140 every row twice

As drawn on p. 1136

Q1 2014 Q2 2014 union Category Quarter Year #Hits ComedyQ120142 ThrillerQ120141 ActionQ120140 ComedyQ220141 ThrillerQ220140 ActionQ220142 the period is now a column

A real union of tables divided by time

Fig. 3. Left: the picture of p. 1136 — a stack of tables that all hold Q1 2014, combined into a table that lists every row twice (red). Right: what the union should be — two periods (the Q2 numbers are made up), the period moved out of the file name into the Quarter column (highlighted), each observation once.

Same thing, different records: keys and duplicates

Combining sources raises a harder question than shape: which rows describe the same thing? Page 1137 puts it in four lines — "Keys are crucial in DB", "Many DBs --> Many keys", "How to align?", "Identification to certain degree of accuracy likely-identities" — with the example "same user match" over eight social-network logos.

Facebook id 100042731 Twitter @chiararossi_mi Instagram chiara.r.89 Chiara Rossi likely the same person

The key problem: many DBs, many keys

id author post p1ChiaraMarket opens Sat! p2MatteoTomatoes are in. p3ChiaraMarket opens Sat! #milano p4FabianaFresh honey today. likely duplicates

The duplicates problem: one DB, two records

Fig. 4. The two problems of pp. 1137–1138. Left: three networks, three keys, one person — no key is shared, so the records can only be judged likely to be the same user (orange dashes). Right: one table, the same post twice with a small difference — a likely duplicate that DISTINCT would not catch.

Inside one database a key identifies a record exactly. Across databases there is no shared key: the same person is a numeric id on one network, a handle on another, an account name on a third. This is the key (matching) problem. With no key in common, records are compared on what they say — name, address, age, behaviour — and a pair similar enough becomes a likely identity. It is a decision that can be wrong, not a lookup. You met its rule-based form as the Matching step of cleansing, with its table of match patterns (p. 1094, lesson 35).

The duplicates problem of p. 1138 is the same question inside one data set: "Related to Key problem", again "Identification to certain degree of accuracy", now of "likely-duplicates", with the example "duplicate posts". A duplicate is a record that should share a key with another one and does not: the same post on four networks, the same customer registered twice with a typo. SELECT DISTINCT removes only identical copies; one letter of difference and it sees two different rows.

Likely duplicates: one threshold, two kinds of mistake

Two farmers' markets merge their lists of buyers — the People of the exam of 12 September 2025 (name, surname, age, address). A-xx come from market A, B-xx from market B, and their IDs do not match. The widget imitates OpenRefine's "Cluster and edit": it compares one field after a fingerprint (lower case, punctuation removed, words sorted) and proposes as one cluster every group linked by pairs at or above the threshold.

  1. Compare on name and surname. At 1.00 three clusters appear. Chiara Rosi (A-06) is a typo for Chiara Rossi: her closest similarity is 0.917. Predict what happens as you lower the threshold towards 0.91 — and in which order.
    What you should see

    At 0.93 Fabiana Lorenzzi joins her twin record (0.938) — correct. At 0.92 Claudia Trenti (B-03) joins Claudio's cluster (0.929): a false merge, two siblings one letter apart. Only at 0.91 does Chiara Rosi join. With names alone no threshold catches the typo without merging the sister: similarity cannot tell a slip of the finger from a different person.

  2. Switch to address, threshold 1.00. Which cluster is wrong whatever the threshold, and which person can an address never find?
    What you should see

    Claudio and Claudia Trenti live at Via Dante 5, so they are merged even at 1.00. Federico Bianchi moved from Via Manzoni to Piazza Duomo: his two records never meet. Chiara's typo is harmless here — same address. "Milan" against "Milano" keeps B-01 out until you reach 0.94 (similarity 0.944), and at 0.61 Antonio Ferrari of Via Verdi 7, Milano is pulled into Chiara Rossi's cluster (similarity 0.611 with Via Roma 12, Milano): a second false merge.

  3. Choose "name and surname, same age". Find the thresholds with no false merge and as few missed duplicates as possible. Who is left over, and why?
    What you should see

    From 0.91 down to 0.60: no false merge — Claudia is 31, Claudio 34 — and one miss, Federico Bianchi, whom market A recorded at 45 and market B a year later at 46. Every rule errs somewhere; you choose where. That is "identification to certain degree of accuracy", and it is why a person confirms each cluster in a real tool. (A birth date instead of an age would have found Federico.)

The widget imitates a real menu: "Cluster and edit…", visible under "Edit cells" in the OpenRefine screenshot of p. 1147. OpenRefine groups values that collide on a fingerprint or lie within an edit distance, and a person accepts or rejects each proposed merge (background from the OpenRefine documentation, not the slides). The widget's own merging rule is the single linkage of hierarchical clustering: two records closer than the threshold share a cluster, and closeness chains — A near B and B near C put all three together.

Why does the slide call matching and duplicates "crucial in data science"? Because they decide what you count. A missed match counts one customer twice; a false merge sends one person's purchases to another. In machine-learning terms: the same image left in both the training and the test split inflates the test accuracy.

Without a shared key, identity is a likelihood: every matching rule trades false merges against missed duplicates.

Pages 1140–1141, "Lessons Learnt on Tables", sum up everything so far in six lines. Learn them as printed:

The six lessons learnt of pp. 1140–1141, verbatim, with the shape each one rules out.
As printedWhat it meansSeen in this lesson
"(Multiple) variables in columns"every variable gets a column; several variables, several columnsSchool | Year | Student Count
"Never values as columns!"a header names a variable, never one of its values2012 | 2013 | 2014; Comedy-Q1; Q1 | Q2 | Q3
"Shape may depend on convenience of queries"what one observation is can be chosen for the queries you runthe Excellent and Good columns of p. 1134
"Matching identities and duplications are crucial in data science!"aligning sources and removing duplicates decide what you countthe same user on many networks; duplicate posts
"Each observation is complete and atomic"one row holds one whole observation, and nothing about anotherthe invoice facts repeated on its lines
"Each variable belongs to (only!) one column!"a variable is neither spread over several columns nor glued to anotherQ1, Q2, Q3 (one variable, many columns); Comedy-Q1 (two variables, one column)

Why the reader has to wrangle: schema on read

Why does a course about databases spend a lecture on cleaning tables? Page 1142 gives the reason in three lines: "Traditional DBMSs enforced writing only data consistent with a pre-designed schema"; "Today data modeling at design time is a luxury"; "In schema on read, data is applied to a plan or schema as it is pulled out of a stored location, rather than as it goes in".

incoming records refused schema check clean table reader

Schema on write: checked at the door

incoming records raw store schema + wrangling reader

Schema on read: every reader cleans

Fig. 5. Where the cleaning happens. Left: the schema stands at the door (violet), the record that does not fit (dashed) is refused, and what is stored is already shaped. Right: every record is stored as it comes, and the schema — together with the wrangling of this lesson — is applied when the data is pulled out, by each reader.

With schema on write the database is a gatekeeper: a row that does not fit the declared table is refused, so whatever is stored has already been shaped and typed — by whoever loaded it. The cost is design time: someone modelled the data before it arrived. That is the luxury. With many sources that change faster than anyone can agree on a schema, nobody gets that time.

With schema on read everything is stored as it comes, and a schema is applied only when the data is read. No designed schema stood in the way of the wide comedy table, the invoice with its repeated totals or the buyer registered twice: they were stored as they came. So every reader inherits the work of this lesson — tidy the shape, align the keys, merge the duplicates — and of lesson 36: fill the holes, question the outliers. Wrangling itself is not new: lesson 35 met it as "(good old) ETL", done once, before the load. Schema on read moves it to the moment of reading, and to every reader. Lesson 4 shows the same shift as two pipelines (pp. 169–170).

"Schema on read means there is no schema." The schema moves from the database to the reader; it does not disappear. The slide's own sentence names it: "a plan or schema", applied as the data is pulled out. Lesson 4 asks the same of NoSQL documents: "Schema-less or implicit schema?" (p. 168).

The wrangler's toolbox

Pages 1143–1151 close the lecture with tools. Know them by name; more importantly, notice the one idea they share.

The open-source stacks of p. 1143 are R with RStudio; IPython with Python and pandas; OpenRefine; and the Data Science Toolkit. Page 1144 adds helpers for getting data out of awkward containers — BeautifulSoup for HTML, Tesseract and OCRFeeder for text in images, textract for text in any document — and two training communities, School of Data and Open Knowledge Labs. The commercial vendors of p. 1145 are Paxata, Platfora, Tamr, Datameer, Alteryx, Trifacta, Dataiku and Datawatch.

The Trifacta Wrangler screenshot of p. 1146 shows what such a tool adds to a spreadsheet. The data set raw-top-50-pop (50 rows, 5 columns, 3 data types) began as one block of text. Above every column the tool shows its type — # for rank, ABC for the strings, a clock for year — a histogram of its values and a summary: "1 - 50", "48 Categories", "50 Categories", "35 Categories", "2000 - 2009". The rough checks of exploration come for free. A dark mark at the end of the company column's bar flags a missing value (the tool's convention), and indeed row 14, Iron & Wine, has no company. The right pane is the real point: every operation that split the block into columns is a step of a script — splitrows, replace, delete row, split, extract, merge, drop, set, rename.

OpenRefine shows the same idea as an Undo / Redo history (p. 1147). Read it as a complete small job: scrape a web page, parse it, clean it, keep the result.

The Undo / Redo history on p. 1147, shortened where the slide's expressions run long.
StepAs printed in the history → what it does
0"Create project" → a project with one row: a URL
1"Create column content at index 1 by fetching URLs based on column Column 1 …" → downloads the page: ingestion by scraping
2"Create new column tables … parseHtml().select("table") …" → parses the HTML and keeps the rows of three tables
3"Split multi-valued cells in column tables" → one table row per cell
4"Create new column all-columns … select("th") … select("td")" → extracts the header and data cells
5"Text transform on 23 cells in column all-columns: grel:value.replace(/\<.*?\>/,"")" → strips the HTML tags
6"Text transform on 6 cells in column all-columns: value.unescape('html')" → turns entities such as &amp; back into characters
7"Split 23 cell(s) in column all-columns into several columns by separator" → one column per field
8–10"Remove column Column 1", "Remove column content", "Remove column tables" → drops the scaffolding
11"Rename column all-columns 1 to time-slot" → names the result

A recorded step can be undone, read by a colleague and replayed on next month's file; the "Extract…" and "Apply…" buttons above the history export it and apply it to another project. Recording is what turns munging — the "vague", "potentially lossy" transformations of p. 1102 (lesson 36) — into a documented, repeatable job.

OpenRefine was released as Freebase Gridworks (Metaweb, January 2010), became Google Refine after Google bought Metaweb, and has been the community-run OpenRefine since Google stopped supporting it in October 2012 — so the slide's title "Google's Open Refine" mixes two eras. Trifacta was founded in October 2012 by three of the four authors of the Stanford Wrangler research prototype of p. 1151 — Kandel, Hellerstein and Heer — and Alteryx bought it in February 2022. Sources: the Wikipedia articles on OpenRefine and Trifacta.

Page 1148 turns the whole lecture into a checklist of five phases, and p. 1149 names the R libraries for it. The table puts them together; the slides' words are verbatim, the mapping of libraries to phases and the Python counterparts are ours.

"Hands on Data Wrangling" (p. 1148) with the R libraries of p. 1149.
PhaseItems on the slideLibraries
Data IngestionCSV, PDF, API/JSON, HTML Web Scraping, XLS, Access, …!R: readxl, xlsx, rvest
Python: pandas readers, requests, BeautifulSoup
Data ExplorationVisual inspection, GraphingR: none of the eight
Python: pandas describe, plots
Data ShapingTidying DataR: tidyr, dplyr, plyr
Python: pandas melt, pivot
Data CleansingMissing values, Format, Measurement Units, Outliers, Data Errors Per Domain, Fat Fingered DataR: stringr, lubridate, gtools
Python: pandas fillna, string and date methods
Data AugmentingAggregate data sources, Fuzzy/Exact matchR: dplyr (joins)
Python: pandas merge, fuzzy matching

The slide lists shaping before cleansing, and the order makes sense. Cleaning rules are written per variable — a range for a pressure, a format for a date — and a variable must be a column before a rule can be attached to it. And matching sits under augmenting: you match records in order to join sources.

The references of pp. 1150–1151 are a data-cleaning guide hosted by GBIF, a Webopedia entry, Witten and Frank's Data Mining, and the book of Dasu and Johnson. That book is titled Exploratory Data Mining and Data Cleaning (Wiley, 2003), not "Exploratory Data Mining and Data Quality … (Wiley, 2004)" as the slide prints. The tool links point to Stanford Wrangler — whose project page now lives at idl.uw.edu, since the slide's vis.stanford.edu address fails — to openrefine.org, okfnlabs.org and schoolofdata.org.

The hook, settled. The comedy query could not be finished because the quarter lived in the column names: the wide table breaks "Never values as columns!". One melt and one split turn it into Category | Quarter | Year | #Hits, where the query is one filter and one sum and survives every new quarter. And the one who melts is you. Under schema on read nothing stopped the wide table from being stored, so its reader must tidy it — best as a recorded step that runs again on next quarter's file.

Exam-style questions

Theory questions in the form of the written exam — short open answers, multiple-choice and true/false questions; wrong answers may be penalised, as announced just before the exam — and one exercise on the scenario of a past paper. The practical part covers Neo4j, MongoDB, Elasticsearch, Redis and SparkQL, and none of the nine past papers has a wrangling exercise: expect this material in the theory part.

Explain the difference between the rows and columns of a table and its observations and variables. Then explain, with an example, the lessons learnt "Never values as columns!" and "Each variable belongs to (only!) one column!".

Show model answer

Rows and columns are the physical layout of a table; observations and variables are what its values mean. A variable is a property measured on every unit (school, year, student count); an observation is the set of values measured on one unit (one school in one year). A table is well shaped when each variable is one column and each observation one row. "Never values as columns!" forbids headers that are values of a variable: in School | 2012 | 2013 | 2014 the years are values of the variable year, so one row holds three observations, and a query cannot filter or group by year without listing columns by hand. Melting the headers gives School | Year | Student Count, one row per school and year. "Each variable belongs to (only!) one column!" forbids spreading one variable over several columns (Q1 | Q2 | Q3) and gluing two variables into one: Comedy-Q1 holds a category and a quarter, and must be split into a Category and a Quarter column.

A good answer has two parts: the physical-versus-semantic distinction, and one concrete repair for each lesson. Quote the two lessons exactly as printed (pp. 1140–1141); a closed question may reuse that wording.

Consider the ER diagram of the exam of 21 January 2025 ("tracking Netflix's digital content"): Digital Content (ID, title, duration, description, language, original), with the children Film, TV Series and Cartoon, ISA (T, E). An analyst exports the number of hits of each kind of content per quarter as the following table.

YearFilm-Q1TV Series-Q1Cartoon-Q1Film-Q2TV Series-Q2Cartoon-Q2
20241273954
202515461182

(a) Explain why the total number of film hits in 2024 is hard to compute on this table. (b) Restructure the table following the lessons learnt on tables. (c) Write the query that computes the total number of film hits in 2024 on the new table, and give its result.

Show model answer

(a) The headers are values: each one glues a kind of content and a quarter (Film-Q1). The query has to list the film columns by hand (Film-Q1 + Film-Q2) and must be rewritten whenever a quarter is added; neither the quarter nor the kind of content can be filtered or grouped, because they are not in any cell.

(b) One column per variable and one row per observation — one kind of content in one quarter of one year:

Content_TypeQuarterYearHits
FilmQ1202412
TV SeriesQ120247
CartoonQ120243
FilmQ220249
TV SeriesQ220245
CartoonQ220244
… and the six rows of 2025, 12 rows in all

(c) The query, which returns 12 + 9 = 21 and does not change when Q3 or a new kind of content arrives:

SELECT SUM(Hits)
FROM content_hits
WHERE Content_Type = 'Film'
  AND Year = 2024;

In a SparkQL exercise, where SQL syntax is not allowed, the same filter and sum is written with the PySpark DataFrame API (after from pyspark.sql import functions as F):

# one row: Total_Hits = 21
(content_hits
    .filter((F.col("Content_Type") == "Film")
            & (F.col("Year") == 2024))
    .agg(F.sum("Hits").alias("Total_Hits"))
    .show())

The scenario is the paper's; the table and its numbers are ours — that paper's SparkQL exercise works on Director and Film tables instead. The DataFrame form is the one a SparkQL exercise demands: the exam of 30 Jan 2026 prints "Note that you are not allowed to use SQL syntax. Use PySpark instead." (lesson 39). A complete answer has all three parts: the reason (values in the headers), the tidy header with an example row, and a query that names no quarter.

Which of the following is NOT one of the data source selection criteria listed in the lecture?

  • Provenance Listed (p. 1131): where the data comes from and through which transformations it went.
  • Currency Listed: it means up-to-dateness — not money, which is Cost.
  • Popularity Not in the list: a popular source can still be inaccurate, outdated or illegal to use.
  • Verifiability Listed: can the values be checked against something else?
Show model answer

Popularity. The twelve criteria are Credibility, Completeness, Accurateness, Verifiability, Currency, Accessibility, Compliance, Cost, Legal issues, Security, Storage and Provenance.

Wrong answers may be penalised, so guessing among intruders is costly. Learn the twelve words in the three groups of the table in section 1: the content, getting it, rules and trust.

True or false: with schema on read, a record that does not match the schema is rejected when it is written to the store.

  • True That is schema on write: traditional DBMSs "enforced writing only data consistent with a pre-designed schema".
  • False With schema on read the data is stored as it comes; the schema is applied "as it is pulled out of a stored location, rather than as it goes in".
Show model answer

False. Rejection at write time is schema on write. With schema on read every record is stored, and a plan or schema is applied when the data is read; the reader must then deal with the records that do not fit, so the wrangling moves from the load to the read. The lecture's reason for the shift: "Today data modeling at design time is a luxury".

The three lines of p. 1142 are short enough to learn word for word; an open variant of this question would ask for them plus the "why".

Describe the "Key" (matching) problem and the "Duplicates" problem presented in the lecture. Why can they be solved only "to certain degree of accuracy"? Give an example of each.

Show model answer

Inside one database a key identifies each record exactly. When data comes from many databases, each has its own keys — a Facebook id, a Twitter handle — and nothing says which records denote the same real entity: aligning them is the key (matching) problem, for example matching the same user across social networks. The duplicates problem is the same question inside one data set: which records are the same observation recorded twice, for example the same post published on several networks, or a customer registered twice with a typo. Because no shared key exists, records are compared on their values (name, address, age) with a similarity measure, and a pair above a chosen threshold is declared a likely identity or a likely duplicate. The decision can be wrong in two ways: a low threshold merges different entities (false matches), a high one misses true duplicates. Exact-duplicate removal such as DISTINCT finds only identical copies.

A good answer separates aligning records of different sources (key problem) from finding repeated records in one data set (duplicates problem), says why no exact solution exists — no shared key, comparison by similarity against a threshold — and names the two kinds of error.

Which statements about the data wrangling tools and the "Hands on Data Wrangling" checklist of the lecture are correct? Select all that apply.

  • Trifacta Wrangler profiles every column (data type, distribution of values) and records each transformation as a step of a script. p. 1146: histograms and summaries above the columns, the script in the right pane.
  • Fuzzy/Exact match is listed under Data Cleansing. It is under Data Augmenting, next to "Aggregate data sources": you match records in order to combine sources.
  • OpenRefine keeps an undo/redo history of the operations applied to a project. p. 1147 shows a twelve-step Undo / Redo history, from "Create project" to "Rename column".
  • stringr, dplyr, tidyr and lubridate are Python libraries for data wrangling. They are R libraries (p. 1149, "R Libraries for Data Wrangling"); in Python, pandas plays their role.
  • Tidying Data belongs to the Data Shaping phase. p. 1148: Data Shaping — Tidying Data.
Show model answer

The first, third and fifth statements. The five phases of the checklist are Data Ingestion, Data Exploration, Data Shaping, Data Cleansing and Data Augmenting.

The trap is the place of matching: it serves augmenting (joining sources), not cleansing. With possible penalties, tick only what you can place on a slide.

Check yourself

In the tidy-it widget, the hits table is at step 1, as printed, and a new year, 2016, arrives. Why does the wide query not break?

  • The widget leaves made-up cells out of the answer They are added and counted: the dashed row is part of the table.
  • Year has its own column, so 2016 arrives as a new row and the filter WHERE Year = 2014 is untouched Only variables that sit in the headers turn new data into new columns.
  • The 2016 row has no Comedy-Q1 value It has one (1); the query simply filters on 2014.

Which of these table layouts follow the lessons learnt on tables? Select all that apply.

  • Patient | Visit_1 | Visit_2 | Visit_3 The visit number is a value used as a column name: one row holds three observations.
  • Patient | Visit | Date One row per visit, each variable in one column.
  • City | Population_2020 | Population_2021 The year sits in the headers: next year's census adds a column.
  • Station | Date | Temp_Min | Temp_Max Minimum and maximum are two variables measured on one observation, a station on a day — like Excellent and Good for one category in one quarter.
  • Order_ID | Customer_Name | Customer_Address | Product_ID | Qty Two observational units in one table: the customer facts repeat on every order line, as in the invoice of p. 1135.

Among the data source selection criteria, "Currency" asks…

  • how much the data costs That is Cost, a separate criterion.
  • in which money unit the prices are stored That is a format question for cleansing, not a reason to choose a source.
  • whether the data is up to date Currency is up-to-dateness: last year's prices may be accurate and still useless.
  • whether you may legally use the data That is Legal issues, with Compliance.

Invoice 1 of p. 1135 stores its total on both of its lines. Which repair removes the duplicated copies of that total?

  • Normalize: an invoice table that holds the total once, and a line table linked to it by Invoice One table per kind of observation: each invoice fact is stored once.
  • Melt the Total($) column Total($) is a variable, not a value sitting in a header; there is nothing to melt.
  • Run SELECT DISTINCT on the table The two lines differ in SKU#, item, quantity and price: no row is an exact copy, so DISTINCT removes nothing.
  • Pivot the items into columns That would put values, the item names, into headers.

Even normalized, a stored total can disagree with the lines it is computed from — the printed 8.03 does. Computing it from the lines removes that last copy.

In the duplicates widget, comparing on name and surname, Claudia Trenti joins Claudio's cluster at 0.92, but Chiara Rosi joins Chiara Rossi only at 0.91. What does this show?

  • The similarity of the widget is miscomputed It is right: one edit in 14 characters (0.929) is more similar than one edit in 12 (0.917).
  • An edit distance cannot tell a typo from a different person: both pairs differ by one letter The string alone carries no meaning; another field, such as age, has to separate them.
  • Lowering the threshold always improves the result Lowering it catches more typos and makes more false merges.
  • Claudia is really a duplicate of Claudio Different people, different ages: the merge is false.

Why does schema on read make data wrangling the reader's job?

  • Because the database cleans the data while writing it No database cleans on its own. Under schema on write it refuses a row that does not fit, so the loader shapes the data before the write.
  • Because the schema is deleted after the write The schema is not deleted: it is applied later, when the data is read.
  • Because nothing checks the data when it is stored, so whoever reads it must shape and clean it "Data modeling at design time is a luxury": the schema, and the wrangling with it, move from the load to the reader.

You join the buyers of a market to a public registry of farms by approximate name matching. In the checklist of p. 1148 this step belongs to…

  • Data Exploration Exploration looks at data — visual inspection, graphing — without changing it.
  • Data Shaping Shaping tidies the form of one table.
  • Data Cleansing Cleansing repairs values within a source: missing values, formats, units, outliers, typos.
  • Data Augmenting "Aggregate data sources" and "Fuzzy/Exact match": you match records in order to add a source.

Key take-aways

What to watch and read