Unit 10 • Lesson 5

Writing Up a Finding

Overview

A number, what it is compared with, and what it does not prove.

Intermediate 20–25 min

What You Will Learn in This Lesson

By the end of this lesson, you will be able to:

  • Write a claim in three parts: the number, the comparison, and the limit.
  • Report the sample size next to every difference between groups.
  • Tell an observed correlation apart from a causal claim.
  • Build the sentence from the data, so it cannot drift from the numbers.

Why This Matters

The write-up is the only part of an analysis most people ever see. A correct table under an overstated sentence is still a wrong analysis, because the sentence is what gets repeated. Writing the claim from the data, with its size and its limits attached, is what makes the number something you can defend when somebody asks.

Step 1: Three parts to a claim

A finding that survives a reader has three parts. Leave out the second and it is trivia; leave out the third and it is overstated.

1

The number

Start with the value, at a precision the data supports. "Team a averages 85.0" is a number; it is not yet a finding, because nothing says whether 85 is good.

import pandas as pd

df = pd.DataFrame({"team": ["a", "a", "b"], "score": [90.0, 80.0, 70.0]})
means = df.groupby("team")["score"].mean()
print(means.round(1))
2

The comparison

A number means something next to another number: the other team, last term, the target. The gap is usually the thing the reader actually wants, so compute it rather than making them subtract.

import pandas as pd

df = pd.DataFrame({"team": ["a", "a", "b"], "score": [90.0, 80.0, 70.0]})
means = df.groupby("team")["score"].mean()
gap = means.max() - means.min()
print(f"{means.idxmax()} leads {means.idxmin()} by {gap:.1f} points")
3

The limit

Say how much data the claim rests on and what period it covers. This is not modesty; it is the information a reader needs to decide how far to trust it.

import pandas as pd

df = pd.DataFrame({"team": ["a", "a", "b"], "score": [90.0, 80.0, 70.0]})
sizes = df["team"].value_counts()
print(f"based on {len(df)} scores: a has {sizes['a']}, b has {sizes['b']}")

Mini Practice #1: Add the comparison

Try It Yourself

The code prints the top team's mean on its own. Change the print so it also says which team it beats and by how many points.

Press Run to see output

Step 2: Build the sentence from the data

A sentence typed by hand is a second copy of the result, and second copies drift. The next time the data changes the table updates and the sentence does not.

1

An f-string over the computed values

Every number in the sentence comes from a variable. If the data changes, the sentence changes with it, and nobody has to remember to edit the report.

import pandas as pd

def headline(df):
    means = df.groupby("team")["score"].mean()
    gap = float(means.max() - means.min())
    return f"{means.idxmax()} scores {gap:.1f} above {means.idxmin()} (n={len(df)})"

print(headline(pd.DataFrame({"team": ["a", "b"], "score": [85.0, 70.0]})))
2

Which n is the right n

The number of rows and the number of groups are both counts, and they say very different things. "n=2" after a comparison of two teams reads as two data points. Report the rows the means were built from.

import pandas as pd

df = pd.DataFrame({"team": ["a", "b", "b", "b"], "score": [10.0, 4.0, 4.0, 4.0]})
means = df.groupby("team")["score"].mean()
print("groups:", len(means))
print("rows:", len(df))

Key idea: When one group is far smaller than the other, say so in the sentence. "a leads b (a: 1 score, b: 300)" is honest; "a leads b (n=301)" hides the problem inside a big number.

Mini Practice #2: The right n

Try It Yourself

Print the sentence twice: once with the number of groups as n, once with the number of rows. Read both aloud and decide which one a reader would misunderstand.

Press Run to see output

Step 3: Moving together is not causing

The most common overstatement in a write-up is turning "these two go up together" into "this one makes that one go up".

1

Measure the correlation

Series.corr gives a number between -1 and 1 for how closely two columns move together. Close to 1 means they rise together in this data. That is the whole of what it says.

import pandas as pd

hours = pd.Series([1.0, 2.0, 3.0, 4.0])
score = pd.Series([60.0, 68.0, 81.0, 88.0])
print(round(float(hours.corr(score)), 2))
2

Say what it does not show

Students who study more may also sleep more, attend more, or already be stronger. The table cannot separate those. The honest sentence reports the association and names that it is one.

import pandas as pd

hours = pd.Series([1.0, 2.0, 3.0, 4.0])
score = pd.Series([60.0, 68.0, 81.0, 88.0])
r = float(hours.corr(score))
print(f"hours and score move together (r={r:.2f}); this data cannot say whether one causes the other")

Step 4: When to Use It

Reporting a comparison

The number, what it is compared with, and how much data it rests on, built from the values.

f"{top} scores {gap:.1f} above {bottom} (n={len(df)})"

Describing an association

Say the columns move together, give r, and say what the data cannot show.

When Not to Use It

Do not write “causes”, “drives” or “leads to” about a correlation, and do not type numbers into a sentence by hand. Both drift from what the data says.

End-of-Lesson Exercises

Exercise 1: A finding with its n

Write finding(df) returning a sentence of the form "a scores 15.0 above b (n=3)", comparing the highest and lowest team means and reporting how many rows the whole comparison rests on.

Group to means, take idxmax and idxmin, subtract for the gap, and len(df) for n. Reporting n is what stops a difference between two rows reading like a result.

Press Run to try it, then Check my work to mark it.

Exercise 2: An association, stated honestly

Write association(df, x, y) that returns "x and y move together (r=0.99)" when the correlation between the two columns is at least 0.5, "x and y move in opposite directions (r=-0.99)" when it is at most -0.5, and "no clear link between x and y (r=0.10)" otherwise. Use the column names given, and two decimal places for r.

r = float(df[x].corr(df[y])). Compare with 0.5 and -0.5 using >= and <=, and format r with :.2f inside each sentence. None of the three sentences says "causes".

Press Run to try it, then Check my work to mark it.