@nahisaho/satori 0.20.0 → 0.22.0
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.
- package/README.md +70 -39
- package/package.json +1 -1
- package/src/.github/skills/scientific-biothings-idmapping/SKILL.md +4 -0
- package/src/.github/skills/scientific-cellxgene-census/SKILL.md +257 -0
- package/src/.github/skills/scientific-clingen-curation/SKILL.md +258 -0
- package/src/.github/skills/scientific-clinical-nlp/SKILL.md +250 -0
- package/src/.github/skills/scientific-clinical-pharmacology/SKILL.md +361 -0
- package/src/.github/skills/scientific-clinical-standards/SKILL.md +444 -0
- package/src/.github/skills/scientific-crispr-design/SKILL.md +369 -0
- package/src/.github/skills/scientific-drug-repurposing/SKILL.md +4 -0
- package/src/.github/skills/scientific-environmental-ecology/SKILL.md +5 -0
- package/src/.github/skills/scientific-epidemiology-public-health/SKILL.md +5 -0
- package/src/.github/skills/scientific-epigenomics-chromatin/SKILL.md +5 -0
- package/src/.github/skills/scientific-glycomics/SKILL.md +274 -0
- package/src/.github/skills/scientific-gtex-tissue-expression/SKILL.md +5 -2
- package/src/.github/skills/scientific-hgnc-nomenclature/SKILL.md +282 -0
- package/src/.github/skills/scientific-human-cell-atlas/SKILL.md +3 -0
- package/src/.github/skills/scientific-human-protein-atlas/SKILL.md +4 -0
- package/src/.github/skills/scientific-immunoinformatics/SKILL.md +9 -0
- package/src/.github/skills/scientific-lipidomics/SKILL.md +284 -0
- package/src/.github/skills/scientific-metabolomics/SKILL.md +3 -0
- package/src/.github/skills/scientific-metabolomics-network/SKILL.md +311 -0
- package/src/.github/skills/scientific-metagenome-assembled-genomes/SKILL.md +299 -0
- package/src/.github/skills/scientific-model-organism-db/SKILL.md +8 -0
- package/src/.github/skills/scientific-pharmacogenomics/SKILL.md +4 -0
- package/src/.github/skills/scientific-pharos-targets/SKILL.md +276 -0
- package/src/.github/skills/scientific-protein-structure-analysis/SKILL.md +4 -0
- package/src/.github/skills/scientific-public-health-data/SKILL.md +11 -0
- package/src/.github/skills/scientific-systems-biology/SKILL.md +11 -0
- package/src/.github/skills/scientific-variant-effect-prediction/SKILL.md +7 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scientific-clinical-standards
|
|
3
|
+
description: |
|
|
4
|
+
臨床標準用語・コードマッピングスキル。LOINC 臨床検査コード・
|
|
5
|
+
ICD-10/ICD-11 疾病分類・FHIR R4 リソースマッピング・
|
|
6
|
+
SNOMED CT 用語変換・臨床用語相互運用パイプライン。
|
|
7
|
+
tu_tools:
|
|
8
|
+
- key: loinc
|
|
9
|
+
name: LOINC
|
|
10
|
+
description: 臨床検査用語標準コード体系
|
|
11
|
+
- key: icd
|
|
12
|
+
name: ICD
|
|
13
|
+
description: WHO 国際疾病分類 ICD-10/ICD-11
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Scientific Clinical Standards
|
|
17
|
+
|
|
18
|
+
LOINC / ICD / FHIR / SNOMED CT を統合した
|
|
19
|
+
臨床標準用語コード検索・マッピング・相互運用パイプラインを提供する。
|
|
20
|
+
|
|
21
|
+
## When to Use
|
|
22
|
+
|
|
23
|
+
- LOINC コードで臨床検査項目を標準化するとき
|
|
24
|
+
- ICD-10/ICD-11 で疾病分類コードを検索するとき
|
|
25
|
+
- FHIR R4 リソースに臨床データをマッピングするとき
|
|
26
|
+
- 異なる用語体系間のコード変換を行うとき
|
|
27
|
+
- 電子カルテ (EHR) データの標準化パイプラインを構築するとき
|
|
28
|
+
- 臨床データウェアハウスの用語統一を行うとき
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
## 1. LOINC コード検索
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import requests
|
|
38
|
+
import pandas as pd
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
LOINC_FHIR = "https://fhir.loinc.org"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def loinc_search(query, max_results=20):
|
|
45
|
+
"""
|
|
46
|
+
LOINC — 臨床検査コード検索 (FHIR API)。
|
|
47
|
+
|
|
48
|
+
Parameters:
|
|
49
|
+
query: str — 検索クエリ (検査名/コード)
|
|
50
|
+
max_results: int — 最大結果数
|
|
51
|
+
"""
|
|
52
|
+
url = f"{LOINC_FHIR}/CodeSystem/$lookup"
|
|
53
|
+
params = {
|
|
54
|
+
"system": "http://loinc.org",
|
|
55
|
+
"code": query,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# まずコード直接検索
|
|
59
|
+
try:
|
|
60
|
+
resp = requests.get(url, params=params,
|
|
61
|
+
timeout=30)
|
|
62
|
+
if resp.status_code == 200:
|
|
63
|
+
data = resp.json()
|
|
64
|
+
params_list = data.get("parameter", [])
|
|
65
|
+
result = {}
|
|
66
|
+
for p in params_list:
|
|
67
|
+
result[p["name"]] = p.get(
|
|
68
|
+
"valueString",
|
|
69
|
+
p.get("valueCode", ""))
|
|
70
|
+
return pd.DataFrame([result])
|
|
71
|
+
except Exception:
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
# テキスト検索にフォールバック
|
|
75
|
+
url = f"{LOINC_FHIR}/CodeSystem"
|
|
76
|
+
params = {
|
|
77
|
+
"_text": query,
|
|
78
|
+
"_count": max_results,
|
|
79
|
+
}
|
|
80
|
+
resp = requests.get(url, params=params,
|
|
81
|
+
timeout=30)
|
|
82
|
+
resp.raise_for_status()
|
|
83
|
+
data = resp.json()
|
|
84
|
+
|
|
85
|
+
entries = data.get("entry", [])
|
|
86
|
+
rows = []
|
|
87
|
+
for entry in entries:
|
|
88
|
+
resource = entry.get("resource", {})
|
|
89
|
+
rows.append({
|
|
90
|
+
"code": resource.get("id", ""),
|
|
91
|
+
"name": resource.get("name", ""),
|
|
92
|
+
"title": resource.get("title", ""),
|
|
93
|
+
"status": resource.get("status", ""),
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
df = pd.DataFrame(rows)
|
|
97
|
+
print(f"LOINC: {len(df)} results for "
|
|
98
|
+
f"'{query}'")
|
|
99
|
+
return df
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def loinc_code_detail(loinc_code):
|
|
103
|
+
"""
|
|
104
|
+
LOINC — コード詳細取得。
|
|
105
|
+
|
|
106
|
+
Parameters:
|
|
107
|
+
loinc_code: str — LOINC コード
|
|
108
|
+
(例: "2160-0" = Creatinine)
|
|
109
|
+
"""
|
|
110
|
+
url = (f"{LOINC_FHIR}/CodeSystem/"
|
|
111
|
+
f"$lookup")
|
|
112
|
+
params = {
|
|
113
|
+
"system": "http://loinc.org",
|
|
114
|
+
"code": loinc_code,
|
|
115
|
+
}
|
|
116
|
+
resp = requests.get(url, params=params,
|
|
117
|
+
timeout=30)
|
|
118
|
+
resp.raise_for_status()
|
|
119
|
+
data = resp.json()
|
|
120
|
+
|
|
121
|
+
detail = {"loinc_code": loinc_code}
|
|
122
|
+
for p in data.get("parameter", []):
|
|
123
|
+
name = p.get("name", "")
|
|
124
|
+
value = p.get(
|
|
125
|
+
"valueString",
|
|
126
|
+
p.get("valueCode",
|
|
127
|
+
p.get("valueBoolean", "")))
|
|
128
|
+
detail[name] = value
|
|
129
|
+
|
|
130
|
+
print(f"LOINC {loinc_code}: "
|
|
131
|
+
f"{detail.get('display', 'N/A')}")
|
|
132
|
+
return detail
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 2. ICD-10/ICD-11 疾病分類検索
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
ICD_API = "https://id.who.int"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def icd11_search(query, max_results=20,
|
|
142
|
+
language="en"):
|
|
143
|
+
"""
|
|
144
|
+
ICD-11 — WHO 疾病分類検索。
|
|
145
|
+
|
|
146
|
+
Parameters:
|
|
147
|
+
query: str — 検索クエリ (疾病名)
|
|
148
|
+
max_results: int — 最大結果数
|
|
149
|
+
language: str — 言語 (en/ja)
|
|
150
|
+
"""
|
|
151
|
+
url = (f"{ICD_API}/icd/release/11/"
|
|
152
|
+
f"2024-01/mms/search")
|
|
153
|
+
headers = {
|
|
154
|
+
"Accept": "application/json",
|
|
155
|
+
"Accept-Language": language,
|
|
156
|
+
"API-Version": "v2",
|
|
157
|
+
}
|
|
158
|
+
params = {
|
|
159
|
+
"q": query,
|
|
160
|
+
"subtreesFilter": "",
|
|
161
|
+
"flatResults": "true",
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
resp = requests.get(url, params=params,
|
|
165
|
+
headers=headers,
|
|
166
|
+
timeout=30)
|
|
167
|
+
resp.raise_for_status()
|
|
168
|
+
data = resp.json()
|
|
169
|
+
|
|
170
|
+
results = []
|
|
171
|
+
for item in data.get(
|
|
172
|
+
"destinationEntities", [])[:max_results]:
|
|
173
|
+
results.append({
|
|
174
|
+
"code": item.get("theCode", ""),
|
|
175
|
+
"title": item.get("title", ""),
|
|
176
|
+
"chapter": item.get("chapter", ""),
|
|
177
|
+
"score": item.get("score", 0),
|
|
178
|
+
"id": item.get("id", ""),
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
df = pd.DataFrame(results)
|
|
182
|
+
print(f"ICD-11: {len(df)} results for "
|
|
183
|
+
f"'{query}'")
|
|
184
|
+
return df
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def icd10_to_icd11_mapping(icd10_code):
|
|
188
|
+
"""
|
|
189
|
+
ICD-10 → ICD-11 マッピング。
|
|
190
|
+
|
|
191
|
+
Parameters:
|
|
192
|
+
icd10_code: str — ICD-10 コード
|
|
193
|
+
(例: "E11" = Type 2 DM)
|
|
194
|
+
"""
|
|
195
|
+
url = (f"{ICD_API}/icd/release/11/"
|
|
196
|
+
f"2024-01/mms/codeinfo/"
|
|
197
|
+
f"{icd10_code}")
|
|
198
|
+
headers = {
|
|
199
|
+
"Accept": "application/json",
|
|
200
|
+
"API-Version": "v2",
|
|
201
|
+
}
|
|
202
|
+
try:
|
|
203
|
+
resp = requests.get(url, headers=headers,
|
|
204
|
+
timeout=30)
|
|
205
|
+
if resp.status_code == 200:
|
|
206
|
+
data = resp.json()
|
|
207
|
+
print(f"ICD mapping: {icd10_code} → "
|
|
208
|
+
f"{data.get('code', 'N/A')}")
|
|
209
|
+
return data
|
|
210
|
+
except Exception:
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
# フォールバック: テキスト検索
|
|
214
|
+
results = icd11_search(icd10_code)
|
|
215
|
+
if not results.empty:
|
|
216
|
+
return results.iloc[0].to_dict()
|
|
217
|
+
|
|
218
|
+
print(f"ICD mapping: {icd10_code} → not found")
|
|
219
|
+
return {}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 3. FHIR R4 リソースマッピング
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
def create_fhir_observation(
|
|
226
|
+
loinc_code, value, unit,
|
|
227
|
+
patient_id, effective_date):
|
|
228
|
+
"""
|
|
229
|
+
FHIR R4 Observation リソース作成。
|
|
230
|
+
|
|
231
|
+
Parameters:
|
|
232
|
+
loinc_code: str — LOINC コード
|
|
233
|
+
value: float — 測定値
|
|
234
|
+
unit: str — 単位
|
|
235
|
+
patient_id: str — 患者 ID
|
|
236
|
+
effective_date: str — 測定日時 (ISO 8601)
|
|
237
|
+
"""
|
|
238
|
+
observation = {
|
|
239
|
+
"resourceType": "Observation",
|
|
240
|
+
"status": "final",
|
|
241
|
+
"code": {
|
|
242
|
+
"coding": [{
|
|
243
|
+
"system": "http://loinc.org",
|
|
244
|
+
"code": loinc_code,
|
|
245
|
+
}]
|
|
246
|
+
},
|
|
247
|
+
"subject": {
|
|
248
|
+
"reference": f"Patient/{patient_id}"
|
|
249
|
+
},
|
|
250
|
+
"effectiveDateTime": effective_date,
|
|
251
|
+
"valueQuantity": {
|
|
252
|
+
"value": value,
|
|
253
|
+
"unit": unit,
|
|
254
|
+
"system": (
|
|
255
|
+
"http://unitsofmeasure.org"),
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
print(f"FHIR Observation: "
|
|
260
|
+
f"LOINC {loinc_code} = {value} {unit}")
|
|
261
|
+
return observation
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def create_fhir_condition(
|
|
265
|
+
icd_code, icd_system,
|
|
266
|
+
patient_id, onset_date,
|
|
267
|
+
clinical_status="active"):
|
|
268
|
+
"""
|
|
269
|
+
FHIR R4 Condition リソース作成。
|
|
270
|
+
|
|
271
|
+
Parameters:
|
|
272
|
+
icd_code: str — ICD コード
|
|
273
|
+
icd_system: str — "icd10" or "icd11"
|
|
274
|
+
patient_id: str — 患者 ID
|
|
275
|
+
onset_date: str — 発症日 (ISO 8601)
|
|
276
|
+
clinical_status: str — 臨床状態
|
|
277
|
+
"""
|
|
278
|
+
system_uri = (
|
|
279
|
+
"http://hl7.org/fhir/sid/icd-10"
|
|
280
|
+
if icd_system == "icd10"
|
|
281
|
+
else "http://id.who.int/icd/release/11/mms"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
condition = {
|
|
285
|
+
"resourceType": "Condition",
|
|
286
|
+
"clinicalStatus": {
|
|
287
|
+
"coding": [{
|
|
288
|
+
"system": ("http://terminology."
|
|
289
|
+
"hl7.org/CodeSystem/"
|
|
290
|
+
"condition-clinical"),
|
|
291
|
+
"code": clinical_status,
|
|
292
|
+
}]
|
|
293
|
+
},
|
|
294
|
+
"code": {
|
|
295
|
+
"coding": [{
|
|
296
|
+
"system": system_uri,
|
|
297
|
+
"code": icd_code,
|
|
298
|
+
}]
|
|
299
|
+
},
|
|
300
|
+
"subject": {
|
|
301
|
+
"reference": f"Patient/{patient_id}"
|
|
302
|
+
},
|
|
303
|
+
"onsetDateTime": onset_date,
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
print(f"FHIR Condition: {icd_system.upper()} "
|
|
307
|
+
f"{icd_code} ({clinical_status})")
|
|
308
|
+
return condition
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## 4. 用語体系相互変換
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
def cross_reference_codes(code, source_system,
|
|
315
|
+
target_systems=None):
|
|
316
|
+
"""
|
|
317
|
+
用語体系間コード相互参照。
|
|
318
|
+
|
|
319
|
+
Parameters:
|
|
320
|
+
code: str — ソースコード
|
|
321
|
+
source_system: str — ソース体系
|
|
322
|
+
("loinc"/"icd10"/"icd11"/"snomed")
|
|
323
|
+
target_systems: list[str] | None —
|
|
324
|
+
ターゲット体系
|
|
325
|
+
"""
|
|
326
|
+
if target_systems is None:
|
|
327
|
+
target_systems = [
|
|
328
|
+
"loinc", "icd10", "icd11", "snomed"]
|
|
329
|
+
target_systems = [
|
|
330
|
+
s for s in target_systems
|
|
331
|
+
if s != source_system]
|
|
332
|
+
|
|
333
|
+
mappings = {"source": code,
|
|
334
|
+
"source_system": source_system}
|
|
335
|
+
|
|
336
|
+
if source_system == "loinc":
|
|
337
|
+
detail = loinc_code_detail(code)
|
|
338
|
+
mappings["display"] = detail.get(
|
|
339
|
+
"display", "")
|
|
340
|
+
|
|
341
|
+
if source_system == "icd10":
|
|
342
|
+
m = icd10_to_icd11_mapping(code)
|
|
343
|
+
if m:
|
|
344
|
+
mappings["icd11"] = m.get("code", "")
|
|
345
|
+
mappings["icd11_title"] = m.get(
|
|
346
|
+
"title", "")
|
|
347
|
+
|
|
348
|
+
print(f"Cross-ref: {source_system}:{code} → "
|
|
349
|
+
f"{len(mappings) - 2} mappings")
|
|
350
|
+
return mappings
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## 5. 臨床標準統合パイプライン
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
def clinical_standards_pipeline(
|
|
357
|
+
lab_data, diagnosis_data,
|
|
358
|
+
output_dir="results"):
|
|
359
|
+
"""
|
|
360
|
+
臨床標準用語統合パイプライン。
|
|
361
|
+
|
|
362
|
+
Parameters:
|
|
363
|
+
lab_data: pd.DataFrame — 検査データ
|
|
364
|
+
columns: [test_name, value, unit, date]
|
|
365
|
+
diagnosis_data: pd.DataFrame — 診断データ
|
|
366
|
+
columns: [diagnosis, icd_code, date]
|
|
367
|
+
output_dir: str — 出力ディレクトリ
|
|
368
|
+
"""
|
|
369
|
+
from pathlib import Path
|
|
370
|
+
out = Path(output_dir)
|
|
371
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
372
|
+
|
|
373
|
+
# 1) 検査 LOINC マッピング
|
|
374
|
+
loinc_maps = []
|
|
375
|
+
for _, row in lab_data.iterrows():
|
|
376
|
+
result = loinc_search(row["test_name"], 1)
|
|
377
|
+
if not result.empty:
|
|
378
|
+
loinc_maps.append({
|
|
379
|
+
"test_name": row["test_name"],
|
|
380
|
+
"loinc_code": result.iloc[0].get(
|
|
381
|
+
"code", ""),
|
|
382
|
+
"value": row["value"],
|
|
383
|
+
"unit": row["unit"],
|
|
384
|
+
})
|
|
385
|
+
if loinc_maps:
|
|
386
|
+
loinc_df = pd.DataFrame(loinc_maps)
|
|
387
|
+
loinc_df.to_csv(
|
|
388
|
+
out / "loinc_mappings.csv",
|
|
389
|
+
index=False)
|
|
390
|
+
|
|
391
|
+
# 2) 診断 ICD マッピング
|
|
392
|
+
icd_maps = []
|
|
393
|
+
for _, row in diagnosis_data.iterrows():
|
|
394
|
+
result = icd11_search(row["diagnosis"], 1)
|
|
395
|
+
if not result.empty:
|
|
396
|
+
icd_maps.append({
|
|
397
|
+
"diagnosis": row["diagnosis"],
|
|
398
|
+
"icd11_code": result.iloc[0].get(
|
|
399
|
+
"code", ""),
|
|
400
|
+
"icd11_title": result.iloc[0].get(
|
|
401
|
+
"title", ""),
|
|
402
|
+
})
|
|
403
|
+
if icd_maps:
|
|
404
|
+
icd_df = pd.DataFrame(icd_maps)
|
|
405
|
+
icd_df.to_csv(
|
|
406
|
+
out / "icd_mappings.csv",
|
|
407
|
+
index=False)
|
|
408
|
+
|
|
409
|
+
# 3) FHIR Bundle 生成
|
|
410
|
+
fhir_resources = []
|
|
411
|
+
for item in loinc_maps:
|
|
412
|
+
obs = create_fhir_observation(
|
|
413
|
+
item["loinc_code"],
|
|
414
|
+
item["value"], item["unit"],
|
|
415
|
+
"patient-001", "2024-01-01")
|
|
416
|
+
fhir_resources.append(obs)
|
|
417
|
+
|
|
418
|
+
print(f"Clinical standards pipeline → {out}")
|
|
419
|
+
return {
|
|
420
|
+
"loinc_mappings": loinc_maps,
|
|
421
|
+
"icd_mappings": icd_maps,
|
|
422
|
+
"fhir_resources": fhir_resources,
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## パイプライン統合
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
clinical-reporting → clinical-standards → clinical-decision-support
|
|
432
|
+
(臨床レポート) (用語標準化) (臨床意思決定支援)
|
|
433
|
+
│ │ ↓
|
|
434
|
+
public-health-data ───────┘ pharmacogenomics
|
|
435
|
+
(公衆衛生データ) (ゲノム薬理学)
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## パイプライン出力
|
|
439
|
+
|
|
440
|
+
| ファイル | 説明 | 次スキル |
|
|
441
|
+
|---------|------|---------|
|
|
442
|
+
| `loinc_mappings.csv` | LOINC コードマッピング | → clinical-decision |
|
|
443
|
+
| `icd_mappings.csv` | ICD コードマッピング | → epidemiology |
|
|
444
|
+
| `fhir_bundle.json` | FHIR R4 バンドル | → EHR 統合 |
|