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
- What data science is (and what it is not)
- The workflow: from question to conclusion
- The roles of the trade: data analyst, data scientist and data engineer
- The PyData ecosystem: which piece does what
- Jupyter Notebook: the usual home of analysis
- The module's dataset:
sales_2026.csv - What you can already do:
statisticsand 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.
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 installcommands go inside the project'svenv(M1). Ifimport pandasfails withModuleNotFoundError, 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
- 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.
- With
csv.DictReaderandstatistics, compute the mean sale amount insales_2026.csvfor thewebchannel only and compare it with thestorechannel. What hypothesis can you think of to explain the difference? - 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
- (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.
-
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.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") - 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
- Introduction to Python
- Setting Up the Development Environment
- Python Syntax and Basic Data Types
- Variables and Constants
- Basic Input and Output
- Virtual Environments and Package Management
Module 2: Control Structures
Module 3: Functions and Modules
- Defining Functions
- Function Arguments
- Lambda Functions
- Modules and Packages
- Standard Library Overview
Module 4: Data Structures
Module 5: Object-Oriented Programming
Module 6: File Handling
Module 7: Error and Exception Handling
- Introduction to Exceptions
- Handling Exceptions
- Raising Exceptions
- Custom Exceptions
- Best Practices and Error Logging
Module 8: Advanced Topics
- Type Hints
- Decorators
- Generators
- Context Managers
- Concurrency: Threads and Processes
- Asyncio for Asynchronous Programming
Module 9: Testing and Debugging
- Introduction to Testing
- Unit Testing with unittest
- Testing with pytest
- Test-Driven Development
- Debugging Techniques
- Using pdb for Debugging
Module 10: Web Development with Python
- Introduction to Web Development
- Flask Framework Fundamentals
- Building REST APIs with Flask
- Introduction to Django
- Building Web Applications with Django
Module 11: Data Science with Python
- Introduction to Data Science
- NumPy for Numerical Computing
- Pandas for Data Manipulation
- Matplotlib for Data Visualization
- Introduction to Machine Learning with scikit-learn
