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
| Word | What it means |
|---|---|
| Sample | a few rows taken from a bigger table |
| Representative | a sample that looks like the whole table |
| Spread | how much values differ from one another |
| Outlier | a value far from the rest, which may be real or a mistake |
| Median | the middle value once the values are in order |
| Group size | how 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
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
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
# 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
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
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
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 see | What you can say | What you cannot say |
|---|---|---|
| 120 rainy days, mean 227.6 | Rainy days had fewer hires here | That rain is the only reason |
| 131 sunny days, mean 312.2 | Sunny days were busier here | That it will be so next year |
| Spread 80.3 and 68.2 | Single days vary a lot | That 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?