要基于存储在Excel中(表现为多张二维表格)的三维数据进行三输入线性插值查表,核心在于解析数据结构和实现三维线性插值算法。以下是一个功能完整的Python函数实现。
### 1. 函数定义与数据加载
该函数首先使用`pandas`加载Excel文件中的所有工作表,将其组织成一个三维数据结构(通常是字典或列表的嵌套),然后基于三个输入坐标`(x, y, z)`执行三维线性插值。
```python
import pandas as pd
import numpy as np
from typing import Union, Dict, List
def excel_3d_interpolation(excel_path: str,
x_val: float,
y_val: float,
z_val: float,
x_col_name: str = 'x',
y_col_name: str = 'y',
z_sheet_prefix: str = 'z_') -> float:
"""
对存储在Excel多工作表(每个工作表代表一个Z平面)中的三维表格进行线性插值查表。
参数:
excel_path: Excel文件路径。
x_val: 要查询的X坐标值。
y_val: 要查询的Y坐标值。
z_val: 要查询的Z坐标值。
x_col_name: 每个工作表中代表X坐标的列名。
y_col_name: 每个工作表中代表Y坐标的列名。
z_sheet_prefix: 工作表名称的前缀,用于识别Z平面。例如,工作表名'z_10'表示Z=10的平面。
返回:
插值结果。如果查询点超出数据范围,将引发ValueError。
"""
# 加载所有工作表
all_sheets = pd.read_excel(excel_path, sheet_name=None)
# 解析三维数据结构:提取Z值并关联数据
z_planes = {}
for sheet_name, df in all_sheets.items():
if sheet_name.startswith(z_sheet_prefix):
try:
z = float(sheet_name[len(z_sheet_prefix):]) # 从工作表名提取Z值
z_planes[z] = df.set_index([x_col_name, y_col_name])
except ValueError:
print(f"警告:工作表名'{sheet_name}'不符合Z平面命名约定,已跳过。")
if not z_planes:
raise ValueError("未找到有效的Z平面工作表。请检查工作表命名前缀。")
# 获取所有唯一的X、Y、Z坐标并排序
z_coords = sorted(z_planes.keys())
# 假设所有Z平面的X、Y网格相同,取第一个平面获取坐标
sample_df = z_planes[z_coords[0]]
x_coords = sorted(sample_df.index.get_level_values(x_col_name).unique())
y_coords = sorted(sample_df.index.get_level_values(y_col_name).unique())
# 检查查询点是否在数据范围内
if (x_val < min(x_coords) or x_val > max(x_coords) or
y_val < min(y_coords) or y_val > max(y_coords) or
z_val < min(z_coords) or z_val > max(z_coords)):
raise ValueError(f"查询点({x_val}, {y_val}, {z_val})超出数据范围。"
f"X范围: [{min(x_coords)}, {max(x_coords)}], "
f"Y范围: [{min(y_coords)}, {max(y_coords)}], "
f"Z范围: [{min(z_coords)}, {max(z_coords)}]")
# 在Z方向找到包围查询点的两个平面
z_low, z_high = None, None
for i in range(len(z_coords) - 1):
if z_coords[i] <= z_val <= z_coords[i + 1]:
z_low, z_high = z_coords[i], z_coords[i + 1]
break
if z_low is None: # 查询点恰好位于某个Z平面
z_low = z_high = max(z for z in z_coords if z <= z_val)
# 获取两个Z平面的数据
df_z_low = z_planes[z_low]
df_z_high = z_planes.get(z_high, df_z_low) # 如果z_high不存在(查询点在边界),使用相同平面
# 在X方向找到包围点
x_low = max(x for x in x_coords if x <= x_val)
x_high = min(x for x in x_coords if x >= x_val)
# 在Y方向找到包围点
y_low = max(y for y in y_coords if y <= y_val)
y_high = min(y for y in y_coords if y >= y_val)
# 提取四个角点的值(在低Z平面)
try:
f_xlyl_zlow = df_z_low.loc[(x_low, y_low)].iloc[0] # 假设值为第一列
f_xlyh_zlow = df_z_low.loc[(x_low, y_high)].iloc[0]
f_xhyl_zlow = df_z_low.loc[(x_high, y_low)].iloc[0]
f_xhyh_zlow = df_z_low.loc[(x_high, y_high)].iloc[0]
except KeyError as e:
raise ValueError(f"在Z={z_low}平面未找到坐标({e})对应的数据点。")
# 如果高低Z平面不同,提取高Z平面的四个角点值
if z_low != z_high:
try:
f_xlyl_zhigh = df_z_high.loc[(x_low, y_low)].iloc[0]
f_xlyh_zhigh = df_z_high.loc[(x_low, y_high)].iloc[0]
f_xhyl_zhigh = df_z_high.loc[(x_high, y_low)].iloc[0]
f_xhyh_zhigh = df_z_high.loc[(x_high, y_high)].iloc[0]
except KeyError as e:
raise ValueError(f"在Z={z_high}平面未找到坐标({e})对应的数据点。")
else:
f_xlyl_zhigh = f_xlyl_zlow
f_xlyh_zhigh = f_xlyh_zlow
f_xhyl_zhigh = f_xhyl_zlow
f_xhyh_zhigh = f_xhyh_zlow
# 第一步:在低Z平面进行二维线性插值
if x_low == x_high:
f_zlow = f_xlyl_zlow if y_low == y_high else (
f_xlyl_zlow + (f_xlyh_zlow - f_xlyl_zlow) * (y_val - y_low) / (y_high - y_low)
)
elif y_low == y_high:
f_zlow = f_xlyl_zlow + (f_xhyl_zlow - f_xlyl_zlow) * (x_val - x_low) / (x_high - x_low)
else:
# 双线性插值
f_zlow_yl = f_xlyl_zlow + (f_xhyl_zlow - f_xlyl_zlow) * (x_val - x_low) / (x_high - x_low)
f_zlow_yh = f_xlyh_zlow + (f_xhyh_zlow - f_xlyh_zlow) * (x_val - x_low) / (x_high - x_low)
f_zlow = f_zlow_yl + (f_zlow_yh - f_zlow_yl) * (y_val - y_low) / (y_high - y_low)
# 第二步:在高Z平面进行二维线性插值
if z_low != z_high:
if x_low == x_high:
f_zhigh = f_xlyl_zhigh if y_low == y_high else (
f_xlyl_zhigh + (f_xlyh_zhigh - f_xlyl_zhigh) * (y_val - y_low) / (y_high - y_low)
)
elif y_low == y_high:
f_zhigh = f_xlyl_zhigh + (f_xhyl_zhigh - f_xlyl_zhigh) * (x_val - x_low) / (x_high - x_low)
else:
f_zhigh_yl = f_xlyl_zhigh + (f_xhyl_zhigh - f_xlyl_zhigh) * (x_val - x_low) / (x_high - x_low)
f_zhigh_yh = f_xlyh_zhigh + (f_xhyh_zhigh - f_xlyh_zhigh) * (x_val - x_low) / (x_high - x_low)
f_zhigh = f_zhigh_yl + (f_zhigh_yh - f_zhigh_yl) * (y_val - y_low) / (y_high - y_low)
else:
f_zhigh = f_zlow
# 第三步:在Z方向进行线性插值
if z_low == z_high:
result = f_zlow
else:
result = f_zlow + (f_zhigh - f_zlow) * (z_val - z_low) / (z_high - z_low)
return result
```
### 2. Excel数据结构要求与示例
为了使上述函数正常工作,Excel文件需要遵循特定的结构约定。每个工作表代表一个Z平面,工作表名称应包含Z值(例如`z_0`、`z_10`、`z_20`)。每个工作表内是一个标准的二维表格,包含X坐标列、Y坐标列和对应的数值列。
| 工作表命名示例 | 含义 |
| :--- | :--- |
| `z_0` | Z=0时的数据平面 |
| `z_5` | Z=5时的数据平面 |
| `z_10` | Z=10时的数据平面 |
每个工作表内的数据结构示例如下(假设`x_col_name='X'`, `y_col_name='Y'`,数值列名为`'Value'`):
**工作表 `z_0` 内容示例:**
| X | Y | Value |
|---|---|-------|
| 0 | 0 | 1.0 |
| 0 | 1 | 1.5 |
| 1 | 0 | 2.0 |
| 1 | 1 | 2.5 |
**工作表 `z_10` 内容示例:**
| X | Y | Value |
|---|---|-------|
| 0 | 0 | 3.0 |
| 0 | 1 | 3.5 |
| 1 | 0 | 4.0 |
| 1 | 1 | 4.5 |
### 3. 函数使用示例
```python
# 示例1:基本调用
result = excel_3d_interpolation(
excel_path='3d_data.xlsx',
x_val=0.5,
y_val=0.5,
z_val=5.0,
x_col_name='X',
y_col_name='Y',
z_sheet_prefix='z_'
)
print(f"插值结果: {result}") # 对于上述示例数据,应输出2.5
# 示例2:批量查询
query_points = [(0.2, 0.8, 2.0), (0.7, 0.3, 8.0), (0.5, 0.5, 5.0)]
results = []
for x, y, z in query_points:
try:
val = excel_3d_interpolation('3d_data.xlsx', x, y, z)
results.append(val)
except ValueError as e:
print(f"查询点({x}, {y}, {z})错误: {e}")
results.append(None)
print(f"批量查询结果: {results}")
```
### 4. 技术实现要点解析
该函数的核心算法遵循三维线性插值的标准流程,其计算过程可分解为以下步骤:
1. **数据解析与验证**:加载Excel文件,根据工作表名称前缀识别Z平面,提取每个平面的二维数据并建立索引,验证数据完整性和查询点范围[ref_1]。
2. **坐标定位**:在X、Y、Z三个维度上分别找到包围查询点的两个最近邻网格点。这是线性插值的前提,确保插值在已知数据点之间进行。
3. **分层插值**:
* 首先在低Z平面和高Z平面上分别执行二维双线性插值。二维插值本身又分为两步:先在X方向线性插值得到两个中间值,再在Y方向对这两个中间值进行插值[ref_3]。
* 然后在Z方向上对两个二维插值结果进行线性插值,得到最终的三维插值结果。
4. **边界处理**:函数包含了完善的边界情况处理逻辑。当查询点恰好位于某个网格点或网格线上时(例如`x_low == x_high`),插值会退化为低维度的线性插值或直接取值,避免除零错误[ref_5]。
这种“先二维后一维”的插值策略(即先在两个Z平面上做双线性插值,再在Z方向线性插值)在数学上等价于三线性插值,是处理规则三维网格数据最高效且准确的方法之一[ref_3]。
### 5. 高级功能扩展
对于更复杂的应用场景,可以考虑以下扩展方向:
* **非网格数据支持**:当前实现假设每个Z平面的X-Y网格是完全相同的。如果网格不一致,需要修改数据结构解析部分,为每个Z平面独立存储其X、Y坐标,并在插值时动态查找每个平面的最近邻点。
* **外推功能**:通过设置`extrapolate=True`参数并配合外推算法(如最近邻外推或线性外推),允许查询点略微超出数据范围。
* **多数值列支持**:当前函数默认使用数据框的第一列作为插值目标。可扩展为支持指定列名或同时插值多列,返回字典或数组。
* **性能优化**:对于大规模数据或频繁查询,可将解析后的三维数据缓存起来,避免每次查询都重新读取Excel文件。也可使用`scipy.interpolate.RegularGridInterpolator`等库函数替代手动插值逻辑,提升计算效率和数值稳定性。
此函数提供了一个从Excel三维表格到Python可调用插值函数的完整解决方案,其结构清晰、鲁棒性强,可直接应用于工程数据分析、仿真结果查询等实际场景[ref_1][ref_3][ref_5]。