Module 10 ended with a warning: by opening Papyrus to the world, the website started generating data. Every sale Marta records, every search Julia types, every visit to a book page leaves a trace. And with the trace came Ana's questions: which titles get browsed a lot but bought little? Which day of the week sells the most? How much stock should we order ahead of Sant Jordi? None of those questions gets answered by programming the shop: they get answered by understanding its numbers. That craft is called data science, and this module gives you its three fundamental tools — NumPy, pandas and Matplotlib — plus a first honest look at machine learning. This lesson lays the groundwork: what data science is (and is not), how the work is done, what pieces make up the ecosystem, and which dataset we will use to answer Ana's questions.

Contents

  1. What data science is (and what it is not)
  2. The workflow: from question to conclusion
  3. The roles of the trade: data analyst, data scientist and data engineer
  4. The PyData ecosystem: which piece does what
  5. Jupyter Notebook: the usual home of analysis
  6. The module's dataset: sales_2026.csv
  7. What you can already do: statistics and its limits

What data science is (and what it is not)

Data science is the discipline of extracting useful knowledge from data to answer questions and make decisions. It combines three ingredients that, separately, you already know or can guess at:

  • Programming — to read, transform and automate. It's what you've been doing for 10 modules.
  • Statistics — to summarize, compare, and avoid fooling yourself with randomness.
  • Domain knowledge — Ana knows that Sant Jordi — Catalonia's Book Day, 23 April — is her annual peak; no algorithm knows that unless someone tells it.

Just as important is what it is not:

Myth Reality
"It's making pretty charts" The chart is the finish line; 80% of the work is getting and cleaning data
"It's artificial intelligence" ML is just one more tool (we'll see it in 11-05); most questions are answered by counting and grouping well
"You need big data" Papyrus has hundreds of rows, not millions, and there are still valuable decisions to make
"It's predictive magic" A model only projects patterns from the past; if the future changes (a pandemic, a fad), the model doesn't see it coming

The workflow: from question to conclusion

Every serious analysis walks the same path, and it's worth keeping it in front of you before writing a single line of code. With Papyrus's questions as the example:

flowchart LR
    A["1. Question<br>How much stock to order<br>for Sant Jordi?"] --> B["2. Data<br>sales_2026.csv<br>web logs (M10)"]
    B --> C["3. Cleaning<br>corrupt rows,<br>duplicates, gaps"]
    C --> D["4. Exploration<br>group, summarize,<br>visualize"]
    D --> E["5. Conclusion or model<br>'order ~220 units,<br>Don Quixote the biggest'"]
    E -.->|new questions| A

Notice the arrow going back: exploring the data almost always raises new questions ("why does Faust get browsed so much and bought so little?"), and the cycle starts again. It's not an assembly line: it's a loop.

The module's division of labour over this flow: cleaning and exploration are pandas territory (11-03), visualization belongs to Matplotlib (11-04), the model to scikit-learn (11-05), and NumPy (11-02) is the numerical engine that holds them all up.

The roles of the trade

"Data science" is an umbrella. In a real company the work is split across (at least) three profiles; at Papyrus all three are Ana wearing different hats:

Role Typical question At Papyrus Star tools
Data analyst What happened and why? "Which day do we sell the most?" pandas, SQL, Matplotlib
Data scientist What will happen? What decision do I make? "How many units should we order in April?" pandas, scikit-learn, statistics
Data engineer How does clean data arrive on time? The script that consolidates store and web sales every night Python, pipelines (your M8 generators!), databases

This module gives you the foundation for the first two roles; of the third you already have more than you think: reading CSV (M6), generator pipelines (M8) and a website that produces data (M10) is data engineering on a small scale.

The PyData ecosystem

All of these pieces install with pip inside your M1 virtual environment (pip install numpy pandas matplotlib scikit-learn). Each one has a clear role:

Package Role in the flow Lesson
NumPy Fast numeric arrays; the engine everyone else uses under the hood 11-02
pandas Tables (DataFrames): load, clean, group, join — M6's csv.DictReader on steroids 11-03
Matplotlib Charts: turning numbers into stories Ana understands at a glance 11-04
scikit-learn Machine learning: models that learn patterns from the history 11-05
Jupyter The interactive environment where all of the above is explored comfortably right here

The dependency between them is no accident: pandas is built on top of NumPy, Matplotlib draws NumPy arrays, and scikit-learn expects NumPy arrays or pandas DataFrames as input. That's why the module starts with NumPy even though day to day you'll mostly write pandas.

Jupyter Notebook, briefly

Data analysis is conversational: you try something, look at the result, adjust, repeat. Notebooks are made for exactly that: living documents with code cells that run one at a time, interleaved with text and charts that get saved alongside the code.

pip install notebook
jupyter notebook   # opens the browser at http://localhost:8888

Inside, each cell runs with Shift+Enter and its result appears below — the last expression in the cell displays on its own, no print() needed. When to use each format?

Situation Notebook (.ipynb) Script (.py)
Exploring data, testing hypotheses Ideal Awkward (re-run everything each time)
Report with code, text and charts Ideal Not applicable
Production code (the M10 website, the papyrus package) Bad idea Ideal
Tests (M9), clean version control Hard to test and to diff Ideal
Automated nightly runs Possible but forced Ideal

All the code in this module works exactly the same in a .py script — if you'd rather stick to your usual editor, go ahead. The notebook is a convenience, not a requirement. The only practical difference: in a script, to see a DataFrame you'll have to wrap it in print().

The module's dataset: sales_2026.csv

In M6 you created data/sales.csv (columns date,title,amount). Since M10, on top of that, the website records its own sales. Ana has consolidated both sources — the data engineer's job from the table above — into a single file we'll use throughout the module: data/sales_2026.csv, with sales from January to June 2026.

Column Type Meaning
date yyyy-mm-dd date Day of the sale
title text One of the 4 titles in the catalog
units integer Copies sold in that transaction
amount decimal Total charged (if the customer is a member, the M3 member tariff applies)
channel text store (physical till) or web (the M10 online shop)

This is how the file starts (it has 487 rows in total):

date,title,units,amount,channel
2026-01-02,The Odyssey,1,12.50,store
2026-01-02,Hamlet,2,19.90,web
2026-01-03,Don Quixote,1,15.71,store
2026-01-03,Faust,1,21.00,web
2026-01-05,Don Quixote,2,31.80,store
...
2026-04-23,Don Quixote,3,47.70,store
2026-04-23,The Odyssey,2,25.00,web
...

Two details you can already read with what you've learned: the January 3rd row charges 15.71 for Don Quixote — the member tariff you computed in M3 (15.90 with 4% VAT and the 5% member discount: 15.90 * 1.04 * 0.95 ≈ 15.71) — and the rows on 23 April pile up suspiciously: that's Sant Jordi.

What you can already do: statistics and its limits

Before installing anything new, let's be fair to the standard library. With csv.DictReader (M6) and the statistics module (M3) you can already answer simple questions:

import csv
import statistics
from collections import Counter
from pathlib import Path

amounts = []
titles = []
with open(Path("data") / "sales_2026.csv", encoding="utf-8", newline="") as f:
    for row in csv.DictReader(f):
        amounts.append(float(row["amount"]))
        titles.append(row["title"])

print(f"Sales recorded: {len(amounts)}")
print(f"Mean amount:   {statistics.mean(amounts):.2f} EUR")
print(f"Median amount: {statistics.median(amounts):.2f} EUR")
print(f"Most frequent title: {Counter(titles).most_common(1)}")
Sales recorded: 487
Mean amount:   15.03 EUR
Median amount: 12.50 EUR
Most frequent title: [('Don Quixote', 168)]

There's already a statistics lesson here: the mean (15.03 EUR) is higher than the median (12.50 EUR). A few large sales (Faust bundles at 21 EUR or the Sant Jordi surge) pull the mean upwards; the median — the middle value — resists those extremes better. When someone hands you "the average", always ask which of the two it is.

Now the limits. Try to answer "which day of the week sells the most?" with the stdlib:

from datetime import date

by_day = Counter()
with open(Path("data") / "sales_2026.csv", encoding="utf-8", newline="") as f:
    for row in csv.DictReader(f):
        day = date.fromisoformat(row["date"]).weekday()   # 0=Monday ... 6=Sunday
        by_day[day] += int(row["units"])
print(by_day)

It works — and in 11-03 we'll confirm the winner is Saturday. But look at the cost: manual type conversion on every row, one Counter per question, and if you now want "units per weekday and per channel, members only, sorted", the code grows into nested loops. And everything is text: "12.50" can't be summed until you convert it, row by row. Data science asks these questions by the dozen; you need tools where each question costs one line, not one loop. That's NumPy and pandas.

Common Mistakes and Tips

  • Skipping the question and jumping straight to code. The number one mistake. "Analyzing the data" is not a goal; "deciding how many units to order for Sant Jordi" is. Write the question down before opening the editor.
  • Installing the libraries outside the virtual environment. This module's pip install commands go inside the project's venv (M1). If import pandas fails with ModuleNotFoundError, the environment is almost certainly not activated.
  • Confusing mean and median. With skewed data (and sales almost always are) they tell different stories. Report both, or at least be aware of which one you're using and why.
  • Trusting data you haven't looked at. Before computing anything, open the CSV and read 20 rows. Impossible dates, negative amounts or misspelled titles are caught faster with your eyes than with code.
  • Running notebook cells out of order. State lives in memory: if you run cell 5 before cell 3, results can be inconsistent. When in doubt: Kernel → Restart & Run All.

Exercises

  1. Classify these Papyrus tasks by the role (data analyst, data scientist or data engineer) that fits best: (a) a nightly script that dumps the Django website's sales into the consolidated CSV; (b) a monthly report of units sold per title; (c) estimating how many copies of Don Quixote will sell next Sant Jordi.
  2. With csv.DictReader and statistics, compute the mean sale amount in sales_2026.csv for the web channel only and compare it with the store channel. What hypothesis can you think of to explain the difference?
  3. Without writing code: for the question "which titles get browsed on the website but not bought?", what data do you need besides sales_2026.csv? At which step of the workflow are you when you realize information is missing?

Solutions

  1. (a) Data engineer: moves and consolidates data, doesn't answer questions. (b) Analyst: describes what happened. (c) Data scientist: predicts and supports a decision. At Papyrus all three tasks are done by the same person; the roles describe the kind of work, not a membership card.
  2. import csv, statistics
    from pathlib import Path
    
    web, store = [], []
    with open(Path("data") / "sales_2026.csv", encoding="utf-8", newline="") as f:
        for row in csv.DictReader(f):
            target = web if row["channel"] == "web" else store
            target.append(float(row["amount"]))
    
    print(f"web:   {statistics.mean(web):.2f} EUR across {len(web)} sales")
    print(f"store: {statistics.mean(store):.2f} EUR across {len(store)} sales")
    
    Reasonable hypotheses (to verify in 11-03, not to take on faith!): in the store people buy more units per transaction (Marta recommends in person), or on the web the cheaper titles carry more weight. Formulating testable hypotheses is exactly step 4 of the workflow.
  3. You need the visits to the book pages — they're in the M10 web logs (every request to /book/<title> leaves a trace), not in the sales CSV. You realize it at step 2 (data): the question compares two quantities (views and purchases) and you only have one. Going back from exploration to data gathering is the diagram's loop in action.

Conclusion

You now have the map: data science is answering questions with data by following the cycle question → data → cleaning → exploration → conclusion; the PyData ecosystem splits the work between NumPy (numbers), pandas (tables), Matplotlib (charts) and scikit-learn (models); Jupyter is the usual workbench even though everything works in scripts; and sales_2026.csv — 487 sales from January to June, with Sant Jordi pulsing on 23 April — is the material on which we'll answer Ana's questions. You also saw the limit: statistics and Counter go only so far, and every new question costs another loop with manual conversions. The root of the problem is that Python lists don't know any maths: adding two lists concatenates them, it doesn't add their numbers. The next lesson introduces the structure that does know: the NumPy array, where amounts * 1.04 does in one line — and a thousand times faster — what until now demanded a loop.

Python Programming Course

Module 1: Introduction to Python

Module 2: Control Structures

Module 3: Functions and Modules

Module 4: Data Structures

Module 5: Object-Oriented Programming

Module 6: File Handling

Module 7: Error and Exception Handling

Module 8: Advanced Topics

Module 9: Testing and Debugging

Module 10: Web Development with Python

Module 11: Data Science with Python

Module 12: Final Project

© Copyright 2026. All rights reserved