Multi-MIMIC 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multi_mimic/__init__.py +7 -0
- multi_mimic/__main__.py +5 -0
- multi_mimic/check.py +145 -0
- multi_mimic/cli.py +235 -0
- multi_mimic/download.py +444 -0
- multi_mimic/figure.py +257 -0
- multi_mimic/http.py +114 -0
- multi_mimic/listing.py +101 -0
- multi_mimic/manifest.py +93 -0
- multi_mimic/origins.py +136 -0
- multi_mimic/sources.py +91 -0
- multi_mimic/tables.py +94 -0
- multi_mimic-0.0.1.dist-info/METADATA +389 -0
- multi_mimic-0.0.1.dist-info/RECORD +16 -0
- multi_mimic-0.0.1.dist-info/WHEEL +4 -0
- multi_mimic-0.0.1.dist-info/entry_points.txt +2 -0
multi_mimic/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Download the MIMIC-IV demo patients across modalities from PhysioNet."""
|
|
2
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
__version__ = version("Multi-MIMIC") # set from the git tag when the package is built
|
|
6
|
+
except PackageNotFoundError:
|
|
7
|
+
__version__ = "0.0.0"
|
multi_mimic/__main__.py
ADDED
multi_mimic/check.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Final file check: is everything there, complete and uncorrupted?
|
|
2
|
+
|
|
3
|
+
Every file is compared with its PhysioNet checksum (SHA256SUMS.txt); every
|
|
4
|
+
filtered table with the checksum recorded when it was written. Results are
|
|
5
|
+
printed as counts only; the paths of problem files go to a local report."""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import gzip
|
|
9
|
+
import csv
|
|
10
|
+
from collections import Counter, defaultdict
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from .download import bar, human, local_path
|
|
14
|
+
from .listing import subject_of
|
|
15
|
+
from .manifest import Item, Manifest
|
|
16
|
+
from .sources import BY_KEY
|
|
17
|
+
from .tables import sha256_file
|
|
18
|
+
|
|
19
|
+
OK, MISSING, INCOMPLETE, CORRUPT, SKIPPED = "ok", "missing", "incomplete", "corrupt", "skipped"
|
|
20
|
+
REPORT = "multi-mimic.check-report.txt"
|
|
21
|
+
LEGACY_REPORT = "mimic-demo-mm.check-report.txt" # written by the pre-release mimic-demo-mm
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def verify_item(root: Path, it: Item, quick: bool) -> str:
|
|
25
|
+
if it.status == "skipped":
|
|
26
|
+
return SKIPPED
|
|
27
|
+
p = local_path(root, it)
|
|
28
|
+
if not p.exists():
|
|
29
|
+
return INCOMPLETE if p.with_name(p.name + ".part").exists() else MISSING
|
|
30
|
+
size = p.stat().st_size
|
|
31
|
+
if it.kind == "table":
|
|
32
|
+
if it.out_sha256 is None:
|
|
33
|
+
return MISSING
|
|
34
|
+
if size != it.out_size:
|
|
35
|
+
return CORRUPT
|
|
36
|
+
return OK if quick or sha256_file(p) == it.out_sha256 else CORRUPT
|
|
37
|
+
if it.size is not None and size != it.size:
|
|
38
|
+
return INCOMPLETE if size < it.size else CORRUPT
|
|
39
|
+
if not quick and it.sha256 and sha256_file(p) != it.sha256:
|
|
40
|
+
return CORRUPT
|
|
41
|
+
return OK
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def verify(root: Path, man: Manifest, keys: list[str] | None = None, quick: bool = False) -> dict[tuple, str]:
|
|
45
|
+
items = [i for i in man.items.values() if keys is None or i.source in keys]
|
|
46
|
+
total = sum((i.out_size if i.kind == "table" else i.size) or 0 for i in items if i.status != "skipped")
|
|
47
|
+
res = {}
|
|
48
|
+
with bar(total=total, unit="B", unit_scale=True, desc=" checking files" + (" (sizes only)" if quick else ""),
|
|
49
|
+
leave=False) as b:
|
|
50
|
+
for it in items:
|
|
51
|
+
res[it.key] = verify_item(root, it, quick)
|
|
52
|
+
b.update((it.out_size if it.kind == "table" else it.size) or 0)
|
|
53
|
+
return res
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _ed_subjects(root: Path) -> set[str]:
|
|
57
|
+
p = root / BY_KEY["mimic-iv-ed-demo"].rel / "ed/edstays.csv.gz"
|
|
58
|
+
if not p.exists():
|
|
59
|
+
return set()
|
|
60
|
+
with gzip.open(p, "rt", newline="") as f:
|
|
61
|
+
r = csv.DictReader(f)
|
|
62
|
+
return {row["subject_id"] for row in r}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def coverage(root: Path, man: Manifest, res: dict) -> list[tuple[str, set[str] | None, str]]:
|
|
66
|
+
"""-> [(label, demo patients with it or None for all of them, modality group)]."""
|
|
67
|
+
ok = [man.items[k] for k, v in res.items() if v == OK]
|
|
68
|
+
present = {src for src, _ in res} # only the sources that were checked
|
|
69
|
+
|
|
70
|
+
def files_of(src, exts):
|
|
71
|
+
return {subject_of(i.path) for i in ok if i.source == src and BY_KEY[src].in_files(i.path)
|
|
72
|
+
and i.path.lower().endswith(exts)} - {None}
|
|
73
|
+
|
|
74
|
+
def tables_of(src, name):
|
|
75
|
+
s = set()
|
|
76
|
+
for i in ok:
|
|
77
|
+
if i.source == src and i.kind == "table" and name in Path(i.path).name:
|
|
78
|
+
s.update(i.subjects)
|
|
79
|
+
return s
|
|
80
|
+
|
|
81
|
+
rows = []
|
|
82
|
+
if "mimic-iv-demo" in present:
|
|
83
|
+
rows.append(("structured, categorical and numeric events (EHR)", None, "EHR"))
|
|
84
|
+
if "mimic-iv-ed-demo" in present:
|
|
85
|
+
rows.append(("emergency department stays", _ed_subjects(root), "ED stay"))
|
|
86
|
+
for key, label, kind, arg, group in (
|
|
87
|
+
("mimic-iv-ecg-demo", "ECG waveforms", "files", (".hea",), "ECG"),
|
|
88
|
+
("mimic-iv-ecg", "ECG machine reports (text)", "tables", "machine_measurements", "ECG"),
|
|
89
|
+
("mimic4wdb", "ICU bedside-monitor waveforms", "files", (".hea",), "ICU waveforms"),
|
|
90
|
+
("mimic-cxr-jpg", "chest X-ray images (JPG)", "files", (".jpg",), "Chest X-ray"),
|
|
91
|
+
("mimic-cxr", "chest X-ray DICOMs", "files", (".dcm",), "Chest X-ray"),
|
|
92
|
+
("mimic-iv-echo", "echo DICOMs", "files", (".dcm",), "Echo"),
|
|
93
|
+
("mimic-iv-echo", "echo measurements", "tables", "measurement", "Echo"),
|
|
94
|
+
("mimic-iv-note", "discharge summaries", "tables", "discharge.csv", "Notes"),
|
|
95
|
+
("mimic-iv-note", "radiology reports", "tables", "radiology.csv", "Notes")):
|
|
96
|
+
if key in present:
|
|
97
|
+
rows.append((label, files_of(key, arg) if kind == "files" else tables_of(key, arg), group))
|
|
98
|
+
return rows
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def report(root: Path, man: Manifest, res: dict, quick: bool) -> bool:
|
|
102
|
+
per = defaultdict(Counter)
|
|
103
|
+
sizes = Counter()
|
|
104
|
+
for (src, _), st in res.items():
|
|
105
|
+
per[src][st] += 1
|
|
106
|
+
it = man.items[(src, _)]
|
|
107
|
+
if st == OK:
|
|
108
|
+
sizes[src] += (it.out_size if it.kind == "table" else it.size) or 0
|
|
109
|
+
print(f"\n== File check{' (sizes only)' if quick else ' (checksums)'} ==")
|
|
110
|
+
print(f" {'source':<18}{'ok':>8}{'missing':>9}{'partial':>9}{'corrupt':>9}{'skipped':>9}{'size':>11} status")
|
|
111
|
+
complete = True
|
|
112
|
+
checked = {src for src, _ in res}
|
|
113
|
+
for key in [k for k in man.sources if k in checked]:
|
|
114
|
+
c = per.get(key, Counter())
|
|
115
|
+
bad = c[MISSING] + c[INCOMPLETE] + c[CORRUPT]
|
|
116
|
+
complete &= bad == 0 and c[OK] > 0
|
|
117
|
+
status = "complete" if bad == 0 and c[OK] else ("EMPTY" if not c[OK] and not bad else "INCOMPLETE")
|
|
118
|
+
print(f" {key:<18}{c[OK]:>8,}{c[MISSING]:>9,}{c[INCOMPLETE]:>9,}{c[CORRUPT]:>9,}{c[SKIPPED]:>9,}"
|
|
119
|
+
f"{human(sizes[key]):>11} {status}")
|
|
120
|
+
n_demo = man.demo_subjects or 0
|
|
121
|
+
print(f"\n== Demo patients per modality (of {n_demo}) ==")
|
|
122
|
+
rows = coverage(root, man, res)
|
|
123
|
+
for label, s, _ in rows:
|
|
124
|
+
print(f" {label:<48}{n_demo if s is None else len(s):>5}")
|
|
125
|
+
optional = ("emergency department stays", "ICU bedside-monitor waveforms") # few patients have these
|
|
126
|
+
sets = [s for label, s, _ in rows if s is not None and label not in optional]
|
|
127
|
+
if len(sets) > 1:
|
|
128
|
+
label = "with all of the above (ED, waveforms optional)"
|
|
129
|
+
print(f" {label:<48}{len(set.intersection(*sets)):>5}")
|
|
130
|
+
|
|
131
|
+
bad = [(k, v) for k, v in res.items() if v in (MISSING, INCOMPLETE, CORRUPT)]
|
|
132
|
+
rep = root / REPORT
|
|
133
|
+
(root / LEGACY_REPORT).unlink(missing_ok=True) # replaced by the report below, if any
|
|
134
|
+
if bad:
|
|
135
|
+
with open(rep, "w") as f:
|
|
136
|
+
for (src, path), st in sorted(bad):
|
|
137
|
+
note = man.items[(src, path)].note
|
|
138
|
+
f.write(f"{st}\t{BY_KEY[src].rel}/{path}\t{note}\n")
|
|
139
|
+
print(f"\nINCOMPLETE: {len(bad):,} problem file(s), listed in {rep} (local file).")
|
|
140
|
+
print("Run `multi-mimic check --fix` (or the same download command again) to fetch them.")
|
|
141
|
+
else:
|
|
142
|
+
rep.unlink(missing_ok=True)
|
|
143
|
+
print("\nCOMPLETE: every expected file is present and matches its checksum." if not quick else
|
|
144
|
+
"\nCOMPLETE (sizes only): run `multi-mimic check` for full checksum verification.")
|
|
145
|
+
return complete and not bad
|
multi_mimic/cli.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Multi-MIMIC: assemble a local multimodal MIMIC-IV demo.
|
|
2
|
+
|
|
3
|
+
multi-mimic sources list the datasets
|
|
4
|
+
multi-mimic download --user NAME download everything (asks for the password)
|
|
5
|
+
multi-mimic download --local mimic-iv-note=/data/mimic-iv-note/2.2 ...
|
|
6
|
+
take subsets from local copies instead
|
|
7
|
+
multi-mimic check [--fix] verify every file; re-fetch broken ones
|
|
8
|
+
multi-mimic figure [--readme README.md] draw the modality coverage / co-occurrence overview
|
|
9
|
+
multi-mimic migrate [--dry-run] rename folders from the mimic-demo-mm layout
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import argparse
|
|
14
|
+
import getpass
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from . import __version__
|
|
20
|
+
from .check import report, verify
|
|
21
|
+
from .download import (Runner, demo_ids, make_origins, migrate_layout, plan, require_current_layout,
|
|
22
|
+
summarize_plan)
|
|
23
|
+
from .http import DEFAULT_BASE_URL, Client, PhysioNetError, base_url
|
|
24
|
+
from .manifest import Manifest, manifest_file
|
|
25
|
+
from .sources import BY_KEY, SOURCES, select
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _local_map(pairs: list[str] | None, man: Manifest | None = None) -> dict[str, Path]:
|
|
29
|
+
out = {}
|
|
30
|
+
if man is not None: # remember local copies used by earlier runs (for check --fix)
|
|
31
|
+
out.update({k: Path(v["local_dir"]) for k, v in man.sources.items() if v.get("local_dir")})
|
|
32
|
+
for p in pairs or []:
|
|
33
|
+
if "=" not in p:
|
|
34
|
+
raise SystemExit(f"--local expects SOURCE=FOLDER, got '{p}'")
|
|
35
|
+
k, d = p.split("=", 1)
|
|
36
|
+
if k not in BY_KEY:
|
|
37
|
+
raise SystemExit(f"--local: unknown source '{k}'. See: multi-mimic sources")
|
|
38
|
+
out[k] = Path(d)
|
|
39
|
+
return out
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _client(sources, local, user) -> Client:
|
|
43
|
+
"""Sign-in for credentialed sources, in this order: --user (or PHYSIONET_USER /
|
|
44
|
+
PHYSIONET_USERNAME) with PHYSIONET_PASSWORD or a prompt; PHYSIONET_TOKEN; the
|
|
45
|
+
sign-in saved by `physionet login`; else ask for a username and password."""
|
|
46
|
+
remote_cred = [s.key for s in sources if s.credentialed and s.key not in local]
|
|
47
|
+
if not remote_cred:
|
|
48
|
+
return Client()
|
|
49
|
+
user = user or os.environ.get("PHYSIONET_USER") or os.environ.get("PHYSIONET_USERNAME")
|
|
50
|
+
if not user:
|
|
51
|
+
if os.environ.get("PHYSIONET_TOKEN"):
|
|
52
|
+
return Client(token=os.environ["PHYSIONET_TOKEN"])
|
|
53
|
+
from physionet.auth import load_saved_login
|
|
54
|
+
if load_saved_login(base_url()) is not None:
|
|
55
|
+
return Client() # the physionet package uses the saved sign-in
|
|
56
|
+
print(" (tip: `uv run physionet login` signs in once, without a password here)")
|
|
57
|
+
user = input("PhysioNet username: ").strip()
|
|
58
|
+
pw = os.environ.get("PHYSIONET_PASSWORD") or getpass.getpass(f"PhysioNet password for {user}: ")
|
|
59
|
+
return Client(user, pw)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def cmd_sources(a) -> int:
|
|
63
|
+
print(f"{'source':<19}{'access':<14}{'what is fetched':<26}{'default':<9}description")
|
|
64
|
+
what = {"full": "everything", "demo": "demo patients' files+rows", "tables": "demo patients' rows"}
|
|
65
|
+
for s in SOURCES:
|
|
66
|
+
print(f"{s.key:<19}{s.access:<14}{what[s.mode]:<26}{'yes' if s.default else 'no':<9}{s.title}")
|
|
67
|
+
print("\nPhysioNet project: https://physionet.org/content/<source>/<version>/ "
|
|
68
|
+
"(versions: " + ", ".join(f"{s.key} {s.version}" for s in SOURCES) + ")")
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _download(root: Path, sources, local, link, user, jobs, yes, plan_only, quick,
|
|
73
|
+
source="auto", subjects=None) -> int:
|
|
74
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
75
|
+
require_current_layout(root)
|
|
76
|
+
client = _client(sources, local, user)
|
|
77
|
+
origins = make_origins(sources, client, local, link, root)
|
|
78
|
+
man = Manifest.load(root)
|
|
79
|
+
ids = demo_ids(client, root, local)
|
|
80
|
+
if subjects:
|
|
81
|
+
unknown = set(subjects) - ids
|
|
82
|
+
if unknown:
|
|
83
|
+
raise SystemExit(f"--subjects: {len(unknown)} ID(s) are not MIMIC-IV demo patients")
|
|
84
|
+
ids = set(subjects)
|
|
85
|
+
man.demo_subjects = len(ids)
|
|
86
|
+
print(f"Multi-MIMIC {__version__}: {len(ids)} demo patients -> {root}\n\nListing files")
|
|
87
|
+
items = plan(origins, sources, ids, man, jobs)
|
|
88
|
+
files, tables, store, free = summarize_plan(root, sources, items, origins)
|
|
89
|
+
man.save()
|
|
90
|
+
if plan_only:
|
|
91
|
+
return 0
|
|
92
|
+
if store > free:
|
|
93
|
+
print(" x not enough free space: choose fewer sources (--only/--skip), use --link with --local, "
|
|
94
|
+
"or --out on a bigger disk")
|
|
95
|
+
return 2
|
|
96
|
+
if (files or tables) and not yes:
|
|
97
|
+
if input("\n Start? [Y/n] ").strip().lower() in ("n", "no"):
|
|
98
|
+
print(" Nothing downloaded. Re-run any time.")
|
|
99
|
+
return 0
|
|
100
|
+
if files or tables:
|
|
101
|
+
print()
|
|
102
|
+
if client.base_url != DEFAULT_BASE_URL:
|
|
103
|
+
source = "physionet" # a test server or mirror: never the AWS copy of the real projects
|
|
104
|
+
Runner(origins, root, man, ids, jobs, client=client, source=source).run(files, tables)
|
|
105
|
+
res = verify(root, man, [s.key for s in sources], quick)
|
|
106
|
+
return 0 if report(root, man, res, quick) else 1
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def cmd_download(a) -> int:
|
|
110
|
+
sources = select(a.only, a.skip, a.include)
|
|
111
|
+
local = _local_map(a.local)
|
|
112
|
+
return _download(Path(a.out).resolve(), sources, local, a.link, a.user, a.jobs, a.yes, a.plan, a.quick_check,
|
|
113
|
+
a.source, a.subjects)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def cmd_check(a) -> int:
|
|
117
|
+
root = Path(a.out).resolve()
|
|
118
|
+
require_current_layout(root)
|
|
119
|
+
man = Manifest.load(root)
|
|
120
|
+
if not man.items:
|
|
121
|
+
print(f"No manifest in {root}. Run `multi-mimic download` first (or pass --out).")
|
|
122
|
+
return 2
|
|
123
|
+
res = verify(root, man, None, a.quick)
|
|
124
|
+
ok = report(root, man, res, a.quick)
|
|
125
|
+
if ok or not a.fix:
|
|
126
|
+
return 0 if ok else 1
|
|
127
|
+
bad = {k for k, v in res.items() if v in ("missing", "incomplete", "corrupt")}
|
|
128
|
+
for k in bad:
|
|
129
|
+
it = man.items[k]
|
|
130
|
+
it.status = "pending"
|
|
131
|
+
p = root / BY_KEY[it.source].rel / it.path
|
|
132
|
+
if res[k] == "corrupt" and p.exists():
|
|
133
|
+
p.unlink()
|
|
134
|
+
print(f"\nFixing {len(bad):,} file(s)...")
|
|
135
|
+
sources = [BY_KEY[k] for k in man.sources if k in BY_KEY]
|
|
136
|
+
local = _local_map(a.local, man)
|
|
137
|
+
return _download(root, sources, local, a.link, a.user, a.jobs, True, False, False, a.source)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def cmd_migrate(a) -> int:
|
|
141
|
+
root = Path(a.out).resolve()
|
|
142
|
+
if manifest_file(root) is None:
|
|
143
|
+
print(f"x {root} has no Multi-MIMIC manifest: not a folder this tool made, nothing changed")
|
|
144
|
+
return 2
|
|
145
|
+
migrate_layout(root, a.dry_run)
|
|
146
|
+
return 0
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def cmd_figure(a) -> int:
|
|
150
|
+
try:
|
|
151
|
+
import matplotlib # noqa: F401
|
|
152
|
+
except ImportError:
|
|
153
|
+
print("x the figure needs matplotlib: run `uv run --extra figure multi-mimic figure ...`")
|
|
154
|
+
return 2
|
|
155
|
+
from .figure import run
|
|
156
|
+
if a.min_count < 1:
|
|
157
|
+
print("x --min-count must be at least 1")
|
|
158
|
+
return 2
|
|
159
|
+
return run(Path(a.out).resolve(), Path(a.to), Path(a.readme) if a.readme else None, a.min_count)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def main(argv=None) -> int:
|
|
163
|
+
ap = argparse.ArgumentParser(prog="multi-mimic", description=__doc__,
|
|
164
|
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
165
|
+
ap.add_argument("--version", action="version", version=__version__)
|
|
166
|
+
sub = ap.add_subparsers(dest="cmd", required=True)
|
|
167
|
+
|
|
168
|
+
sub.add_parser("sources", help="list the datasets").set_defaults(fn=cmd_sources)
|
|
169
|
+
|
|
170
|
+
def common(p):
|
|
171
|
+
p.add_argument("--out", default="mimic-demo-multimodal", help="output folder (default: %(default)s)")
|
|
172
|
+
p.add_argument("--user", help="PhysioNet username (or env PHYSIONET_USER; password: env PHYSIONET_PASSWORD "
|
|
173
|
+
"or prompt). Without it: PHYSIONET_TOKEN, or the sign-in saved by `physionet login`")
|
|
174
|
+
p.add_argument("--local", action="append", metavar="SOURCE=FOLDER",
|
|
175
|
+
help="take this source from a local copy instead of PhysioNet (repeatable), "
|
|
176
|
+
"e.g. mimic-iv-note=/data/mimic-iv-note/2.2")
|
|
177
|
+
p.add_argument("--link", choices=["copy", "hardlink", "symlink"], default="copy",
|
|
178
|
+
help="for --local sources: copy files (default), or hard-link / symlink to save space")
|
|
179
|
+
p.add_argument("--jobs", type=int, default=4,
|
|
180
|
+
help="parallel size checks, local copies and fallback downloads (default: %(default)s)")
|
|
181
|
+
p.add_argument("--source", choices=["auto", "physionet", "aws"], default="auto",
|
|
182
|
+
help="where the physionet package downloads files from: auto (AWS open-data mirror "
|
|
183
|
+
"when a project is on it, else PhysioNet), physionet or aws (default: %(default)s)")
|
|
184
|
+
|
|
185
|
+
d = sub.add_parser("download", help="download / extract everything, then check it")
|
|
186
|
+
common(d)
|
|
187
|
+
d.add_argument("--only", nargs="+", metavar="SOURCE", help="only these sources")
|
|
188
|
+
d.add_argument("--skip", nargs="+", metavar="SOURCE", help="leave these sources out")
|
|
189
|
+
d.add_argument("--include", nargs="+", metavar="SOURCE", help="add optional sources (e.g. mimic-cxr)")
|
|
190
|
+
d.add_argument("--yes", "-y", action="store_true", help="don't ask before starting")
|
|
191
|
+
d.add_argument("--plan", action="store_true", help="only list what would be fetched and how big it is")
|
|
192
|
+
d.add_argument("--quick-check", action="store_true", help="final check by size only (skip checksums)")
|
|
193
|
+
d.add_argument("--subjects", nargs="+", metavar="ID",
|
|
194
|
+
help="only these demo patients (for a quick or partial download); full sources stay whole")
|
|
195
|
+
d.set_defaults(fn=cmd_download)
|
|
196
|
+
|
|
197
|
+
c = sub.add_parser("check", help="verify every file against its checksum")
|
|
198
|
+
common(c)
|
|
199
|
+
c.add_argument("--quick", action="store_true", help="sizes only (skip checksums)")
|
|
200
|
+
c.add_argument("--fix", action="store_true", help="re-fetch missing, partial or corrupt files")
|
|
201
|
+
c.set_defaults(fn=cmd_check)
|
|
202
|
+
|
|
203
|
+
m = sub.add_parser("migrate", help="rename folders from the mimic-demo-mm layout (<project>/<version>/) "
|
|
204
|
+
"to <project>-<version>/")
|
|
205
|
+
m.add_argument("--out", default="mimic-demo-multimodal", help="data folder (default: %(default)s)")
|
|
206
|
+
m.add_argument("--dry-run", action="store_true", help="only show what would be renamed")
|
|
207
|
+
m.set_defaults(fn=cmd_migrate)
|
|
208
|
+
|
|
209
|
+
g = sub.add_parser("figure", help="draw the modality coverage and co-occurrence overview (counts only)")
|
|
210
|
+
g.add_argument("--out", default="mimic-demo-multimodal", help="data folder (default: %(default)s)")
|
|
211
|
+
g.add_argument("--to", default="docs", help="folder for the SVGs (default: %(default)s)")
|
|
212
|
+
g.add_argument("--readme", help="also update the overview section of this README")
|
|
213
|
+
g.add_argument("--min-count", type=int, default=5,
|
|
214
|
+
help="show smaller counts as '<N' and merge smaller combinations into 'other' "
|
|
215
|
+
"(default: %(default)s; 1 shows everything)")
|
|
216
|
+
g.set_defaults(fn=cmd_figure)
|
|
217
|
+
|
|
218
|
+
a = ap.parse_args(argv)
|
|
219
|
+
try:
|
|
220
|
+
return a.fn(a)
|
|
221
|
+
except PhysioNetError as e:
|
|
222
|
+
print(f"x {e}")
|
|
223
|
+
return 2
|
|
224
|
+
except SystemExit as e:
|
|
225
|
+
if isinstance(e.code, str): # a readable error message from deeper down
|
|
226
|
+
print(e.code)
|
|
227
|
+
return 2
|
|
228
|
+
raise
|
|
229
|
+
except KeyboardInterrupt:
|
|
230
|
+
print("\nStopped.")
|
|
231
|
+
return 130
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
if __name__ == "__main__":
|
|
235
|
+
sys.exit(main())
|