SPB Git

spb/wp44_uqo

Public
22.4 KB · 298 lines python
1#!/usr/bin/env python32"""WP44 — generate manuscript/tables/numbers.tex: every number quoted in the prose is a LaTeX macro produced here from3result files (never typed by hand). Macro names use letters only (no digits: `\\nCoreEligible`, not `\\n2026`).4Sources: data/metadata/prisma_counts.json, linkage_report.json, results/bibliometrics/*.csv, results/econometrics/*.json,5taxonomy/validation/metrics.json, results/emerging/metrics_h12.json (each optional: missing → macro undefined → compile error,6which is intended: the prose cannot cite a number that does not exist)."""7import json, os, sys, csv, re8R = sys.argv[1] if len(sys.argv) > 1 else os.path.dirname(os.path.dirname(os.path.abspath(__file__)))9out = []10WORDS = {"0": "Zero", "1": "One", "2": "Two", "3": "Three", "4": "Four", "5": "Five", "6": "Six", "7": "Seven", "8": "Eight", "9": "Nine"}11def name(s):12    s = re.sub(r"[^A-Za-z0-9]+", " ", s).title().replace(" ", "")13    return "".join(WORDS[ch] if ch.isdigit() else ch for ch in s)14def fmt(v):15    if isinstance(v, bool): return str(v)16    if isinstance(v, int): return f"{v:,}".replace(",", "{,}")17    if isinstance(v, float):18        return f"{v:,.3f}".replace(",", "{,}") if abs(v) < 100 else f"{v:,.1f}".replace(",", "{,}")19    return str(v).replace("%", "\\%").replace("_", "\\_")20def emit(k, v):21    out.append(f"\\newcommand{{\\{name(k)}}}{{{fmt(v)}}}")22def walk(prefix, obj):23    if isinstance(obj, dict):24        for k, v in obj.items(): walk(f"{prefix} {k}", v)25    elif isinstance(obj, (int, float, str, bool)) and obj is not None:26        emit(prefix, obj)27p = os.path.join(R, "data/metadata/prisma_counts.json")28if os.path.exists(p): walk("prisma", json.load(open(p)))29p = os.path.join(R, "data/metadata/linkage_report.json")30if os.path.exists(p): walk("link", json.load(open(p)))31for f in ["volume_yearly", "citation_windows_yearly", "international_collab_yearly", "industry_share_yearly", "venue_hhi_yearly"]:32    p = os.path.join(R, "results/bibliometrics", f + ".csv")33    if os.path.exists(p):34        for row in csv.DictReader(open(p)):35            y = row.get("year") or row.get("publication_year")36            for k, v in row.items():37                if k in ("year", "publication_year") or v in ("", None): continue38                try: v = int(v) if re.fullmatch(r"-?\d+", v) else float(v)39                except ValueError: pass40                emit(f"{f} {y} {k}", v)41for f, key in [("results/econometrics/its_results.json", "its"), ("taxonomy/validation/metrics.json", "val"), ("results/emerging/metrics_h12.json", "emerg"), ("results/emerging/metrics_h24.json", "emergTwoFour")]:42    p = os.path.join(R, f)43    if os.path.exists(p):44        d = json.load(open(p))45        walk(key, {k: v for k, v in d.items() if not isinstance(v, (list,))})46for f, key in [("data/metadata/dblp_parse_report.json", "dblp"), ("data/metadata/acl_parse_report.json", "acl"), ("data/metadata/api_increment_ledger.json", "increment"), ("data/metadata/increment_report.json", "incrementIngest")]:47    p = os.path.join(R, f)48    if os.path.exists(p):49        d = json.load(open(p))50        if key == "increment":51            sl = d.get("slices", {}); emit("increment api calls", d.get("calls", 0)); emit("increment records retrieved", sum(v.get("records", 0) for v in sl.values()))52            emit("increment truncated slices", sum(1 for v in sl.values() if v.get("truncated"))); emit("increment slices done", sum(1 for v in sl.values() if v.get("done"))); emit("increment cost usd", round(d.get("cost_usd_total", 0), 4))53        elif key == "incrementIngest":54            emit("increment new works", d.get("new_works", 0)); emit("increment already in snapshot", d.get("already_in_snapshot", 0))55        elif key == "dblp":56            emit("dblp parsed records", d.get("rows_written", 0)); [emit(f"dblp type {k}", v) for k, v in d.get("counts_by_type", {}).items()]57        elif key == "acl":58            emit("acl parsed entries", d.get("entries", 0))59for f, key in [("reports/classification_report.json", "classification"), ("literature_synthesis/landmarks/selection_report.json", "selection"), ("data/metadata/linkage_sample_summary.json", "linkagesample")]:60    p = os.path.join(R, f)61    if os.path.exists(p): walk(key, json.load(open(p)))62p = os.path.join(R, "results/emerging/known_set_lead_times.csv")63if os.path.exists(p):64    for row in csv.DictReader(open(p)):65        for k in ("total_works", "detection_month", "peak_growth_month", "lead_months"):66            v = row.get(k)67            if v in ("", None): continue68            try: v = int(float(v)) if re.fullmatch(r"-?\d+(\.0)?", v) else v69            except ValueError: pass70            emit(f"known {row['concept']} {k}", v)71    leads = [int(float(r["lead_months"])) for r in csv.DictReader(open(p)) if r.get("lead_months") not in ("", None)]72    if leads:73        import statistics; emit("known set median lead months", int(statistics.median(leads))); emit("known set n detected", len(leads)); emit("known set n positive lead", sum(1 for l in leads if l > 0))74# ---- per-category growth and yearly primary counts (appended by Agent F, 2026-11-03; used by Parts V–VII) ----75p = os.path.join(R, "results/bibliometrics/cagr_by_category.csv")76if os.path.exists(p):77    for row in csv.DictReader(open(p)):78        for k, v in row.items():79            if k == "category" or v in ("", None): continue80            try: v = int(v) if re.fullmatch(r"-?\d+", v) else float(v)81            except ValueError: pass82            emit(f"cagr {row['category']} {k}", v)83p = os.path.join(R, "results/graph/category_counts_yearly.csv")84if os.path.exists(p):85    agg = {}86    for row in csv.DictReader(open(p)):  # one row per (year, period, category): sum the periods87        k = (row["category_id"], row["year"]); agg[k] = agg.get(k, 0) + int(float(row["n_primary"]))88    for (c, y), v in sorted(agg.items()): emit(f"catcount {c} {y} primary", v)89    post = {}; base = {}90    for (c, y), v in agg.items():91        (post if int(y) >= 2023 else base).setdefault(c, 0); (post if int(y) >= 2023 else base)[c] += v92    for c in sorted(set(base) | set(post)): emit(f"catcount {c} baseline primary", base.get(c, 0)); emit(f"catcount {c} post primary", post.get(c, 0))93p = os.path.join(R, "results/graph/category_flows_yearly.csv")94if os.path.exists(p):  # Agent F: citation flows between primary categories by citing period (calendar years: baseline <= 2022, post >= 2023)95    fl = {}; ft = {}96    for row in csv.DictReader(open(p)):97        per = "post" if int(row["citing_year"]) >= 2023 else "baseline"; k = (row["citing_cat"], per)98        fl[(k, row["cited_cat"])] = fl.get((k, row["cited_cat"]), 0) + int(float(row["n"])); ft[k] = ft.get(k, 0) + int(float(row["n"]))99    for k, n in sorted(ft.items()): emit(f"sysflow {k[0]} {k[1]} n", n)100    for (k, cited), n in sorted(fl.items()): emit(f"sysflow {k[0]} {k[1]} to {cited} share", f"{100.0 * n / ft[k]:.1f}")101# de-duplicate macro names (last definition dropped) so that independent appends cannot break the compile102_seen = set(); _out = []103for line in out:104    n = line.split("}")[0]105    if n in _seen: continue106    _seen.add(n); _out.append(line)107out = _out108open(os.path.join(R, "manuscript/tables/numbers.tex"), "w").write("% machine-generated by tables/make_numbers.py — do not edit\n" + "\n".join(out) + "\n")109print(len(out), "macros")110111# ---- Part VII-b (security half of Part VII; Agent H, 2026-11-03): category-level macros for the five security_privacy ----112# categories, appended to numbers.tex. Sources: results/graph/category_counts_yearly.csv (eligible works, multi-label and113# primary counts), results/bibliometrics/cagr_by_category.csv, results/econometrics/breaks.csv,114# results/graph/category_flows_yearly.csv, results/graph/category_cooccurrence_yearly.csv,115# literature_synthesis/landmarks/security_privacy_landmarks.csv. Percentages are pre-formatted strings (1 or 2 decimals).116SEC = ["cybersecurity", "applied_cryptography", "blockchain", "privacy_preserving_computation", "federated_learning"]117extra = []118def emit2(k, v): extra.append(f"\\newcommand{{\\{name(k)}}}{{{fmt(v)}}}")119agg = {}; tot_primary = {}120p = os.path.join(R, "results/graph/category_counts_yearly.csv")121if os.path.exists(p):122    for row in csv.DictReader(open(p)):123        tot_primary[row["year"]] = tot_primary.get(row["year"], 0) + int(row["n_primary"])124        if row["category_id"] in SEC:125            a = agg.setdefault((row["category_id"], row["year"]), [0, 0]); a[0] += int(row["n_multilabel"]); a[1] += int(row["n_primary"])126    for (c, y), (m, pr) in sorted(agg.items()):127        emit2(f"sec count {c} {y} multilabel", m); emit2(f"sec count {c} {y} primary", pr)128        if tot_primary.get(y): emit2(f"sec share primary {c} {y} pct", f"{100 * pr / tot_primary[y]:.2f}")129    for c in SEC:130        for y0, y1, tag in [("2018", "2022", "baseline"), ("2023", "2025", "post"), ("2022", "2025", "span")]:131            m0, m1 = agg.get((c, y0), [0, 0])[0], agg.get((c, y1), [0, 0])[0]132            if m0 > 0:133                emit2(f"sec cagr multilabel {c} {tag} pct", f"{100 * ((m1 / m0) ** (1 / (int(y1) - int(y0))) - 1):.1f}"); emit2(f"sec ratio multilabel {c} {tag}", f"{m1 / m0:.2f}")134            p0, p1 = agg.get((c, y0), [0, 0])[1], agg.get((c, y1), [0, 0])[1]135            if p0 > 0: emit2(f"sec cagr primary {c} {tag} pct", f"{100 * ((p1 / p0) ** (1 / (int(y1) - int(y0))) - 1):.1f}")136        s = sum(v[0] for (cc, y), v in agg.items() if cc == c); emit2(f"sec count {c} all multilabel", s)137        s = sum(v[1] for (cc, y), v in agg.items() if cc == c); emit2(f"sec count {c} all primary", s)138p = os.path.join(R, "results/bibliometrics/cagr_by_category.csv")139if os.path.exists(p):140    for row in csv.DictReader(open(p)):141        if row["category"] in SEC:142            c = row["category"]143            for k in ("n_2018", "n_2022", "n_2023", "n_2025"): emit2(f"sec cagr {c} {k}", int(row[k]))144            for k in ("cagr_baseline", "cagr_post"): emit2(f"sec cagr {c} {k} pct", f"{100 * float(row[k]):.1f}")145            for k in ("share_2018", "share_2022", "share_2025"): emit2(f"sec cagr {c} {k} pct", f"{100 * float(row[k]):.2f}")146p = os.path.join(R, "results/econometrics/breaks.csv")147if os.path.exists(p):148    for row in csv.DictReader(open(p)):149        c = row["series"].replace("cat_", "")150        if c in SEC:151            for k in ("bp_breaks_bic", "pelt", "bocpd_resets"): emit2(f"sec break {c} {k}", (row[k] or "none").replace(";", ", "))152            for k in ("chatgpt_level", "gpt3_level", "dalle2_level", "gpt4_level", "o1_level"): emit2(f"sec break {c} {k}", float(row[k])); emit2(f"sec break {c} {k} p", float(row[k + "_p"]))153p = os.path.join(R, "results/graph/category_flows_yearly.csv")154if os.path.exists(p):155    want = SEC + ["computer_networks", "computer_vision", "ml_theory", "quantum_computing", "llm", "ai_safety", "ai_agents", "generative_ai", "distributed_computing", "edge_computing", "software_engineering", "database_systems", "hci"]156    YRS = ("2019", "2022", "2025", "2026"); rows = list(csv.DictReader(open(p))); tot = {}157    for row in rows:158        if row["citing_cat"] in SEC: tot[(row["citing_cat"], row["citing_year"])] = tot.get((row["citing_cat"], row["citing_year"]), 0) + int(row["n"])159    for row in rows:160        if row["citing_year"] not in YRS: continue161        if (row["citing_cat"] in SEC and row["cited_cat"] in want) or (row["cited_cat"] in SEC and row["citing_cat"] in ("llm", "ai_agents", "ai_safety", "generative_ai", "computer_networks", "distributed_computing", "edge_computing")):162            emit2(f"sec flow {row['citing_cat']} to {row['cited_cat']} {row['citing_year']} pct", f"{100 * float(row['row_share']):.1f}")163    for (c, y), n in sorted(tot.items()):164        if y in YRS: emit2(f"sec flow {c} total {y}", n)165p = os.path.join(R, "results/graph/category_cooccurrence_yearly.csv")166if os.path.exists(p):167    partners = ["llm", "ai_agents", "ai_safety", "generative_ai", "foundation_models"]168    for row in csv.DictReader(open(p)):169        a, b, y = row["cat_a"], row["cat_b"], row["year"]170        if a in SEC and b in partners: c, q = a, b171        elif b in SEC and a in partners: c, q = b, a172        elif a in SEC and b in SEC: c, q = a, b173        else: continue174        n = int(row["n"]); emit2(f"sec cooc {c} {q} {y}", n)175        m = agg.get((c, y), [0, 0])[0]176        if m: emit2(f"sec cooc {c} {q} {y} pct", f"{100 * n / m:.1f}")177p = os.path.join(R, "literature_synthesis/landmarks/security_privacy_landmarks.csv")178if os.path.exists(p):179    from collections import Counter180    L = list(csv.DictReader(open(p))); n = len(L); emit2("sec land n", n)181    wa = sum(r["has_abstract"] == "True" for r in L); emit2("sec land with abstract", wa); emit2("sec land with abstract pct", f"{100 * wa / n:.0f}")182    emit2("sec land oa pct", f"{100 * sum(r['is_oa'] == 'True' for r in L) / n:.0f}")183    emit2("sec land post", sum(r["period"] == "post" for r in L)); emit2("sec land baseline", sum(r["period"] == "baseline" for r in L)); emit2("sec land year 2026", sum(r["year"] == "2026" for r in L))184    emit2("sec land no doi", sum(not r["doi"] for r in L))185    for t, k in Counter(r["type"] for r in L).items(): emit2(f"sec land type {t}", k)186    for c in SEC:187        g = [r for r in L if r["primary_category"] == c]; emit2(f"sec land {c} n", len(g)); emit2(f"sec land {c} post", sum(r["period"] == "post" for r in g))188        emit2(f"sec land {c} with abstract", sum(r["has_abstract"] == "True" for r in g)); emit2(f"sec land {c} year 2026", sum(r["year"] == "2026" for r in g))189        emit2(f"sec land {c} preprints", sum(r["type"] == "preprint" for r in g)); emit2(f"sec land {c} articles", sum(r["type"] in ("article", "review") for r in g))190        emit2(f"sec land {c} conference", sum(r["type"] == "conference-paper" for r in g)); emit2(f"sec land {c} grey", sum(r["type"] in ("dissertation", "report", "book-chapter", "book") for r in g))191open(os.path.join(R, "manuscript/tables/numbers.tex"), "a").write("% ---- Part VII-b security macros (Agent H) ----\n" + "\n".join(extra) + "\n")192print(len(extra), "Part VII-b macros appended")193194# ---- Part VII-a (theory half of Part VII; Agent G, 2026-11-03): category-level macros for the six theory/software_pl ----195# categories (algorithms, computational_complexity, programming_languages, compilers, formal_verification,196# software_engineering), appended to numbers.tex with prefix "thy". Sources: results/graph/category_counts_yearly.csv,197# results/bibliometrics/cagr_by_category.csv, results/econometrics/breaks.csv, results/graph/category_flows_yearly.csv198# (aggregated by period: citing_year <= 2022 baseline, >= 2023 post), results/graph/category_cooccurrence_yearly.csv,199# literature_synthesis/landmarks/{theory,software_pl}_landmarks.csv and literature_synthesis/agent_G/contributions.csv200# (the specialist's audit of the candidate landmarks). Percentages are pre-formatted strings.201THY = ["algorithms", "computational_complexity", "programming_languages", "compilers", "formal_verification", "software_engineering"]202extraG = []203def emitG(k, v): extraG.append(f"\\newcommand{{\\{name(k)}}}{{{fmt(v)}}}")204aggG = {}; totG = {}; totmG = {}205p = os.path.join(R, "results/graph/category_counts_yearly.csv")206if os.path.exists(p):207    for row in csv.DictReader(open(p)):208        totG[row["year"]] = totG.get(row["year"], 0) + int(row["n_primary"]); totmG[row["year"]] = totmG.get(row["year"], 0) + int(row["n_multilabel"])209        if row["category_id"] in THY:210            a = aggG.setdefault((row["category_id"], row["year"]), [0, 0]); a[0] += int(row["n_multilabel"]); a[1] += int(row["n_primary"])211    for (c, y), (m, pr) in sorted(aggG.items()):212        emitG(f"thy count {c} {y} multilabel", m); emitG(f"thy count {c} {y} primary", pr)213        if totG.get(y): emitG(f"thy share primary {c} {y} pct", f"{100 * pr / totG[y]:.2f}")214        if totmG.get(y): emitG(f"thy share multilabel {c} {y} pct", f"{100 * m / totmG[y]:.2f}")215    for c in THY:216        for y0, y1, tag in [("2018", "2022", "baseline"), ("2023", "2025", "post"), ("2022", "2025", "span")]:217            m0, m1 = aggG.get((c, y0), [0, 0])[0], aggG.get((c, y1), [0, 0])[0]218            if m0 > 0: emitG(f"thy cagr multilabel {c} {tag} pct", f"{100 * ((m1 / m0) ** (1 / (int(y1) - int(y0))) - 1):.1f}"); emitG(f"thy ratio multilabel {c} {tag}", f"{m1 / m0:.2f}")219            p0, p1 = aggG.get((c, y0), [0, 0])[1], aggG.get((c, y1), [0, 0])[1]220            if p0 > 0: emitG(f"thy cagr primary {c} {tag} pct", f"{100 * ((p1 / p0) ** (1 / (int(y1) - int(y0))) - 1):.1f}"); emitG(f"thy ratio primary {c} {tag}", f"{p1 / p0:.2f}")221        emitG(f"thy count {c} all multilabel", sum(v[0] for (cc, y), v in aggG.items() if cc == c)); emitG(f"thy count {c} all primary", sum(v[1] for (cc, y), v in aggG.items() if cc == c))222    for y0, y1, tag in [("2018", "2022", "baseline"), ("2023", "2025", "post")]:223        if totG.get(y0): emitG(f"thy cagr primary total {tag} pct", f"{100 * ((totG[y1] / totG[y0]) ** (1 / (int(y1) - int(y0))) - 1):.1f}")224        if totmG.get(y0): emitG(f"thy cagr multilabel total {tag} pct", f"{100 * ((totmG[y1] / totmG[y0]) ** (1 / (int(y1) - int(y0))) - 1):.1f}")225p = os.path.join(R, "results/bibliometrics/cagr_by_category.csv")226if os.path.exists(p):227    for row in csv.DictReader(open(p)):228        if row["category"] in THY:229            c = row["category"]230            for k in ("n_2018", "n_2022", "n_2023", "n_2025"): emitG(f"thy cagrfile {c} {k}", int(row[k]))231            for k in ("cagr_baseline", "cagr_post"): emitG(f"thy cagrfile {c} {k} pct", f"{100 * float(row[k]):.1f}")232            for k in ("share_2018", "share_2022", "share_2025"): emitG(f"thy cagrfile {c} {k} pct", f"{100 * float(row[k]):.2f}")233p = os.path.join(R, "results/econometrics/breaks.csv")234if os.path.exists(p):235    for row in csv.DictReader(open(p)):236        c = row["series"].replace("cat_", "")237        if c in THY:238            for k in ("bp_breaks_bic", "pelt", "bocpd_resets"): emitG(f"thy break {c} {k}", (row[k] or "none").replace(";", ", "))239            for k in ("chatgpt_level", "gpt3_level", "dalle2_level", "gpt4_level", "o1_level", "chatgpt_slope"): emitG(f"thy break {c} {k}", float(row[k])); emitG(f"thy break {c} {k} p", float(row[k + "_p"]))240            for k in ("placebo_level_p", "placebo_slope_p"): emitG(f"thy break {c} {k}", float(row[k]))241            emitG(f"thy break {c} chatgpt slope pct", f"{100 * float(row['chatgpt_slope']):.1f}"); emitG(f"thy break {c} chatgpt level pct", f"{100 * float(row['chatgpt_level']):.1f}")242p = os.path.join(R, "results/graph/category_flows_yearly.csv")243if os.path.exists(p):244    rows = list(csv.DictReader(open(p))); per = lambda y: "baseline" if int(y) <= 2022 else "post"245    outf = {}; inf = {}; tout = {}; tin = {}246    for row in rows:247        pd_ = per(row["citing_year"]); n = int(row["n"])248        if row["citing_cat"] in THY:249            outf[(row["citing_cat"], row["cited_cat"], pd_)] = outf.get((row["citing_cat"], row["cited_cat"], pd_), 0) + n; tout[(row["citing_cat"], pd_)] = tout.get((row["citing_cat"], pd_), 0) + n250        if row["cited_cat"] in THY:251            inf[(row["cited_cat"], row["citing_cat"], pd_)] = inf.get((row["cited_cat"], row["citing_cat"], pd_), 0) + n; tin[(row["cited_cat"], pd_)] = tin.get((row["cited_cat"], pd_), 0) + n252    for (c, d, pd_), n in sorted(outf.items()):253        if tout[(c, pd_)] and (n / tout[(c, pd_)] >= 0.01 or d in ("llm", "ai_agents", "generative_ai", "foundation_models", "ai_safety", "quantum_computing")): emitG(f"thy flow {c} to {d} {pd_} pct", f"{100 * n / tout[(c, pd_)]:.1f}")254    for (c, s, pd_), n in sorted(inf.items()):255        if tin[(c, pd_)] and (n / tin[(c, pd_)] >= 0.01 or s in ("llm", "ai_agents", "generative_ai", "foundation_models", "ai_safety", "quantum_computing")): emitG(f"thy inflow {c} from {s} {pd_} pct", f"{100 * n / tin[(c, pd_)]:.1f}")256    for (c, pd_), n in sorted(tout.items()): emitG(f"thy flow {c} total {pd_}", n)257    for (c, pd_), n in sorted(tin.items()): emitG(f"thy inflow {c} total {pd_}", n)258p = os.path.join(R, "results/graph/category_cooccurrence_yearly.csv")259if os.path.exists(p):260    partners = ["llm", "ai_agents", "ai_safety", "generative_ai", "foundation_models", "ml_theory", "deep_learning_architectures", "quantum_computing", "cybersecurity"]261    for row in csv.DictReader(open(p)):262        a, b, y = row["cat_a"], row["cat_b"], row["year"]263        if a in THY and (b in partners or b in THY): c, q = a, b264        elif b in THY and (a in partners or a in THY): c, q = b, a265        else: continue266        n = int(row["n"]); emitG(f"thy cooc {c} {q} {y}", n)267        m = aggG.get((c, y), [0, 0])[0]268        if m: emitG(f"thy cooc {c} {q} {y} pct", f"{100 * n / m:.1f}")269        if q in THY:270            m2 = aggG.get((q, y), [0, 0])[0]271            if m2: emitG(f"thy cooc {q} {c} {y} pct", f"{100 * n / m2:.1f}")272from collections import Counter as _C273for ar in ("theory", "software_pl"):274    p = os.path.join(R, f"literature_synthesis/landmarks/{ar}_landmarks.csv")275    if os.path.exists(p):276        L = list(csv.DictReader(open(p))); emitG(f"thy land {ar} n", len(L))277        emitG(f"thy land {ar} post", sum(r["period"] == "post" for r in L)); emitG(f"thy land {ar} dated twentysix", sum(r["year"] == "2026" for r in L))278        emitG(f"thy land {ar} abstract", sum(r["has_abstract"] == "True" for r in L)); emitG(f"thy land {ar} oa", sum(r["is_oa"] == "True" for r in L))279        for t, n in _C(r["type"] for r in L).items(): emitG(f"thy land {ar} type {t}", n)280        for c, n in _C(r["primary_category"] for r in L).items(): emitG(f"thy land {ar} cat {c}", n)281p = os.path.join(R, "literature_synthesis/agent_G/contributions.csv")282if os.path.exists(p):283    L = list(csv.DictReader(open(p))); emitG("thy audit n", len(L))284    for v, n in _C(r["verdict"] for r in L).items(): emitG(f"thy audit verdict {v}", n)285    for v, n in _C(r["contribution_type"] for r in L).items(): emitG(f"thy audit type {v}", n)286    emitG("thy audit id collision", sum(r["id_resolves_to_other_work"] == "yes" for r in L))287    emitG("thy audit in scope research post", sum(r["verdict"] == "in_scope_research" and r["period"] == "post" for r in L))288    emitG("thy audit in scope research", sum(r["verdict"] == "in_scope_research" for r in L))289    for c in THY:290        g = [r for r in L if r["primary_category"] == c]; emitG(f"thy audit {c} n", len(g)); emitG(f"thy audit {c} in scope research", sum(r["verdict"] == "in_scope_research" for r in g)); emitG(f"thy audit {c} misclassified", sum(r["verdict"] == "misclassified" for r in g))291_seenG = set(); _outG = []292for line in extraG:293    n = line.split("}")[0]294    if n in _seenG: continue295    _seenG.add(n); _outG.append(line)296open(os.path.join(R, "manuscript/tables/numbers.tex"), "a").write("% ---- Part VII-a theory macros (Agent G) ----\n" + "\n".join(_outG) + "\n")297print(len(_outG), "Part VII-a macros appended")298
Files · 11bd332d3577f462d05ae925c8a7bf9176b9d691
Menu

Theme