web-dev-qa-db-fra.com

Comment créer une ligne de somme et une colonne de somme dans des pandas?

Je vais suivre le cours de statistique de la Khan Academy sur les statistiques pour me rafraîchir la mémoire et pour me mettre au courant de pandas et autres scientifiques Python.

J'ai une table qui ressemble à ceci de Khan Academy:

             | Undergraduate | Graduate | Total
-------------+---------------+----------+------
Straight A's |           240 |       60 |   300
-------------+---------------+----------+------
Not          |         3,760 |      440 | 4,200
-------------+---------------+----------+------
Total        |         4,000 |      500 | 4,500

Je voudrais recréer cette table en utilisant des pandas. Bien sûr, je pourrais créer un DataFrame en utilisant quelque chose comme

"Graduate": {...},
"Undergraduate": {...},
"Total": {...},

Mais cela semble être une approche naïve qui tomberait à la fois rapidement et ne serait tout simplement pas extensible.

J'ai les parties non totales du tableau comme ceci:

df = pd.DataFrame(
    {
        "Undergraduate": {"Straight A's": 240, "Not": 3_760},
        "Graduate": {"Straight A's": 60, "Not": 440},
    }
)
df

J'ai cherché et trouvé quelques choses prometteuses, comme:

df['Total'] = df.sum(axis=1)

Mais je n'ai rien trouvé terriblement élégant.

J'ai trouvé la fonction crosstab qui semble devoir faire ce que je veux, mais pour ce faire, il me faudrait créer un cadre de données constitué de 1/0 pour toutes ces valeurs, ce qui semble idiot parce que j'ai déjà un agrégat.

J'ai trouvé quelques approches qui semblent construire manuellement une nouvelle ligne de totaux, mais il semble qu'il devrait y avoir une meilleure façon, quelque chose comme:

totals(df, rows=True, columns=True)

ou quelque chose.

Cela existe-t-il dans les pandas ou dois-je simplement bricoler ma propre approche?

20
Wayne Werner

Ou en deux étapes, en utilisant la fonction .sum() comme vous l'avez suggéré (qui pourrait également être un peu plus lisible):

import pandas as pd

df = pd.DataFrame( {"Undergraduate": {"Straight A's": 240, "Not": 3_760},"Graduate": {"Straight A's": 60, "Not": 440},})

#Total sum per column: 
df.loc['Total',:]= df.sum(axis=0)

#Total sum per row: 
df.loc[:,'Total'] = df.sum(axis=1)

Sortie:

              Graduate  Undergraduate  Total
Not                440           3760   4200
Straight A's        60            240    300
Total              500           4000   4500
21
Archie

append et assign

Le but de cette réponse est de fournir une solution en ligne et non une solution en place.

append

J'utilise append pour empiler Series ou DataFrame verticalement. Cela crée également un copy pour que je puisse continuer à chaîner.

assign

J'utilise assign pour ajouter une colonne. Cependant, le DataFrame sur lequel je travaille se situe entre les deux. J'utilise donc lambda dans l'argument assign qui indique à Pandas de l'appliquer à l'appelant DataFrame.


df.append(df.sum().rename('Total')).assign(Total=lambda d: d.sum(1))

              Graduate  Undergraduate  Total
Not                440           3760   4200
Straight A's        60            240    300
Total              500           4000   4500

Alternative amusante

Utilise drop avec errors='ignore' pour supprimer les lignes et colonnes Total potentiellement préexistantes.

Aussi, toujours en ligne.

def tc(d):
  return d.assign(Total=d.drop('Total', errors='ignore', axis=1).sum(1))

df.pipe(tc).T.pipe(tc).T

              Graduate  Undergraduate  Total
Not                440           3760   4200
Straight A's        60            240    300
Total              500           4000   4500
11
piRSquared

À partir des données d'origine en utilisant crosstab, si vous vous basez simplement sur votre entrée, vous avez simplement besoin de melt avant crosstab

s=df.reset_index().melt('index')
pd.crosstab(index=s['index'],columns=s.variable,values=s.value,aggfunc='sum',margins=True)
Out[33]: 
variable      Graduate  Undergraduate   All
index                                      
Not                440           3760  4200
Straight A's        60            240   300
All                500           4000  4500

Données de jouets

df=pd.DataFrame({'c1':[1,2,2,3,4],'c2':[2,2,3,3,3],'c3':[1,2,3,4,5]}) 
# before `agg`, I think your input is the result after `groupby` 
df
Out[37]: 
   c1  c2  c3
0   1   2   1
1   2   2   2
2   2   3   3
3   3   3   4
4   4   3   5


pd.crosstab(df.c1,df.c2,df.c3,aggfunc='sum',margins
=True)
Out[38]: 
c2     2     3  All
c1                 
1    1.0   NaN    1
2    2.0   3.0    5
3    NaN   4.0    4
4    NaN   5.0    5
All  3.0  12.0   15
4
WeNYoBen

Les données d'origine sont:

>>> df = pd.DataFrame(dict(Undergraduate=[240, 3760], Graduate=[60, 440]), index=["Straight A's", "Not"])
>>> df
Out: 
              Graduate  Undergraduate
Straight A's        60            240
Not                440           3760

Vous pouvez uniquement utiliser df.T pour reconstituer ce tableau:

>>> df_new = df.T
>>> df_new
Out: 
               Straight A's   Not
Graduate                 60   440
Undergraduate           240  3760

Après avoir calculé la Total par rangée et par colonne:

>>> df_new.loc['Total',:]= df_new.sum(axis=0)
>>> df_new.loc[:,'Total'] = df_new.sum(axis=1)
>>> df_new
Out: 
               Straight A's     Not   Total
Graduate               60.0   440.0   500.0
Undergraduate         240.0  3760.0  4000.0
Total                 300.0  4200.0  4500.0
0
TimeSeam