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

Regretted, and benchmarked

Two questions follow every attrition number: was it the people we wanted to keep, and is it high? Both sound like measurements and both are choices dressed as measurements. "Regretted" is a list of reason categories somebody ticked, and the share moves when the list does: on Apex's 210 constructed exits, five ticked categories give 65.2 percent, seven give 81.4, three give 45.2. "High" needs a comparison, and the only open, monthly, national one is the US quits rate from BLS JOLTS, which is a different base on a different clock. Tonight computes both in pandas, fetches JOLTS live without a key, and writes the arithmetic that stops a monthly 2.0 from being read as an annual 2.0, or an annual 44.6 from being read as twenty times the nation.

🟡 Core HR analysts, people data teams, anyone asked "is our attrition high" Python, pandas, requests to a public API 45 min
0-3 · Welcome 3-18 · Reasons, bases, the benchmark 18-40 · Build-along: Apex reasons and JOLTS in Python 40-45 · Q&A
Part 0

Where we start

Deep-dive session 2 left the 210 exits on a curve and proved the curve too high. Tonight leaves the curve alone and asks two things of the same 210 rows: the reason column, and the department column with its base. Then it reaches outside the constructed company for the first time, to a real monthly series from the US Bureau of Labor Statistics, and spends most of its care on why that number and Apex's cannot be laid side by side without a sentence of arithmetic between them. Apex is constructed; JOLTS is real and fetched on the page's build date, 26 September 2026.

🎫 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 regretted share whose list is printed beside it; department rates with the base under every one; a working call to the BLS public API in seven lines; the peak, the trough and two annual means of the US quits rate; and the two-line caveat that turns a monthly rate into a rough annual scale without pretending it is one.

Part 1 · covers reason categories and regretted as a classification

Regretted is a list you chose

5 min live

Apex's exits carry a free-text reason, already coded into eleven categories: Better Opportunity 33, Career Growth 33, Compensation 29, Management/Culture 23, Relocation 19, Work-Life Balance 19, Unknown 17, Personal/Health 15, Involuntary/Performance 10, Retirement 9, Further Study 3. None of those is "regretted". Regretted is a line drawn through the list, and where the line goes is a decision that deserves to be printed next to the share it produces.

ELEVEN REASONS, AND A LINE SOMEBODY DREW THROUGH THEM Better Opportunity33 Career Growth33 Compensation29 Management/Culture23 Relocation19 Work-Life Balance19 Unknown17 Personal/Health15 Involuntary/Performance10 Retirement9 Further Study3 REGRETTED IS A LIST YOU CHOSE five ticked categories: 137 of 210 65.2% add Relocation and Personal/Health: 171 of 210 81.4% only the three market reasons: 95 of 210 45.2% same five, Unknown out of the base: 137 of 193 71.0% one table, four defensible shares the list is the finding; print it beside the number Constructed Apex table, 210 exits. The five ticked rows are the list these pages use. The other three lists are computed on the same rows to show the swing.
🔍 Click to zoom - the tick boxes are the analysis; the bars are only the data
LiveFour shares from one table, and which one is right3 min▶

The list these pages use ticks five categories as regretted: Better Opportunity, Career Growth, Compensation, Management/Culture and Work-Life Balance. The idea behind the line is "could we have kept them": those five are reasons an employer might have answered, and the rest are reasons it could not (Retirement, Further Study, Relocation), did not want to (Involuntary/Performance), or cannot classify (Unknown, Personal/Health). Five ticks give 137 of 210 = 65.2 percent.

Every one of those placements is arguable. Relocation is unavoidable unless remote work was on the table; Personal/Health is unknowable unless the exit interview said more. Tick both and the share is 171 of 210 = 81.4 percent. Decide that only the three market reasons count, the ones a counter-offer could have touched, and it is 95 of 210 = 45.2 percent. Keep the five but take the 17 Unknowns out of the base, because they might be anything, and it is 137 of 193 = 71.0 percent. All four are computed on the same constructed rows; none is wrong.

So the deliverable is not a share. It is a share with its list printed beside it, and the list is what a colleague can disagree with. "Regretted attrition is 65 percent" is an assertion; "65.2 percent, counting these five categories as regretted" is a finding, and it tells the reader exactly which tick to argue about.

Self-studyWhere the categories came from, and the row that says "offer I couldn't refuse"2 min▶

Apex's reason_category is itself a coding of a free-text column, and the free text is what the exit interview actually captured: "offer i couldnt refuse", "underpaid for the work", "bad manager", "no growth path here". A coding step sits between those phrases and the eleven categories, and it was a person or a rule that put "offer i couldnt refuse" under Better Opportunity rather than Compensation. Print the raw phrases under each category once a year. If the coding drifts, the regretted share drifts with it, and nobody will see the drift in the category counts. On a real table, the coding rule belongs in the six decisions from deep-dive session 1, as a seventh line.

Real world

The regretted rate that fell nine points in a quarter. A people team reported regretted attrition dropping from the high sixties to the high fifties and was congratulated. The list had changed: a new HR system shipped with "Relocation" and "Work-Life Balance" mapped to a non-regretted bucket by default, and nobody had ticked them back. The 210 exits were the same kind of exits. The line had moved. Constructed setting; the mechanism is the figure above.

Part 2 · covers rates by department with the base

Eight rates, eight bases, one start date

5 min live

Cut the 210 exits by department and divide by each department's headcount on 1 January 2026: Sales 50.0 percent, Customer Service 37.6, Operations 25.2, Marketing 19.7, Engineering 11.3, HR 10.7, Finance 8.3, R&D 7.4. Eight numbers that look like one kind of thing. The base printed under each one shows they are not: Sales is 54 of 108, HR is 8 of 75, and one person leaving HR moves its rate by more than a point.

EIGHT RATES, EIGHT BASES, ONE START DATE Sales50.0%54 of 108 Customer Service37.6%35 of 93 Operations25.2%55 of 218 Marketing19.7%15 of 76 Engineering11.3%24 of 213 HR10.7%8 of 75 Finance8.3%12 of 145 R&D7.4%7 of 94 THE BASE UNDER THE RATE HR: 10.7% is eight people one more exit: 9 of 75 = 12.0% one fewer: 7 of 75 = 9.3% Sales: 50.0% is 54 people one more: 55 of 108 = 50.9% a rate that survives a person Leavers January to June 2026 over headcount on 1 January 2026. Constructed Apex table; one exit moves HR by 1.3 points.
🔍 Click to zoom - the small grey line under each bar is the whole argument: a rate without its base is a number without a size
LiveThe base under every rate, and the rate that is one person wide3 min▶

All eight rates use the same recipe from deep-dive session 1: leavers in the window over headcount on the start date. That makes them comparable in construction. It does not make them comparable in precision. Sales at 50.0 percent is 54 exits over 108 people; one more exit would read 50.9. HR at 10.7 percent is 8 exits over 75; one more exit reads 12.0, one fewer reads 9.3. The HR rate is a number about one person wide, and a ranking that puts HR at 10.7 above Finance at 8.3 is a ranking of noise.

The habit is to print the base under the rate, every time, in the table and on the slide. "HR 10.7 percent (8 of 75)" lets the reader do the one-person arithmetic in their head; "HR 10.7 percent" invites them to compare it with Finance. On a dashboard the same discipline is one row per rate with the numerator and denominator visible, which Foundations session 6 builds without code.

The two big rates are the ones worth a meeting: Sales at 50.0 percent on a base of 108 and Customer Service at 37.6 on 93 are far from the rest and wide enough to survive a person or two. That is what a base tells you: which differences are worth a conversation and which are the arithmetic of small numbers.

Self-studyRegretted by department, and why the table gets thin fast2 min▶

Cross regretted with department and the cells shrink: HR has 8 exits in total, so its regretted count is at most 8 and its regretted share is a fraction of a handful. Two cuts on 210 rows produce a table where most cells are under ten, and a share computed on a cell under ten should be shown as the count, not the percentage. The same limit applies to reason categories by entity, by tenure band, by anything. A useful rule for a people table: below about 20 in the base, print the count and the base and no rate at all; the reader can divide, and will be reminded not to.

Real world

"HR attrition has risen 40 percent." The rate had gone from 10.7 to about 15 percent, from 8 exits to 11 on a base of 75. Three people. The headline was arithmetically correct and it triggered a retention programme for a department where three departures in six months is a normal spread. Printing "8 of 75" beside the 10.7 in the earlier report would have stopped the sentence before it was written. Constructed setting; the arithmetic is the figure above.

Part 3 · covers the JOLTS benchmark and its arithmetic

A benchmark is a rate with a different base and a different clock

5 min live

"Is 44.6 percent high?" wants a comparison, and the only open, monthly, national one is the US Job Openings and Labor Turnover Survey. Its quits rate is quits during the month as a percent of employment, seasonally adjusted, all US employers, voluntary quits only. It peaked at 3.0 percent in November 2021, fell to 1.5 in April 2020, averaged 2.32 across 2019 and 2.02 across 2025, and stood at 2.0 in December 2025. Every one of those is a monthly number, and Apex's 44.6 is an annual one built by doubling a half year. They share a word and nothing else until the arithmetic is written down.

THE US QUITS RATE, 120 MONTHS, DRAWN FROM THE FETCHED SERIES QUITS, % OF EMPLOYMENT PER MONTH MONTH 1.5 2.0 2.5 3.0 3.5 2016 2018 2020 2022 2024 2026 3.0 · November 2021 1.5 · April 2020 2019 mean 2.32 2025 mean 2.02 · Dec 2.0 Peak 3.0 in November 2021; trough 1.5 in April 2020; 2019 mean 2.32; 2025 mean 2.02; December 2025 total 2.0. December 2025 by industry: leisure and hospitality 4.4 · professional and business 1.9 · education and health 1.9 · manufacturing 1.4. BLS JOLTS, seasonally adjusted: quits during the month as a percent of employment, January 2016 to December 2025. US, all employers, voluntary quits only. Fetched 26 September 2026. Not a target for any one company.
🔍 Click to zoom - the whole series lives between 1.5 and 3.0 percent per month; the unit is the first thing to say about it
LiveMonthly is not annual, and the two-line caveat3 min▶

A quits rate of 2.0 percent means that in that month, quits equalled 2.0 percent of employment. Twelve such months are about 24 percent of employment in quits over a year, if the same people never quit twice and the base never moves. Neither holds exactly, so 24 is a rough scale, not an annual rate, and the honest sentence carries both conditions: "roughly 24 percent a year, multiplying the monthly rate by 12, which assumes no repeat quits and a fixed base". Written that way it is useful. Written as "the US annual quit rate is 24 percent" it is a number nobody measured.

Apex's side has the same shape with a different clock. 22.3 percent for the half year, times two, is 44.6 percent a year, if the second half repeats the first. Now the two numbers can sit on one line: "Apex roughly 45 percent a year (H1 doubled), the US roughly 24 (monthly times 12)". Both are labelled scales, and the labels say why a reader should not subtract one from the other and call it a gap.

Then the bases. JOLTS counts voluntary quits only, across every US employer, in every industry, on the employer's own employment count. Apex's 210 include involuntary exits, retirements and study leave, in one constructed group of four entities. Remove the 22 involuntary, retirement and study exits from Apex and the comparison is a little closer; it is still a different country, a different mix of industries, and a different definition of a person. The benchmark answers "is this order of magnitude unusual", and nothing finer.

Self-studyIndustries, and the cost figure that is not claimed2 min▶

The total hides a spread. In December 2025 the quits rate was 4.4 percent in leisure and hospitality, 1.9 in professional and business services, 1.9 in private education and health, 1.4 in manufacturing; across 2025 the means were 3.92, 2.34, 1.93 and 1.39. A company in hospitality comparing itself with the total is comparing itself with the wrong line, and the API returns the industry line for the same seven lines of code. Which industry Apex "is" is itself a choice: four entities, one of them retail and one logistics, and no single JOLTS series describes the group.

One number that does not appear on any page of the track is the cost of a departure. The figure that circulates, "replacing someone costs 1.5 to 2 times salary", has no measured source; it is attributed to an analyst rather than to a study, and the range SHRM itself publishes is 50 to 200 percent of salary, which is wide enough to mean anything. A cost model can be built for one employer from its own recruiting spend, ramp time and vacancy cover, and that is a finance exercise with the employer's own inputs. It is not a benchmark, and quoting the folk number as one is the fastest way to lose the room when someone asks where it came from.

Real world

"Our attrition is twenty times the national rate." The slide compared an annualised 44.6 with a monthly 2.0 and found a ratio of twenty-two. The audience included a finance director who asked what the unit of the 2.0 was, and the meeting did not recover. The same two numbers on the same clock, 44.6 against roughly 24, both labelled with their assumption, would have made a real point: about double, on a rough scale, with a different definition of an exit. Constructed setting; the arithmetic is the build-along's last step.

Build-along

Apex reasons in pandas, and JOLTS from the BLS public API

★ 22 min · everyone builds

Seven steps: four on materials/apex-staff.csv, three on a live request to the BLS public API version 1, which needs no key and returns ten years of monthly data in one call. Every Apex number is what the code prints on the constructed table. The JOLTS numbers are what it printed on 26 September 2026; BLS revises recent months, so a later fetch can differ by a tenth, and the honest report carries its fetch date.

TWO CLOCKS, TWO ASSUMPTIONS, ONE LINE THEY CAN SHARE JOLTS · monthly, US, real 2.0 % / month x 12 about 24 % / year if nobody quits twice in the year and the base never moves a rough scale, not an annual rate Apex · half year, constructed 22.3 % / H1 x 2 44.6 % / year if H2 repeats H1 average base 941.5; all exits, not only quits an assumption, printed in the label NOT COMPARABLE: US, all employers, voluntary quits only, per month · one constructed group, all exits, per half year cost of attrition: not computed. The circulating "1.5 to 2x salary" has no measured source; SHRM's own published range is 50 to 200 percent of salary, which is wide enough to mean anything. Two annualisations, two assumptions, both printed. Each is a scale for the other; neither is a gap.
🔍 Click to zoom - the dashed box is the honest shape of a number nobody measured: drawn, labelled, and left empty

Load, keep the exits, count the reasons. The eleven categories with their counts are the data; nothing below this step is.

★ Step 1 · the eleven categoriesimport json, urllib.request import numpy as np, pandas as pd d = pd.read_csv("materials/apex-staff.csv", parse_dates=["Date Join", "Last Day"]) ex = d[d["is_attrited"]].copy() print(len(ex)) # 210 print(ex["reason_category"].value_counts().to_dict()) # {'Better Opportunity': 33, 'Career Growth': 33, 'Compensation': 29, 'Management/Culture': 23, # 'Relocation': 19, 'Work-Life Balance': 19, 'Unknown': 17, 'Personal/Health': 15, # 'Involuntary/Performance': 10, 'Retirement': 9, 'Further Study': 3}

Regretted as a list, printed beside its share. Three lists, three shares, one table. The function takes the list as an argument so that the list is visible in every call.

★ Step 2 · regretted is an argumentdef regretted_share(exits, categories, drop_unknown=False): base = exits if not drop_unknown else exits[exits["reason_category"] != "Unknown"] n = int(base["reason_category"].isin(categories).sum()) return n, len(base), round(n / len(base) * 100, 1) FIVE = ["Better Opportunity", "Career Growth", "Compensation", "Management/Culture", "Work-Life Balance"] SEVEN = FIVE + ["Relocation", "Personal/Health"] THREE = ["Better Opportunity", "Career Growth", "Compensation"] print("five: ", regretted_share(ex, FIVE)) # (137, 210, 65.2) print("seven:", regretted_share(ex, SEVEN)) # (171, 210, 81.4) print("three:", regretted_share(ex, THREE)) # (95, 210, 45.2) print("five, Unknown out of base:", regretted_share(ex, FIVE, drop_unknown=True)) # (137, 193, 71.0)

Department rates with the base printed. The same headcount function as deep-dive session 1, and a table that carries the numerator and denominator on every row.

★ Step 3 · the base under the rateSTART, END = pd.Timestamp("2026-01-01"), pd.Timestamp("2026-07-01") def headcount(df, date): return int(((df["Date Join"] <= date) & (df["Last Day"].isna() | (df["Last Day"] >= date))).sum()) rows = [] for dept, g in d.groupby("Department"): base = headcount(g, START) left = int(((g["Last Day"] >= START) & (g["Last Day"] < END)).sum()) rows.append((dept, left, base, round(left / base * 100, 1))) rates = pd.DataFrame(rows, columns=["department", "leavers", "base", "rate"]).sort_values("rate", ascending=False) print(rates.to_string(index=False)) # department leavers base rate # Sales 54 108 50.0 # Customer Service 35 93 37.6 # Operations 55 218 25.2 # Marketing 15 76 19.7 # Engineering 24 213 11.3 # HR 8 75 10.7 # Finance 12 145 8.3 # R&D 7 94 7.4

How wide is each rate? One exit more, one exit fewer. The width is the base talking, and it goes on the slide next to the rate.

★ Step 4 · one person either wayrates["plus_one"] = ((rates["leavers"] + 1) / rates["base"] * 100).round(1) rates["minus_one"] = ((rates["leavers"] - 1) / rates["base"] * 100).round(1) print(rates[rates["department"].isin(["Sales", "HR"])].to_string(index=False)) # department leavers base rate plus_one minus_one # Sales 54 108 50.0 50.9 49.1 # HR 8 75 10.7 12.0 9.3 rates.to_csv("dept_rates_2026-01-01_to_2026-07-01_with_base.csv", index=False)

Fetch JOLTS from the BLS public API, version 1, no key. One POST with the series ids. The total quits rate id is JTS followed by fifteen zeros and QUR; industry ids carry a six-digit code in place of the first six zeros. Ten years is the most version 1 returns in one call.

★ Step 5 · the requestIDS = {"total": "JTS000000000000000QUR", "manufacturing": "JTS300000000000000QUR", "leisure_hospitality": "JTS700000000000000QUR", "prof_business": "JTS540099000000000QUR", "health_education": "JTS600000000000000QUR"} body = json.dumps({"seriesid": list(IDS.values()), "startyear": "2016", "endyear": "2025"}).encode() req = urllib.request.Request("https://api.bls.gov/publicAPI/v1/timeseries/data/", data=body, headers={"Content-Type": "application/json"}) resp = json.load(urllib.request.urlopen(req, timeout=60)) print(resp["status"]) # REQUEST_SUCCEEDED series = {} for s in resp["Results"]["series"]: name = [k for k, v in IDS.items() if v == s["seriesID"]][0] series[name] = pd.Series({r["year"] + "-" + r["period"][1:]: float(r["value"]) for r in s["data"] if r["period"] != "M13"}).sort_index() print(len(series["total"]), series["total"].index[0], series["total"].index[-1]) # 120 2016-01 2025-12

Peak, trough, two annual means, and the industries. The printed values are the fetch of 26 September 2026. Print the fetch date with them; BLS revises.

★ Step 6 · the benchmark numbersq = series["total"] print("peak", q.idxmax(), q.max(), "| trough", q.idxmin(), q.min()) # peak 2021-11 3.0 | trough 2020-04 1.5 print("2019 mean", round(q[q.index.str.startswith("2019")].mean(), 2)) # 2.32 print("2025 mean", round(q[q.index.str.startswith("2025")].mean(), 2)) # 2.02 print("Dec 2025", q["2025-12"]) # 2.0 for k in ("leisure_hospitality", "prof_business", "health_education", "manufacturing"): s = series[k] print(f"{k:20s} 2025 mean {s[s.index.str.startswith('2025')].mean():.2f} Dec 2025 {s['2025-12']}") # leisure_hospitality 2025 mean 3.92 Dec 2025 4.4 # prof_business 2025 mean 2.34 Dec 2025 1.9 # health_education 2025 mean 1.93 Dec 2025 1.9 # manufacturing 2025 mean 1.39 Dec 2025 1.4 print("fetched", pd.Timestamp.today().date())

The two clocks on one line, and the number that stays uncomputed. Both annualisations carry their assumption in the string. The cost line prints a refusal, on purpose, so the script cannot be read as having a cost figure in it.

★ Step 7 · the comparison sentencejolts_scale = q["2025-12"] * 12 # 24.0 apex_annual = 210 / 941.5 * 200 # 44.6, average base, H1 doubled print(f"US quits, December 2025: {q['2025-12']} per month; x12 = roughly {jolts_scale:.0f} a year " f"(assumes no repeat quits and a fixed base). Voluntary quits only, all US employers.") print(f"Apex H1 2026: 22.3 percent on the average base; x2 = {apex_annual:.1f} a year " f"(assumes H2 repeats H1). All exits, including 22 involuntary, retirement and study.") print("Same order of magnitude test only. Different country, industries, exit definition and clock.") print("cost of attrition: NOT computed; the 1.5 to 2x salary figure has no measured source.")

Keep the list in the function signature and the fetch date in the output. regretted_share(ex, FIVE) cannot be run without showing which five. A JOLTS printout with a date on it can be reproduced or explained a year later when the revised series says 2.1 where the slide said 2.0. Both habits cost one line and save a meeting.

Homework

Before deep-dive session 4

30 min
  1. Write your own regretted list for your export, as a Python list with a one-line comment per category saying why it is in or out. Run regretted_share with it and with the five-category list, and keep both numbers.
  2. Print the base under every rate in your department table, then add the plus-one and minus-one columns. Mark every row whose width is over two points; those rates get a count on the slide, not a percentage.
  3. Fetch one more JOLTS series, the industry closest to yours, and re-run step 7 with it in place of the total. Write the comparison sentence with both assumptions in it.
  4. Bring a funnel. Deep-dive session 4 leaves the leavers and turns to the joiners: applicants, screens, interviews, offers, hires, and the one ratio in hiring that the law has an opinion about.
Check yourself

Three questions

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

1 · Regretted attrition on Apex is 65.2 percent with five categories ticked, 81.4 with seven, 45.2 with three. Which is the correct regretted rate?

All four shares on the figure are computed correctly on the same 210 rows. The number is not the analysis; the line through the eleven categories is.

2 · HR shows 10.7 percent and Finance 8.3. A slide ranks HR above Finance on attrition. What is wrong with the ranking?

The rates share a recipe, not a precision. Sales at 54 of 108 survives a person; HR at 8 of 75 does not. The base under the rate is what says which is which.

3 · The US quits rate was 2.0 percent in December 2025. Apex's annualised attrition is 44.6 percent. A colleague writes "Apex is 22 times the national rate". What is the honest version?

A monthly rate and an annual rate share a word and nothing else until the arithmetic is written. Option C is arithmetic on a different base and clock again; the assumptions still have to be said.

Sources covered

What this session teaches, and where it came from

✓The eleven Apex reason categories and counts; regretted as a five-category list, 137 of 210 = 65.2 percent; the alternative lists 81.4, 45.2 and 71.0Computed from the constructed Apex table that ships with these pages; the five-category list is a stated choice
✓Department rates on the start base with n: Sales 50.0 (54 of 108) to R&D 7.4 (7 of 94); the one-person width of HR at 8 of 75Computed from the constructed Apex table
✓BLS JOLTS quits rate: definition; peak 3.0 November 2021; trough 1.5 April 2020; 2019 mean 2.32; 2025 mean 2.02; December 2025 total 2.0; industries 4.4 / 1.9 / 1.9 / 1.4Primary; fetched from the BLS public API version 1 on 26 September 2026; the December 2025 figure matches the BLS release of 5 February 2026
✓Monthly times 12 as a rough scale; H1 times 2 as 44.6Modelled; both assumptions are printed with the number, and neither is a benchmark for the other
◐"Replacing an employee costs 1.5 to 2 times salary"Not claimed; no measured source; SHRM's own range is 50 to 200 percent of salary; the page teaches why it is left uncomputed
◐The free-text to category coding stepNamed in self-study as a seventh decision; not audited here

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

Deep-dive session 3 cheat sheet · pin this

Regretted is an argumentregretted_share(exits, LIST). Apex: 137 of 210 = 65.2 with five ticked; 81.4 with seven; 45.2 with three.
Print the list with the shareThe list is the finding. The share is what the list produces.
Base under every rateHR 10.7 is 8 of 75: one exit moves it to 12.0 or 9.3. Below about 20 in the base, print the count.
JOLTS in seven linesPOST to api.bls.gov v1, no key, ids JTS + 15 zeros + QUR; industries swap the first six.
The benchmarkPeak 3.0 Nov 2021, trough 1.5 Apr 2020, 2025 mean 2.02, Dec 2025 2.0. Monthly, US, voluntary quits.
Two clocks, both labelled2.0 x 12 is roughly 24 (no repeats, fixed base). 22.3 x 2 = 44.6 (H2 repeats H1). No cost figure.