import numpy as np import pandas as pd import pyvista as pv from pykrige.ok3d import OrdinaryKriging3D import os # ===== 1. 数据加载与预处理 ===== def load_data(file_path): df = pd.read_excel(file_path, sheet_name='土壤') mask = ~df['@@甲苯'].isna() & (df['@@甲苯'] >= 0.01) df = df[mask].copy() # 提取坐标和值(假设列名正确) x = df['x'].values.astype(float) y = df['y'].values.astype(float) z = df['Elevation'].values.astype(float) values = df['@@甲苯'].values.astype(float) # IQR过滤极端值 q25, q75 = np.percentile(values, [25, 75]) iqr = q75 - q25 valid_mask = values <= (q75 + 3*iqr) return x[valid_mask], y[valid_mask], z[valid_mask], values[valid_mask] x, y, z, values = load_data('head/Dinglong2.xlsx') # ===== 2. 对数变换 ===== epsilon = 1e-6 log_values = np.log(values + epsilon) # ===== 3. 克里金插值 ===== ok3d = OrdinaryKriging3D( x, y, z, log_values, variogram_model='spherical', variogram_parameters={'nugget': 0.1, 'sill': np.var(log_values), 'range': 100.0}, nlags=20, enable_plotting=False ) # 生成网格(修正扩展逻辑) grid_x = np.linspace(x.min() - 50, x.max() + 50, 50) # 添加缓冲距离50米 grid_y = np.linspace(y.min() - 50, y.max() + 50, 50) grid_z = np.linspace(z.min() - 10, z.max() + 10, 30) # Z轴扩展减少幅度 log_krig, log_var = ok3d.execute('grid', grid_x, grid_y, grid_z) # ===== 4. 反变换与数据清洗 ===== krig_values = np.exp(log_krig + 0.5 * log_var) - epsilon krig_values = np.nan_to_num(krig_values, nan=0.0) # 处理NaN[3](@ref) krig_values = np.clip(krig_values, 0, None) # 确保非负 # ===== 5. 三维网格构建 ===== def create_volume_grid(grid_x, grid_y, grid_z, data): """创建三维网格(修正数据对齐)""" # 强制维度对齐 (nx, ny, nz) data_3d = data.reshape(len(grid_x), len(grid_y), len(grid_z), order='F') grid = pv.RectilinearGrid(grid_x, grid_y, grid_z) grid["浓度"] = data_3d.flatten(order='F') return grid grid = create_volume_grid(grid_x, grid_y, grid_z, krig_values) # 数据验证 print("[数据验证]") print(f"网格点数: {grid.n_points}") print(f"浓度范围: {grid['浓度'].min():.2f} ~ {grid['浓度'].max():.2f}") # 动态设置等值面阈值 if grid['浓度'].max() < 1e-3: raise ValueError("浓度数据异常,请检查克里金参数") else: threshold = np.percentile(grid['浓度'][grid['浓度'] > 0], 75) contour = grid.contour([threshold]) print(f"有效阈值: {threshold:.2f}") # 可视化 plotter = pv.Plotter() try: plotter.add_mesh( contour, cmap='hot', opacity=0.7, clim=[grid['浓度'].min(), grid['浓度'].max()], show_scalar_bar=True ) except ValueError: print("等值面数据为空,启用备用渲染方案") slices = grid.slice_orthogonal() plotter.add_mesh(slices, cmap='hot', opacity=0.7) # 添加钻孔点 points = pv.PolyData(np.column_stack((x, y, z))) points["浓度"] = values plotter.add_mesh( points, scalars="浓度", point_size=10, render_points_as_spheres=True, cmap='hot' ) plotter.show()分析代码,生成的vtk文件np.log转回原数据
时间: 2025-07-08 11:38:04 浏览: 18
<think>嗯,我现在需要分析用户提供的Python代码,特别是关于生成的vtk文件在使用np.log转换后如何正确转回原始数据的问题。让我仔细看一下代码的结构和流程,然后逐步理清楚数据处理的步骤。
首先,用户导入了必要的库,包括numpy、pandas、pyvista,还有pykrige.ok3d的OrdinaryKriging3D。看起来这是一个进行三维克里金插值的程序,用于处理土壤中的甲苯浓度数据。
代码分为几个部分:
1. **数据加载与预处理**:从Excel文件中读取数据,筛选出有效的甲苯浓度数据(非空且大于等于0.01),然后通过IQR方法去除极端值。这一步得到x、y、z坐标和对应的浓度值。
2. **对数变换**:对浓度值进行对数变换,加上epsilon(1e-6)避免对零或负数取对数。这里可能因为原始数据有较大的范围,或者分布偏斜,使用对数变换可以使其更接近正态分布,适合克里金插值。
3. **克里金插值**:使用OrdinaryKriging3D进行三维插值。参数包括球状变差函数,设置了块金值、基台值和范围。然后生成网格,并进行插值,得到log_krig和log_var,这应该是插值后的对数浓度和对应的方差。
4. **反变换与数据清洗**:将插值后的对数结果转换回原始浓度。这里使用np.exp,并且加上0.5*log_var,这可能是因为对数正态分布的性质,需要调整方差。然后处理NaN值和负值。
5. **三维网格构建与可视化**:创建pyvista的RectilinearGrid,将数据映射到网格上,生成等值面并进行可视化。同时添加钻孔点显示原始数据。
用户的问题是关于生成的vtk文件中,使用np.log转换后的数据如何正确转回原始数据。我需要检查代码中的反变换步骤是否正确,特别是是否有考虑克里金插值带来的方差影响。
首先,在反变换部分,代码中的处理是:
log_krig和log_var是克里金插值后的结果,分别代表对数空间中的估计值和方差。根据对数正态分布的性质,如果原始数据经过自然对数变换后(即log_values = ln(values + epsilon)),那么在反变换时,正确的转换应该是:
values = exp(log_krig + 0.5 * sigma²) - epsilon
这里的sigma²是log_var,即对数空间中的方差。因此,代码中的krig_values = np.exp(log_krig + 0.5 * log_var) - epsilon这一步是正确的,因为它考虑了对数正态分布的期望值,即E[Y] = exp(μ + 0.5σ²),其中μ是log_krig,σ²是log_var。
不过,需要注意log_var是否确实是方差。在克里金插值中,返回的log_var应该是预测值的方差,也就是在克里金过程中估计的方差。这里的OrdinaryKriging3D的execute方法返回的是kriging方差吗?需要查看pykrige库的文档确认。假设log_var确实是预测值的方差,那么反变换是正确的。
另外,epsilon的处理是否正确?在对数变换时,代码用了values + epsilon,所以在反变换时应该减去同一个epsilon,否则会引入偏差。例如,如果原始数据是0,那么log_values会是ln(0 + epsilon),反变换时应该exp(log_krig + ...) - epsilon。这一步处理是正确的,但需要注意epsilon的值是否足够小,不会对结果造成显著影响。用户代码中使用的是1e-6,这在浓度数据中可能是可以接受的。
接下来,在数据清洗步骤中,代码处理了NaN值,将其替换为0,并裁剪负值为0。这可能是因为插值过程中可能产生极小的负数,或者由于数值计算问题导致的异常值。这一步保证了最终浓度值的合理性,因为浓度不能为负。
然后,关于生成的vtk文件中的浓度值是否已经正确反变换。在代码中,网格数据是通过krig_values赋值的,而krig_values已经进行了反变换和清洗,所以在创建网格时,grid["浓度"] = data_3d.flatten(order='F'),这里的data_3d已经是原始浓度单位的数值了。因此,vtk文件中存储的浓度值应该是已经正确反变换后的数据,不需要额外处理。
不过,用户可能担心是否在生成vtk文件时,是否误用了log_krig而没有进行反变换。需要确认在create_volume_grid函数中传入的是krig_values,而不是log_krig。查看代码,create_volume_grid的参数是krig_values,所以是正确的。
此外,数据验证部分输出了浓度范围,应该检查这个范围是否符合原始数据的预期。如果原始数据经过对数变换和反变换,那么这里的浓度范围应该和原始数据经过清洗后的范围大体一致,但可能因为插值而扩展或平滑。
可能的潜在问题:
1. **epsilon的选择**:如果原始数据中存在非常小的浓度值(接近0),加上epsilon可能会对结果有轻微影响。需要确认epsilon的大小是否合理。
2. **方差的使用**:是否正确处理了克里金方差。例如,如果log_var是克里金方差,那么加上0.5*log_var是正确的。如果log_var是标准差,那就会有问题。不过根据pykrige的文档,execute方法返回的第二个值应该是方差,所以这里正确。
3. **网格生成顺序**:在reshape数据时,使用了order='F'(Fortran顺序),这可能与网格坐标的排列方式有关。需要确认生成的3D数组是否与grid_x、grid_y、grid_z的维度正确对应。例如,reshape(len(grid_x), len(grid_y), len(grid_z), order='F')是否正确。如果grid_x是x轴,grid_y是y轴,grid_z是z轴,那么通常三维数组的形状应为(len(grid_x), len(grid_y), len(grid_z))),而flatten时也使用order='F'保证数据顺序正确。这部分可能需要进一步检查,但如果是按照pyvista的RectilinearGrid的要求,应该是正确的。
4. **异常处理**:在动态设置等值面阈值时,如果浓度最大值过小,会抛出异常。这可能是因为反变换后的数据存在问题,比如所有值都很小,这时候可能需要检查克里金参数是否正确,或者数据预处理是否有误。
总结:代码中的反变换步骤是正确的,考虑了克里金方差的影响,并且对异常值进行了处理。生成的vtk文件中的浓度值已经是反变换后的原始单位数据,不需要额外转换。用户可能需要验证输出结果的浓度范围是否符合预期,并检查克里金参数(如变差函数模型、参数设置)是否适合当前数据集。</think>以下是对代码中数据转换逻辑的逐步分析:
$\boxed{\text{数据转换验证}}$
1. **对数变换公式**
$$log\_values = \ln(values + \epsilon)$$
$\epsilon=1e^{-6}$用于处理零值
2. **克里金反变换公式**
根据对数正态分布性质:
$$krig\_values = e^{(log\_krig + 0.5 \cdot log\_var)} - \epsilon$$
$\bullet$ $log\_krig$是对数空间的预测值
$\bullet$ $log\_var$是预测方差
$\bullet$ $0.5 \cdot log\_var$修正偏差
3. **vtk数据验证**
```python
print(f"浓度范围: {grid['浓度'].min():.2f} ~ {grid['浓度'].max():.2f}")
```
$\Rightarrow$ 输出结果应与原始数据范围匹配
$\boxed{\text{关键参数验证}}$
| 参数 | 当前值 | 建议验证方式 |
|------|--------|--------------|
| $\epsilon$ | $1e^{-6}$ | 检查原始数据最小值是否$\geq 0$ |
| variogram_range | 100.0 | 应与空间自相关距离匹配 |
| threshold计算 | 75分位数 | 需确认是否符合污染分布特征 |
$\boxed{\text{改进建议}}$
1. **反变换验证方法**
```python
# 添加反变换验证代码
sample_idx = np.argmax(values)
pred_log = ok3d.execute('points', x[sample_idx], y[sample_idx], z[sample_idx])[0]
reconstructed = np.exp(pred_log + 0.5 * log_var.flatten()[sample_idx]) - epsilon
print(f"实测值: {values[sample_idx]:.4f}, 重构值: {reconstructed:.4f}")
```
2. **空间参考系确认**
$\circ$ 检查x,y坐标单位是否统一(应均为米)
$\circ$ 验证Elevation是否经过高程校正
$\boxed{\text{误差分析}}$
可能引入误差的环节:
1. 变差函数模型选择(spherical可能不适合复杂地质结构)
2. 缓冲距离设置(当前±50米可能不足)
3. 方差修正项假设数据服从对数正态分布
建议通过交叉验证评估插值精度:
```python
from sklearn.model_selection import KFold
kf = KFold(n_splits=5)
for train_idx, test_idx in kf.split(x):
# 使用训练数据建模,预测测试点
# 计算预测值与实测值的相关系数
```
阅读全文
相关推荐



















