Pandas:如何在不使用 scikit 的情况下进行交叉验证?
Posted
技术标签:
【中文标题】Pandas:如何在不使用 scikit 的情况下进行交叉验证?【英文标题】:Pandas: How can I do cross validation without using scikit? 【发布时间】:2017-09-12 12:22:55 【问题描述】:我正在尝试实现自己的交叉验证功能。我在link 上阅读了有关交叉验证的信息,并且能够将我的数据集拆分为训练和测试。但是如何定义折叠? 例如我的数据框是这样的。
Dataframe:
MMC MET_lep MASS_Vis Pt_H Y
0 138.70 51.65 97.82 0.91 0
1 160.93 68.78 103.23 -999.00 0
2 -999.00 162.17 125.95 -999.00 0
3 143.90 81.41 80.94 -999.00 1
4 175.86 16.91 134.80 -999.00 0
5 -999.00 162.17 125.95 -999.00 0
6 143.90 81.41 80.94 -999.00 1
7 175.86 16.91 134.80 -999.00 0
8 -999.00 162.17 125.95 -999.00 0
9 143.90 81.41 80.94 -999.00 1
想要这样的输出:
For K=3 (Folds)
When K=1
Training:
MMC MET_lep MASS_Vis Pt_H Y
0 138.70 51.65 97.82 0.91 0
1 160.93 68.78 103.23 -999.00 0
2 -999.00 162.17 125.95 -999.00 0
3 143.90 81.41 80.94 -999.00 1
4 175.86 16.91 134.80 -999.00 0
5 -999.00 162.17 125.95 -999.00 0
6 143.90 81.41 80.94 -999.00 1
Test:
7 175.86 16.91 134.80 -999.00 0
8 -999.00 162.17 125.95 -999.00 0
9 143.90 81.41 80.94 -999.00 1
When K=2
Training:
MMC MET_lep MASS_Vis Pt_H Y
0 138.70 51.65 97.82 0.91 0
1 160.93 68.78 103.23 -999.00 0
2 -999.00 162.17 125.95 -999.00 0
6 143.90 81.41 80.94 -999.00 1
7 175.86 16.91 134.80 -999.00 0
8 -999.00 162.17 125.95 -999.00 0
9 143.90 81.41 80.94 -999.00 1
Test:
3 143.90 81.41 80.94 -999.00 1
4 175.86 16.91 134.80 -999.00 0
5 -999.00 162.17 125.95 -999.00 0
When K=3
Training:
MMC MET_lep MASS_Vis Pt_H Y
0 138.70 51.65 97.82 0.91 0
1 160.93 68.78 103.23 -999.00 0
2 -999.00 162.17 125.95 -999.00 0
3 143.90 81.41 80.94 -999.00 1
7 175.86 16.91 134.80 -999.00 0
8 -999.00 162.17 125.95 -999.00 0
9 143.90 81.41 80.94 -999.00 1
Test:
4 175.86 16.91 134.80 -999.00 0
5 -999.00 162.17 125.95 -999.00 0
6 143.90 81.41 80.94 -999.00 1
下面是我的代码,它完成了拆分但不折叠的工作:
split = math.floor(dataset.shape[0]*0.8)
data_train = dataset[:split]
data_test = dataset[split:]
提前感谢您对此提供的帮助。
【问题讨论】:
【参考方案1】:您是否打算将 K=2 折叠与 K=3 测试折叠 (3,4,5) 与 (4,5,6) 重叠?此外,在您的示例中,似乎 K 被重载以表示折叠数和当前折叠的索引。在我的回答中,我将 i 用于 k 总折叠中的第 i 折叠。
假设目标是创建不重叠的折叠,那么有一个函数可以在 0 到 len(dataset) - 1 的范围内产生大致均匀的范围就足够了。即使你的list 不能被在 floor((n*i)/k) 处的 k 分裂完全整除。在 python 中,你可以使用这样的函数:
def fold_i_of_k(dataset, i, k):
n = len(dataset)
return dataset[n*(i-1)//k:n*i//k]
这是一个关于一维数据集的示例(应该同样适用于 DataFrame):
>>> fold_i_of_k(list(range(0,11)),1,3)
[0, 1, 2]
>>> fold_i_of_k(list(range(0,11)),2,3)
[3, 4, 5, 6]
>>> fold_i_of_k(list(range(0,11)),3,3)
[7, 8, 9, 10]
【讨论】:
谢谢!很好的答案,请注意您在return
行上有一个额外的括号:它应该是n*(i-1)//k
,而不是n*(i-1))//k
。我确认它在 pandas.DataFrame 上效果很好:)【参考方案2】:
此解决方案基于 pandas 和 numpy 库:
import pandas as pd
import numpy as np
首先将数据集拆分为 k 个部分:
k = 10
folds = np.array_split(data, k)
然后你迭代你的折叠,使用一个作为测试集,另一个 k-1 作为训练,所以最后你执行了 k 次拟合:
for i in range(k):
train = folds.copy() // you wanna work on a copy of your array
test = folds[i]
del train[i]
train = pd.concat(train, sort=False)
perform(clf, train.copy(), test.copy()) // do the fitting, here you also want to copy
在此函数中,您从集合中删除标签列并拟合 scikit 分类器 (clf),然后返回预测。
def perform(clf, train_set, test_set):
# remove labels from data
train_labels = train_set.pop('Y').values
test_labels = test_set.pop('Y').values
clf.fit(train_set, train_labels)
return clf.score(test_set, test_labels)
【讨论】:
而不是拆分数组,如果您可以获取索引并使用这些索引迭代数组会更好以上是关于Pandas:如何在不使用 scikit 的情况下进行交叉验证?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不拟合的情况下实例化具有已知系数的 Scikit-Learn 线性模型
如何在不使用外部库(例如 Numpy、Pandas)的情况下读取 CSV 文件?
如何在不使用 Pandas 的情况下创建等效于 numpy.nan 的日期时间对象?