【问题标题】:create three samples groups in a pd.DataFrame在 pd.DataFrame 中创建三个样本组
【发布时间】:2017-03-12 22:55:57
【问题描述】:

我有一个pd.DataFrame,其结构与下面的示例类似:

index  x     y     z
0      x0    y0    None
1      x1    y1    None
2      x2    y2    None
3      x3    y3    None
4      x4    y4    None
5      x5    y5    None
6      x6    y6    None

我的目标是创建 DataFrame 的 3 个子集:

  • Group1 是一个训练集,可用于训练模型以预测 xy 的模型;
  • Group2是一个验证集,用于评估Group1中训练的模型(或不同模型/参数调优)的准确性,我会填写正确的z 对于Group12

  • Group3 一直保持,直到选择一个模型来预测 z

在这种情况下,最有效的分配方式是什么?我正在考虑在一个 DataFrame 中简单地创建子组,如下所示:

index  x     y     z       group
- - - - - - - - - - - - - - - - - - - - 
0      x0    y0    None    training
1      x1    y1    None    validation
2      x2    y2    None    held out
3      x3    y3    None    held out
4      x4    y4    None    validation
5      x5    y5    None    training
6      x6    y6    None    held out

但是我看到的随机分配技巧elsewhere 通常会创建一个新的DataFrame。是因为这样更可行吗?

rows = np.random.choice(df.index.values, 10)
sampled_df = df.ix[rows]

另外,由于我想一次采样 3 组而不是 2 组,我不确定在不更换的情况下进行采样的最佳方法是什么。

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

你可以使用

df['group'] = np.random.choice(
    np.repeat(['training', 'validation', 'held out'], (2,2,3)), len(df), replace=False)

为每一行分配一个training/validation/held out 标签。 (2,2,3) 上面表示您希望拥有的每种类型的行数。由于每一行 应该得到一个标签,元组的总和应该等于len(df)


分配标签比创建子数据帧更好吗?

如果你分配标签,你最终会得到如下代码:

df['group'] = np.random.choice(
    np.repeat(['training', 'validation', 'held out'], (2,2,3)), len(df), replace=False)
goodness = dict()
params = dicts()
for model in models: 
    params[model] = fit(model, df.loc[df['group'] == 'train'])
    goodness[model] = validate(model, params[model], df.loc[df['group'] == 'validation'])
best_model = max(models, key=goodness.get)
result = process(best_model, params[best_model], df.loc[df['group'] == 'held_out'])

如果你拆分df(使用MaxU's solution),你会得到如下代码:

train, validate, held_out = np.split(df.sample(frac=1), [2,4])
goodness = dict()
params = dicts()
for model in models: 
    params[model] = fit(model, train)
    goodness[model] = validate(model, params[model], validate)
best_model = max(models, key=goodness.get)
result = process(best_model, params[best_model], held_out)

Python每次遇到df['group'] == 'train',整个系列 df['group'] 被扫描——一个 O(N) 操作。 df.loc[f['group'] == 'train'] 然后从df 复制行以形成一个新的子数据帧。由于这是在一个 循环,for model in models,每个循环执行两次,这个 O(N) 操作执行2*len(model) 次。

相反,如果你一开始就拆分DataFrame,那么复制 只做一次。所以MaxU的代码更快。

另一方面,使用标签按需创建子数据帧将节省 一点内存,因为您不会在 一次。但是,除非您的内存非常紧张,否则您可能会想要更快 代码比内存效率更高的代码。因此,如果是这种情况,请使用MaxU's solution


当然,你可以使用

df['group'] = np.random.choice(
    np.repeat(['training', 'validation', 'held out'], (2,2,3)), len(df), replace=False)
train, validate, held_out = [df.loc[df['group'] == label] for label in ['train', 'validation', 'held out']]

而不是

train, validate, held_out = np.split(df.sample(frac=1), [2,4])

但是这样做也没有速度或内存优势。你 仍然从 DataFrame 扫描和复制三次而不是一次。所以 还是应该首选 MaxU 的解决方案。

【讨论】:

  • 非常感谢您的详细解释!真的帮助我理解了每种方法的差异。
猜你喜欢
  • 1970-01-01
  • 2018-03-20
  • 1970-01-01
  • 1970-01-01
  • 2014-12-05
  • 1970-01-01
  • 1970-01-01
  • 2016-02-28
  • 2017-04-29
相关资源
最近更新 更多