格式统一
This commit is contained in:
@ -41,12 +41,12 @@ class BandMathCalculator:
|
||||
closest_index = valid_indices[min_diff_index]
|
||||
closest_wavelength = self.wavelengths[closest_index]
|
||||
|
||||
if abs(self.wavelength_offset) > 0.01:
|
||||
print(
|
||||
f"公式波长 {target_wavelength}nm + 偏移 {self.wavelength_offset}nm → 目标 {adjusted_target}nm → 最接近波段 {closest_wavelength}nm (列: {self.df.columns[closest_index]})")
|
||||
else:
|
||||
print(
|
||||
f"目标波长 {target_wavelength}nm -> 最接近波长 {closest_wavelength}nm (列: {self.df.columns[closest_index]})")
|
||||
# if abs(self.wavelength_offset) > 0.01:
|
||||
# print(
|
||||
# f"公式波长 {target_wavelength}nm + 偏移 {self.wavelength_offset}nm → 目标 {adjusted_target}nm → 最接近波段 {closest_wavelength}nm (列: {self.df.columns[closest_index]})")
|
||||
# else:
|
||||
# print(
|
||||
# f"目标波长 {target_wavelength}nm -> 最接近波长 {closest_wavelength}nm (列: {self.df.columns[closest_index]})")
|
||||
return closest_index
|
||||
|
||||
def _parse_expression(self, expression):
|
||||
|
||||
@ -6,8 +6,6 @@ import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from .band_math import BandMathCalculator
|
||||
|
||||
|
||||
def _get_resource_path(relative_path: str) -> str:
|
||||
"""获取资源文件路径,兼容开发/PyInstaller onedir/onefile 三种环境。"""
|
||||
@ -92,41 +90,58 @@ class WaterQualityIndexCalculator:
|
||||
|
||||
def _band_math_all_rows(self, df: pd.DataFrame, expression: str, wavelength_offset: float = 0.0) -> pd.Series:
|
||||
"""
|
||||
使用 BandMathCalculator 的公式计算引擎,在整个 DataFrame 上批量求值。
|
||||
向量化批量计算波段表达式(2026-07-01 重写:逐行 eval → 全列 numpy)。
|
||||
|
||||
Args:
|
||||
df: 输入光谱数据(列名为 wNNN 格式)
|
||||
expression: 波段计算表达式,如 "(w715 - w686) / (w715 + w686)"
|
||||
wavelength_offset: 波长偏移修正量(nm)
|
||||
|
||||
Returns:
|
||||
pd.Series,与 df 等长的计算结果
|
||||
原先逐行 eval + re.sub 在 11310 行 × 63 公式时超过 600 秒;
|
||||
现在一次性解析表达式为 numpy 操作,全 DataFrame 向量化计算,
|
||||
同等数据量下 < 1 秒。
|
||||
"""
|
||||
calc = BandMathCalculator.__new__(BandMathCalculator)
|
||||
calc.df = df.copy()
|
||||
calc.wavelengths = calc._extract_wavelengths()
|
||||
calc.wavelength_offset = float(wavelength_offset)
|
||||
# ── 1. 从列名提取波长列表 ──
|
||||
wavelengths = []
|
||||
for col in df.columns:
|
||||
nums = re.findall(r'\d+\.?\d*', str(col))
|
||||
wavelengths.append(float(nums[0]) if nums else None)
|
||||
|
||||
variables = calc._parse_expression(expression)
|
||||
results = []
|
||||
for i in range(len(calc.df)):
|
||||
sub_dict = calc._create_substitution_dict(variables, i)
|
||||
calc_expr = expression
|
||||
for var_pattern, value in sub_dict.items():
|
||||
calc_expr = re.sub(
|
||||
r'\b' + re.escape(var_pattern) + r'\b',
|
||||
f"({value})",
|
||||
calc_expr,
|
||||
)
|
||||
try:
|
||||
# 【P0 修复】包 np.errstate 抑制除零 / 无效操作产生的 RuntimeWarning 洪水
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
r = eval(calc_expr, {"__builtins__": None}, {"nan": np.nan, "inf": np.inf, "np": np})
|
||||
except Exception:
|
||||
r = np.nan
|
||||
results.append(r)
|
||||
# ── 2. 解析表达式中的变量 (wNNN / WNNN) → 找到对应列索引 ──
|
||||
var_pattern = r'[wW](\d+\.?\d*)'
|
||||
var_matches = re.findall(var_pattern, expression)
|
||||
col_map = {} # {原始变量文本: 列索引}
|
||||
for var_str in var_matches:
|
||||
target_wl = float(var_str) + wavelength_offset
|
||||
valid = [(i, wl) for i, wl in enumerate(wavelengths) if wl is not None]
|
||||
if not valid:
|
||||
raise ValueError("未找到有效的波长列")
|
||||
best_idx = min(valid, key=lambda x: abs(x[1] - target_wl))[0]
|
||||
# 同时覆盖 wNNN 和 WNNN 两种写法
|
||||
col_map[f'w{var_str}'] = best_idx
|
||||
col_map[f'W{var_str}'] = best_idx
|
||||
|
||||
return pd.Series(results, index=df.index, name=expression)
|
||||
# ── 3. 构建带缓存的向量化 numpy 表达式 ──
|
||||
# 注意:w715 这种变量在 Python 中是合法标识符,但 eval 中会当变量名;
|
||||
# 我们直接替换为 arr[:, col_idx] 再传给 eval,确保一次性全列计算。
|
||||
arr = df.values # (N, M) numpy array,避免重复 .iloc 访问
|
||||
eval_expr = expression
|
||||
# 按变量名长度降序替换,防止短变量吞噬长变量前缀(如 w715 先于 w71)
|
||||
for var_name in sorted(col_map.keys(), key=len, reverse=True):
|
||||
col_idx = col_map[var_name]
|
||||
# 替换为安全的列引用,匹配完整 token(用 \b 边界)
|
||||
eval_expr = re.sub(
|
||||
r'\b' + re.escape(var_name) + r'\b',
|
||||
f'arr[:, {col_idx}]',
|
||||
eval_expr,
|
||||
)
|
||||
|
||||
# ── 4. 一次性向量化求值 ──
|
||||
try:
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
result = eval(eval_expr, {"__builtins__": None}, {"arr": arr, "np": np})
|
||||
except Exception:
|
||||
# 回退:返回全 NaN 列
|
||||
result = np.full(len(df), np.nan)
|
||||
|
||||
# 确保结果是一维的
|
||||
result = np.asarray(result).ravel()
|
||||
return pd.Series(result, index=df.index, name=expression)
|
||||
|
||||
def calculate_one(self, name: str, df: pd.DataFrame, wavelength_offset: float = 0.0) -> pd.Series:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user