在pandas中具有特定和长度的动态索引

时间:2018-06-01 17:12:15

标签: python pandas indexing dynamic grouping

我有以下数据框:

public boolean doSomething(MyEnum param1Enum, String param2){
   ...
   String param1 = param1Enum.getString();
}

每次V1的总和达到50k时,我需要得到索引。因此,当值V1大于50k的限制时,则索引重复达到限制的次数,或者如果值V1低于50k的限制,则将行分组直到达到或超过50k的限制。结果将是这样的:

    V1
1   100000
2   50000
3   20000
4   30000
5   150000
6   30000
7   20000
8   200000

我设法用循环来解决它,但我想知道是否可以使用具有pandas功能的分组。

1 个答案:

答案 0 :(得分:1)

在你用

提供的例子中
df = pd.DataFrame({'V1':[100000,50000, 20000, 30000, 
                         150000, 30000, 20000, 200000]},
                   index=range(1,9))

我所理解的“获取每次V1的总和达到50k的索引”,你可以通过创建一个数字恰好为50k的列来实现直到每一行:

ser_50k = (df.V1.cumsum()/50000).astype(int)
df['nb_50'] = (ser_50k - ser_50k.shift()).fillna(ser_50k).astype(int)

现在,当50k达到多次时,您可以使用stack创建具有相同索引的行:

df_join = (df['nb_50'].apply(lambda x: pd.Series(range(x)))
                      .stack().reset_index(level=1).drop('level_1',1))
df = df.join(df_join).dropna().drop(['nb_50',0],1)

您可以使用输入示例获得预期的输出。

问题是,当你有

df= pd.DataFrame({'V1':[180000, 20000, 30000, 50000]}) 

我的方法为您提供:

       V1
0  180000
0  180000
0  180000
1   20000
3   50000

并且稍后可能会发生一些错误,而当您说“当值V1大于50k的限制时,则索引会重复达到限制的次数,或者如果值V1低于达到50k的限制,将行数分组直到达到或超过50k的限制“我理解你会期望:

       V1
0  180000
0  180000
0  180000
2   30000
3   50000

在这种情况下,如果您不想使用循环,当数字高于50K时,您可以这样做(与以前的想法大致相同):

df = df.join(df['V1'].apply(lambda x: pd.Series(range(x/50000)))
                       .stack().reset_index(level=1).drop('level_1',1)).drop(0,1)

但我无法找到一个简单的方法来处理数字小于50k的情况,因此你的循环for可能已经足够了。否则,我想到了这个方式:

def nb_group_under(v1):
    global nb_group
    if v1 < 50000:
        return nb_group
    else:
        nb_group += 1
nb_group = 1
df['under_50'] = df['V1'].apply(nb_group_under)

然后你试图找到在创建的组中传递50k的位置:

df['sum_under50'] = (df.groupby('under_50').V1.cumsum()/50000).astype(int)
df['sum_under50'] = df.sum_under50 - (df.groupby('under_50').sum_under50
                                        .shift().fillna(df.sum_under50))
df = (df[(df['sum_under50']>0) | (df['V1'] >= 50000)]
              .drop(['under_50', 'sum_under50'],1))

我对50岁以下的案件的方法并不特别满意,但却想不到另一种方式。

希望无论如何它是有用的,或者给你一些关于如何在没有循环的情况下解决问题的想法

编辑:对于更通用的解决方案,你可以创建一个函数,返回50k在值v1中的时间,或者在50k以上时返回1的部分和,你还需要一个全局变量:

def nb_lim_reached (v1, lim_v1):
    global partial_sum
    if v1 >= lim_v1:
        partial_sum = 0
        return pd.np.floor(v1/lim_v1)
    else:
        partial_sum += v1
        if partial_sum >= lim_v1:
            partial_sum -= lim_v1
            return 1
        else:
            return 0

现在您可以使用此功能创建另一列:

v1_lim = 50000
partial_sum = 0
df['nb_lim'] = df['V1'].apply(nb_lim_reached, args=( v1_lim,)).astype(int)

现在,您使用与pd.Seriesstack的reviosu解决方案相同的想法:

df = (df.join(df['nb_lim'].apply(lambda nb: pd.Series(range(nb)))
          .stack().reset_index(level=1).drop('level_1',1))
            .dropna().drop(['nb_lim',0],1))