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

Survey psychometrics

Pay is recorded. Tenure is computed from a date. A survey score is different: somebody chose to answer, chose a number between one and five, and the twelve numbers were averaged into a score that then appears on a dashboard with the same confidence as a headcount. Tonight is about what that score can carry. A constructed survey of 600 responses to 12 five-point items, generated with a seed and with one item planted to measure almost nothing, is scored, tested with Cronbach's alpha by the formula, and re-scored without the bad item. Then the two questions that no formula answers: who answered, and how small a group may be shown before a mean becomes a name. The Gallup engagement figure is read by edition, 23 percent in one, 21 and 20 in the next two, and the sentence that usually follows it about managers is left where it belongs, unmeasured.

🟡 Core People analysts, survey owners, anyone who reports an engagement score Python, numpy, pandas 45 min
0-3 · Welcome 3-18 · Alpha, the bad item, who answers 18-40 · Build-along: 600 constructed responses 40-45 · Q&A
Part 0

Where we start

Foundations session 5 made the case in words: a five-point average is not a temperature, the people who answer are not the people, and a group of three has no anonymity. Tonight makes each of those a computation. The survey is constructed on the page: Apex's real headcount on 1 January 2026, 1,022 people, is the invited population; 600 of them answer, a chosen count; every answer is generated from an invented trait with noise; and item 9 is built to carry almost none of that trait, so that the reliability formula has something to find. Nothing in it describes any real survey. The mechanism does.

🎫 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 seeded survey generator with a planted bad item, Cronbach's alpha written from the 1951 formula in four lines, item-rest correlations that point at the item, a response funnel whose bias was planted on purpose so you can see what it does to the score, a minimum group size applied in code before any group is shown, and a correlation printed next to the words that stop it becoming a cause.

Part 1 · covers items as a bundle and Cronbach's alpha

Twelve items, one score, and the number that says whether they belong together

5 min live

An engagement score is usually the mean of a bundle of items: I know what is expected of me, I have what I need, my opinion counts, and so on. Averaging them is a claim that they measure one underlying thing. Cronbach 1951 (Psychometrika 16(3) 297-334) gave the number that tests the claim: alpha, computed from the item variances and the variance of the total, which rises when the items move together and falls when they do not. It is a lower bound on the reliability of the total score, and it is the first thing to compute before a score is shown to anybody.

TWELVE ITEMS, ONE SCORE, ONE FORMULA 600 sheets invented q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 item 9: planted to carry almost no signal one score mean of the items or 11, one dropped Cronbach's alpha k / (k - 1) times (1 - sum of item variances / variance of the total score) k = 12, or 11 without q9 Cronbach 1951, Psychometrika 16(3) 297-334: alpha is a lower bound on the reliability of the total score. 600 constructed responses, five-point items, seed stated; the bad item is q9, and the code has to find it. Alpha says whether the items move together. It does not say whether they measure the thing on the label.
🔍 Click to zoom - the hachured item is the one the generator starved of signal; alpha drops because of it and rises without it
LiveWhat alpha computes, and what it does not3 min▶

Take the total score, the sum of the twelve items per person. Its variance has two sources: each item's own variance, and the covariance between items. If the items measure one thing, they co-vary, and the variance of the total is much larger than the sum of the item variances. Alpha is k over k minus 1, times one minus the ratio of those two, so it approaches one when the covariance dominates and falls toward zero when the items are unrelated noise. Four lines of numpy; step 2 of the build-along.

What it does not compute. Alpha does not say the items measure engagement, or anything with a name; it says they measure the same thing as each other. Twelve rewordings of one question give a superb alpha and no information. Alpha also rises mechanically with the number of items, so a long scale with weak items can outscore a short scale with strong ones. Read it with the item-rest correlations in Part 2, never alone, and never as a grade for the survey.

Self-studyA five-point mean is not a temperature2 min▶

Foundations session 5 drew the same mean from two distributions: a team where everyone answered 3, and a team where half answered 1 and half answered 5. The mean is identical; the teams are not. In code the habit is to print the distribution beside the mean: the share at each point, or at least the share at 1 and 2 together and at 4 and 5 together. A change of 0.2 in a mean can be everyone moving slightly or a few people moving a long way, and only the distribution says which. The same applies to alpha: it summarises a covariance matrix, and the matrix is what to look at when the number surprises you.

Real world

The item that was really a different question. Constructed setting, common mechanism. A twelve-item engagement scale carried a question about the canteen. It had been added years earlier because somebody wanted the data, and it had been averaged into the engagement score ever since. Alpha for the scale was acceptable, because eleven good items carry a twelfth; the item-rest correlation for the canteen question sat near zero. Dropping it raised alpha and moved every team's score by a small amount that had nothing to do with engagement. Nobody had ever computed either number. The formula is from 1951 and takes four lines.

Part 2 · covers alpha as shared variance and the planted item

Finding the item that does not belong

5 min live

Alpha is a property of the bundle. To find which item is dragging it down, correlate each item with the sum of the other eleven: the item-rest correlation. An item that measures the shared thing correlates well with the rest; an item that measures something else, or nothing, does not. On the constructed survey item 9 was built with almost no loading on the trait, so its item-rest correlation sits near zero and alpha without it is higher. On a real survey nobody planted anything, and the same two numbers are how you find out.

ALPHA IS SHARED VARIANCE, AND ONE ITEM SHARES NONE 0 high q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 ITEM-REST CORRELATION: EACH ITEM AGAINST THE SUM OF THE OTHER ELEVEN alpha, all 12 items alpha, q9 dropped: higher alpha rises when a noisy item leaves; it also rises when items repeat each other Constructed: 600 responses, 12 items; the generator gave q9 almost no loading. Your bars will differ. Bar heights are schematic. The build-along prints the real item-rest correlations for your seed.
🔍 Click to zoom - eleven bars share the trait and one shares nothing; the magnifier is the item-rest correlation, and it is the number that names the item
LiveDrop it, re-score, and say what moved3 min▶

The decision to drop an item is not statistical alone. Alpha says the item does not share variance with the rest; it does not say whether the item was wrong or the rest were. If the eleven items ask about the manager and the twelfth asks about pay, the twelfth is a different construct, not a bad question, and it belongs in its own scale. In the constructed survey the case is clean because the generator made it so. In a real survey, read the item's wording before reading its correlation, and write down why it was dropped, because every team's score will move when it goes and someone will ask.

Say what moved. When an item is dropped, the score changes for everyone, by an amount unrelated to anything anybody did. Publish the old score, the new score and the reason, once. A dashboard that silently switches from twelve items to eleven between two quarters has manufactured a trend, and that trend will be read as one.

Self-studyCorrelation is not cause, and the engagement literature is where it hurts most2 min▶

Step 6 of the build-along correlates the survey score with an invented flag for leaving within six months, and finds a correlation, because the generator planted one. On a real table the same correlation appears often, and it does not say which way the arrow points: disengaged people leave, people who have already decided to leave answer surveys as disengaged, and a bad quarter produces both. The figure "engagement drives performance", in any of its circulating forms, has no measured source in the map and is not claimed. The experimentation and causal inference courses are where an arrow gets a direction; a survey correlation is a reason to design that study, not a substitute for it.

Real world

Same survey, two scales. Constructed setting. A people team ran the item-rest correlations on a fourteen-item scale and found that the items split cleanly into two bundles of seven, with high correlations inside each bundle and low ones across. The scale had been measuring two things, roughly "my manager" and "my workload", and averaging them into one score had been hiding that teams with a good manager and a bad workload looked the same as the reverse. Two scores, two alphas, and a dashboard that finally distinguished the two problems. The formula did not find that on its own; a person looked at the matrix.

Part 3 · covers response bias and the anonymity threshold

Who answered, and who may be shown

5 min live

Alpha is computed on the people who answered. The score is reported as if it described everyone. Between those two sits the response funnel, and the funnel is not random: the people most likely to skip a survey are often the people the survey most needs to hear. The constructed survey plants that bias deliberately, so that you can measure the gap between the respondents' trait and the population's. Then a second, unrelated constraint: a mean over three people is not a statistic, it is a disclosure, and a minimum group size has to be applied in code before any group is shown.

WHO ANSWERS, AND WHO MAY BE REPORTED invited 1,022 = Apex headcount on 1 Jan 2026 responded 600, a chosen count, 58.7 percent who answers is not random: the generator makes the engaged more likely to answer, on purpose MIN GROUP SIZE 8, a stated choice not reported reported A mean over 3 people identifies them; the threshold is a minimum count, applied before any group is shown. Common practice puts it at 5 to 10, no single source; the code uses 8 and says so. Constructed survey. 1,022 is Apex's real headcount; the 600 responses, every answer and the group sizes are invented.
🔍 Click to zoom - the wall is a filter on the count, not on the score; a group is either big enough to be shown or it is not, whatever its mean
LiveResponse bias you can measure, and response bias you cannot3 min▶

In the constructed survey the trait is known for all 1,022 people, so the mean trait of the 600 who answered can be compared with the mean of the 422 who did not, and the gap is the bias. On a real survey the trait of the non-respondents is exactly what is unknown, and the only handles are the things known for everyone: department, tenure, grade, entity. Compare the respondents to the invited on those. If Sales, with its 50 percent attrition on the start base, answered at half the rate of Finance, the company score is a Finance score wearing a company label, and reweighting by department is the least that is owed.

The threshold is a count, applied first. Group the respondents, count each group, and drop every group under the minimum before computing a single mean. The order matters: a pipeline that computes means and then hides the small ones has already put the means in a file. The minimum itself is a choice; five to ten is common practice with no single source, and the code uses eight and prints the word "stated" beside it. What is not a choice is that the threshold applies to every cut, including the cuts nobody planned: department by tenure band by sex is a small group even when each margin is large.

Self-studyGallup's number, by edition, and the sentence that follows it2 min▶

Gallup's State of the Global Workplace reported 23 percent of employees engaged in its 2024 edition, on 2023 data. The 2025 edition reported 21 percent and the 2026 edition 20. Any of the three is quotable with its edition named; "Gallup says 23 percent" without the year is a number drifting loose from its measurement, and it is reported here as Gallup's figure, not recomputed. The sentence that usually travels with it, that people leave managers and not companies, has no measured source: it traces to Buckingham and Coffman's First, Break All the Rules of 1999, a book and not a statistic, and it is not claimed on any page here. Apex's own exit reasons give Management/Culture 23 of 210, behind Better Opportunity, Career Growth and Compensation, and a reason on an exit form is what the leaver chose to write, which is its own response bias.

Build-along

Alpha, the bad item and the wall, on 600 constructed responses

★ 22 min · everyone builds

Seven steps. Step 1 invites Apex's real 1,022 people, spread across the eight departments at their real 1 January headcounts, gives each an invented trait, and draws 600 respondents with a probability that rises with the trait. Twelve five-point items are generated from the trait; item 9 gets almost no loading. Everything after that is computation on invented answers, so no output is printed on the page: your numbers will differ.

SIX STATIONS 1 · generate 1,022 invited 600 answer 2 · items 12 five-point q9 planted 3 · alpha by the formula 12 items 4 · drop q9 item-rest r alpha again 5 · the wall min group 8 who is shown 6 · the sentence no cause claimed no folk claim Station 1 decides who answers, so the score is biased before an item is read. On a real survey nobody knows how much. Save alpha with the seed in the filename. A survey score never enters the headcount model of session 8.
🔍 Click to zoom - the hachured station is where the bias is planted; every later number inherits it, and on a real survey the amount is unknown

Invite Apex, plant the trait, draw the respondents. Departments at their real 1 January 2026 headcounts. The trait is invented; the response draw depends on it, which is the planted bias.

★ Step 1 · generate, seededimport numpy as np, pandas as pd rng = np.random.default_rng(2026) # the seed; change it and every number below moves # Apex headcount by department on 1 January 2026, the real counts from deep-dive session 1 depts = {"Operations": 218, "Engineering": 213, "Finance": 145, "Sales": 108, "Customer Service": 93, "R&D": 94, "Marketing": 76, "HR": 75} invited = pd.DataFrame({"department": np.repeat(list(depts), list(depts.values()))}) N = len(invited) # 1022 invited["manager"] = rng.integers(1, 121, N) # invented: about 8.5 people per manager invited["trait"] = rng.normal(0, 1, N) # invented "engagement", one per person # who answers: probability rises with the trait, on purpose; 600 is a chosen count p = 1 / (1 + np.exp(-(0.3 + 0.8 * invited["trait"]))) idx = rng.choice(N, 600, replace=False, p=p / p.sum()) invited["responded"] = False invited.loc[idx, "responded"] = True print(N, invited["responded"].sum(), f"{600 / N * 100:.1f}%") # 1022 600 58.7%

Twelve five-point items from the trait. Eleven load on it; item 9 barely does. Rounded and clipped to 1 to 5, which is what a five-point scale does to a continuous trait.

★ Step 2 · items, one plantedk = 12 loading = np.full(k, 0.9) loading[8] = 0.05 # q9: planted to carry almost no signal resp = invited[invited["responded"]].copy() raw = 3 + np.outer(resp["trait"], loading) + rng.normal(0, 0.8, (len(resp), k)) items = pd.DataFrame(np.clip(np.rint(raw), 1, 5).astype(int), columns=[f"q{j + 1}" for j in range(k)], index=resp.index) print(items.describe().loc[["mean", "std"]].round(2)) # your numbers will differ print((items == 3).mean().round(2).to_dict()) # the share at the midpoint, per item

Cronbach's alpha, by the formula. Sum of item variances over the variance of the total, from Cronbach 1951. Four lines, no library.

★ Step 3 · alphadef alpha(df): k = df.shape[1] item_var = df.var(ddof=1).sum() total_var = df.sum(axis=1).var(ddof=1) return k / (k - 1) * (1 - item_var / total_var) print(f"alpha, {items.shape[1]} items: {alpha(items):.3f}") # your numbers will differ

Item-rest correlations, then drop the item. Each item against the sum of the others. The planted item is the one near zero; alpha without it is higher, by construction.

★ Step 4 · find it, drop itrest = {c: items[c].corr(items.drop(columns=c).sum(axis=1)) for c in items} for c, r in sorted(rest.items(), key=lambda kv: kv[1]): print(f"{c:4s} item-rest r {r:+.3f}") # q9 should sit at the bottom worst = min(rest, key=rest.get) items11 = items.drop(columns=worst) print(f"alpha without {worst}: {alpha(items11):.3f}") # your numbers will differ; higher resp["score12"] = items.mean(axis=1) resp["score11"] = items11.mean(axis=1) print(f"every score moved by {(resp['score11'] - resp['score12']).abs().mean():.3f} on average, for no reason of anyone's")

The response bias, measured because it can be here. The trait is known for everyone in the constructed table, so the respondents and the silent can be compared. On a real survey only the department mix can be.

★ Step 5 · who answeredsilent = invited[~invited["responded"]] print(f"mean trait, respondents {resp['trait'].mean():+.3f}; silent {silent['trait'].mean():+.3f}") # the gap is the bias, planted by step 1; on a real survey it cannot be measured rate = invited.groupby("department")["responded"].mean().mul(100).round(1) print(rate.sort_values().to_string()) # response rate by department, your numbers will differ # the reweighting that is at least owed: weight each department's respondents back to its invited count n_resp = resp["department"].map(resp["department"].value_counts()) resp["w"] = resp["department"].map(depts) / n_resp # invited count over respondent count, per department print(f"score, unweighted {resp['score11'].mean():.3f}; reweighted to the invited mix {np.average(resp['score11'], weights=resp['w']):.3f}")

The wall, and a correlation with its warning. Count first, drop the small groups, then compute the means. Then correlate the score with an invented leaving flag and print the words that keep it a correlation.

★ Step 6 · minimum group size, then the correlationMIN_N = 8 # stated choice; 5 to 10 is common practice, no single source by_mgr = resp.groupby("manager")["score11"].agg(["count", "mean"]) shown = by_mgr[by_mgr["count"] >= MIN_N] print(f"{len(by_mgr)} manager groups, {len(shown)} shown, {len(by_mgr) - len(shown)} under the threshold of {MIN_N} (stated)") # the order matters: the means for the hidden groups were never written anywhere # invented: a leaving flag whose probability falls with the trait, planted, not observed resp["left_6m"] = (rng.random(len(resp)) < 1 / (1 + np.exp(1.6 + 0.9 * resp["trait"]))).astype(int) r = resp["score11"].corr(resp["left_6m"]) print(f"correlation of score with leaving: {r:+.3f} (planted by the generator; direction unknown on real data)")

The sentence. Alpha with the item count, the response rate with the invited base, the threshold with the word stated, and no cause.

★ Step 7 · the sentencesentence = (f"Constructed survey: {len(resp)} of {N} invited answered ({600 / N * 100:.1f}%), " f"and respondents sit higher on the trait than the silent by construction. " f"Alpha is {alpha(items11):.2f} on 11 items after dropping {worst} (item-rest r {rest[worst]:+.2f}). " f"Groups under {MIN_N} respondents (stated) are not shown. " f"The score correlates with leaving at r {r:+.2f}; no direction is claimed.") print(sentence) resp.drop(columns=["trait"]).to_csv("apex_survey_seed2026.csv", index=False) # the trait is never exported

Set the loading of item 9 to 0.9 and run again. Alpha rises, the item-rest correlations line up, and the eleven-item score becomes a twelve-item score that differs from it only by noise. Then set the response coefficient 0.8 to zero: the respondents' mean trait meets the silent's, and the score stops overstating. Two parameters, two lessons, and on a real survey neither can be turned off.

Homework

Before deep-dive session 8

30 min
  1. Run the seven steps with the seed as written, then with two others. Record alpha with and without the worst item each time, and whether the worst item was q9 every time.
  2. Plant a second construct: give items 10 to 12 a loading on a second invented trait instead of the first. Re-run step 4 and write what the item-rest correlations show, and why alpha alone would not have.
  3. Change the threshold to 5 and to 10 and count how many manager groups are shown at each. Write the sentence that goes with the number you would choose.
  4. For a survey you own, compute the response rate by department against the invited count. Name the department that would be over-represented in an unweighted company score.
Check yourself

Three questions

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

1 · Alpha for a twelve-item scale is acceptable. Why compute the item-rest correlations anyway?

Alpha rises with item count and with any shared variance, so it hides one bad item well. Item against the sum of the rest is the diagnostic; the wording of the item is the decision.

2 · 600 of 1,022 invited answered. The company score is reported as the mean of the 600. What is the least that is owed before it is called the company's score?

The constructed survey planted the bias so it could be measured. A real one cannot measure it; it can only check the mix on known variables and reweight, and say so.

3 · A dashboard shows a mean score for a team of four. Where in the pipeline should the fix go?

A mean that was computed and then hidden was still written to a file. The wall goes first, on the count, on every grouping.

Sources covered

What this session teaches, and where it came from

✓Cronbach's alpha computed by the formula; item-rest correlationsCronbach 1951, "Coefficient alpha and the internal structure of tests", Psychometrika 16(3) 297-334, primary
✓The constructed survey: 1,022 invited at Apex's real department headcounts, 600 respondents, 12 five-point items, q9 planted, response bias planted, seed 2026Generated on the page's own build script; every answer invented; 1,022 and the department counts are computed from the constructed Apex table; no output printed
✓Response bias and reweighting; the anonymity threshold as a minimum group size applied before any meanStandard practice, no single source; the threshold of 8 is a stated choice, 5 to 10 being common
✓Gallup, State of the Global Workplace: 23 percent engaged in the 2024 edition (2023 data); 21 in the 2025 edition; 20 in the 2026 editionSecondary; reported with the edition named
◐"People leave managers, not companies"Not claimed: traces to Buckingham and Coffman, First, Break All the Rules (1999), not to a measured statistic
◐Correlation of a survey score with leaving; "engagement drives performance"The correlation is planted on constructed data; no direction claimed; no performance figure claimed; the experimentation and causal inference courses own the arrow

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

Deep-dive session 7 cheat sheet · pin this

Alphak/(k-1) x (1 - sum of item variances / variance of the total). Lower bound on reliability.
Item-rest correlationEach item against the sum of the others. The one near zero is the one to read.
Alpha is not validityTwelve rewordings score high and say nothing. Read the matrix.
Who answeredCompare respondents to invited on what is known for all. Reweight. Say so.
The wallCount, drop under the stated minimum, then compute. Every cut, planned or not.
Gallup by edition23 (2024 edition), 21 (2025), 20 (2026). The managers sentence has no measured source.