Pandas – adding a total row to each subgroup as first row

Question:

I know this questions has been mentioned various times on StackOverflow, I do not find it trivial to accomplish this task. This and many other answers: Adding total row to pandas DataFrame groupby

Sample of my data (there are actually 25 columns for this but they are similar, only numerical):

owner   player  val1    val1    val3
A       x       5.60    3.18    0.76
A       y       12.08   15.95   -0.24
A       z       0.03    0.05    -0.41
B       x       0.02    0.01    2.06
B       z       2.36    2.37    0.00
C       x       0.16    0.15    0.05
C       y       0.72    0.75    -0.04
D       x       0.33    0.56    -0.41

My intended output is as follows, where for each owner the total is calculated
and placed as the first row in the subgroup.

owner   player  val1    val1    val3
A      total    17.71   19.18   0.11
A      x        5.60    3.18    0.76
A      y        12.08   15.95   -0.24
A      z        0.03    0.05    -0.41
B      total    2.38    2.38    2.05
B      x        0.02    0.01    2.06
B      z        2.36    2.37    0.00
C      total    0.88    0.90    0.01
C      x        0.16    0.15    0.05
C      y        0.72    0.75    -0.04
D      total    0.33    0.56    -0.41
D      x        0.33    0.56    -0.41

I attempted to use something that I also found on StackOverflow which looked like what I was searching for but I couldn’t make it quite right.

def lambda_t(x):
    df = x.sort_values(['owner']).drop(['owner'],axis=1)
    df.loc['total'] = df.sum()
    return df

df.groupby(['owner']).apply(lambda_t)

While in theory this might have been something interesting, the total is not placed where I want and on top of that the values on player name are concatenating, so I end up having a really packed column. This way I end up having a multiindex.

owner       player  val1    val1    val3
A   0        x      5.60    3.18    0.76
    1        y      12.08   15.95   -0.24
    2        z      0.03    0.05    -0.41
    total    xzy    17.71   19.18   0.11
.....

Apparently, dropping level of a multiindex can help but I’m missing the total this way, it disappears.

df.groupby(['owner']).apply(lambda_t).droplevel(level=1)

owner       player  val1    val1    val3
A            x      5.60    3.18    0.76
A            y      12.08   15.95   -0.24
A            z      0.03    0.05    -0.41
A            xzy    17.71   19.18   0.11

Any ideas if this is possible? I’ve seen that with groupby, assign and loc you can’t order them properly.

Asked By: Geosphere

||

Answers:

IIUC, you can use groupby.sum to compute the totals, assign the total name as player, concat the two DataFrames in order and sort_values with a stable method:

out = (pd
  .concat([df.groupby('owner', as_index=False).sum().assign(player='total'),
           df])
   .sort_values(by='owner', kind='stable', ignore_index=True)
   [df.columns]
)

output:

   owner player   val1    val1  val3
0      A  total  17.71   19.18  0.11
1      A      x   5.60    3.18  0.76
2      A      y  12.08   15.95 -0.24
3      A      z   0.03    0.05 -0.41
4      B  total   2.38    2.38  2.06
5      B      x   0.02    0.01  2.06
6      B      z   2.36    2.37  0.00
7      C  total   0.88    0.90  0.01
8      C      x   0.16    0.15  0.05
9      C      y   0.72    0.75 -0.04
10     D  total   0.33    0.56 -0.41
11     D      x   0.33    0.56 -0.41
Answered By: mozway
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.