按时间拆分分组的熊猫数据帧

时间:2021-07-13 16:04:02

标签: python pandas dataframe validation

我需要将我的数据集分成两个部分:80% 和 20%。

我的数据集如下所示:

PersonID    Timestamp   Foo1    Foo2    Foo3    Label
1   1626184812  6   5   2   1
2   616243602   8   5   2   1
2   634551342   4   8   3   1
2   1531905378  3   8   8   1
3   616243602   10  7   8   2
3   634551342   7   5   8   2
4   1626184812  7   9   1   2
4   616243602   5   7   9   1
4   634551342   9   1   6   2
4   1531905378  3   3   3   1
4   768303369   6   1   7   2
5   1626184812  5   7   8   2
5   616243602   6   2   6   1
6   1280851467  3   2   2   2
7   1626184812  10  1   10  1
7   616243602   6   3   6   2
7   1531905378  9   5   7   2
7   634551342   3   7   9   1
8   616243602   8   7   4   2
8   634551342   2   2   4   1

(注意,您应该可以使用 pd.read_clipboard() to get this data into a dataframe。)

我想要完成的是:

  • 将此数据集拆分为 80/20 的拆分(训练、测试)
  • 数据集应主要按 Timestamp 组织,这意味着 数据应在训练中, 数据应在测试中立>
  • 此外,不应在训练和测试之间拆分单个人员。例如,给定 PersonID 的所有样本都必须属于一组或另一组!

前两点在下面的最小示例中完成。第三点是我遇到的问题。例如,使用 sklearn's train_test_split:

下面的最小示例:

# Imports
import pandas as pd
from sklearn.model_selection import train_test_split

# Normal split
x = pd.read_clipboard()
train, test = train_test_split(x, train_size=0.8, test_size=0.20, random_state=8)

# Organizing it by time
x = pd.read_clipboard()
x = x.sort_values(by='Timestamp')
train, test = train_test_split(x, train_size=0.8, test_size=0.20, random_state=8)

我正在努力弄清楚如何对数据框进行分组,以便一个人不会在训练和测试之间分开。例如,在上面,PersonID 数据帧中的每个 test 出现在 train 数据帧中。如何在确保 PersonID 不被拆分的同时保持 about 的比例相等?

1 个答案:

答案 0 :(得分:2)

这两个条件很难严格地放在一起:训练中的旧时间戳与单个 PersonID 训练或测试,但不能同时进行。这里有两个想法。

对于火车中较旧的时间戳,可能不是最严格的解决方案,但您可以尝试获取 max(或 {{1} } 或 min 由您决定)每个 personID 的时间戳和 mean,然后 count 最大到 sort_values 计数。使用 cumsummax 乘以 80% 的拆分训练测试。

cumsum

然后你得到

# calculate the cumsum of count
s = (
    x.groupby('PersonID')
     ['Timestamp'].agg(['max','count'])
     .sort_values('max')
     ['count'].cumsum()
)
s = s<s.max()*0.8 # get boolean mask for train personID

另一种选择是使用 sklearn.model_selection.GroupShuffleSplit 像:

train = x.loc[x['PersonID'].isin(s[s].index)]
print(train)
#     PersonID   Timestamp  Foo1  Foo2  Foo3  Label
# 0          1  1626184812     6     5     2      1
# 1          2   616243602     8     5     2      1
# 2          2   634551342     4     8     3      1
# 3          2  1531905378     3     8     8      1
# 4          3   616243602    10     7     8      2
# 5          3   634551342     7     5     8      2
# 6          4  1626184812     7     9     1      2
# 7          4   616243602     5     7     9      1
# 8          4   634551342     9     1     6      2
# 9          4  1531905378     3     3     3      1
# 10         4   768303369     6     1     7      2
# 13         6  1280851467     3     2     2      2
# 18         8   616243602     8     7     4      2
# 19         8   634551342     2     2     4      1


test = x.loc[x['PersonID'].isin(s[~s].index)]
print(test)
#     PersonID   Timestamp  Foo1  Foo2  Foo3  Label
# 11         5  1626184812     5     7     8      2
# 12         5   616243602     6     2     6      1
# 14         7  1626184812    10     1    10      1
# 15         7   616243602     6     3     6      2
# 16         7  1531905378     9     5     7      2
# 17         7   634551342     3     7     9      1

并增加参数 from sklearn.model_selection import GroupShuffleSplit gss = GroupShuffleSplit(n_splits=1, train_size=.8, random_state=42) for train_idx, test_idx in gss.split(x.index, groups=x['PersonID']): print("TRAIN ID:", x.loc[train_idx,'PersonID'].unique(), ", Timestamp:", x.loc[train_idx,'Timestamp'].mean(),) print("TEST ID:", x.loc[test_idx,'PersonID'].unique(), ", Timestamp:", x.loc[test_idx,'Timestamp'].mean()) # TRAIN ID: [1 3 4 5 7 8] , Timestamp: 997267296.9375 # TEST ID: [2 6] , Timestamp: 1015887947.25 并保留为训练提供最小均值或为测试提供最高均值的分割。