myspace / query_tools /query_sprfmo.py
StarHidden's picture
新增cpue和sprfmo查询函数;优化工程结构
5d4bed3
Raw
History Blame Contribute Delete
10.1 kB
"""
query_sprfmo.py - SPRFMO 查询函数(最简稳定版)
"""
from pathlib import Path
import pandas as pd
# ============================================================
# 1. 路径配置
# ============================================================
DATA_DIR = Path(r"C:\Users\niniy\Desktop\PythonProject\SPRFMO南太平洋 5×5")
# ============================================================
# 2. 字段映射(原始 Excel 字段 → 标准字段)
# ============================================================
CATCH_MAP = {
"Year": "year",
"Flag": "country",
"Latitude": "lat",
"Longitude": "lon",
"Species": "species",
"NumVesels": "numvessels",
"Harvest_kg": "catch",
"Discard_kg": "discard",
}
EFFORT_MAP = {
"Year": "year",
"Fishery": "gear_type",
"NumDays": "effort",
"NumEvents": "num_events",
"NumVessels": "numvessels",
}
# ============================================================
# 3. 国家代码映射
# ============================================================
COUNTRY_MAP = {
"中国": "CHN", "China": "CHN", "CHN": "CHN",
"日本": "JPN", "Japan": "JPN", "JPN": "JPN",
"韩国": "KOR", "Korea": "KOR", "KOR": "KOR",
"中国台湾": "TWN", "Taiwan": "TWN", "TWN": "TWN",
"智利": "CHL", "Chile": "CHL", "CHL": "CHL",
"秘鲁": "PER", "Peru": "PER", "PER": "PER",
"俄罗斯": "RUS", "Russia": "RUS", "RUS": "RUS",
"美国": "USA", "USA": "USA", "United States": "USA",
"新西兰": "NZL", "New Zealand": "NZL", "NZL": "NZL",
"澳大利亚": "AUS", "Australia": "AUS", "AUS": "AUS",
}
# ============================================================
# 4. 主查询入口
# ============================================================
def query_sprfmo(filters=None, group_by=None, metrics="catch",
data_type="catch", output_format="markdown"):
"""SPRFMO 数据查询入口"""
if filters is None:
filters = {}
if group_by is None:
group_by = []
elif isinstance(group_by, str):
group_by = [group_by]
if isinstance(metrics, str):
metrics = [metrics]
warnings = []
print("\n" + "=" * 60)
print(f"【query_sprfmo】data_type={data_type}")
print(f" filters={filters}")
print(f" group_by={group_by}, metrics={metrics}")
print("=" * 60)
# 诊断信息
print(f"\n📂 数据文件夹: {DATA_DIR}")
print(f" 文件夹存在: {DATA_DIR.exists()}")
if DATA_DIR.exists():
all_files = list(DATA_DIR.glob("*.xlsx"))
print(f" 所有 Excel: {[f.name for f in all_files]}")
if not all_files:
warnings.append(f"⚠️ 文件夹下没有任何 .xlsx 文件")
else:
warnings.append(f"⚠️ 文件夹不存在: {DATA_DIR}")
return _error_result(f"文件夹不存在: {DATA_DIR}", warnings)
# 加载数据
if data_type == "catch":
df, source = _load_catch_data()
elif data_type == "effort":
df, source = _load_effort_data()
else:
return _error_result(f"未知 data_type: {data_type}", warnings)
# 字段翻译
df = _translate(df, data_type)
print(f" 翻译后字段: {df.columns.tolist()}")
# 国家代码翻译
if "country" in filters and filters["country"]:
c = str(filters["country"])
translated = COUNTRY_MAP.get(c, c)
filters["country"] = translated
if translated != c:
print(f" 国家翻译: '{c}' → '{translated}'")
# 筛选
df = _apply_filters(df, filters, data_type)
if len(df) == 0:
warnings.append("⚠️ 筛选后无数据,请检查筛选条件")
# 聚合
df = _aggregate(df, group_by, metrics, data_type)
# 返回
return {
"preview_markdown": df.to_markdown(index=False, floatfmt=".2f") if len(df) > 0 else "*(无数据)*",
"records": df.to_dict(orient="records"),
"csv_path": None,
"excel_path": None,
"summary": _build_summary(df, data_type),
"source_files": [source],
"warnings": warnings,
"metadata": {
"spatial_resolution": "5x5 degree",
"time_resolution": "annual",
"region": "South Pacific",
"unit": "kg" if data_type == "catch" else "days",
}
}
# ============================================================
# 5. 加载数据
# ============================================================
def _load_catch_data():
"""加载捕捞量 Excel"""
files = []
for kw in ["捕捞", "Catch", "catch"]:
files.extend(DATA_DIR.glob(f"*{kw}*.xlsx"))
files = list(set(files))
if not files:
all_files = [f.name for f in DATA_DIR.glob("*.xlsx")]
raise FileNotFoundError(
f"找不到捕捞量文件(需含 '捕捞' 或 'Catch')\n"
f"文件夹下所有 Excel: {all_files}"
)
df = pd.read_excel(files[0])
print(f" ✅ 加载: {files[0].name} ({len(df)} 条)")
return df, str(files[0])
def _load_effort_data():
"""加载努力量 Excel"""
files = []
for kw in ["努力", "Effort", "effort"]:
files.extend(DATA_DIR.glob(f"*{kw}*.xlsx"))
files = list(set(files))
if not files:
all_files = [f.name for f in DATA_DIR.glob("*.xlsx")]
raise FileNotFoundError(
f"找不到努力量文件(需含 '努力' 或 'Effort')\n"
f"文件夹下所有 Excel: {all_files}"
)
df = pd.read_excel(files[0])
print(f" ✅ 加载: {files[0].name} ({len(df)} 条)")
return df, str(files[0])
# ============================================================
# 6. 字段翻译
# ============================================================
def _translate(df, data_type):
"""原始字段 → 标准字段"""
fmap = CATCH_MAP if data_type == "catch" else EFFORT_MAP
used = {k: v for k, v in fmap.items() if k in df.columns}
return df.rename(columns=used)
# ============================================================
# 7. 筛选
# ============================================================
def _apply_filters(df, filters, data_type):
n0 = len(df)
if "year_start" in filters and filters["year_start"] is not None:
df = df[df["year"] >= filters["year_start"]]
if "year_end" in filters and filters["year_end"] is not None:
df = df[df["year"] <= filters["year_end"]]
if data_type == "catch":
if "country" in filters and filters["country"]:
c = str(filters["country"]).upper()
df = df[df["country"].astype(str).str.upper() == c]
if "species" in filters and filters["species"]:
kw = filters["species"]
df = df[df["species"].astype(str).str.contains(kw, case=False, na=False)]
elif data_type == "effort":
if "gear_type" in filters and filters["gear_type"]:
kw = filters["gear_type"]
df = df[df["gear_type"].astype(str).str.contains(kw, case=False, na=False)]
print(f" 筛选: {n0}{len(df)} 条")
return df
# ============================================================
# 8. 聚合
# ============================================================
def _aggregate(df, group_by, metrics, data_type):
if not group_by:
return df
valid_g = [g for g in group_by if g in df.columns]
if not valid_g:
return df
valid_m = [m for m in metrics if m in df.columns]
if not valid_m:
return df
agg = {m: ("first" if m == "numvessels" else "sum") for m in valid_m}
df2 = df.groupby(valid_g, as_index=False).agg(agg)
print(f" 聚合: {len(df2)} 条")
return df2
# ============================================================
# 9. 摘要
# ============================================================
def _build_summary(df, data_type):
summary = {
"data_type": data_type,
"total_records": len(df),
"data_source": "SPRFMO",
}
if "year" in df.columns and len(df) > 0:
years = df["year"].dropna()
if len(years) > 0:
summary["year_range"] = f"{int(years.min())}-{int(years.max())}"
if data_type == "catch" and "catch" in df.columns:
summary["total_catch"] = float(df["catch"].sum())
elif data_type == "effort" and "effort" in df.columns:
summary["total_effort"] = float(df["effort"].sum())
return summary
def _error_result(msg, warnings=None):
if warnings is None:
warnings = [msg]
return {
"preview_markdown": f"**错误**: {msg}",
"records": [],
"csv_path": None,
"excel_path": None,
"summary": {"data_type": None, "total_records": 0},
"source_files": [],
"warnings": warnings,
"metadata": {},
}
# ============================================================
# 10. 测试代码
# ============================================================
if __name__ == "__main__":
print("\n" + "🧪" * 30)
print("测试 query_sprfmo")
print("🧪" * 30)
# 测试 1
print("\n【测试 1】中国 2015-2020 年渔获量(按年)")
print("-" * 60)
r = query_sprfmo(
filters={"country": "CHN", "year_start": 2015, "year_end": 2020},
group_by=["year"],
metrics=["catch"],
data_type="catch",
)
print(r["preview_markdown"])
print("\n摘要:", r["summary"])
if r["warnings"]:
print("警告:")
for w in r["warnings"]:
print(f" {w}")
# 测试 2
print("\n\n【测试 2】2018 年努力量(按渔业类型)")
print("-" * 60)
r2 = query_sprfmo(
filters={"year_start": 2018, "year_end": 2018},
group_by=["gear_type"],
metrics=["effort"],
data_type="effort",
)
print(r2["preview_markdown"])
print("\n摘要:", r2["summary"])
if r2["warnings"]:
print("警告:")
for w in r2["warnings"]:
print(f" {w}")
print("\n" + "✅" * 30)
print("测试完成")
print("✅" * 30)