Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Reading a result honestly

After this lesson: I can say how far a result from a sample can be trusted.

60 min lesson5 commandsJupyterLab data science lab: pandas, Matplotlib and scikit-learn in the browser

Ten winter days

A new member of staff counts the hires on the first ten days of the year. She says, "We hire about 197 bikes a day." The owner nods.

The whole year says about 270. The number was correct and the claim was wrong. A result is only as honest as the sample, the spread and the group behind it.

By the end of this lesson

Note

I can say how far a result from a sample can be trusted.

Key words

WordWhat it means
Samplea few rows taken from a bigger table
Representativea sample that looks like the whole table
Spreadhow much values differ from one another
Outliera value far from the rest, which may be real or a mistake
Medianthe middle value once the values are in order
Group sizehow many rows a mean or a count is based on

The cells students fill in are left blank in this preview.

A sample must look like the whole

Often you cannot look at everything, so you take a sample. A good sample is representative: it looks like the whole table in the ways that matter.

The first ten rows of a table are rarely representative. They are all from one place or time.

Compare three ways to pick days

Command
docker exec jupyter python3 -c "import pandas as pd; h=pd.read_csv('hires.csv'); print('whole year mean:', round(h['hires'].mean(),1)); print('first 10 days mean:', round(h['hires'].head(10).mean(),1)); print('every 36th day mean:', round(h['hires'].iloc[::36].mean(),1), len(h['hires'].iloc[::36]))"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A random sample still wobbles

Picking rows at random avoids a pattern. But a small random sample still gives a different mean each time, and a bigger one wobbles less.

In pandas, sample(10, random_state=1) picks ten days. The random_state is a seed, so the same number gives the same days again.

Six samples of 10 days, six of 100

Command
docker exec jupyter python3 -c "import pandas as pd; h=pd.read_csv('hires.csv'); print([round(h['hires'].sample(10, random_state=s).mean(),1) for s in range(1,7)]); print([round(h['hires'].sample(100, random_state=s).mean(),1) for s in range(1,7)])"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

What to do with one big value

An outlier is a value far from the rest. It pulls the mean towards it, but hardly moves the median.

Do not delete one because it is large. Ask if it could be true. A mistake, such as 5000 hires in a shop with 30 bikes, is dropped. A real busy day stays and is mentioned.

One huge day, worked out in Python

Code: python
# Seven days of hires at one station, then the same list with one huge day added.
days = [204, 173, 327, 173, 240, 198, 220]
def middle(values):
    in_order = sorted(values)
    half = len(in_order) // 2
    if len(in_order) % 2 == 1:
        return in_order[half]
    return (in_order[half - 1] + in_order[half]) / 2

print("mean:", round(sum(days) / len(days), 1), "median:", middle(days))
days.append(5000)
print("mean:", round(sum(days) / len(days), 1), "median:", middle(days))
# Which of the two numbers moved more?

Put an invented 5000 in the first day

Command
docker exec jupyter python3 -c "import pandas as pd; h=pd.read_csv('hires.csv'); t=h.copy(); t.loc[t.index[0],'hires']=5000; print(round(h['hires'].mean(),1), h['hires'].median(), round(t['hires'].mean(),1), t['hires'].median())"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Look at the three biggest days

Command
docker exec jupyter python3 -c "import pandas as pd; h=pd.read_csv('hires.csv'); print(h.nlargest(3,'hires')[['date','weather','hires']].to_string(index=False))"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A common mistake

Warning

Many people think the median is always better than the mean. It is steadier when there is an outlier, but it hides how big the outlier is. Report both, and say what the biggest value was and why you kept it.

Look at the size of each group

A mean over 8 days and a mean over 200 days are not equal evidence. Always print how many rows are in each group beside its mean.

And be careful with the verb. Rainy days go with fewer hires in this table. Showing that rain causes them needs more than a mean.

Rainy days against sunny days

Command
docker exec jupyter python3 -c "import pandas as pd; h=pd.read_csv('hires.csv'); r=h[h['weather']=='rainy']['hires']; s=h[h['weather']=='sunny']['hires']; print(len(r),len(s),round(r.mean(),1),round(s.mean(),1)); print(round(r.std(),1), round(s.std(),1))"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Worked example: reading the numbers

What you seeWhat you can sayWhat you cannot say
120 rainy days, mean 227.6Rainy days had fewer hires hereThat rain is the only reason
131 sunny days, mean 312.2Sunny days were busier hereThat it will be so next year
Spread 80.3 and 68.2Single days vary a lotThat any one day will match its mean

The cells students fill in are left blank in this preview.

Going further: a fair rule for outliers

In your notebook, find the 25th and 75th percentiles of hires with quantile. Call their gap the IQR.

Count the days below the 25th minus 1.5 IQR or above the 75th plus 1.5 IQR. Which days are they, and would you drop any?

About this lab and its source: JupyterLab data science lab: pandas, Matplotlib and scikit-learn in the browser

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.