Skip to content

Commit d44df44

Browse files
committed
AASHTO VTC enrichment: per-crash victim-type matrix for 2023-2025
Extends `agg.py`'s VTC scaffolding (25 cols: 5 victim types × 5 conditions) to AASHTO years by computing per-crash counts from the person supplements. - `supplement_with_sp.py`: adds `compute_vtc()` helper + person-supplement inputs (`-O`/`-P`). Merges 25 VTC cols into `aashto_supplemented_crashes`; NJSP-supplement rows land in `uf`. NJSP-residual county list updated to match observed lag pattern (Monmouth/Mercer/Essex). - `to_njdot_persons.py`: ghost-Driver rows now get `pos=0` (routes to 'u' bucket via VTC aggregation) instead of being dropped. Preserves the fatal count while avoiding driver-fatal inflation. Ped/cyclist remain under-reported at person-level (mostly land in 'u'); crash-level `pk` stays authoritative. - DVX wiring: `aashto_supplemented_crashes.parquet.dvc` now declares the person supplements as deps; `ymccmc.dvc`/`ymccmcs.dvc` updated md5. - Regenerated `cmymc.db` + `ys`/`yms`/`yccs`/`ymccs`/`ymccmc(s)/` aggs. Known: VTC fatal sum is ~5% over crash-level `tk` for AASHTO years due to fatal-tagged persons appearing in non-fatal-crash records (~140 cases across 2023-2025). Documented in `.dvc` notes.
1 parent cad5bf4 commit d44df44

53 files changed

Lines changed: 183 additions & 79 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

njdot/aashto/supplement_with_sp.py

Lines changed: 122 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,30 @@
33
# requires-python = ">=3.11"
44
# dependencies = ["click", "pandas", "pyarrow"]
55
# ///
6-
"""Supplement AASHTO crashes with NJSP-only fatals (AASHTO ingestion lag).
6+
"""Supplement AASHTO crashes with NJSP-only fatals (AASHTO ingestion lag)
7+
and per-crash VTC (victim-type × condition) counts.
78
89
AASHTO 2025 has a fatal-classification lag concentrated in specific
9-
counties (Middlesex, Hudson, Essex etc.) — fatal status takes time to
10+
counties (Monmouth, Mercer, Essex etc.) — fatal status takes time to
1011
propagate (death-cert / 30-day rule), while injury/PDO records arrive
1112
on schedule. NJSP carries the up-to-date fatal classifications, so we
1213
back-fill the 111 NJSP-only-2025 fatals into the AASHTO output to keep
1314
the homepage NJDOT plot honest until AASHTO catches up.
1415
16+
Also computes 25-column VTC matrix (`df`/`ds`/.../`un`) from the AASHTO
17+
person supplements so `agg.py` can emit per-VT aggregations for AASHTO
18+
years, matching the legacy master crashes parquet. NJSP-supplement rows
19+
land in the `uf` bucket (driver/passenger/ped/cyclist breakdown not
20+
available at residual granularity).
21+
1522
Removable: when a fresh `Crash.csv` shows AASHTO in sync with NJSP,
1623
delete this step from the pipeline.
1724
1825
Reads:
1926
- njdot/data/aashto_combined_crashes.parquet (pure AASHTO, schema-mapped)
2027
- njsp/data/njsp_njdot_residuals.parquet (NJSP-side residuals from match_njdot)
28+
- njdot/data/aashto_supplemented_occupants.parquet (for VTC, optional)
29+
- njdot/data/aashto_supplemented_pedestrians.parquet (for VTC, optional)
2130
2231
Writes:
2332
- njdot/data/aashto_supplemented_crashes.parquet
@@ -31,15 +40,69 @@
3140

3241
err = partial(print, file=sys.stderr)
3342

43+
# VTC matrix: 5 victim types × 5 conditions = 25 columns
44+
VICTIM_TYPES = ['d', 'o', 'p', 'b', 'u'] # driver, passenger, pedestrian, bicyclist, unknown
45+
CONDITIONS = ['f', 's', 'm', 'p', 'n'] # fatal, serious, minor, possible, none
46+
VTC_COLS = [f'{vt}{c}' for vt in VICTIM_TYPES for c in CONDITIONS]
47+
CONDITION_MAP = {1: 'f', 2: 's', 3: 'm', 4: 'p', 5: 'n', 0: 'n'}
48+
CRASH_PK = ['year', 'cc', 'mc', 'case']
49+
50+
51+
def _pos_to_vt(pos):
52+
if pd.isna(pos) or pos == 0:
53+
return 'u'
54+
return 'd' if pos == 1 else 'o'
55+
56+
57+
def compute_vtc(occupants: pd.DataFrame, pedestrians: pd.DataFrame) -> pd.DataFrame:
58+
"""Compute 25-col VTC matrix aggregated by (year, cc, mc, case).
59+
60+
Inputs are AASHTO-supplemented O/P frames with `condition` (Int 1-5),
61+
`pos` (occupants, 1=driver / 2-12=passenger), `cyclist` (peds).
62+
Returns a DataFrame indexed by CRASH_PK with VTC_COLS columns (int).
63+
"""
64+
o = occupants.copy()
65+
o['cond'] = o['condition'].map(CONDITION_MAP).fillna('n')
66+
o['vt'] = o['pos'].apply(_pos_to_vt)
67+
o['vtc'] = o['vt'] + o['cond']
68+
69+
p = pedestrians.copy()
70+
p['cond'] = p['condition'].map(CONDITION_MAP).fillna('n')
71+
p['vt'] = p['cyclist'].apply(lambda b: 'b' if b else 'p')
72+
p['vtc'] = p['vt'] + p['cond']
73+
74+
parts = []
75+
for df in (o, p):
76+
if not len(df):
77+
continue
78+
agg = df.groupby(CRASH_PK + ['vtc']).size().unstack(fill_value=0)
79+
parts.append(agg)
80+
if not parts:
81+
return pd.DataFrame(columns=CRASH_PK + VTC_COLS).set_index(CRASH_PK)
82+
83+
combined = parts[0]
84+
for extra in parts[1:]:
85+
combined = combined.add(extra, fill_value=0)
86+
for col in VTC_COLS:
87+
if col not in combined.columns:
88+
combined[col] = 0
89+
combined = combined[VTC_COLS].fillna(0).astype(int)
90+
return combined
91+
3492

3593
@click.command()
3694
@click.option("-a", "--aashto", type=click.Path(path_type=Path),
3795
default=Path("njdot/data/aashto_combined_crashes.parquet"))
3896
@click.option("-r", "--residuals", type=click.Path(path_type=Path),
3997
default=Path("njsp/data/njsp_njdot_residuals.parquet"))
98+
@click.option("-O", "--occupants-supplement", type=click.Path(path_type=Path),
99+
default=Path("njdot/data/aashto_supplemented_occupants.parquet"))
100+
@click.option("-P", "--pedestrians-supplement", type=click.Path(path_type=Path),
101+
default=Path("njdot/data/aashto_supplemented_pedestrians.parquet"))
40102
@click.option("-o", "--out", type=click.Path(path_type=Path),
41103
default=Path("njdot/data/aashto_supplemented_crashes.parquet"))
42-
def main(aashto: Path, residuals: Path, out: Path):
104+
def main(aashto: Path, residuals: Path, occupants_supplement: Path,
105+
pedestrians_supplement: Path, out: Path):
43106
a = pd.read_parquet(aashto)
44107
aashto_years = sorted(a["year"].dropna().astype(int).unique())
45108
err(f"AASHTO: {len(a):,} crashes, years {aashto_years[0]}{aashto_years[-1]}")
@@ -48,41 +111,64 @@ def main(aashto: Path, residuals: Path, out: Path):
48111
sp_only = res[(res["side"] == "njsp") & res["year"].isin(aashto_years)].copy()
49112
err(f"NJSP-side residuals in AASHTO years: {len(sp_only)} "
50113
f"({sp_only['tk'].sum()} deaths)")
51-
if not len(sp_only):
52-
a.to_parquet(out, index=False)
53-
err(f"No supplement needed; copied AASHTO → {out}")
54-
return
55-
56-
# Build AASHTO-schema rows from NJSP residuals. Only fields that matter
57-
# for downstream `agg.py` (year, cc, mc, severity, tk, ti, pk, pi, tv).
58-
# `case` carries an `NJSP-{i}` synthetic id for provenance.
59-
supp = pd.DataFrame({
60-
"year": sp_only["year"].astype("int32").values,
61-
"cc": sp_only["cc"].astype("Int8").values,
62-
"mc": sp_only["mc"].astype("Int16").values,
63-
"case": [f"NJSP-supplement-{i}" for i in range(len(sp_only))],
64-
# Use date as datetime (no time-of-day in residuals)
65-
"dt": pd.to_datetime(sp_only["date"]).values,
66-
"severity": pd.array(["f"] * len(sp_only), dtype="string"),
67-
"tk": sp_only["tk"].astype("int8").values,
68-
"tk_broad": sp_only["tk"].astype("int8").values,
69-
"ti": pd.array([0] * len(sp_only), dtype="int8"),
70-
"pk": pd.array([0] * len(sp_only), dtype="int8"),
71-
"pi": pd.array([0] * len(sp_only), dtype="int8"),
72-
"tv": pd.array([0] * len(sp_only), dtype="int8"),
73-
"cc0": sp_only["cc"].astype("Int8").values,
74-
"mc0": sp_only["mc"].astype("Int16").values,
75-
})
76-
# Add any AASHTO columns not yet set, as nulls.
77-
for col in a.columns:
78-
if col not in supp.columns:
79-
supp[col] = pd.NA
80-
supp = supp[a.columns]
81-
82-
out_df = pd.concat([a, supp], ignore_index=True)
114+
if len(sp_only):
115+
# Build AASHTO-schema rows from NJSP residuals. Only fields that matter
116+
# for downstream `agg.py` (year, cc, mc, severity, tk, ti, pk, pi, tv).
117+
# `case` carries an `NJSP-{i}` synthetic id for provenance.
118+
supp = pd.DataFrame({
119+
"year": sp_only["year"].astype("int32").values,
120+
"cc": sp_only["cc"].astype("Int8").values,
121+
"mc": sp_only["mc"].astype("Int16").values,
122+
"case": [f"NJSP-supplement-{i}" for i in range(len(sp_only))],
123+
# Use date as datetime (no time-of-day in residuals)
124+
"dt": pd.to_datetime(sp_only["date"]).values,
125+
"severity": pd.array(["f"] * len(sp_only), dtype="string"),
126+
"tk": sp_only["tk"].astype("int8").values,
127+
"tk_broad": sp_only["tk"].astype("int8").values,
128+
"ti": pd.array([0] * len(sp_only), dtype="int8"),
129+
"pk": pd.array([0] * len(sp_only), dtype="int8"),
130+
"pi": pd.array([0] * len(sp_only), dtype="int8"),
131+
"tv": pd.array([0] * len(sp_only), dtype="int8"),
132+
"cc0": sp_only["cc"].astype("Int8").values,
133+
"mc0": sp_only["mc"].astype("Int16").values,
134+
})
135+
# Add any AASHTO columns not yet set, as nulls.
136+
for col in a.columns:
137+
if col not in supp.columns:
138+
supp[col] = pd.NA
139+
supp = supp[a.columns]
140+
out_df = pd.concat([a, supp], ignore_index=True)
141+
n_supplemented = len(supp)
142+
else:
143+
out_df = a.copy()
144+
n_supplemented = 0
145+
146+
# VTC enrichment from person supplements
147+
if occupants_supplement.exists() and pedestrians_supplement.exists():
148+
err(f"\nComputing VTC from {occupants_supplement.name} + {pedestrians_supplement.name}…")
149+
occ = pd.read_parquet(occupants_supplement)
150+
ped = pd.read_parquet(pedestrians_supplement)
151+
err(f" loaded {len(occ):,} occupants + {len(ped):,} pedestrians")
152+
vtc = compute_vtc(occ, ped).reset_index()
153+
# Align dtypes for merge (out_df has Int8/Int16 for cc/mc; vtc has Int8/Int16 too via supplements)
154+
out_df = out_df.merge(vtc, on=CRASH_PK, how='left')
155+
for col in VTC_COLS:
156+
out_df[col] = out_df[col].fillna(0).astype('int8')
157+
# NJSP-supplemented rows: each row's tk into `uf` (we don't know VT
158+
# breakdown for residual fatals). cc/mc nullable issues left as-is.
159+
is_njsp_supp = out_df['case'].astype(str).str.startswith('NJSP-supplement-')
160+
if is_njsp_supp.any():
161+
out_df.loc[is_njsp_supp, 'uf'] = out_df.loc[is_njsp_supp, 'tk'].astype('int8')
162+
err(f" marked {is_njsp_supp.sum()} NJSP-supplement rows with uf={out_df.loc[is_njsp_supp, 'uf'].sum()}")
163+
err(f" total VTC fatals (df+of+pf+bf+uf): "
164+
f"{int(out_df[['df','of','pf','bf','uf']].sum().sum()):,}")
165+
else:
166+
err(f" Person supplements not found; VTC columns omitted "
167+
f"({occupants_supplement.name}, {pedestrians_supplement.name})")
168+
83169
out.parent.mkdir(parents=True, exist_ok=True)
84170
out_df.to_parquet(out, index=False)
85-
err(f"Wrote {out} ({len(out_df):,} crashes; +{len(supp)} from NJSP)")
171+
err(f"Wrote {out} ({len(out_df):,} crashes; +{n_supplemented} from NJSP)")
86172

87173
# Quick verification: post-supplement fatals per year
88174
fa = out_df[out_df["severity"] == "f"]

njdot/aashto/to_njdot_persons.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@
3131
Vehicle` blank. ~45K/yr; ~240/yr carry `Fatal Injury (K)`. These
3232
are placeholder rows — for fatal-ped crashes the actual victim is
3333
on a separate row with `Person Type='Pedestrian'` and
34-
`Severity Rating='No Apparent Injury (O)'`. We drop ghost-Drivers
35-
from the occupants supplement entirely. Net effect: 2024+ ped/
36-
cyclist fatal counts are *under*-reported in the person-level
37-
breakdown (~19/yr instead of ~228/yr). Crash-level `pk` in
38-
`aashto_supplemented_crashes.parquet` remains authoritative.
34+
`Severity Rating='No Apparent Injury (O)'`. We reclassify these
35+
rows with `pos=0` so VTC aggregation routes them to the 'u'
36+
(unknown) bucket — preserves the death count while not inflating
37+
driver totals. Net effect: 2024+ ped/cyclist breakdown remains
38+
*under*-reported by ~228/yr at the person-level (those land in
39+
`u` bucket), but the strict total killed (`tk`/`uf`+`df`+...)
40+
matches crash-level. Crash-level `pk` in
41+
`aashto_supplemented_crashes.parquet` remains the authoritative
42+
pedestrian count.
3943
"""
4044
import sys
4145
from functools import partial
@@ -116,15 +120,16 @@ def normalize_age(age_series: pd.Series) -> pd.Series:
116120
def to_occupants(joined: pd.DataFrame, year: int) -> pd.DataFrame:
117121
"""Convert AASHTO Driver/Passenger rows to DOTr-style occupants frame.
118122
119-
Drops "ghost-Driver" rows (Driver Person Type + blank Position in
120-
Vehicle) — see module docstring."""
123+
"Ghost-Driver" rows (Driver Person Type + blank Position in Vehicle)
124+
get `pos=0` so they aggregate to the 'u' (unknown-VT) bucket — see
125+
module docstring."""
121126
occ_mask = joined['Person Type'].isin(['Driver', 'Passenger'])
122-
pos_blank = joined['Position in Vehicle'].isna() | (joined['Position in Vehicle'] == 'None')
123-
ghost_drivers = (joined['Person Type'] == 'Driver') & pos_blank
127+
o = joined[occ_mask].copy()
128+
pos_blank = o['Position in Vehicle'].isna() | (o['Position in Vehicle'] == 'None')
129+
ghost_drivers = (o['Person Type'] == 'Driver') & pos_blank
124130
n_ghost = ghost_drivers.sum()
125131
if n_ghost:
126-
err(f' dropping {n_ghost:,} ghost-Driver rows (blank Position)')
127-
o = joined[occ_mask & ~ghost_drivers].copy()
132+
err(f' flagging {n_ghost:,} ghost-Driver rows as pos=0 (unknown VT)')
128133

129134
o['year'] = pd.Series(year, index=o.index, dtype='int32')
130135
# vehicle_index is 0-based in AASHTO; vn is 1-based in DOTr
@@ -135,8 +140,12 @@ def to_occupants(joined: pd.DataFrame, year: int) -> pd.DataFrame:
135140

136141
o['condition'] = o['Severity Rating (Person)'].map(SEVERITY_MAP).astype('Int8')
137142
o['pos'] = o['Position in Vehicle'].map(POSITION_MAP).astype('Int8')
138-
# Driver Person Type forces pos=1 (covers AASHTO 'Driver' position field being blank for some rows)
139-
o.loc[o['Person Type'] == 'Driver', 'pos'] = 1
143+
# Driver Person Type with a *real* Position field gets pos=1 (handles
144+
# cases where AASHTO Position='Driver' string vs Person Type field
145+
# disagree). Ghost-Drivers (blank Position) stay at pos=0 → 'u' bucket.
146+
real_driver = (o['Person Type'] == 'Driver') & ~ghost_drivers
147+
o.loc[real_driver, 'pos'] = 1
148+
o.loc[ghost_drivers, 'pos'] = 0
140149
o['eject'] = o['Ejection'].map(EJECTION_MAP).astype('Int8')
141150
o['age'] = normalize_age(o['Age'])
142151
o['sex'] = o['Sex'].map(SEX_MAP).fillna('')
Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
outs:
2-
- md5: 2cd7616a654b357444d08aa05c41b311
3-
size: 36111389
2+
- md5: 92ac7e301eab0f949748c6061317cf9f
3+
size: 37286075
44
hash: md5
55
path: aashto_supplemented_crashes.parquet
66
meta:
77
computation:
88
cmd: njdot/aashto/supplement_with_sp.py
99
git_deps:
10-
/njdot/aashto/supplement_with_sp.py: 4f25ec341452526bf5181ed876069da555e3a863
10+
/njdot/aashto/supplement_with_sp.py: e88a1d8b0d3f43c460f23f76c97df7e557da19f5
1111
deps:
1212
/njdot/data/aashto_combined_crashes.parquet: 0ae2595cd33ce71d47ab2d022a22b60e
1313
/njsp/data/njsp_njdot_residuals.parquet: e50b477c45576b6357ed94060e7b669e
14+
/njdot/data/aashto_supplemented_occupants.parquet: 806dd7a46ebafbe426faeb05373359b9
15+
/njdot/data/aashto_supplemented_pedestrians.parquet: d3d913d7d0716ee4b5588e8380bb456a
1416
note: |
15-
AASHTO + NJSP-only-fatal supplement. Backfills the NJSP fatals
16-
that AASHTO hasn't ingested yet (concentrated in Middlesex /
17-
Hudson / Essex 2025 — fatal-classification lag while
18-
injury/PDO records are on schedule). Removable when a fresh
19-
`Crash.csv` shows AASHTO in sync with NJSP.
17+
AASHTO + NJSP-only-fatal supplement + per-crash VTC matrix
18+
(5 victim types x 5 conditions). Backfills the NJSP fatals
19+
that AASHTO hasn't ingested yet (concentrated in Monmouth /
20+
Mercer / Essex 2025 - fatal-classification lag while
21+
injury/PDO records are on schedule). NJSP-supplement rows
22+
land in the `uf` bucket. Ghost-Driver rows from AASHTO also
23+
land in `u*` buckets (Person Type=Driver with blank Position,
24+
~45K/yr, mostly mis-tagged ped/cyclist victims). VTC fatal
25+
sum is ~5% over crash-level `tk` due to AASHTO data quality
26+
(fatal-tagged persons in non-fatal-crash records).
27+
Removable when a fresh `Crash.csv` shows AASHTO in sync with NJSP.
Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
outs:
2-
- md5: 6957204e8f53b2fbde29b0f9775cf33f
3-
size: 13286278
2+
- md5: 806dd7a46ebafbe426faeb05373359b9
3+
size: 14335377
44
hash: md5
55
path: aashto_supplemented_occupants.parquet
66
meta:
77
computation:
88
cmd: python njdot/aashto/to_njdot_persons.py -y 2023,2024,2025
99
git_deps:
10-
/njdot/aashto/to_njdot_persons.py: fecfc0545896518b3047c8ffb0c1d4c054fbd596
10+
/njdot/aashto/to_njdot_persons.py: 2f0d9eb2505089b276cd82eff4d3d2b77f34f615
1111
/njdot/aashto/to_njdot_schema.py: 0ad39efb3151ef4b58b4fd0d4c9bd8900074fce6
1212
deps:
1313
/njdot/data/2023/persons.parquet: 72625f5a685ceb7489789e886f4f3171
@@ -18,10 +18,11 @@ meta:
1818
/njdot/data/2025/crashes.parquet: 26e6bc7f841bbaa6b5878e57771869dd
1919
/www/public/njdot/cc2mc2mn.json: 2c12563c5ea3a3fdd443fc14610822e8
2020
note: |
21-
AASHTO persons → DOTr-style occupants supplement for 2023-2025.
22-
Drops "ghost-Driver" placeholder rows (Person Type=Driver +
23-
blank Position in Vehicle). Pedestrian/cyclist fatals are
24-
under-reported here; crash-level pk in
25-
`aashto_supplemented_crashes.parquet` remains authoritative.
26-
Produced alongside `aashto_supplemented_pedestrians.parquet`
27-
from the same adapter run.
21+
AASHTO persons -> DOTr-style occupants supplement for 2023-2025.
22+
"Ghost-Driver" placeholder rows (Person Type=Driver + blank
23+
Position) get `pos=0` so VTC aggregation routes them to the
24+
'u' (unknown-VT) bucket. Ped/cyclist fatals remain
25+
*under*-reported at person-level (those mostly land in 'u');
26+
crash-level `pk` in `aashto_supplemented_crashes.parquet`
27+
remains authoritative. Produced alongside
28+
`aashto_supplemented_pedestrians.parquet` from the same run.

njdot/data/aashto_supplemented_pedestrians.parquet.dvc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ meta:
77
computation:
88
cmd: python njdot/aashto/to_njdot_persons.py -y 2023,2024,2025
99
git_deps:
10-
/njdot/aashto/to_njdot_persons.py: fecfc0545896518b3047c8ffb0c1d4c054fbd596
10+
/njdot/aashto/to_njdot_persons.py: 2f0d9eb2505089b276cd82eff4d3d2b77f34f615
1111
/njdot/aashto/to_njdot_schema.py: 0ad39efb3151ef4b58b4fd0d4c9bd8900074fce6
1212
deps:
1313
/njdot/data/2023/persons.parquet: 72625f5a685ceb7489789e886f4f3171

www/public/data/njdot/yccs.parquet

20.2 KB
Binary file not shown.

www/public/data/njdot/ymccmc.dvc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
outs:
2-
- md5: 103160954b4356002b8c445dd18402e2.dir
3-
size: 810803
2+
- md5: c9627d129e88f4c9f48b48f692ccd2e8.dir
3+
size: 1247206
44
hash: md5
55
path: ymccmc
66
meta:
@@ -10,11 +10,11 @@ meta:
1010
/njdot/agg.py: ab9ab62177e15b118d5b80bef44e192ffb1bc7c2
1111
deps:
1212
/njdot/data/crashes.parquet: cfb770bf205ce85aedb90cc268607540
13-
/njdot/data/aashto_supplemented_crashes.parquet: 2cd7616a654b357444d08aa05c41b311
13+
/njdot/data/aashto_supplemented_crashes.parquet: 92ac7e301eab0f949748c6061317cf9f
1414
note: |
1515
`agg.py` produces multiple outputs in `www/public/data/njdot/`:
1616
smaller state/county aggregates (`ys`/`yms`/`yccs`/`ymccs`) live
17-
in git; the larger muni-level dirs (`ymccmc/`, `ymccmcs/`
17+
in git; the larger muni-level dirs (`ymccmc/`, `ymccmcs/` -
1818
~2.4MB across 42 files) are DVX-tracked here. `ymccmcs.dvc` is
1919
a co-output of the same `agg.py` invocation; dvx runs the cmd
2020
twice when both are stale (~10s, fine).
20.1 KB
Binary file not shown.
16.8 KB
Binary file not shown.
17.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)