在数据分割过程中保留数据的空间分布

问题描述 投票:0回答:1

我正在尝试使用随机森林模型来模拟德国巴伐利亚河流中的硝酸盐浓度。我使用 Python,主要使用 sklearn。我有 490 个水质站的数据。我遵循 LongzhuQ.Shen 等人论文中的方法,可以在这里找到:https://www.nature.com/articles/s41597-020-0478-7

我想将数据集分成训练集和测试集,以便两个集中数据的空间分布相同。这个想法是,如果数据分割忽略空间分布,则存在训练集最终可能集中来自人口稠密区域的点而忽略稀疏区域的风险。这可能会扭曲模型的学习过程,使其在整个感兴趣领域的准确性或概括性降低。 sklearn train_test_split 只是将数据随机划分为训练集和测试集,并且不考虑数据中的空间模式。

我上面提到的论文遵循了这种方法:“我们将完整的数据集分为两个子数据集,分别是训练和测试。为了考虑计量站空间分布的异质性,我们在数据中采用了空间密度估计技术通过使用带宽为 50 km 的高斯核(使用 GRASS GIS33 中可用的 v.kernel)为每个物种和季节构建密度表面来进行分割步骤。所得密度表面的像素值用作将数据分割为的权重因子。训练和测试具有相同空间分布的子集。”

我想遵循相同的方法,但我不使用草地 GIS,而是自己用 Python 构建密度表面。我还提取了概率密度值和站点的权重。 (附图)

现在我面临的唯一问题是如何使用这些权重将数据分成训练集和测试集?我检查了sklearn train_test_split函数中没有可以考虑权重的关键字。我也与GPT 4聊天来回,但它也无法给我一个明确的答案。我在互联网上也没有找到任何关于此的具体信息。也许我错过了一些东西。

还有其他功能可以用来执行此操作吗?或者我必须编写自己的算法来进行分割?如果是后者,您能否建议我一种方法,以便我可以自己编写代码?

在附图中您可以看到站点的位置以及使用核密度估计方法(使用高斯核)生成的概率密度面。

还附上我的数据框的屏幕截图,让您了解数据结构。 (经度(‘lon’)列之后的所有列都用作特征。NO3 列用作目标变量。)

请查找附件图片以供参考。

Probability density surface generated using the kernel density estimation method with gaussian kernels.

the dataset I am using to model the nitrate concentrations

python machine-learning
1个回答
0
投票

这似乎按要求工作。至少从视觉上看是这样。您需要进一步探索结果以确认它们是正确的且符合预期。我认为各种参数的分布(例如

df.hist()
)在采样集和余数集之间应该看起来相似。

我遵循的过程听起来与您引用的过程类似,其中我使用“像素值”(每个数据点的 KDE 得分)作为“加权因子”。

我安装了一个

KernelDensity
估计器,以便导出每个数据点的密度分数,然后将密度分数提供给
weights=
<dataframe>.resample(...)
参数。

下面是未加权和加权采样结果之间的视觉比较。

无权重采样: enter image description here

带权重采样: enter image description here


可重现的示例:

import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

#
#Data for testing
#
np.random.seed(0)
from sklearn.datasets import make_moons

X, _ = make_moons(n_samples=400, noise=0.1, random_state=11)
X = np.concatenate([X, np.random.uniform(-2, 2, size=[500, 2])], axis=0)

#Adjust scale and location
X[:, 0] = (X[:, 0] - X[:, 0].min()) / (X[:, 0] - X[:, 0].min()).max() * 4 + 10
X[:, 1] = (X[:, 1] - X[:, 1].min()) / (X[:, 0] - X[:, 0].min()).max() * 7.5 + 47

df = pd.DataFrame({'latitude': X[:, 1], 'longitude': X[:, 0]})

f, ax = plt.subplots(figsize=(8, 3.5))
ax.scatter(df.longitude, df.latitude, marker='.', color='black', label='monitoring station')
ax.set(xlabel='longitude', ylabel='latitude')
ax.spines[:].set_visible(False)

#
#Fit a density estimator & get the density map
#
from sklearn.neighbors import KernelDensity

kd_estimator = KernelDensity(bandwidth=0.3, kernel='gaussian').fit(df[['longitude', 'latitude']])
density_scores = np.exp(kd_estimator.score_samples( df[['longitude', 'latitude']] ))
df['sample_weights'] = density_scores / density_scores.sum() #optionally norm to 1
 
#Overlay the density map
im = ax.tricontourf(
    df.longitude, df.latitude, density_scores,
    zorder=0, cmap='BuGn', levels=100
)
f.colorbar(im, label='density')

#Sample based on weight
df_test = df.sample(frac=0.2, weights=df.sample_weights, random_state=0)
# df_test = df.sample(frac=0.2, random_state=0) #sample without weights

df_train = df.drop(index=df_test.index)

#Visualise sampling distribution
ax.scatter(
    df_test.longitude, df_test.latitude,
    marker='o', s=50, facecolor='none', edgecolor='tab:red', linewidth=2,
    label='test sample'
)
f.legend(ncol=2)
f.tight_layout()
© www.soinside.com 2019 - 2024. All rights reserved.