Iris — and why it is too easy
The most famous dataset in the field, and one flower is worth 3.33 accuracy points
load_irismulti-classclass_weightconfusion_matrixsmall-sample noiseWatch it happen
Play it through, or step back and forth yourself.
from sklearn.datasets import load_iris iris = load_iris(as_frame=True) iris.data.shape # (150, 4) iris.target_names # setosa, versicolor, virginica
Ronald Fisher published these 150 flowers in 1936, and they've been the first dataset in practically every course since. Three species, four measurements each, and no missing values anywhere.
The idea
Ronald Fisher published these 150 flowers in 1936, and they have been the first dataset in practically every course since. Three species, four measurements, no missing values, and it loads from inside the scikit-learn wheel in about 0.02 seconds — no download at all.
from sklearn.datasets import load_iris
iris = load_iris(as_frame=True)
iris.data.shape # (150, 4)
iris.target_names # ['setosa' 'versicolor' 'virginica']
np.bincount(iris.target) # [50 50 50]Three classes, not two
Our chai target was binary. This is the first genuinely multi-class problem in the track, and a few things change:
- The dummy scores 0.3333 — perfectly balanced, so the floor is 1/3 rather than the majority share.
- The confusion matrix is 3×3, and the off-diagonal now tells you which pair gets confused.
- Precision, recall and F1 need an
average=—"macro"treats every class equally,"weighted"by support. With balanced classes they agree. coef_gains a row per class, andpredict_probaa column per class.
scikit-learn handles multi-class automatically, which is why almost none of this needed mentioning until now.
The gap
Look at petal length by species:
setosa max 1.9 cm
others min 3.0 cmThere is a clear gap with nothing in it. One comparison classifies a third of the dataset perfectly:
if petal_length < 2.5:
return "setosa" # 50 of 50, no errorsA depth-1 decision tree on petal length alone scores 0.667: it nails setosa and then has to guess between the other two.
Which is not what real data looks like
Our chai data had rain running from 15.7% to 56.5% late — signal everywhere, certainty nowhere. Iris has a boundary you can draw on paper.
It became famous for being teachable: three tidy classes, four interpretable columns, no missing values, no leakage, no imbalance, and a visible decision boundary. Every one of those properties makes it unrepresentative of the problems you will actually be handed.
The scores
logistic regression 0.9600 ± 0.0389
kNN (k=5) 0.9600 ± 0.0249
tree, depth 2 0.9333 ± 0.0471
dummy 0.3333Everything works and nothing distinguishes itself — the models are within one standard deviation of each other. On a problem this easy, the choice of algorithm is the least interesting decision available.
One flower
Here is the number worth carrying away. A 20% test set is 30 flowers.
100 / 30 = 3.33 accuracy points per row
confusion matrix: [[10 0 0]
[ 0 10 0]
[ 0 1 9]]One mistake, in the whole test set. A model at 0.967 and a model at 1.000 differ by a single flower. There is no meaningful comparison available at that resolution, and yet "my method gets 98% on iris" is a sentence people still write.
This is lesson 8's discipline on a dataset small enough to feel it: the fold spreads (±0.039 against ±0.025) carry more information than the means.
So what is it actually for?
- Checking your code runs. Four columns, no
NaN, instant. - Demonstrating an API with nothing in the way.
- Drawing a decision boundary — two features, three classes, visible on a slide.
- Teaching multi-class metrics, since a 3×3 matrix fits in your head.
And not for comparing algorithms, benchmarking a method, or practising any of the parts that are hard in real work. It's a teaching aid, and a very good one — as long as you know that's what it is.
See it run
The lesson's code, ready to run and to fiddle with.
Putting the kettle on…
Starting up…
Worked example
not gradedAlready written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.
trytrying random_state=1, 2, 3 on the split and watching the single error move around.
Your turn
3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
Load iris and return [list(shape), class_counts, n_classes] — the shape of the feature frame, the count per class as a list, and how many classes there are.
Find the gap. Return [setosa_max_petal_length, others_min_petal_length] — the two numbers that show one comparison separates setosa perfectly.
Show the resolution problem. Split 20% stratified at random_state=0, fit a logistic pipeline, and return [n_test_rows, points_per_row, n_errors] — points rounded to 2 places.
