Quiero que todas las filas agrupadas sean del mismo tamaño. Es decir, ya sea eliminando las últimas filas o agregando ceros si el grupo tiene un tamaño pequeño.

d = {'ID':['a12', 'a12','a12','a12','a12','b33','b33','b33','b33','v55','v55','v55','v55','v55','v55'], 'Exp_A':[2.2,2.2,2.2,2.2,2.2,3.1,3.1,3.1,3.1,1.5,1.5,1.5,1.5,1.5,1.5], 
     'Exp_B':[2.4,2.4,2.4,2.4,2.4,1.2,1.2,1.2,1.2,1.5,1.5,1.5,1.5,1.5,1.5], 
     'A':[0,0,1,0,1,0,1,0,1,0,1,1,1,0,1], 'B':[0,0,1,1,1,0,0,1,1,1,0,0,1,0,1]}
df1 = pd.DataFrame(data=d)

Quiero que todos df1.ID tengan el tamaño df1.groupby('ID').size().mean(). Entonces df1 debería verse así:

    A   B   Exp_A   Exp_B   ID
0   0   0   2.2      2.4    a12
1   0   0   2.2      2.4    a12
2   1   1   2.2      2.4    a12
3   0   1   2.2      2.4    a12
4   1   1   2.2      2.4    a12

5   0   0   3.1      1.2    b33
6   1   0   3.1      1.2    b33
7   0   1   3.1      1.2    b33
8   1   1   3.1      1.2    b33
9   0   0   3.1      1.2    b33

10  0   1   1.5      1.5    v55
11  1   0   1.5      1.5    v55
12  1   0   1.5      1.5    v55
13  1   1   1.5      1.5    v55
14  0   0   1.5      1.5    v55
2
MatN 16 oct. 2018 a las 14:35

2 respuestas

La mejor respuesta

Aquí hay una solución usando GroupBy. La complicación surge con su condición de agregar filas adicionales con ciertas columnas establecidas en 0, siempre que un grupo en particular sea demasiado pequeño.

g = df1.groupby('ID')
n = int(g.size().mean())

res = []
for _, df in g:
    k = len(df.index)
    excess = n - k
    if excess > 0:
        df = df.append(pd.concat([df.iloc[[-1]].assign(A=0, B=0)]*excess))
    res.append(df.iloc[:n])

res = pd.concat(res, ignore_index=True)

print(res)

    A  B  Exp_A  Exp_B   ID
0   0  0    2.2    2.4  a12
1   0  0    2.2    2.4  a12
2   1  1    2.2    2.4  a12
3   0  1    2.2    2.4  a12
4   1  1    2.2    2.4  a12
5   0  0    3.1    1.2  b33
6   1  0    3.1    1.2  b33
7   0  1    3.1    1.2  b33
8   1  1    3.1    1.2  b33
9   0  0    3.1    1.2  b33
10  0  1    1.5    1.5  v55
11  1  0    1.5    1.5  v55
12  1  0    1.5    1.5  v55
13  1  1    1.5    1.5  v55
14  0  0    1.5    1.5  v55
2
jpp 16 oct. 2018 a las 12:09

Aquí hay una solución sin bucles. Primero puede determinar el número de filas para cada ID y luego cambiar las cosas.

# Getting the minimum required number of rows for each ID
min_req = df.groupby('ID').size().mean()

# Adding auto-increment column with respect to ID column
df['row_count'] = df.groupby(['ID']).cumcount()+1

# Adding excess rows equal to required rows
# we will delete unneeded ones later
df2 = df.groupby('ID', as_index=False).max()
df2 = df2.loc[df2['row_count']<int(min_req)]
df2 = df2.assign(A=0, B=0)
df = df.append([df2]*int(min_req), ignore_index=True)

# recalculating the count
df = df.drop('row_count', axis=1)
df = df.sort_values(by=['ID', 'A', 'B'], ascending=[True, False, False])
df['row_count'] = df.groupby(['ID']).cumcount()+1

# Dropping excess rows
df = df.drop((df.loc[df['row_count']>5]).index)
df = df.drop('row_count', axis=1)

df

    A  B  Exp_A  Exp_B   ID
0   0  0    2.2    2.4  a12
1   0  0    2.2    2.4  a12
2   1  1    2.2    2.4  a12
3   0  1    2.2    2.4  a12
4   1  1    2.2    2.4  a12
17  0  0    3.1    1.2  b33
16  0  0    3.1    1.2  b33
15  0  0    3.1    1.2  b33
18  0  0    3.1    1.2  b33
19  0  0    3.1    1.2  b33
10  1  0    1.5    1.5  v55
11  1  0    1.5    1.5  v55
12  1  1    1.5    1.5  v55
13  0  0    1.5    1.5  v55
14  1  1    1.5    1.5  v55
2
Rahul Chawla 16 oct. 2018 a las 14:59