@nahisaho/satori 0.15.0 → 0.17.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 +67 -29
- package/package.json +1 -1
- package/src/.github/skills/scientific-data-submission/SKILL.md +357 -0
- package/src/.github/skills/scientific-encode-screen/SKILL.md +315 -0
- package/src/.github/skills/scientific-environmental-geodata/SKILL.md +255 -0
- package/src/.github/skills/scientific-geo-expression/SKILL.md +274 -0
- package/src/.github/skills/scientific-gpu-singlecell/SKILL.md +296 -0
- package/src/.github/skills/scientific-human-cell-atlas/SKILL.md +294 -0
- package/src/.github/skills/scientific-marine-ecology/SKILL.md +429 -0
- package/src/.github/skills/scientific-metabolic-atlas/SKILL.md +263 -0
- package/src/.github/skills/scientific-nci60-screening/SKILL.md +307 -0
- package/src/.github/skills/scientific-paleobiology/SKILL.md +265 -0
- package/src/.github/skills/scientific-parasite-genomics/SKILL.md +280 -0
- package/src/.github/skills/scientific-plant-biology/SKILL.md +321 -0
- package/src/.github/skills/scientific-rrna-taxonomy/SKILL.md +379 -0
- package/src/.github/skills/scientific-scatac-signac/SKILL.md +300 -0
- package/src/.github/skills/scientific-squidpy-advanced/SKILL.md +251 -0
- package/src/.github/skills/scientific-toxicology-env/SKILL.md +309 -0
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scientific-marine-ecology
|
|
3
|
+
description: |
|
|
4
|
+
海洋生態学統合スキル。OBIS 海洋生物分布・WoRMS 海洋分類体系・
|
|
5
|
+
GBIF 生物多様性レコード・FishBase 魚類データ。ToolUniverse
|
|
6
|
+
連携: obis, worms, gbif。
|
|
7
|
+
tu_tools:
|
|
8
|
+
- key: obis
|
|
9
|
+
name: OBIS (Ocean Biodiversity Information System)
|
|
10
|
+
description: 海洋生物の出現・分布データを提供する国際プラットフォーム
|
|
11
|
+
- key: worms
|
|
12
|
+
name: WoRMS (World Register of Marine Species)
|
|
13
|
+
description: 海洋生物種の権威ある分類学的参照データベース
|
|
14
|
+
- key: gbif
|
|
15
|
+
name: GBIF (Global Biodiversity Information Facility)
|
|
16
|
+
description: 生物多様性データの国際的なオープンアクセスインフラ
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Scientific Marine Ecology
|
|
20
|
+
|
|
21
|
+
OBIS / WoRMS / GBIF / FishBase を活用した海洋生物多様性・
|
|
22
|
+
分布解析パイプラインを提供する。
|
|
23
|
+
|
|
24
|
+
## When to Use
|
|
25
|
+
|
|
26
|
+
- 海洋生物の地理的分布データを取得するとき
|
|
27
|
+
- 海洋生物の分類学的情報 (WoRMS) を検証するとき
|
|
28
|
+
- 生物多様性ホットスポットを解析するとき
|
|
29
|
+
- 魚類の生態・形態データ (FishBase) を取得するとき
|
|
30
|
+
- 海洋保全区域の生物多様性評価を行うとき
|
|
31
|
+
- 海洋環境変動と種分布の関係を分析するとき
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
## 1. OBIS 海洋生物分布
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import requests
|
|
41
|
+
import pandas as pd
|
|
42
|
+
import numpy as np
|
|
43
|
+
|
|
44
|
+
OBIS_BASE = "https://api.obis.org/v3"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def obis_occurrence_search(taxon_name=None, taxon_id=None,
|
|
48
|
+
geometry=None, year_range=None, limit=1000):
|
|
49
|
+
"""
|
|
50
|
+
OBIS — 海洋生物出現記録検索。
|
|
51
|
+
|
|
52
|
+
Parameters:
|
|
53
|
+
taxon_name: str — 学名 (例: "Delphinidae")
|
|
54
|
+
taxon_id: int — AphiaID
|
|
55
|
+
geometry: str — WKT ジオメトリ (例: "POLYGON((...))")
|
|
56
|
+
year_range: tuple — (start_year, end_year)
|
|
57
|
+
limit: int — 最大取得件数
|
|
58
|
+
"""
|
|
59
|
+
url = f"{OBIS_BASE}/occurrence"
|
|
60
|
+
params = {"size": min(limit, 5000)}
|
|
61
|
+
|
|
62
|
+
if taxon_name:
|
|
63
|
+
params["scientificname"] = taxon_name
|
|
64
|
+
if taxon_id:
|
|
65
|
+
params["taxonid"] = taxon_id
|
|
66
|
+
if geometry:
|
|
67
|
+
params["geometry"] = geometry
|
|
68
|
+
if year_range:
|
|
69
|
+
params["startdate"] = f"{year_range[0]}-01-01"
|
|
70
|
+
params["enddate"] = f"{year_range[1]}-12-31"
|
|
71
|
+
|
|
72
|
+
resp = requests.get(url, params=params, timeout=60)
|
|
73
|
+
resp.raise_for_status()
|
|
74
|
+
data = resp.json()
|
|
75
|
+
|
|
76
|
+
records = []
|
|
77
|
+
for rec in data.get("results", []):
|
|
78
|
+
records.append({
|
|
79
|
+
"scientific_name": rec.get("scientificName", ""),
|
|
80
|
+
"aphia_id": rec.get("aphiaID", ""),
|
|
81
|
+
"latitude": rec.get("decimalLatitude", None),
|
|
82
|
+
"longitude": rec.get("decimalLongitude", None),
|
|
83
|
+
"depth": rec.get("depth", None),
|
|
84
|
+
"date": rec.get("date_mid", ""),
|
|
85
|
+
"dataset_id": rec.get("dataset_id", ""),
|
|
86
|
+
"basis_of_record": rec.get("basisOfRecord", ""),
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
df = pd.DataFrame(records)
|
|
90
|
+
print(f"OBIS: '{taxon_name or taxon_id}' → {len(df)} occurrences")
|
|
91
|
+
return df
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def obis_checklist(geometry=None, area_id=None):
|
|
95
|
+
"""
|
|
96
|
+
OBIS — 地域別種チェックリスト。
|
|
97
|
+
|
|
98
|
+
Parameters:
|
|
99
|
+
geometry: str — WKT ジオメトリ
|
|
100
|
+
area_id: int — OBIS エリア ID
|
|
101
|
+
"""
|
|
102
|
+
url = f"{OBIS_BASE}/checklist"
|
|
103
|
+
params = {"size": 5000}
|
|
104
|
+
if geometry:
|
|
105
|
+
params["geometry"] = geometry
|
|
106
|
+
if area_id:
|
|
107
|
+
params["areaid"] = area_id
|
|
108
|
+
|
|
109
|
+
resp = requests.get(url, params=params, timeout=60)
|
|
110
|
+
resp.raise_for_status()
|
|
111
|
+
data = resp.json()
|
|
112
|
+
|
|
113
|
+
species = []
|
|
114
|
+
for sp in data.get("results", []):
|
|
115
|
+
species.append({
|
|
116
|
+
"scientific_name": sp.get("scientificName", ""),
|
|
117
|
+
"aphia_id": sp.get("taxonID", ""),
|
|
118
|
+
"records": sp.get("records", 0),
|
|
119
|
+
"kingdom": sp.get("kingdom", ""),
|
|
120
|
+
"phylum": sp.get("phylum", ""),
|
|
121
|
+
"class": sp.get("class", ""),
|
|
122
|
+
"order": sp.get("order", ""),
|
|
123
|
+
"family": sp.get("family", ""),
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
df = pd.DataFrame(species)
|
|
127
|
+
print(f"OBIS checklist: {len(df)} species")
|
|
128
|
+
return df
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 2. WoRMS 分類学検索
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
WORMS_BASE = "https://www.marinespecies.org/rest"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def worms_taxon_search(name, fuzzy=True, marine_only=True):
|
|
138
|
+
"""
|
|
139
|
+
WoRMS — 海洋生物分類学的検索。
|
|
140
|
+
|
|
141
|
+
Parameters:
|
|
142
|
+
name: str — 種名/属名
|
|
143
|
+
fuzzy: bool — ファジー検索
|
|
144
|
+
marine_only: bool — 海洋種のみ
|
|
145
|
+
"""
|
|
146
|
+
url = f"{WORMS_BASE}/AphiaRecordsByName/{name}"
|
|
147
|
+
params = {
|
|
148
|
+
"like": str(fuzzy).lower(),
|
|
149
|
+
"marine_only": str(marine_only).lower(),
|
|
150
|
+
}
|
|
151
|
+
resp = requests.get(url, params=params, timeout=30)
|
|
152
|
+
resp.raise_for_status()
|
|
153
|
+
data = resp.json()
|
|
154
|
+
|
|
155
|
+
results = []
|
|
156
|
+
for taxon in data if isinstance(data, list) else [data]:
|
|
157
|
+
results.append({
|
|
158
|
+
"aphia_id": taxon.get("AphiaID", ""),
|
|
159
|
+
"scientific_name": taxon.get("scientificname", ""),
|
|
160
|
+
"authority": taxon.get("authority", ""),
|
|
161
|
+
"status": taxon.get("status", ""),
|
|
162
|
+
"rank": taxon.get("rank", ""),
|
|
163
|
+
"valid_name": taxon.get("valid_name", ""),
|
|
164
|
+
"kingdom": taxon.get("kingdom", ""),
|
|
165
|
+
"phylum": taxon.get("phylum", ""),
|
|
166
|
+
"class": taxon.get("class", ""),
|
|
167
|
+
"order": taxon.get("order", ""),
|
|
168
|
+
"family": taxon.get("family", ""),
|
|
169
|
+
"genus": taxon.get("genus", ""),
|
|
170
|
+
"is_marine": taxon.get("isMarine", 0),
|
|
171
|
+
"is_brackish": taxon.get("isBrackish", 0),
|
|
172
|
+
"is_freshwater": taxon.get("isFreshwater", 0),
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
df = pd.DataFrame(results)
|
|
176
|
+
print(f"WoRMS: '{name}' → {len(df)} taxa")
|
|
177
|
+
return df
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def worms_classification(aphia_id):
|
|
181
|
+
"""
|
|
182
|
+
WoRMS — 完全分類階層取得。
|
|
183
|
+
|
|
184
|
+
Parameters:
|
|
185
|
+
aphia_id: int — AphiaID
|
|
186
|
+
"""
|
|
187
|
+
url = f"{WORMS_BASE}/AphiaClassificationByAphiaID/{aphia_id}"
|
|
188
|
+
resp = requests.get(url, timeout=30)
|
|
189
|
+
resp.raise_for_status()
|
|
190
|
+
data = resp.json()
|
|
191
|
+
|
|
192
|
+
hierarchy = []
|
|
193
|
+
node = data
|
|
194
|
+
while node:
|
|
195
|
+
hierarchy.append({
|
|
196
|
+
"aphia_id": node.get("AphiaID", ""),
|
|
197
|
+
"name": node.get("scientificname", ""),
|
|
198
|
+
"rank": node.get("rank", ""),
|
|
199
|
+
})
|
|
200
|
+
node = node.get("child")
|
|
201
|
+
|
|
202
|
+
df = pd.DataFrame(hierarchy)
|
|
203
|
+
print(f"WoRMS classification: {len(df)} levels")
|
|
204
|
+
return df
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## 3. GBIF 生物多様性レコード
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
GBIF_BASE = "https://api.gbif.org/v1"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def gbif_species_search(name, limit=20):
|
|
214
|
+
"""
|
|
215
|
+
GBIF — 種名検索・分類マッチング。
|
|
216
|
+
|
|
217
|
+
Parameters:
|
|
218
|
+
name: str — 種名
|
|
219
|
+
limit: int — 結果上限
|
|
220
|
+
"""
|
|
221
|
+
url = f"{GBIF_BASE}/species/search"
|
|
222
|
+
params = {"q": name, "limit": limit}
|
|
223
|
+
resp = requests.get(url, params=params, timeout=30)
|
|
224
|
+
resp.raise_for_status()
|
|
225
|
+
data = resp.json()
|
|
226
|
+
|
|
227
|
+
results = []
|
|
228
|
+
for sp in data.get("results", []):
|
|
229
|
+
results.append({
|
|
230
|
+
"taxon_key": sp.get("key", ""),
|
|
231
|
+
"scientific_name": sp.get("scientificName", ""),
|
|
232
|
+
"canonical_name": sp.get("canonicalName", ""),
|
|
233
|
+
"status": sp.get("taxonomicStatus", ""),
|
|
234
|
+
"rank": sp.get("rank", ""),
|
|
235
|
+
"kingdom": sp.get("kingdom", ""),
|
|
236
|
+
"phylum": sp.get("phylum", ""),
|
|
237
|
+
"class": sp.get("class", ""),
|
|
238
|
+
"order": sp.get("order", ""),
|
|
239
|
+
"family": sp.get("family", ""),
|
|
240
|
+
"num_occurrences": sp.get("numOccurrences", 0),
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
df = pd.DataFrame(results)
|
|
244
|
+
print(f"GBIF species: '{name}' → {len(df)} taxa")
|
|
245
|
+
return df
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def gbif_occurrence_search(taxon_key=None, country=None,
|
|
249
|
+
year_range=None, limit=300):
|
|
250
|
+
"""
|
|
251
|
+
GBIF — 出現記録検索。
|
|
252
|
+
|
|
253
|
+
Parameters:
|
|
254
|
+
taxon_key: int — GBIF taxon key
|
|
255
|
+
country: str — ISO 国コード (例: "JP")
|
|
256
|
+
year_range: tuple — (start, end)
|
|
257
|
+
limit: int — 最大件数
|
|
258
|
+
"""
|
|
259
|
+
url = f"{GBIF_BASE}/occurrence/search"
|
|
260
|
+
params = {"limit": min(limit, 300)}
|
|
261
|
+
|
|
262
|
+
if taxon_key:
|
|
263
|
+
params["taxonKey"] = taxon_key
|
|
264
|
+
if country:
|
|
265
|
+
params["country"] = country
|
|
266
|
+
if year_range:
|
|
267
|
+
params["year"] = f"{year_range[0]},{year_range[1]}"
|
|
268
|
+
|
|
269
|
+
resp = requests.get(url, params=params, timeout=60)
|
|
270
|
+
resp.raise_for_status()
|
|
271
|
+
data = resp.json()
|
|
272
|
+
|
|
273
|
+
records = []
|
|
274
|
+
for rec in data.get("results", []):
|
|
275
|
+
records.append({
|
|
276
|
+
"gbif_id": rec.get("gbifID", ""),
|
|
277
|
+
"scientific_name": rec.get("scientificName", ""),
|
|
278
|
+
"latitude": rec.get("decimalLatitude", None),
|
|
279
|
+
"longitude": rec.get("decimalLongitude", None),
|
|
280
|
+
"country": rec.get("country", ""),
|
|
281
|
+
"year": rec.get("year", ""),
|
|
282
|
+
"basis_of_record": rec.get("basisOfRecord", ""),
|
|
283
|
+
"institution": rec.get("institutionCode", ""),
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
df = pd.DataFrame(records)
|
|
287
|
+
print(f"GBIF occurrences: {len(df)} records (total: {data.get('count', 0)})")
|
|
288
|
+
return df
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## 4. FishBase 魚類データ
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
FISHBASE_BASE = "https://fishbase.ropensci.org"
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def fishbase_species(genus=None, species=None, family=None):
|
|
298
|
+
"""
|
|
299
|
+
FishBase — 魚類種データ取得。
|
|
300
|
+
|
|
301
|
+
Parameters:
|
|
302
|
+
genus: str — 属名
|
|
303
|
+
species: str — 種小名
|
|
304
|
+
family: str — 科名
|
|
305
|
+
"""
|
|
306
|
+
url = f"{FISHBASE_BASE}/species"
|
|
307
|
+
params = {"limit": 100}
|
|
308
|
+
if genus:
|
|
309
|
+
params["Genus"] = genus
|
|
310
|
+
if species:
|
|
311
|
+
params["Species"] = species
|
|
312
|
+
if family:
|
|
313
|
+
params["Family"] = family
|
|
314
|
+
|
|
315
|
+
resp = requests.get(url, params=params, timeout=30)
|
|
316
|
+
resp.raise_for_status()
|
|
317
|
+
data = resp.json()
|
|
318
|
+
|
|
319
|
+
records = []
|
|
320
|
+
for fish in data.get("data", []):
|
|
321
|
+
records.append({
|
|
322
|
+
"spec_code": fish.get("SpecCode", ""),
|
|
323
|
+
"genus": fish.get("Genus", ""),
|
|
324
|
+
"species": fish.get("Species", ""),
|
|
325
|
+
"family": fish.get("Family", ""),
|
|
326
|
+
"body_shape": fish.get("BodyShapeI", ""),
|
|
327
|
+
"max_length": fish.get("Length", None),
|
|
328
|
+
"vulnerability": fish.get("Vulnerability", None),
|
|
329
|
+
"importance": fish.get("Importance", ""),
|
|
330
|
+
"habitat": fish.get("DemersPelag", ""),
|
|
331
|
+
"depth_range": f"{fish.get('DepthRangeShallow', '')}-{fish.get('DepthRangeDeep', '')}",
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
df = pd.DataFrame(records)
|
|
335
|
+
print(f"FishBase: {len(df)} species")
|
|
336
|
+
return df
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## 5. 海洋生態学統合パイプライン
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
def marine_ecology_pipeline(taxon_name, region_wkt=None,
|
|
343
|
+
output_dir="results"):
|
|
344
|
+
"""
|
|
345
|
+
OBIS + WoRMS + GBIF + FishBase 統合パイプライン。
|
|
346
|
+
|
|
347
|
+
Parameters:
|
|
348
|
+
taxon_name: str — 分類群名
|
|
349
|
+
region_wkt: str — 調査海域 WKT
|
|
350
|
+
output_dir: str — 出力ディレクトリ
|
|
351
|
+
"""
|
|
352
|
+
from pathlib import Path
|
|
353
|
+
output_dir = Path(output_dir)
|
|
354
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
355
|
+
|
|
356
|
+
# 1) WoRMS 分類検証
|
|
357
|
+
taxonomy = worms_taxon_search(taxon_name)
|
|
358
|
+
taxonomy.to_csv(output_dir / "worms_taxonomy.csv", index=False)
|
|
359
|
+
|
|
360
|
+
aphia_id = None
|
|
361
|
+
if len(taxonomy) > 0:
|
|
362
|
+
aphia_id = taxonomy.iloc[0]["aphia_id"]
|
|
363
|
+
classification = worms_classification(aphia_id)
|
|
364
|
+
classification.to_csv(output_dir / "classification.csv", index=False)
|
|
365
|
+
|
|
366
|
+
# 2) OBIS 海洋分布
|
|
367
|
+
obis_data = obis_occurrence_search(
|
|
368
|
+
taxon_name=taxon_name,
|
|
369
|
+
taxon_id=aphia_id,
|
|
370
|
+
geometry=region_wkt,
|
|
371
|
+
)
|
|
372
|
+
obis_data.to_csv(output_dir / "obis_occurrences.csv", index=False)
|
|
373
|
+
|
|
374
|
+
# 3) GBIF 補完
|
|
375
|
+
gbif_sp = gbif_species_search(taxon_name)
|
|
376
|
+
if len(gbif_sp) > 0:
|
|
377
|
+
taxon_key = gbif_sp.iloc[0]["taxon_key"]
|
|
378
|
+
gbif_occ = gbif_occurrence_search(taxon_key=taxon_key)
|
|
379
|
+
gbif_occ.to_csv(output_dir / "gbif_occurrences.csv", index=False)
|
|
380
|
+
else:
|
|
381
|
+
gbif_occ = pd.DataFrame()
|
|
382
|
+
|
|
383
|
+
# 4) 多様性指標
|
|
384
|
+
if len(obis_data) > 0:
|
|
385
|
+
n_species = obis_data["scientific_name"].nunique()
|
|
386
|
+
lat_range = (obis_data["latitude"].min(), obis_data["latitude"].max())
|
|
387
|
+
depth_range = (obis_data["depth"].min(), obis_data["depth"].max())
|
|
388
|
+
print(f"Diversity: {n_species} species, "
|
|
389
|
+
f"lat {lat_range}, depth {depth_range}")
|
|
390
|
+
|
|
391
|
+
print(f"Marine ecology pipeline: {output_dir}")
|
|
392
|
+
return {
|
|
393
|
+
"taxonomy": taxonomy,
|
|
394
|
+
"obis_occurrences": obis_data,
|
|
395
|
+
"gbif_occurrences": gbif_occ,
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## ToolUniverse 連携
|
|
402
|
+
|
|
403
|
+
| TU Key | ツール名 | 連携内容 |
|
|
404
|
+
|--------|---------|---------|
|
|
405
|
+
| `obis` | OBIS | 海洋生物出現・分布レコード検索 |
|
|
406
|
+
| `worms` | WoRMS | 海洋種分類学的検証・階層取得 |
|
|
407
|
+
| `gbif` | GBIF | 生物多様性出現レコード検索 |
|
|
408
|
+
|
|
409
|
+
## パイプライン統合
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
environmental-ecology → marine-ecology → phylogenetics
|
|
413
|
+
(陸域生態学) (OBIS/WoRMS/GBIF) (系統解析)
|
|
414
|
+
│ │ ↓
|
|
415
|
+
biodiversity-db ───────────┘ species-distribution
|
|
416
|
+
(ENA/BOLD) │ (空間分布モデル)
|
|
417
|
+
↓
|
|
418
|
+
fisheries-management
|
|
419
|
+
(水産資源管理)
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## パイプライン出力
|
|
423
|
+
|
|
424
|
+
| ファイル | 説明 | 次スキル |
|
|
425
|
+
|---------|------|---------|
|
|
426
|
+
| `results/worms_taxonomy.csv` | WoRMS 分類情報 | → phylogenetics |
|
|
427
|
+
| `results/obis_occurrences.csv` | OBIS 出現記録 | → species-distribution |
|
|
428
|
+
| `results/gbif_occurrences.csv` | GBIF 出現記録 | → biodiversity-db |
|
|
429
|
+
| `results/classification.csv` | 分類階層 | → environmental-ecology |
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scientific-metabolic-atlas
|
|
3
|
+
description: |
|
|
4
|
+
代謝アトラススキル。Metabolic Atlas / Human-GEM REST API による
|
|
5
|
+
代謝反応・代謝産物・コンパートメント検索、フラックス解析統合、
|
|
6
|
+
代謝ネットワーク可視化。K-Dense 連携: metabolic-atlas。
|
|
7
|
+
tu_tools: []
|
|
8
|
+
kdense_ref: metabolic-atlas
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Scientific Metabolic Atlas
|
|
12
|
+
|
|
13
|
+
Metabolic Atlas REST API を活用したゲノムスケール代謝モデル
|
|
14
|
+
(GEM) 解析パイプラインを提供する。
|
|
15
|
+
|
|
16
|
+
## When to Use
|
|
17
|
+
|
|
18
|
+
- ヒト代謝反応・代謝産物を検索するとき
|
|
19
|
+
- Human-GEM のコンパートメント情報を取得するとき
|
|
20
|
+
- 代謝経路のネットワーク構造を解析するとき
|
|
21
|
+
- フラックスバランス解析 (FBA) の入力を準備するとき
|
|
22
|
+
- 代謝産物コネクティビティを可視化するとき
|
|
23
|
+
- 組織特異的代謝モデルを構築するとき
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
## 1. 代謝反応検索
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import requests
|
|
33
|
+
import pandas as pd
|
|
34
|
+
import numpy as np
|
|
35
|
+
|
|
36
|
+
MA_BASE = "https://metabolicatlas.org/api/v2"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def metabolic_atlas_search_reactions(query, model="Human-GEM",
|
|
40
|
+
compartment=None, limit=50):
|
|
41
|
+
"""
|
|
42
|
+
Metabolic Atlas — 代謝反応検索。
|
|
43
|
+
|
|
44
|
+
Parameters:
|
|
45
|
+
query: str — 検索クエリ (例: "glycolysis", "citrate")
|
|
46
|
+
model: str — GEM モデル名
|
|
47
|
+
compartment: str — コンパートメント (例: "cytosol", "mitochondria")
|
|
48
|
+
limit: int — 最大結果数
|
|
49
|
+
"""
|
|
50
|
+
url = f"{MA_BASE}/search"
|
|
51
|
+
params = {
|
|
52
|
+
"query": query,
|
|
53
|
+
"model": model,
|
|
54
|
+
"type": "reaction",
|
|
55
|
+
"limit": limit,
|
|
56
|
+
}
|
|
57
|
+
resp = requests.get(url, params=params, timeout=30)
|
|
58
|
+
resp.raise_for_status()
|
|
59
|
+
data = resp.json()
|
|
60
|
+
|
|
61
|
+
results = []
|
|
62
|
+
for r in data.get("results", data) if isinstance(data, dict) else data:
|
|
63
|
+
rxn = r if isinstance(r, dict) else {}
|
|
64
|
+
row = {
|
|
65
|
+
"reaction_id": rxn.get("id", ""),
|
|
66
|
+
"name": rxn.get("name", ""),
|
|
67
|
+
"equation": rxn.get("equation", ""),
|
|
68
|
+
"subsystem": rxn.get("subsystem", ""),
|
|
69
|
+
"compartment": rxn.get("compartment", ""),
|
|
70
|
+
"gene_rule": rxn.get("geneRule", ""),
|
|
71
|
+
"lower_bound": rxn.get("lowerBound", None),
|
|
72
|
+
"upper_bound": rxn.get("upperBound", None),
|
|
73
|
+
}
|
|
74
|
+
if compartment and compartment.lower() not in str(
|
|
75
|
+
row.get("compartment", "")).lower():
|
|
76
|
+
continue
|
|
77
|
+
results.append(row)
|
|
78
|
+
|
|
79
|
+
df = pd.DataFrame(results[:limit])
|
|
80
|
+
print(f"Metabolic Atlas reactions: {len(df)} results "
|
|
81
|
+
f"(query={query})")
|
|
82
|
+
return df
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 2. 代謝産物検索
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
def metabolic_atlas_search_metabolites(query, model="Human-GEM",
|
|
89
|
+
limit=50):
|
|
90
|
+
"""
|
|
91
|
+
Metabolic Atlas — 代謝産物検索。
|
|
92
|
+
|
|
93
|
+
Parameters:
|
|
94
|
+
query: str — 検索クエリ (例: "glucose", "ATP")
|
|
95
|
+
model: str — GEM モデル名
|
|
96
|
+
limit: int — 最大結果数
|
|
97
|
+
"""
|
|
98
|
+
url = f"{MA_BASE}/search"
|
|
99
|
+
params = {
|
|
100
|
+
"query": query,
|
|
101
|
+
"model": model,
|
|
102
|
+
"type": "metabolite",
|
|
103
|
+
"limit": limit,
|
|
104
|
+
}
|
|
105
|
+
resp = requests.get(url, params=params, timeout=30)
|
|
106
|
+
resp.raise_for_status()
|
|
107
|
+
data = resp.json()
|
|
108
|
+
|
|
109
|
+
results = []
|
|
110
|
+
for m in data.get("results", data) if isinstance(data, dict) else data:
|
|
111
|
+
met = m if isinstance(m, dict) else {}
|
|
112
|
+
results.append({
|
|
113
|
+
"metabolite_id": met.get("id", ""),
|
|
114
|
+
"name": met.get("name", ""),
|
|
115
|
+
"formula": met.get("formula", ""),
|
|
116
|
+
"charge": met.get("charge", None),
|
|
117
|
+
"compartment": met.get("compartment", ""),
|
|
118
|
+
"chebi_id": met.get("chebiId", ""),
|
|
119
|
+
"kegg_id": met.get("keggId", ""),
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
df = pd.DataFrame(results[:limit])
|
|
123
|
+
print(f"Metabolic Atlas metabolites: {len(df)} results "
|
|
124
|
+
f"(query={query})")
|
|
125
|
+
return df
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 3. 代謝ネットワーク解析
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import networkx as nx
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def metabolic_atlas_network(subsystem, model="Human-GEM"):
|
|
135
|
+
"""
|
|
136
|
+
Metabolic Atlas — サブシステム代謝ネットワーク構築。
|
|
137
|
+
|
|
138
|
+
Parameters:
|
|
139
|
+
subsystem: str — サブシステム名 (例: "Glycolysis")
|
|
140
|
+
model: str — GEM モデル名
|
|
141
|
+
"""
|
|
142
|
+
reactions = metabolic_atlas_search_reactions(
|
|
143
|
+
subsystem, model=model, limit=200)
|
|
144
|
+
|
|
145
|
+
G = nx.DiGraph()
|
|
146
|
+
|
|
147
|
+
for _, rxn in reactions.iterrows():
|
|
148
|
+
rxn_id = rxn["reaction_id"]
|
|
149
|
+
equation = str(rxn.get("equation", ""))
|
|
150
|
+
|
|
151
|
+
# 簡易パーサ: "A + B => C + D"
|
|
152
|
+
if "=>" in equation:
|
|
153
|
+
substrates_str, products_str = equation.split("=>", 1)
|
|
154
|
+
elif "=" in equation:
|
|
155
|
+
substrates_str, products_str = equation.split("=", 1)
|
|
156
|
+
else:
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
substrates = [s.strip() for s in substrates_str.split("+")
|
|
160
|
+
if s.strip()]
|
|
161
|
+
products = [p.strip() for p in products_str.split("+")
|
|
162
|
+
if p.strip()]
|
|
163
|
+
|
|
164
|
+
G.add_node(rxn_id, type="reaction",
|
|
165
|
+
name=rxn.get("name", ""))
|
|
166
|
+
|
|
167
|
+
for s in substrates:
|
|
168
|
+
G.add_node(s, type="metabolite")
|
|
169
|
+
G.add_edge(s, rxn_id)
|
|
170
|
+
|
|
171
|
+
for p in products:
|
|
172
|
+
G.add_node(p, type="metabolite")
|
|
173
|
+
G.add_edge(rxn_id, p)
|
|
174
|
+
|
|
175
|
+
# ネットワーク統計
|
|
176
|
+
n_reactions = sum(1 for _, d in G.nodes(data=True)
|
|
177
|
+
if d.get("type") == "reaction")
|
|
178
|
+
n_metabolites = sum(1 for _, d in G.nodes(data=True)
|
|
179
|
+
if d.get("type") == "metabolite")
|
|
180
|
+
|
|
181
|
+
print(f"Metabolic network: {n_reactions} reactions, "
|
|
182
|
+
f"{n_metabolites} metabolites, {G.number_of_edges()} edges")
|
|
183
|
+
return G
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 4. 代謝アトラス統合パイプライン
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
def metabolic_atlas_pipeline(query, model="Human-GEM",
|
|
190
|
+
output_dir="results"):
|
|
191
|
+
"""
|
|
192
|
+
代謝アトラス統合パイプライン。
|
|
193
|
+
|
|
194
|
+
Parameters:
|
|
195
|
+
query: str — 代謝経路/サブシステム名
|
|
196
|
+
model: str — GEM モデル名
|
|
197
|
+
output_dir: str — 出力ディレクトリ
|
|
198
|
+
"""
|
|
199
|
+
from pathlib import Path
|
|
200
|
+
output_dir = Path(output_dir)
|
|
201
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
202
|
+
|
|
203
|
+
# 1) 反応検索
|
|
204
|
+
reactions = metabolic_atlas_search_reactions(query, model=model)
|
|
205
|
+
reactions.to_csv(output_dir / "reactions.csv", index=False)
|
|
206
|
+
|
|
207
|
+
# 2) 代謝産物検索
|
|
208
|
+
metabolites = metabolic_atlas_search_metabolites(query, model=model)
|
|
209
|
+
metabolites.to_csv(output_dir / "metabolites.csv", index=False)
|
|
210
|
+
|
|
211
|
+
# 3) ネットワーク構築
|
|
212
|
+
G = metabolic_atlas_network(query, model=model)
|
|
213
|
+
nx.write_graphml(G, str(output_dir / "metabolic_network.graphml"))
|
|
214
|
+
|
|
215
|
+
# 4) ハブ代謝産物
|
|
216
|
+
met_nodes = [n for n, d in G.nodes(data=True)
|
|
217
|
+
if d.get("type") == "metabolite"]
|
|
218
|
+
hub_scores = {n: G.degree(n) for n in met_nodes}
|
|
219
|
+
hub_df = pd.DataFrame([
|
|
220
|
+
{"metabolite": k, "degree": v}
|
|
221
|
+
for k, v in sorted(hub_scores.items(),
|
|
222
|
+
key=lambda x: -x[1])[:20]
|
|
223
|
+
])
|
|
224
|
+
hub_df.to_csv(output_dir / "hub_metabolites.csv", index=False)
|
|
225
|
+
|
|
226
|
+
print(f"Metabolic Atlas pipeline: {output_dir}")
|
|
227
|
+
return {
|
|
228
|
+
"reactions": reactions,
|
|
229
|
+
"metabolites": metabolites,
|
|
230
|
+
"network": G,
|
|
231
|
+
"hubs": hub_df,
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## K-Dense 連携
|
|
238
|
+
|
|
239
|
+
| K-Dense Key | 参照内容 |
|
|
240
|
+
|-------------|---------|
|
|
241
|
+
| `metabolic-atlas` | 代謝モデル構造・反応データベース |
|
|
242
|
+
|
|
243
|
+
## パイプライン統合
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
metabolic-modeling → metabolic-atlas → systems-biology
|
|
247
|
+
(COBRA/FBA) (Human-GEM) (統合モデリング)
|
|
248
|
+
│ │ ↓
|
|
249
|
+
pathway-enrichment ─────┘ gene-expression
|
|
250
|
+
(KEGG/Reactome) │ (発現データ)
|
|
251
|
+
↓
|
|
252
|
+
multi-omics
|
|
253
|
+
(メタボロミクス統合)
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## パイプライン出力
|
|
257
|
+
|
|
258
|
+
| ファイル | 説明 | 次スキル |
|
|
259
|
+
|---------|------|---------|
|
|
260
|
+
| `results/reactions.csv` | 代謝反応一覧 | → metabolic-modeling |
|
|
261
|
+
| `results/metabolites.csv` | 代謝産物一覧 | → pathway-enrichment |
|
|
262
|
+
| `results/metabolic_network.graphml` | 代謝ネットワーク | → systems-biology |
|
|
263
|
+
| `results/hub_metabolites.csv` | ハブ代謝産物 | → multi-omics |
|