如何从每个子组的n行中从熊猫DataFrame均匀采样

时间:2019-06-23 17:36:42

标签: python pandas pandas-groupby

在大熊猫数据框中,我有多个具有不同且大量行的子组。我想减少用于初步分析的行数,同时确保数据在整个范围内仍然具有代表性。

我使用2个因子或参数('A','B')和每个因子2个水平或值('A1','A2','B1','B2')进行了仿真。每个模拟对应于'A','B'值的组合。计数器超过定义的数字(在下面的示例中为“ 35”)之后,模拟停止。 对于每个模拟,计数器及其增加都是不同的。并且在每个步骤中,从模拟中总结出一个值'eval'

下面的示例显示了模拟结果的示例。现在,该模拟实际上运行了更长的时间(例如,直到达到10000以上),并且在我的初步分析中需要花几个小时来绘制eval值的变化。

此代码生成模拟结果的示例:

import pandas as pd
import numpy as np

columns = ['FactorA', 'FactorB', 'step']
data = [['A1', 'B1', 8], ['A1', 'B1', 13], ['A1', 'B1', 18], ['A1', 'B1', 23], ['A1', 'B1', 28], ['A1', 'B1', 33], ['A1', 'B1', 38],
        ['A1', 'B2', 7], ['A1', 'B2', 13],['A1', 'B2', 19],['A1', 'B2', 25],['A1', 'B2', 31],['A1', 'B2', 37],
        ['A2', 'B1', 6], ['A2', 'B1', 14],['A2', 'B1', 22],['A2', 'B1', 30],['A2', 'B1', 38],
        ['A2', 'B2', 10], ['A2', 'B2', 12],['A2', 'B2', 14],['A2', 'B2', 16],['A2', 'B2', 18],['A2', 'B2', 20],['A2', 'B2', 22],['A2', 'B2', 24],['A2', 'B2', 26],['A2', 'B2', 28],['A2', 'B2', 30],['A2', 'B2', 32],['A2', 'B2', 34],['A2', 'B2', 36]
       ]
df = pd.DataFrame(data, columns=columns)
df['eval'] = np.random.randint(1, 6, df.shape[0])

我尝试过此方法,但是虽然它减少了数据点,但并不能平衡每次模拟的数据点数量:

df_reduced = df.iloc[::2]

也尝试过:

df_reduced = df.sample(n=int(len(df)/6))

但是它也不能平衡每次模拟的数据点数量。

我想要一个每个子组具有相同行数的DataFrame。 为了确保选择或采样平衡,我希望使用.iloc的每个子组的切片考虑确保每个子组选择'n'个成员的步骤。 最好包括每个子组的第一行和最后一行。

2 个答案:

答案 0 :(得分:0)

请查看帖子中的以下说明:

df.sample(n=int(len(df)))

这里有什么奇怪的地方

    不需要
  • int len 已经具有 int 类型)。
  • len(df) df 中选择所有行,因此您的“样本”是通过这种方式创建的 包含完整原始 df ,仅将订单改组。 这就是你想要的吗?

关于组平衡:

确定如何保持平衡:

  • 选项1:每个组中样本行的数量
  • 选择2:各组样本的分数相等。

下定决心:

  • 根据组条件运行groupby源DataFrame,
  • 应用一个函数返回当前组中的相应样本。

示例:如果要从 df 中进行选择,请从中选择 2 行的示例 每个组(选项1),运行:

df.groupby(['FactorA', 'FactorB']).apply(lambda grp: grp.sample(n=2))

如果要返回原始(单级)索引,请添加:

.reset_index(level=[0, 1], drop=True)

上述说明。

如果您喜欢选项2(分数),请将 n = ... 更改为 frac = ...

答案 1 :(得分:0)

在@Valdi_Bo回答之后,我转到Group By: split-apply-combine上的页面,并在herehereherehere,和here得到一个非优雅但可行的解决方案。

对于示例中的数据框,我们可以计算每组的行数:

grouped = df.groupby(['FactorA','FactorB'])
grouped.size()

这将产生:

FactorA  FactorB
A1       B1          7
         B2          6
A2       B1          5
         B2         14
dtype: int64

为了将每个组的数据行减少到一个以上但接近三的数字,并且每个子组的step列中的值之间具有相等的间距,并强制包含最大的step,我使用:

def filter_group(dfg, col, qty):
    col_min = dfg[col].min()
    col_max = dfg[col].max()
    col_length = dfg[col].size
    jumps = col_length-1
    jump_size = int((col_max - col_min) / jumps)
    new_jump_size =  jumps/qty
    if new_jump_size > 1:
        new_jump_size = int(new_jump_size)*jump_size
    else:
        new_jump_size = jump_size

    col_select = list(range(col_min, col_max, new_jump_size))
    col_select.append(col_max)

    return dfg[dfg[col].isin(col_select)]
grouped = df.groupby(['FactorA','FactorB'], group_keys=False).apply(lambda x: filter_group(x,'step',3))

我们可以验证演示数据框的行数:

grouped = grouped.groupby(['FactorA','FactorB'])
grouped.size()

这将产生:

FactorA  FactorB
A1       B1         4
         B2         6
A2       B1         5
         B2         5
dtype: int64

如果有时间,我会尝试的,并将其修改为使用具有与steps列范围相关的权重的样本函数时发布。这样我从范围的前三分之一获得了一半的数据点。