SPB Git

spb/firc0001_uqo

Public

N9: fixed-2010 cohort series and windowed-key (k = 3, 5) first-seen series for Table 14 / Figure 5

Items: FIRC0001-ADVE-13 ADVE-14 ECON-14 THEO-15.
2011-2025 first-seen range 6,240-8,216 (all issuers; 2010 = 7,835 left-censored, 2026 partial); fixed cohort of 1,389 issuers:
new projects per issuer 4.7 (2011) -> 2.8 (2025) while mentions per issuer stay 9.6 -> 8.8; k = 3 / 5 episodes 131,823 / 126,318 vs 121,099.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Nov 5, 2026) parent 09b4ff3 Browse files

5 changed files +202 −0

added code/audit/a09_timeseries_cohort.py +47 −0
@@ -0,0 +1,47 @@
1+"""N9 - fixed-2010 cohort series and windowed-key first-seen series for Table 14 / Figure 5 (cluster K). Read-only."""
2+from __future__ import annotations
3+import sys
4+from pathlib import Path
5+sys.path.insert(0, str(Path(__file__).resolve().parent))
6+from common import *
7+
8+r = Run("timeseries")
9+con = connect(SPID_DB)
10+con.execute("""CREATE TEMP TABLE fy AS SELECT cik, year(filing_date) yr, count(*) n FROM src_filings GROUP BY 1,2""")
11+con.execute("""CREATE TEMP TABLE cohort AS SELECT cik FROM fy WHERE yr BETWEEN 2010 AND 2025 GROUP BY 1 HAVING count(DISTINCT yr) = 16""")
12+S = {"cohort_issuers": con.execute("select count(*) from cohort").fetchone()[0], "all_issuers_with_filings": con.execute("select count(distinct cik) from src_filings").fetchone()[0],
13+ "max_filing_date": str(con.execute("select max(filing_date) from src_filings").fetchone()[0]), "min_filing_date": str(con.execute("select min(filing_date) from src_filings").fetchone()[0]),
14+ "note": "2010 is left-censored (first_seen cannot precede the corpus start); 2026 is partial (filings to the max date)"}
15+con.execute("""CREATE TEMP TABLE _k AS SELECT m.*, lower(coalesce(try(locations[1]), '')) AS loc1,
16+ regexp_extract(lower(regexp_replace(project_name, '[^A-Za-z ]', '', 'g')), '([a-z]{4,})', 1) AS name_tok FROM project_mentions m""")
17+con.execute("""CREATE TEMP TABLE k AS SELECT *, cik || '|' || project_type || '|' || CASE WHEN loc1 <> '' THEN loc1 ELSE coalesce(nullif(name_tok,''),'general') END AS gkey FROM _k""")
18+# windowed key: a new episode starts when the gap to the previous mention of the same gkey exceeds kk years
19+con.execute("""CREATE TEMP TABLE ep AS SELECT gkey, cik, filing_date,
20+ lag(filing_date) OVER (PARTITION BY gkey ORDER BY filing_date, mention_id) prev FROM k""")
21+def series(where_cohort: str, label: str):
22+ return con.execute(f"""
23+ WITH yrs AS (SELECT unnest(generate_series(2010, 2026)) yr),
24+ iss AS (SELECT yr, count(DISTINCT cik) issuers_filing FROM fy WHERE {where_cohort.replace('k.cik','fy.cik')} GROUP BY 1),
25+ men AS (SELECT year(filing_date) yr, count(*) mentions, count(DISTINCT cik) issuers_with_mentions FROM k WHERE {where_cohort} GROUP BY 1),
26+ fs AS (SELECT year(min(filing_date)) yr, gkey FROM k WHERE {where_cohort} GROUP BY gkey),
27+ fsy AS (SELECT yr, count(*) first_seen_current FROM fs GROUP BY 1),
28+ e3 AS (SELECT year(filing_date) yr, count(*) first_seen_k3 FROM ep e WHERE (prev IS NULL OR filing_date - prev > 3*365) AND {where_cohort.replace('k.cik','e.cik')} GROUP BY 1),
29+ e5 AS (SELECT year(filing_date) yr, count(*) first_seen_k5 FROM ep e WHERE (prev IS NULL OR filing_date - prev > 5*365) AND {where_cohort.replace('k.cik','e.cik')} GROUP BY 1)
30+ SELECT '{label}' series, yrs.yr, coalesce(iss.issuers_filing,0) issuers_filing, coalesce(men.mentions,0) mentions, coalesce(men.issuers_with_mentions,0) issuers_with_mentions,
31+ round(coalesce(men.mentions,0)*1.0/nullif(iss.issuers_filing,0),2) mentions_per_issuer_filing,
32+ coalesce(fsy.first_seen_current,0) first_seen_current_key, round(coalesce(fsy.first_seen_current,0)*1.0/nullif(iss.issuers_filing,0),2) first_seen_per_issuer,
33+ coalesce(e3.first_seen_k3,0) first_seen_k3, coalesce(e5.first_seen_k5,0) first_seen_k5,
34+ CASE WHEN yrs.yr = 2010 THEN 'left-censored' WHEN yrs.yr = 2026 THEN 'partial year' ELSE '' END flag
35+ FROM yrs LEFT JOIN iss USING (yr) LEFT JOIN men USING (yr) LEFT JOIN fsy USING (yr) LEFT JOIN e3 USING (yr) LEFT JOIN e5 USING (yr) ORDER BY 2""").fetchall()
36+cols = ["series","year","issuers_filing","mentions","issuers_with_mentions","mentions_per_issuer_filing","first_seen_current_key","first_seen_per_issuer","first_seen_k3","first_seen_k5","flag"]
37+allr = series("TRUE", "all_issuers"); coh = series("k.cik IN (SELECT cik FROM cohort)", "fixed_2010_cohort")
38+r.write_csv("first_seen_fixed_cohort.csv", cols, allr + coh)
39+r.write_csv("windowed_key_k3_k5.csv", ["series","year","first_seen_current_key","first_seen_k3","first_seen_k5","flag"], [(x[0], x[1], x[6], x[8], x[9], x[10]) for x in allr + coh])
40+S["range_2011_2025_first_seen_all"] = [min(x[6] for x in allr if 2011 <= x[1] <= 2025), max(x[6] for x in allr if 2011 <= x[1] <= 2025)]
41+S["range_2011_2025_first_seen_cohort"] = [min(x[6] for x in coh if 2011 <= x[1] <= 2025), max(x[6] for x in coh if 2011 <= x[1] <= 2025)]
42+S["first_seen_2010_all"] = [x[6] for x in allr if x[1] == 2010][0]
43+S["mentions_per_issuer_cohort_2011_vs_2025"] = [[x[1], x[5]] for x in coh if x[1] in (2011, 2015, 2020, 2025)]
44+S["first_seen_per_issuer_cohort_2011_vs_2025"] = [[x[1], x[7]] for x in coh if x[1] in (2011, 2015, 2020, 2025)]
45+S["episodes_k3_total"], S["episodes_k5_total"], S["projects_current"] = con.execute("select count(*) filter (where prev is null or filing_date - prev > 3*365), count(*) filter (where prev is null or filing_date - prev > 5*365), count(distinct gkey) from ep").fetchone()
46+r.write_json("summary.json", S)
47+r.finish()
added results/timeseries/first_seen_fixed_cohort.csv +35 −0
@@ -0,0 +1,35 @@
1+series,year,issuers_filing,mentions,issuers_with_mentions,mentions_per_issuer_filing,first_seen_current_key,first_seen_per_issuer,first_seen_k3,first_seen_k5,flag
2+all_issuers,2010,1392,11908,1224,8.55,7835,5.63,7835,7835,left-censored
3+all_issuers,2011,1424,13551,1287,9.52,6679,4.69,6679,6679,
4+all_issuers,2012,1472,14302,1333,9.72,6444,4.38,6444,6444,
5+all_issuers,2013,1539,14611,1408,9.49,6286,4.08,6385,6286,
6+all_issuers,2014,1619,16274,1490,10.05,6732,4.16,7004,6732,
7+all_issuers,2015,1689,17073,1535,10.11,6675,3.95,7092,6734,
8+all_issuers,2016,1744,16429,1579,9.42,6240,3.58,6710,6372,
9+all_issuers,2017,1810,17350,1636,9.59,6490,3.59,7120,6725,
10+all_issuers,2018,1893,18064,1737,9.54,6815,3.6,7529,7115,
11+all_issuers,2019,1973,18737,1806,9.5,6837,3.47,7644,7208,
12+all_issuers,2020,2089,17970,1919,8.6,7212,3.45,8010,7649,
13+all_issuers,2021,2247,21312,2077,9.48,8216,3.66,9191,8732,
14+all_issuers,2022,2291,22720,2105,9.92,8158,3.56,9135,8689,
15+all_issuers,2023,2329,22955,2129,9.86,7977,3.43,9061,8557,
16+all_issuers,2024,2387,22503,2204,9.43,7819,3.28,8954,8500,
17+all_issuers,2025,2453,23220,2280,9.47,7990,3.26,9252,8719,
18+all_issuers,2026,2498,19086,2260,7.64,6694,2.68,7778,7342,partial year
19+fixed_2010_cohort,2010,1389,11889,1221,8.56,7819,5.63,7819,7819,left-censored
20+fixed_2010_cohort,2011,1389,13333,1255,9.6,6533,4.7,6533,6533,
21+fixed_2010_cohort,2012,1389,13775,1262,9.92,6125,4.41,6125,6125,
22+fixed_2010_cohort,2013,1389,13411,1276,9.66,5602,4.03,5701,5602,
23+fixed_2010_cohort,2014,1389,13999,1285,10.08,5583,4.02,5852,5583,
24+fixed_2010_cohort,2015,1389,14038,1270,10.11,5288,3.81,5700,5347,
25+fixed_2010_cohort,2016,1389,13093,1275,9.43,4810,3.46,5260,4941,
26+fixed_2010_cohort,2017,1389,13088,1258,9.42,4674,3.37,5274,4907,
27+fixed_2010_cohort,2018,1389,13035,1271,9.38,4719,3.4,5373,5009,
28+fixed_2010_cohort,2019,1389,12937,1277,9.31,4496,3.24,5235,4852,
29+fixed_2010_cohort,2020,1389,11371,1294,8.19,4380,3.15,5069,4779,
30+fixed_2010_cohort,2021,1389,12376,1284,8.91,4505,3.24,5327,4972,
31+fixed_2010_cohort,2022,1389,12870,1273,9.27,4447,3.2,5224,4902,
32+fixed_2010_cohort,2023,1389,12649,1254,9.11,4251,3.06,5098,4752,
33+fixed_2010_cohort,2024,1389,12170,1277,8.76,4085,2.94,4953,4663,
34+fixed_2010_cohort,2025,1389,12156,1285,8.75,3926,2.83,4858,4518,
35+fixed_2010_cohort,2026,1389,9792,1244,7.05,3192,2.3,3958,3697,partial year
added results/timeseries/manifest.json +30 −0
@@ -0,0 +1,30 @@
1+{
2+ "package": "timeseries",
3+ "script": "/Users/simon-pierreboucher/firc0001_review/scripts/a09_timeseries_cohort.py",
4+ "script_sha256": "e8220e405587cc0c1f4ea1ae3750d85fe8f5240ab0f464444351f41988f05083",
5+ "started": "2026-11-05T14:29:36",
6+ "ended": "2026-11-05T14:29:36",
7+ "wall_seconds": 0.4,
8+ "node": "m3u96b",
9+ "python": "3.12.15",
10+ "duckdb": "2.0.0",
11+ "threads": 4,
12+ "memory_limit": "24GB",
13+ "seed": 20261105,
14+ "databases": {
15+ "/Users/simon-pierreboucher/UQO_RESEARCH_DB/project-db/data/db/spid.duckdb": {
16+ "bytes": 3155701760,
17+ "mtime": "2026-10-28T12:21:11.091389"
18+ },
19+ "/Users/simon-pierreboucher/UQO_RESEARCH_DB/data/duckdb/edgar_research.duckdb": {
20+ "bytes": 73500995584,
21+ "mtime": "2026-10-27T21:34:46.297884"
22+ }
23+ },
24+ "read_only": true,
25+ "row_counts": {
26+ "first_seen_fixed_cohort.csv": 34,
27+ "windowed_key_k3_k5.csv": 34
28+ },
29+ "notes": {}
30+}
\ No newline at end of file
added results/timeseries/summary.json +55 −0
@@ -0,0 +1,55 @@
1+{
2+ "cohort_issuers": 1389,
3+ "all_issuers_with_filings": 2499,
4+ "max_filing_date": "2026-10-27",
5+ "min_filing_date": "2010-01-04",
6+ "note": "2010 is left-censored (first_seen cannot precede the corpus start); 2026 is partial (filings to the max date)",
7+ "range_2011_2025_first_seen_all": [
8+ 6240,
9+ 8216
10+ ],
11+ "range_2011_2025_first_seen_cohort": [
12+ 3926,
13+ 6533
14+ ],
15+ "first_seen_2010_all": 7835,
16+ "mentions_per_issuer_cohort_2011_vs_2025": [
17+ [
18+ 2011,
19+ 9.6
20+ ],
21+ [
22+ 2015,
23+ 10.11
24+ ],
25+ [
26+ 2020,
27+ 8.19
28+ ],
29+ [
30+ 2025,
31+ 8.75
32+ ]
33+ ],
34+ "first_seen_per_issuer_cohort_2011_vs_2025": [
35+ [
36+ 2011,
37+ 4.7
38+ ],
39+ [
40+ 2015,
41+ 3.81
42+ ],
43+ [
44+ 2020,
45+ 3.15
46+ ],
47+ [
48+ 2025,
49+ 2.83
50+ ]
51+ ],
52+ "episodes_k3_total": 131823,
53+ "episodes_k5_total": 126318,
54+ "projects_current": 121099
55+}
\ No newline at end of file
added results/timeseries/windowed_key_k3_k5.csv +35 −0
@@ -0,0 +1,35 @@
1+series,year,first_seen_current_key,first_seen_k3,first_seen_k5,flag
2+all_issuers,2010,7835,7835,7835,left-censored
3+all_issuers,2011,6679,6679,6679,
4+all_issuers,2012,6444,6444,6444,
5+all_issuers,2013,6286,6385,6286,
6+all_issuers,2014,6732,7004,6732,
7+all_issuers,2015,6675,7092,6734,
8+all_issuers,2016,6240,6710,6372,
9+all_issuers,2017,6490,7120,6725,
10+all_issuers,2018,6815,7529,7115,
11+all_issuers,2019,6837,7644,7208,
12+all_issuers,2020,7212,8010,7649,
13+all_issuers,2021,8216,9191,8732,
14+all_issuers,2022,8158,9135,8689,
15+all_issuers,2023,7977,9061,8557,
16+all_issuers,2024,7819,8954,8500,
17+all_issuers,2025,7990,9252,8719,
18+all_issuers,2026,6694,7778,7342,partial year
19+fixed_2010_cohort,2010,7819,7819,7819,left-censored
20+fixed_2010_cohort,2011,6533,6533,6533,
21+fixed_2010_cohort,2012,6125,6125,6125,
22+fixed_2010_cohort,2013,5602,5701,5602,
23+fixed_2010_cohort,2014,5583,5852,5583,
24+fixed_2010_cohort,2015,5288,5700,5347,
25+fixed_2010_cohort,2016,4810,5260,4941,
26+fixed_2010_cohort,2017,4674,5274,4907,
27+fixed_2010_cohort,2018,4719,5373,5009,
28+fixed_2010_cohort,2019,4496,5235,4852,
29+fixed_2010_cohort,2020,4380,5069,4779,
30+fixed_2010_cohort,2021,4505,5327,4972,
31+fixed_2010_cohort,2022,4447,5224,4902,
32+fixed_2010_cohort,2023,4251,5098,4752,
33+fixed_2010_cohort,2024,4085,4953,4663,
34+fixed_2010_cohort,2025,3926,4858,4518,
35+fixed_2010_cohort,2026,3192,3958,3697,partial year
36
Menu

Theme