STAT163 — Data Manipulation Essentials
2026-09-01
Open floor
Garbage in, garbage out: everything above the line is only as good as everything below it.
Look at any dataset and see its potential: what you can get out of it, what you cannot, and which techniques help you get from the table you have to the answer you need.
| By November you can | Which means |
|---|---|
| Open an unfamiliar dataset and say what it holds | Shape, grain, types, what is missing, which values cannot be real |
| Reshape a table to answer a question | Filter, derive, group, pivot — and know which one you need |
| Combine tables without corrupting the result | Choose the key, and check the row count before and after |
| Handle missing values on purpose | Decide what to do with them and record why |
| Hand your work to someone else | They re-run it and get your number back |
| Component | Points |
|---|---|
| Practices (7 × 3) | 21 |
| Pre-reading quizzes (5 × 2) | 10 |
| Assignments (3) | 24 |
| Mid-term | 20 |
| Final | 35 |
| Total | 110 |
Practices and assignments
AI allowed, use it with disclosure
Quizzes, mid-term, final
No AI. Closed book
Use AI to learn, prove you learned without it.
Say what you used One line in the notebook — the tool and the step. "Used Claude to debug the groupby in Task 3.2."
You own what you submit An AI error is your error. "The model wrote it" is not a defence.
Expect to explain it aloud If you cannot say why your code does what it does, the exams will show it.
Slides and quiz questions Drafted with Claude. I review, edit and iterate on every one.
Practices and assignments Checked semi-automatically against an answer key. I review the output.
Exams Read and graded by me, start to finish. No automation.
After any assignment, I (or TA) may pick a random submission and ask the submitter to talk me through their work for five to ten minutes: why this join, why this grain, why this number. The talk confirms or adjusts that assignment's mark.
The dataframe is the key unit we work with, in this course and beyond it. R, SQL and spreadsheets hold data shaped the same way, so everything you learn on it carries over.
Whatever other data structures we'll encounter during this course, we will transform the data to be in tabular format.
A column pulled from a DataFrame is a Series. Inside a Series, the values usually sit in a NumPy array. A DataFrame is many Series sharing one index.
The rest of the lecture asks two questions of any table: what does one column hold, and what does one row mean?
deliveries — as recorded
| parcel | destination | weight | rating | delivered |
|---|---|---|---|---|
| P-1041 | Lviv | 2.4 | 5 | 2026-03-02 |
| P-1042 | Kyiv | 1800 g | great | yesterday |
| P-1043 | Odesa | heavy | ★★★★ | 2026-03-04 |
| P-1044 | Kyiv | 0.8 | 4 | last week |
| P-1045 | Lviv | 3.1 | good | 2026-03-05 |
| P-1046 | Dnipro | 12 | 2 | 2026-03-06 |
Try it
| Question | Why it fails |
|---|---|
| Total weight | 2.4, 1800 g and heavy are three different things in one column |
| Best average rating | Stars, words and numbers do not add |
| Parcels in week one | yesterday has no position on a calendar |
As long as a column mixes kinds of value, almost nothing can be computed from it.
deliveries — after cleaning
| parcel | destination | weight_kg | rating | delivered |
|---|---|---|---|---|
| P-1041 | Lviv | 2.4 | 5 | 2026-03-02 |
| P-1042 | Kyiv | 1.8 | 5 | 2026-03-03 |
| P-1043 | Odesa | 11.0 | 4 | 2026-03-04 |
| P-1044 | Kyiv | 0.8 | 4 | 2026-03-01 |
| P-1045 | Lviv | 3.1 | 4 | 2026-03-05 |
| P-1046 | Dnipro | 12.0 | 2 | 2026-03-06 |
heavy became 11.0 because someone decided what it meant. The table no longer shows that decision, which is why you write it down.
The kind is not the storage type. A column stored as a number can be any of the four.
Check yourself
79000. Which kind is it?The operations team wants to know
daily — 7 rows
| day | parcels | total_kg |
|---|---|---|
| Mon | 180 | 402 |
| Tue | 165 | 371 |
| Wed | 172 | 388 |
| Thu | 190 | 447 |
| Fri | 240 | 566 |
| Sat | 262 | 611 |
| Sun | 155 | 344 |
The operations team wants to know
deliveries — Saturday only, 262 rows
| parcel | destination | weight_kg | courier |
|---|---|---|---|
| P-1041 | Lviv | 2.4 | C-07 |
| P-1042 | Kyiv | 1.8 | C-07 |
| P-1043 | Odesa | 11.0 | C-12 |
| P-1044 | Kyiv | 0.8 | C-03 |
| … | … | … | … |
The operations team wants to know
And questions the daily table cannot reach: the heaviest parcel, deliveries per courier, the share of the day going to one city.
Grain (granularity)
what one row counts
The level of detail at which a table records one observation. Before you compute anything from a table, you must be able to finish the sentence: one row is one …
daily is one row per day. deliveries is one row per parcel. Read one grain as the other and every number you compute answers a different question from the one you asked.
A finer table can always be summarised into a coarser one. The detail a summary drops cannot be computed back.
store_daily
| store | date | receipts | revenue | avg_receipt |
|---|---|---|---|---|
| ST-1 | 2026-03-05 | 120 | 14400 | 120 |
| ST-1 | 2026-03-06 | 95 | 11020 | 116 |
| ST-2 | 2026-03-05 | 60 | 7800 | 130 |
| ST-2 | 2026-03-06 | 80 | 9600 | 120 |
Answerable or not?
store_daily — ST-1 only
| store | date | receipts | revenue | avg_receipt |
|---|---|---|---|---|
| ST-1 | 2026-03-05 | 120 | 14400 | 120 |
| ST-1 | 2026-03-06 | 95 | 11020 | 116 |
Mean the avg_receipt column
(120 + 116) ÷ 2. The two days count equally, though one had 120 receipts and the other 95.
118.0
Rebuild from the totals
(14400 + 11020) ÷ (120 + 95). Every receipt counts once.
118.2
Here the gap is small because the two days have similar counts. The more unbalanced the groups, the bigger the gap, and you only learn its size by doing the check.
Check yourself
| Item | Why |
|---|---|
| A GitHub account | Practice notebooks are handed out and submitted through it |
| Git installed | Clone, commit, push |
uv installed |
Creates the Python environment from a locked file, identically on every machine |
Full instructions are in the Week 1 section on Moodle, including a short setup check to run before your first practice. If it fails, bring the error message to the session.
McKinney, Python for Data Analysis, 3rd ed. — Ch. 1, Ch. 5.
McGregor, Practical Python Data Wrangling and Data Quality — Ch. 1, Ch. 3.
Wickham, "Tidy Data", Journal of Statistical Software 59(10).
goals
| home | away | score | scorer | minute |
|---|---|---|---|---|
| Arsenal | Chelsea | 3:1 | Saka | 12 |
| Arsenal | Chelsea | 3:1 | Saka | 47 |
| Arsenal | Chelsea | 3:1 | Ødegaard | 60 |
| Arsenal | Chelsea | 3:1 | Palmer | 80 |
| Everton | Fulham | 1:1 | Keane | 25 |
| Everton | Fulham | 1:1 | Wilson | 78 |
Answerable or not?
readings
| sensor_id | recorded_at | temp_c | humidity |
|---|---|---|---|
| S-04 | 2026-03-02 09:00:00 | 4.1 | 71 |
| S-04 | 2026-03-02 09:15:00 | 4.3 | 70 |
| S-04 | 2026-03-02 09:30:00 | 4.6 | 70 |
| S-09 | 2026-03-02 09:00:00 | 6.8 | 63 |
| S-09 | 2026-03-02 09:15:00 | 7.0 | 62 |
Answerable or not?

Kyiv School of Economics