204 lines
6.3 KiB
Python
Executable File
204 lines
6.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
STRING Database API Client
|
||
用于获取蛋白互作网络数据
|
||
"""
|
||
|
||
import requests
|
||
import time
|
||
from pathlib import Path
|
||
from typing import List, Dict, Any, Optional
|
||
import json
|
||
|
||
from gfa_core.ppi_string_policy import STRING_SPECIES_MAP, string_taxon_id
|
||
|
||
|
||
class StringAPI:
|
||
"""STRING数据库API客户端"""
|
||
|
||
BASE_URL = "https://string-db.org/api"
|
||
|
||
SPECIES_MAP = STRING_SPECIES_MAP
|
||
|
||
def __init__(self, species: str = "ath", timeout: int = 30):
|
||
"""
|
||
初始化STRING API客户端
|
||
|
||
Args:
|
||
species: 物种代码(ath, osa, zma等)
|
||
timeout: 请求超时时间(秒)
|
||
"""
|
||
self.species = species
|
||
self.species_id = string_taxon_id(species, "3702")
|
||
self.timeout = timeout
|
||
self.session = requests.Session()
|
||
|
||
def get_network(self,
|
||
proteins: List[str],
|
||
required_score: int = 200,
|
||
network_type: str = "functional") -> Dict[str, Any]:
|
||
"""
|
||
获取蛋白互作网络
|
||
|
||
Args:
|
||
proteins: 蛋白ID列表
|
||
required_score: 互作置信度阈值(0-1000,默认200=0.7)
|
||
network_type: 网络类型(functional/physical)
|
||
|
||
Returns:
|
||
互作网络数据
|
||
"""
|
||
if not proteins:
|
||
return {"error": "No proteins provided"}
|
||
|
||
# STRING API限制:每次最多2000个蛋白
|
||
if len(proteins) > 2000:
|
||
proteins = proteins[:2000]
|
||
print(f"[WARNING] Protein list truncated to 2000 (STRING API limit)")
|
||
|
||
params = {
|
||
"identifiers": "\r".join(proteins),
|
||
"species": self.species_id,
|
||
"required_score": required_score,
|
||
"network_type": network_type,
|
||
"caller_identity": "BotanicalBaseAgent"
|
||
}
|
||
|
||
try:
|
||
response = self.session.get(
|
||
f"{self.BASE_URL}/json/network",
|
||
params=params,
|
||
timeout=self.timeout
|
||
)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
except requests.exceptions.RequestException as e:
|
||
return {"error": f"API request failed: {str(e)}"}
|
||
|
||
def get_interaction_partners(self,
|
||
protein: str,
|
||
required_score: int = 200,
|
||
limit: int = 100) -> Dict[str, Any]:
|
||
"""
|
||
获取指定蛋白的互作伙伴
|
||
|
||
Args:
|
||
protein: 蛋白ID
|
||
required_score: 互作置信度阈值
|
||
limit: 返回的最大互作数
|
||
|
||
Returns:
|
||
互作伙伴数据
|
||
"""
|
||
params = {
|
||
"identifiers": protein,
|
||
"species": self.species_id,
|
||
"required_score": required_score,
|
||
"limit": limit,
|
||
"caller_identity": "BotanicalBaseAgent"
|
||
}
|
||
|
||
try:
|
||
response = self.session.get(
|
||
f"{self.BASE_URL}/json/interaction_partners",
|
||
params=params,
|
||
timeout=self.timeout
|
||
)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
except requests.exceptions.RequestException as e:
|
||
return {"error": f"API request failed: {str(e)}"}
|
||
|
||
def get_protein_info(self, proteins: List[str]) -> Dict[str, Any]:
|
||
"""
|
||
获取蛋白详细信息
|
||
|
||
Args:
|
||
proteins: 蛋白ID列表
|
||
|
||
Returns:
|
||
蛋白信息数据
|
||
"""
|
||
if not proteins:
|
||
return {"error": "No proteins provided"}
|
||
|
||
params = {
|
||
"identifiers": "\r".join(proteins),
|
||
"species": self.species_id,
|
||
"caller_identity": "BotanicalBaseAgent"
|
||
}
|
||
|
||
try:
|
||
response = self.session.get(
|
||
f"{self.BASE_URL}/json/protein_info",
|
||
params=params,
|
||
timeout=self.timeout
|
||
)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
except requests.exceptions.RequestException as e:
|
||
return {"error": f"API request failed: {str(e)}"}
|
||
|
||
def save_network_to_file(self,
|
||
network_data: Dict[str, Any],
|
||
output_file: Path,
|
||
format: str = "tsv"):
|
||
"""
|
||
保存网络数据到文件
|
||
|
||
Args:
|
||
network_data: 网络数据
|
||
output_file: 输出文件路径
|
||
format: 输出格式(tsv/json)
|
||
"""
|
||
output_file = Path(output_file)
|
||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||
|
||
if format == "json":
|
||
with open(output_file, 'w') as f:
|
||
json.dump(network_data, f, indent=2)
|
||
elif format == "tsv":
|
||
# 转换为边列表格式
|
||
edges = []
|
||
for interaction in network_data:
|
||
if isinstance(interaction, dict):
|
||
edges.append({
|
||
"protein1": interaction.get("preferredName_A", ""),
|
||
"protein2": interaction.get("preferredName_B", ""),
|
||
"score": interaction.get("score", 0),
|
||
"ncbi_tax_id": interaction.get("ncbiTaxonId", ""),
|
||
})
|
||
|
||
import pandas as pd
|
||
df = pd.DataFrame(edges)
|
||
df.to_csv(output_file, sep="\t", index=False)
|
||
|
||
print(f"[INFO] Network saved to: {output_file}")
|
||
|
||
|
||
def test_string_api():
|
||
"""测试STRING API"""
|
||
api = StringAPI(species="ath")
|
||
|
||
# 测试基因
|
||
test_genes = ["AT1G01010", "AT1G01020", "AT1G01030"]
|
||
|
||
print("Testing STRING API...")
|
||
print(f"Species: {api.species} (ID: {api.species_id})")
|
||
print(f"Test genes: {test_genes}")
|
||
|
||
# 获取网络
|
||
network = api.get_network(test_genes, required_score=400)
|
||
|
||
if "error" in network:
|
||
print(f"Error: {network['error']}")
|
||
else:
|
||
print(f"Interactions found: {len(network)}")
|
||
if network:
|
||
print("Sample interaction:")
|
||
print(json.dumps(network[0], indent=2))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
test_string_api()
|