Pre-class: Python and Jupyter basics

Learn just enough Jupyter, Python and pandas to run every notebook in this course: cells and outputs, variables and lists, loading a table, selecting, filtering and grouping, the kind of data each column holds, what one row means, and a first chart.

Open this notebook in JupyterLab from the course folder and run each cell from top to bottom with Shift + Enter.


What you will be able to do

  1. Open, run, edit, restart and export a notebook in JupyterLab.
  2. Use Python variables, numbers, text, lists and dictionaries, and call a function.
  3. Load a CSV file into a pandas DataFrame and inspect it with head, shape, dtypes and describe.
  4. Select columns, filter rows, group and count.
  5. Name the level of measurement of a column and choose a sensible summary for it.
  6. Say what one row of a table represents (its grain).
  7. Draw a first chart with matplotlib and seaborn.

How the course uses code

Every notebook gives you working code with explanations. You read it, run it, then change a few lines. You will not be asked to write programs from scratch.

Part Topic
1 A tour of JupyterLab
2 Python essentials
3 DataFrames with pandas
4 Data types and levels of measurement
5 What does a row mean?
6 A first chart

Part 1: A tour of JupyterLab

JupyterLab is the program you use to open and run notebooks. You start it from Anaconda Navigator (click Launch under JupyterLab) or by typing jupyter lab in Anaconda Prompt (Windows) or Terminal (macOS). It opens in your web browser but runs on your computer, so it works without internet.

Area What it is for
File browser (left) Open the course folder and double-click a notebook (.ipynb)
Notebook (centre) A list of cells, run from top to bottom
Toolbar Run, stop, restart, add cells, choose the cell type
Kernel (top right) The Python process that runs your code. "Idle" means ready

Two kinds of cell

Cell type Contains When you run it
Markdown Formatted text, like this cell It shows as formatted text
Code Python Python runs it and shows the result underneath

Keys worth learning first

Key Action
Shift + Enter Run the cell and move to the next one
Enter Start editing the selected cell
Esc Stop editing (the cell stays selected)
A / B (not editing) Add a cell above / below
M / Y (not editing) Turn the cell into Markdown / Code
D, D (not editing) Delete the cell
Ctrl + S (Cmd + S on macOS) Save

Run the next cell with Shift + Enter. The number in brackets on its left, [1], records the order in which cells ran.

price = 120
quantity = 3
price * quantity
360

Predict, then reveal: running cells out of order

The next cell uses quantity. Suppose you went back up, changed quantity = 3 to quantity = 10 and ran only that cell, then ran the next one. Which value would the next cell use? Write your guess, then try it.

print("Quantity Python remembers right now:", quantity)
Quantity Python remembers right now: 3

Python remembers whatever ran last, not what is written higher up on the page. That is the most common source of confusion in notebooks. The cure:

Restart and run all

Choose Kernel → Restart Kernel and Run All Cells. Python forgets everything and runs the notebook from the top, in order. Do this before you export or submit any notebook, so the outputs match the code.

Exporting to PDF

Every graded lab is submitted as a PDF:

  1. Kernel → Restart Kernel and Run All Cells, and check every output and chart is visible.
  2. File → Print (or Ctrl + P / Cmd + P).
  3. In the print window, set the destination to Save as PDF. Under More settings, tick Background graphics so chart colours print.
  4. Save with the file name the lab asks for.

Check your understanding

You change a number in cell 3 of a notebook but forget to rerun cells 4 to 10, then export to PDF. What could be wrong in the PDF, and how do you prevent it?


Part 2: Python essentials

Variables and types

A variable is a name for a value. = stores the value under the name. Every value has a type.

store = "Shanghai West"   # text: a string, inside quotes
orders = 42               # whole number: an int
average_basket = 18.75    # decimal number: a float
is_open = True            # True or False: a bool

print(type(store), type(orders), type(average_basket), type(is_open))
print("Revenue estimate:", orders * average_basket)
<class 'str'> <class 'int'> <class 'float'> <class 'bool'>
Revenue estimate: 787.5

print shows several things on one line. An f-string (a string starting with f) puts values inside text, and :.2f rounds to two decimals:

print(f"{store} had {orders} orders, about {orders * average_basket:.2f} dollars in total.")
Shanghai West had 42 orders, about 787.50 dollars in total.

Lists: several values in order

A list holds values in square brackets. Python counts positions from 0.

monthly_sales = [120, 95, 140, 180]
print("First month:", monthly_sales[0])
print("Last month: ", monthly_sales[-1])
print("Total:", sum(monthly_sales), "| months:", len(monthly_sales))
First month: 120
Last month:  180
Total: 535 | months: 4

Dictionaries: values looked up by name

A dictionary pairs keys with values, inside curly brackets. A table is like a dictionary of columns.

prices = {"coffee": 3.5, "tea": 2.8, "cake": 4.2}
print("Price of tea:", prices["tea"])
prices["juice"] = 3.9   # add a new key
print("Items on the menu:", list(prices.keys()))
Price of tea: 2.8
Items on the menu: ['coffee', 'tea', 'cake', 'juice']

Calling functions and methods

A function does a job when you call it with brackets: len(monthly_sales), round(3.14159, 2), max(...). A method is a function that belongs to a value and is called with a dot: store.upper().

print(round(3.14159, 2))
print(max(monthly_sales))
print(store.upper())
print(sorted(prices, key=prices.get))   # menu items from cheapest to dearest
3.14
180
SHANGHAI WEST
['tea', 'coffee', 'juice', 'cake']

When something goes wrong

Errors are normal. Read the last line of the red message first.

Error message Usual cause Fix
NameError: name 'x' is not defined A cell that creates x has not run yet Run the earlier cells, or restart and run all
SyntaxError A missing bracket, quote or comma Look at the line the arrow points to
KeyError: 'Tip' A column or key spelled differently (capital letters count) Check the exact name, for example with df.columns
FileNotFoundError The notebook is not next to the data folder Open the notebook from inside the course folder

Check your understanding

What does monthly_sales[1] return, and why not 120? What type is prices["cake"]?


Part 3: DataFrames with pandas

pandas is the Python library analysts use for tables. A table in pandas is a DataFrame: rows and named columns, like a spreadsheet you control with code.

Our table records restaurant bills: one row per table served, with the bill, the tip, and details about the party. It comes from a well-known teaching dataset (Bryant and Smith, 1995) in which one waiter recorded every tip over a few months. Money is in US dollars.

Load and look

import pandas as pd

tips = pd.read_csv("data/tips.csv")
tips.head()
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

head() shows the first five rows. Next, the size of the table and the type of each column:

print("Rows and columns:", tips.shape)
print()
print(tips.dtypes)
Rows and columns: (244, 7)

total_bill    float64
tip           float64
sex            object
smoker         object
day            object
time           object
size            int64
dtype: object

float64 means decimal numbers, int64 whole numbers, and object text.

describe() summarises every numeric column: how many values, the mean, standard deviation, minimum, quartiles and maximum.

tips.describe().round(2)
total_bill tip size
count 244.00 244.00 244.00
mean 19.79 3.00 2.57
std 8.90 1.38 0.95
min 3.07 1.00 1.00
25% 13.35 2.00 2.00
50% 17.80 2.90 2.00
75% 24.13 3.56 3.00
max 50.81 10.00 6.00

For text columns, describe(include="object") counts values and shows the most common one:

tips.describe(include="object")
sex smoker day time
count 244 244 244 244
unique 2 2 4 2
top Male No Sat Dinner
freq 157 151 87 176

Select columns

tips["tip"].head()          # one column, as a Series
0    1.01
1    1.66
2    3.50
3    3.31
4    3.61
Name: tip, dtype: float64
tips[["day", "time", "total_bill", "tip"]].head()   # several columns: a list inside the brackets
day time total_bill tip
0 Sun Dinner 16.99 1.01
1 Sun Dinner 10.34 1.66
2 Sun Dinner 21.01 3.50
3 Sun Dinner 23.68 3.31
4 Sun Dinner 24.59 3.61

Filter rows

A condition inside the brackets keeps only the rows where it is true. Combine conditions with & (and) or | (or), each in its own brackets.

dinners = tips[tips["time"] == "Dinner"]
big_weekend = tips[(tips["day"].isin(["Sat", "Sun"])) & (tips["size"] >= 4)]

print("Dinner bills:", len(dinners))
print("Weekend parties of four or more:", len(big_weekend))
Dinner bills: 176
Weekend parties of four or more: 36

Add a column

New columns are calculated from existing ones, row by row. The tip as a percentage of the bill:

tips["tip_percent"] = 100 * tips["tip"] / tips["total_bill"]
tips[["total_bill", "tip", "tip_percent"]].head().round(1)
total_bill tip tip_percent
0 17.0 1.0 5.9
1 10.3 1.7 16.1
2 21.0 3.5 16.7
3 23.7 3.3 14.0
4 24.6 3.6 14.7

Group and count

value_counts() counts how often each value appears. groupby splits the table into groups and summarises each one.

tips["day"].value_counts()
day
Sat     87
Sun     76
Thur    62
Fri     19
Name: count, dtype: int64
tips.groupby("day").agg(
    bills=("total_bill", "count"),
    average_bill=("total_bill", "mean"),
    average_tip_percent=("tip_percent", "mean"),
).round(2)
bills average_bill average_tip_percent
day
Fri 19 17.15 16.99
Sat 87 20.44 15.32
Sun 76 21.41 16.69
Thur 62 17.68 16.13

Each line inside agg reads: new column name = (column to use, summary to compute).

Predict, then reveal

Which is larger: the number of lunch bills or dinner bills? Guess from what you know about restaurants, then change "day" to "time" in the value_counts() cell above and run it.

Check your understanding

Write the code that keeps only bills from smokers (smoker is "Yes") and computes their average tip_percent. Which two ideas from this part do you combine?


Part 4: Data types and levels of measurement

The type pandas shows (int64, object) is about how a value is stored. The level of measurement is about what it means, and it decides which calculations make sense.

Level What the values do Examples What makes sense
Nominal Name a group; no order Sex, smoker, product ID, postcode Count, share, mode
Ordinal Name a group in order; gaps not equal Shirt size, satisfaction 1–5 + median, "higher than"
Interval Ordered with equal gaps; zero is arbitrary Temperature in °C, calendar dates + differences, mean
Ratio Equal gaps and a true zero Bill, tip, party size, age + ratios: "twice as much"

Nominal and ordinal variables are categorical. Interval and ratio variables are numeric (quantitative).

The columns of the tips table:

Column Stored as Level A sensible summary
total_bill, tip float64 Ratio Mean, median, total
size int64 Ratio (a count of people) Mean, median, counts per size
sex, smoker, time object Nominal Counts and shares
day object Nominal, or ordinal if you care about the order of days Counts; sort in real day order

Predict, then reveal: numbers that are not quantities

pandas sorts text alphabetically. In what order will the days appear in the next cell? Is that the order you want in a report?

print("Alphabetical:", sorted(tips["day"].unique()))

day_order = ["Thur", "Fri", "Sat", "Sun"]
tips["day"] = pd.Categorical(tips["day"], categories=day_order, ordered=True)
print("Real order:  ", list(tips["day"].cat.categories))
tips["day"].value_counts(sort=False)
Alphabetical: ['Fri', 'Sat', 'Sun', 'Thur']
Real order:   ['Thur', 'Fri', 'Sat', 'Sun']





day
Thur    62
Fri     19
Sat     87
Sun     76
Name: count, dtype: int64

An ordered categorical keeps the real order, so tables and charts list Thursday to Sunday.

Remember

An ID or postcode stored as a number is still nominal: its average means nothing. Always ask what a column means before you average it.

Check your understanding

A survey stores satisfaction as 1, 2, 3, 4 or 5 in an int64 column. Which level is it? Why is "the share scoring 4 or 5" safer to report than the mean?


Part 5: What does a row mean?

The grain of a table is what one row represents. Here: one table's bill on one visit, served by one waiter. Grain decides what counting means:

  • len(tips) counts bills (tables served), not customers.
  • The number of customers is the sum of size, because each row is a party.
print("Bills (rows):            ", len(tips))
print("Customers (sum of size): ", tips["size"].sum())
Bills (rows):             244
Customers (sum of size):  627

The table has no ID column, so nothing guarantees that each row is a different bill.

Predict, then reveal

Do you expect any row to appear twice, with every value identical? If one does, is it a mistake?

print("Fully duplicated rows:", tips.duplicated().sum())
tips[tips.duplicated(keep=False)]
Fully duplicated rows: 1
total_bill tip sex smoker day time size tip_percent
198 13.0 2.0 Female Yes Thur Lunch 2 15.384615
202 13.0 2.0 Female Yes Thur Lunch 2 15.384615

Two different parties could really have had the same bill, tip, day and size. Without an ID (a primary key) we cannot tell a genuine repeat from a double entry, so we keep the row and note it. Deleting data needs evidence.

Grain also limits what you can conclude

All these bills come from one waiter in one restaurant. They describe that waiter's tips, not tipping in general.

Check your understanding

A sales table has one row per product within an order. Why does len(sales) not give the number of orders? Which pandas method would?


Part 6: A first chart

A chart needs a title, labelled axes with units, and ends with plt.show().

A histogram shows how one numeric column is spread out: how many bills fall into each range of values.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(7, 4))
ax.hist(tips["total_bill"], bins=20, color="#2a78d6", edgecolor="white")
ax.set_title("How big are the bills? Total bill per table served")
ax.set_xlabel("Total bill (US dollars)")
ax.set_ylabel("Number of bills")
plt.show()

png

seaborn builds on matplotlib and makes charts of a DataFrame in one call. A scatter plot shows two numbers for each row; hue colours the dots by a category.

import seaborn as sns

fig, ax = plt.subplots(figsize=(7, 4.5))
sns.scatterplot(
    data=tips, x="total_bill", y="tip", hue="time",
    palette={"Lunch": "#eb6834", "Dinner": "#2a78d6"}, ax=ax,
)
ax.set_title("Larger bills tend to come with larger tips")
ax.set_xlabel("Total bill (US dollars)")
ax.set_ylabel("Tip (US dollars)")
plt.show()

png

Check your understanding

  1. The histogram title asks a question. Write a better title that states the main message of the chart in one sentence.
  2. In the scatter plot, what does one dot represent? (Hint: the grain.)

Quick reference

You want to… Code
Load a CSV file df = pd.read_csv("data/file.csv")
See the first rows / size / types df.head() / df.shape / df.dtypes
Summary statistics df.describe()
One column / several columns df["tip"] / df[["day", "tip"]]
Filter rows df[df["time"] == "Dinner"]
New column df["tip_percent"] = 100 * df["tip"] / df["total_bill"]
Count values df["day"].value_counts()
Summary per group df.groupby("day").agg(average_tip=("tip", "mean"))
Duplicated rows df.duplicated().sum()
Histogram / scatter plot plt.hist(...) / sns.scatterplot(data=df, x=..., y=...)

Summary

  • A notebook is cells run in order. Restart and run all before you export; export with File → Print → Save as PDF.
  • Python stores values in variables; lists hold values in order, dictionaries by name; functions are called with brackets.
  • A DataFrame is a table: inspect it with head, shape, dtypes, describe; select, filter, group and count with one line each.
  • Storage type is not meaning. Name the level of measurement before choosing a summary.
  • Know the grain before you count anything.

Quiz

  1. What does Shift + Enter do, and what does the number in [ ] beside a cell tell you?
  2. What is the difference between tips["tip"] and tips[["tip"]]?
  3. Which code gives the average tip for each value of time?
  4. Is size in the tips table nominal, ordinal, interval or ratio? Why?
  5. What does one row of the tips table represent, and how do you count customers?

Next: Activity 1

Activity 1, the readiness check, uses exactly these skills on a new table, the Palmer Penguins, and then checks that Tableau is ready. Complete it before the course starts.