learn-people-analytics-with-phoebe / Deep-dive session 4 of 10
Learn People Analytics with Phoebe · Deep-dive session 4 of 10

Funnel maths and adverse impact

Three sessions on the people who leave; tonight the people who arrive. A hiring funnel is five counts and four ratios, and every ratio has a base that is the stage before it. Split any stage by a group the company records and each group gets its own selection rate, and the lower rate over the highest one is the impact ratio: the single number in hiring that a regulation has an opinion about. Apex's funnel is constructed, 400 applicants to 10 hires, with an invented split by sex. Computed stage by stage in pandas it produces a result worth the whole evening: two stages under the four-fifths rule of thumb, in opposite directions, and an end-to-end ratio of exactly 1.00 that hides both.

🟡 Core HR analysts, talent acquisition teams, anyone who reports a funnel Python, pandas 45 min
0-3 · Welcome 3-18 · Conversions, ratios, the audit 18-40 · Build-along: the funnel in pandas 40-45 · Q&A
Part 0

Where we start

Foundations session 3 worked the same funnel by hand: 400 applied, 120 were screened in, 40 interviewed, 12 offered, 10 hired. Every count is invented, and so is the split by sex, 240 men and 160 women at the top. Nobody in it is real. What is real is the arithmetic, and the arithmetic is what a people analyst is asked to defend when a recruiting update reaches a board, an auditor or a lawyer. Tonight it becomes a DataFrame, a function that takes a dictionary of groups, and a table of impact ratios with the base printed beside every one.

🎫 Clear all three quiz questions to stamp your passport Live = presented in the 45 minutes Self-study = the full depth, after class ★ Build-along = run the code yourself

★ What you walk out with tonight. A funnel table with a conversion and a yield column that name their different bases; selection rates by group at every stage with both counts printed; an impact_ratios function that works for two groups or twelve; the stage-by-stage table that shows why an end-to-end ratio is not an audit; and the one-person test that says how far to trust a ratio built on a handful of offers.

Part 1 · covers conversion per stage and its base

Five stages, four conversions, one base each

5 min live

A funnel has two kinds of ratio and they divide by different things. The conversion at a stage divides by the stage before it: 40 interviewed of 120 screened in is 33.3 percent. The yield divides by the top: 40 interviewed of 400 applicants is 10.0 percent. Both are correct, both are called "conversion" in somebody's dashboard, and a number quoted without its base could be either. Print the base, and the ambiguity disappears.

FIVE COUNTS, FOUR CONVERSIONS, AND THE BASE UNDER EACH ONE APPLIED · 400 SCREENED IN · 120 INTERVIEWED · 40 OFFERED · 12 HIRED · 10 CONVERSION, BASE = STAGE BEFORE YIELD, BASE 400 120 / 400 = 30.0% screened in, of those who applied 30.0% 40 / 120 = 33.3% interviewed, of those screened in 10.0% 12 / 40 = 30.0% offered, of those interviewed 3.0% 10 / 12 = 83.3% accepted, of those offered 2.5% Constructed Apex funnel, invented. Every conversion names its base; every yield divides by the same 400.
🔍 Click to zoom - the moss row is the widest stage, where the most people are decided fastest, and where Part 2 splits the base
LiveConversion and yield: two ratios, two bases, one column name3 min▶

The conversions on Apex's funnel are 30.0, 33.3, 30.0 and 83.3 percent, and each divides by the stage above it. The yields are 30.0, 10.0, 3.0 and 2.5 percent, and each divides by the 400 applicants. The screen and the yield agree at the first stage by construction, which is how the two get confused: the first row of either column reads 30.0, and from the second row on they part company.

In pandas the difference is one method. The conversion is total / total.shift(1), the stage before; the yield is total / total.iloc[0], the top. Write both columns, and write the base as its own column beside the conversion, so that "33.3 percent" can never be read without "of 120" next to it. A reader who sees 40 of 120 knows the size of the number; a reader who sees 33.3 alone has to trust the analyst about which 33.3 it is.

The last conversion, 83.3 percent, is the one that flatters: 10 of 12 offers accepted sounds like a strong funnel. It is ten people. The first conversion, 120 of 400, is the one that decides: 280 people were turned away at the screen, more than at every later stage put together. When a funnel is judged, it is judged at its widest stage, and that is where Part 2 splits the base.

Self-studyA funnel is a cohort, and most funnel reports are not2 min▶

The five counts only divide cleanly if they are the same people moving through: the 120 screened in are 120 of the 400 who applied. A monthly recruiting report usually counts events in the month instead: 400 applications received in March, 12 offers made in March, some of them to people who applied in January. The ratio of those two counts is not a conversion of anybody; it is two flows from different cohorts divided by each other, and it moves when the hiring volume moves even if nothing about the process changed.

The fix is the event-log discipline from deep-dive session 1 applied to candidates: one row per candidate per stage with a date, then count each stage for the same application cohort. Apex's constructed funnel is a cohort by construction. A real one has to be built that way on purpose, and the six decisions at the top of the script gain a seventh: which cohort, defined by which date.

Real world

The funnel does not end at the hire. Hoffman, Kahn and Li 2018, "Discretion in Hiring", Quarterly Journal of Economics 133(2) 765-800, studied firms that introduced a job test to rank applicants for low-skill service jobs while managers could still hire against its recommendation. Test-based hiring raised completed tenure by about 25 percent (0.23 log points), and the hires managers made by overriding the test left sooner. The outcome they measured sits after the last stage of the funnel: how long the hire stayed. A funnel that stops counting at acceptance cannot see it, which is why deep-dive session 2's survival curve belongs on the same people as tonight's conversions.

Part 2 · covers the impact ratio, stage by stage

Lower rate over highest, at every stage

5 min live

Split the screen by sex, with the invented counts: 80 of 240 men screened in, 33.3 percent; 40 of 160 women, 25.0 percent. The impact ratio is the lower group's selection rate over the highest group's: 25.0 over 33.3, which is 0.750. Now do it at every stage, on each stage's own base, and the funnel tells a story that neither its totals nor its end-to-end ratio can.

SELECTION RATE BY SEX AT EACH STAGE, ON THAT STAGE'S OWN BASE invented split · light bar = men, wine bar = women · 3px per point IMPACT RATIO screen base: applied men 33.3% (80 of 240) women 25.0% (40 of 160) 0.750 women lower under four-fifths interview base: screened in men 32.5% (26 of 80) women 35.0% (14 of 40) 0.929 men lower over four-fifths offer base: interviewed men 26.9% (7 of 26) women 35.7% (5 of 14) 0.754 men lower under four-fifths hire base: offered men 85.7% (6 of 7) women 80.0% (4 of 5) 0.933 women lower over four-fifths end to end base: applied men 2.5% (6 of 240) women 2.5% (4 of 160) 1.000 equal rates hides two stages Constructed funnel, invented split. At each stage: the lower group's selection rate over the highest group's.
🔍 Click to zoom - two stages fail the rule of thumb in opposite directions, and the end-to-end ratio averages them into a perfect 1.000
LiveAdverse impact as one metric: the impact ratio and the rule of thumb3 min▶

The impact ratio at a stage is the lower group's selection rate over the highest group's selection rate, both on that stage's own base. With two groups it is one division; with a dozen ethnicity categories it is every group's rate over the top rate, which is why the build-along writes it as a function that takes a dictionary. It is a ratio of two rates, so it carries four counts behind it, and all four belong beside it: 40 of 160 over 80 of 240, not "0.75".

The US Uniform Guidelines on Employee Selection Procedures, 29 CFR 1607.4(D), 1978, give the rule of thumb: a selection rate less than four-fifths (80 percent) of the highest group's rate is generally regarded as evidence of adverse impact. That is the one sentence of law on this page, and it is a rule of thumb, not a verdict. What counts as a group, what to do with small numbers, how a screening tool moves the ratio and what each jurisdiction requires are taught with a working screening simulator and a legal map in the AI + HR course. Use that simulator to see a tool shift the ratio; tonight's job is the arithmetic that feeds it.

On Apex's invented split the screen gives 0.750 with women lower, and the offer stage gives 0.754 with men lower. Both are under four-fifths, in opposite directions. The interview (0.929) and the hire (0.933) are over it. That is a funnel with two stages worth a question and two that are not, and the next card shows why the headline ratio says none of this.

Self-studyWhy an end-to-end 1.000 is not an audit2 min▶

End to end, 6 of 240 men and 4 of 160 women were hired: 2.5 percent each, an impact ratio of exactly 1.000. A report that computes only this number declares the funnel even. It is even in outcome and uneven in process: the screen removed women at a lower rate of selection, the offer stage removed men at a lower rate, and the two stages cancelled on this particular draw of invented counts. Change either stage and the cancellation goes with it.

Each group's end-to-end rate is the product of its stage rates, so the end-to-end ratio multiplies the stage ratios together. It can hide a failing stage behind a lenient one, or compound two mild stages into a failing whole. Neither direction says anything about the stage where a decision was made. A screening tool acts at the screen; a hiring manager acts at the offer. The audit belongs at the stage where the decision is taken, with that stage's base, which is the whole reason the build-along loops over stages.

Self-studyOne person across the line: impact ratios on small counts2 min▶

The offer stage is 7 of 26 men and 5 of 14 women. Offer one more man and the ratio goes from 0.754 to 0.862, from under four-fifths to over it. At the hire stage, 6 of 7 and 4 of 5: one fewer woman hired and 0.933 becomes 0.700. A ratio that crosses the line when one person changes is not a finding about a process; it is a finding about the size of the base, the same lesson deep-dive session 3 drew from HR's 8 of 75.

The screen is different. 40 of 160 women were screened in; the ratio reaches 0.8 only at 43 of 160, three more people on a base of 160. That is still a small number of people, but it is a stage with enough people for the ratio to mean something. The honest report prints the one-person width beside every ratio. Whether a difference is statistically distinguishable from zero is a question for a significance test, which the Statistics course teaches; the width is the version a board can read.

Real world

Why the widest stage gets the scrutiny. Bertrand and Mullainathan 2004, "Are Emily and Greg More Employable Than Lakisha and Jamal?", American Economic Review 94(4) 991-1013, sent otherwise comparable resumes to real job advertisements, with names chosen to sound white or African-American, and counted callbacks. Resumes with white-sounding names received about 50 percent more callbacks. The stage they measured is the screen: a few seconds with a resume, the most people, the fastest decision. That is where the first row of the figure above sits, and where an impact ratio computed every quarter would have been the first place to look.

Part 3 · covers the NYC Local Law 144 bias audit

What a bias audit publishes

5 min live

New York City Local Law 144 of 2021, enforced from 5 July 2023, requires an employer that uses an automated employment decision tool to have an independent bias audit done every year and to publish a summary of it. The summary reports impact ratios by sex and by race/ethnicity. Strip away the legal wrapper and it is tonight's table: for each category, how many were assessed, how many were selected, the selection rate, and the rate over the highest category's rate.

WHAT A BIAS AUDIT PUBLISHES: TWO COLUMNS PER CATEGORY NYC Local Law 144, enforced from 5 July 2023 · constructed numbers for one screening tool CATEGORY ASSESSED SELECTED SEL. RATE IMPACT RATIO men 240 80 33.3% 1.000 women 160 40 25.0% 0.750 race/ethnicity one row per category, the same two columns each rate over the highest-rate category's rate THE SUMMARY SAYS the selection rate per category each impact ratio, against the highest done by an independent auditor, yearly IT DOES NOT SAY whether the tool is lawful what to do about 0.750 anything about the next stage the table is the start of the question Constructed numbers: the Apex screen from Part 2, reused with its invented split by sex. What 0.750 means in law, and the map across places, is taught with a simulator in the AI + HR course.
🔍 Click to zoom - the two moss columns are what the law asks to see published; the rest of the sheet is how you check them
LiveThe audit's two columns, and the base they hide3 min▶

A published summary under Local Law 144 reports, by sex and by race/ethnicity, the selection rate of each category and the impact ratio of each category against the category with the highest rate. Those two columns are the moss block in the figure. They are the same two numbers the build-along prints at every stage, and they are only as good as the counts behind them: 0.750 on 40 of 160 women against 80 of 240 men, and 0.750 on 3 of 12 against 4 of 12, are the same number and not the same finding. Read a published audit with the counts columns first.

The audit covers one decision point, the tool, which is exactly the stage-by-stage view of Part 2. A tool that screens resumes sits at the screen; its audit says nothing about the interview panel downstream, and an end-to-end number would say less. The analyst's contribution is to produce the same table for every stage the company controls, not only the one the law names, with the one-person width beside every ratio.

Deep-dive session 9 returns to the same law alongside GDPR Article 22 and the EU AI Act, when the question is what a model may see at all. The legal map in depth, and what an employer does when a ratio comes back under four-fifths, stay with the AI + HR course.

Self-studyCategories the company does not record, and the base that shrinks2 min▶

Every impact ratio needs the group recorded for every person in the base. Applicants who decline to state their sex or ethnicity drop out of both the numerator and the denominator, and if they decline at different rates at different stages the stage bases shrink unevenly. Print, for every stage, how many people in the base had the category recorded, before any ratio. A screen with 400 applicants and 150 recorded categories is an impact ratio on 150 people, and the sentence should say so.

The other trap is the category list itself. Race/ethnicity with twelve categories gives twelve rates, several on single-digit counts, and a ratio of every one against the highest. The rates at the bottom of the list will swing by whole tenths on one person. Merging categories to get stable counts is a decision with consequences for who becomes invisible, and it belongs in the decisions block at the top of the script, written before any number.

Real world

The manager is a stage too. In Hoffman, Kahn and Li 2018, the hires managers made by overriding the job test's recommendation left sooner than the ones the test would have chosen. The study does not say managers know nothing; it says that in that setting, the exceptions were on average worse than the rule they overrode. For an audit it means the discretionary step is a decision point with a selection rate like any other. If the tool's stage gets an impact ratio every year, the override stage deserves one on the same terms, and nothing in the arithmetic stops you computing it.

Build-along

The constructed funnel in pandas, stage by stage

★ 22 min · everyone builds

Seven steps on a funnel typed straight into a DataFrame. The counts are constructed and the split by sex is invented, so every printed output below is exactly what the code produces on those counts; on your own funnel the numbers will differ and the steps will not. Nothing here is a simulator: the screening simulator lives in the AI + HR course, and this is the table you would bring to it.

SEVEN STATIONS 1 · the table 5 stages 2 groups 2 · conversion 30.0 · 33.3 30.0 · 83.3 3 · yield base is 400 2.5 at the hire 4 · rates men, women at every stage 5 · the ratio lower rate over the highest 6 · the flag 2 under 0.8 1.000 overall 7 · one person offer 0.754 one more: 0.862 Station 5 is the metric. Station 6 applies the rule of thumb stage by stage. Station 7 says how far to trust it. Save the stage table with both rates and both bases. Deep-dive session 9 puts it beside the law.
🔍 Click to zoom - the moss station is one small function, and it is the same function whether there are two groups or twelve

The table. One row per stage, one column per group, typed in. The comment says what the numbers are before any code touches them.

★ Step 1 · the constructed funnelimport pandas as pd # constructed Apex funnel with an invented split by sex; nobody in it is real # cohort: one application cohort, every count is the same people moving through f = pd.DataFrame({ "stage": ["applied", "screened", "interviewed", "offered", "hired"], "men": [240, 80, 26, 7, 6], "women": [160, 40, 14, 5, 4], }) f["total"] = f["men"] + f["women"] print(f["total"].tolist()) # [400, 120, 40, 12, 10]

Conversion, with the base as its own column. The base is the stage before; shift(1) puts it on the same row so it prints beside the ratio.

★ Step 2 · conversion and its basef["base"] = f["total"].shift(1) f["conversion"] = (f["total"] / f["base"] * 100).round(1) print(f[["stage", "total", "base", "conversion"]].to_string(index=False)) # stage total base conversion # applied 400 NaN NaN # screened 120 400.0 30.0 # interviewed 40 120.0 33.3 # offered 12 40.0 30.0 # hired 10 12.0 83.3

Yield, on the top of the funnel. A different base and a different column name, so the two can never be confused in a dashboard.

★ Step 3 · yield on 400f["yield"] = (f["total"] / f["total"].iloc[0] * 100).round(1) print(dict(zip(f["stage"], f["yield"]))) # {'applied': 100.0, 'screened': 30.0, 'interviewed': 10.0, 'offered': 3.0, 'hired': 2.5} print(f"end to end: {f['total'].iloc[-1]} hires of {f['total'].iloc[0]} applicants") # end to end: 10 hires of 400 applicants

Selection rates by group, at every stage, on each stage's own base. A long table with the selected count and the base on every row, because the ratio in the next step is built from these four columns.

★ Step 4 · rates with both countsGROUPS = ["men", "women"] rows = [] for i in range(1, len(f)): for g in GROUPS: sel, base = int(f.loc[i, g]), int(f.loc[i - 1, g]) rows.append((f.loc[i, "stage"], g, sel, base, round(sel / base * 100, 1))) rates = pd.DataFrame(rows, columns=["stage", "group", "selected", "base", "rate"]) print(rates.to_string(index=False)) # stage group selected base rate # screened men 80 240 33.3 # screened women 40 160 25.0 # interviewed men 26 80 32.5 # interviewed women 14 40 35.0 # offered men 7 26 26.9 # offered women 5 14 35.7 # hired men 6 7 85.7 # hired women 4 5 80.0

The impact ratio, as a function of any number of groups. Each group's rate over the highest group's rate. With two groups the lower one is the number that matters; with twelve, every one under the rule of thumb does.

★ Step 5 · lower rate over highestdef impact_ratios(sel, base): """Each group's selection rate over the highest group's rate. sel, base: {group: count}.""" r = {g: sel[g] / base[g] for g in sel} top = max(r.values()) return {g: round(v / top, 3) for g, v in r.items()} print(impact_ratios({"men": 80, "women": 40}, {"men": 240, "women": 160})) # {'men': 1.0, 'women': 0.75}

Every stage, flagged, and the end-to-end row at the bottom. The flag is the four-fifths rule of thumb applied per stage; the last row shows what a single headline ratio would have reported.

★ Step 6 · the stage tableout = [] for i in range(1, len(f)): ir = impact_ratios({g: f.loc[i, g] for g in GROUPS}, {g: f.loc[i - 1, g] for g in GROUPS}) low = min(ir, key=ir.get) if min(ir.values()) < 1 else "equal" out.append((f.loc[i, "stage"], low, ir[low] if low != "equal" else 1.0, min(ir.values()) < 0.8)) e2e = impact_ratios({g: f[g].iloc[-1] for g in GROUPS}, {g: f[g].iloc[0] for g in GROUPS}) out.append(("end to end", "equal" if min(e2e.values()) == 1 else min(e2e, key=e2e.get), min(e2e.values()), min(e2e.values()) < 0.8)) table = pd.DataFrame(out, columns=["stage", "lower_group", "impact_ratio", "under_four_fifths"]) print(table.to_string(index=False)) # stage lower_group impact_ratio under_four_fifths # screened women 0.750 True # interviewed men 0.929 False # offered men 0.754 True # hired women 0.933 False # end to end equal 1.000 False

One person either way, and the distance to the line. Printed beside every ratio, so a reader can see which ones would survive a single changed decision.

★ Step 7 · the one-person testprint("offer as is: ", impact_ratios({"men": 7, "women": 5}, {"men": 26, "women": 14})) print("offer, one more man: ", impact_ratios({"men": 8, "women": 5}, {"men": 26, "women": 14})) print("hire, one fewer woman:", impact_ratios({"men": 6, "women": 3}, {"men": 7, "women": 5})) # offer as is: {'men': 0.754, 'women': 1.0} # offer, one more man: {'men': 0.862, 'women': 1.0} # hire, one fewer woman: {'men': 1.0, 'women': 0.7} need = next(k for k in range(40, 161) if min(impact_ratios({"men": 80, "women": k}, {"men": 240, "women": 160}).values()) >= 0.8) print("women screened in for the screen to reach 0.8:", need, "of 160") # 43 of 160 table.to_csv("funnel_impact_ratios_constructed.csv", index=False)

Keep the stage loop and the counts. A funnel report that computes one impact ratio, at the end, can pass while two stages fail. A report that loops over stages, prints both counts beside every rate and adds the one-person width is three more lines of pandas and is the version an auditor, a lawyer and a board can each read without asking which base you used.

Homework

Before deep-dive session 5

30 min
  1. Run the seven steps on the constructed funnel, then change the offer stage to 8 of 26 men and re-run step 6. Write which rows changed and why the end-to-end row changed too.
  2. Add a third group to the DataFrame with counts you invent and label as invented. Confirm impact_ratios needs no change, and note which group is now the highest at each stage.
  3. Build the table for a funnel you own, as one application cohort, with a "recorded" count per stage before any ratio. Mark every ratio whose one-person width crosses 0.8.
  4. Open the screening simulator in the AI + HR course and move a threshold until the screen's impact ratio crosses four-fifths. Write one sentence on what moved: the rates, the bases, or both.
Check yourself

Three questions

Clear all three and deep-dive session 4 gets stamped in your passport.

1 · At Apex's screen, 80 of 240 men and 40 of 160 women were screened in (an invented split). What is the impact ratio?

The impact ratio divides selection rates, each on its own base. Dividing the counts ignores that more men applied; the share of the screened pool is a composition, not a rate.

2 · The end-to-end impact ratio on the constructed funnel is exactly 1.000. A colleague writes "the funnel shows no adverse impact". What is wrong?

An end-to-end ratio is an outcome, not a process. It can hide a failing stage behind a lenient one, or compound mild stages into a failing whole. Loop over the stages.

3 · The offer stage is 7 of 26 men and 5 of 14 women, an impact ratio of 0.754. With one more man offered it would be 0.862. How should the stage be reported?

Small bases make ratios swing across the line on one person. The honest report shows the swing rather than hiding the stage or choosing the flattering version.

Sources covered

What this session teaches, and where it came from

✓The Apex funnel 400 / 120 / 40 / 12 / 10; conversions 30.0 / 33.3 / 30.0 / 83.3; yields 30.0 / 10.0 / 3.0 / 2.5; the split by sex and the stage impact ratios 0.750 / 0.929 / 0.754 / 0.933, end to end 1.000Constructed; the funnel and the split by sex are invented, and every output is the arithmetic of the typed-in counts
✓Conversion, yield, selection rate and impact ratio as definitions; the stage-by-stage audit; the one-person widthStandard practice, no single source
✓29 CFR 1607.4(D), Uniform Guidelines 1978: a selection rate less than four-fifths (80 percent) of the highest group's rate is generally regarded as evidence of adverse impactPrimary; stated in one card as a rule of thumb; the simulator and legal map are in the AI + HR course
✓New York City Local Law 144 of 2021, enforced from 5 July 2023: annual independent bias audit of automated employment decision tools, published summary, impact ratios by sex and race/ethnicityPrimary
✓Bertrand and Mullainathan 2004, "Are Emily and Greg More Employable Than Lakisha and Jamal?", AER 94(4) 991-1013: about 50 percent more callbacks for white-sounding namesPrimary
✓Hoffman, Kahn and Li 2018, "Discretion in Hiring", QJE 133(2) 765-800: test-based hiring raised completed tenure by about 25 percent (0.23 log points); override hires left soonerPrimary
◐The screening simulator, fairness in practice and the legal mapLinked to the AI + HR course; not built here
◐Significance tests for impact ratios on small countsNamed; taught in the Statistics course

Every fact on this page, and its verification tier, is recorded in the course's source map.

Deep-dive session 4 cheat sheet · pin this

Conversion vs yieldtotal / total.shift(1) vs total / total.iloc[0]. Apex: 33.3 of 120 vs 10.0 of 400. Print the base.
A funnel is a cohortSame people through every stage. Monthly event counts divided by each other are not a conversion.
Impact ratioEach group's selection rate over the highest group's, on that stage's own base. Four counts behind it.
Four-fifths29 CFR 1607.4(D): under 80 percent of the highest rate is generally regarded as evidence of adverse impact. A rule of thumb.
Stage by stageScreen 0.750, offer 0.754, end to end 1.000 (constructed). The headline hides both. Audit where the decision is.
One personOffer 0.754 becomes 0.862 with one more man. Print the width; LL144 publishes the ratios, read the counts first.