À quoi servent les arguments «niveaux», «clés» et noms dans la fonction concat de Pandas?

94

Des questions

  • Comment utiliser pd.concat?
  • Quel est l' levelsargument pour?
  • Quel est l' keysargument pour?
  • Existe-t-il un tas d'exemples pour aider à expliquer comment utiliser tous les arguments?

La concatfonction de Pandas est le couteau suisse des services publics qui fusionnent. La variété des situations dans lesquelles il est utile sont nombreuses. La documentation existante laisse de côté quelques détails sur certains des arguments optionnels. Parmi eux se trouvent les arguments levelset keys. J'ai cherché à comprendre ce que faisaient ces arguments.

Je vais poser une question qui servira de passerelle vers de nombreux aspects de pd.concat.

Considérez les trames de données d1, d2et d3:

import pandas as pd

d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), [2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), [1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), [1, 3])

Si je devais les concaténer avec

pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'])

J'obtiens le résultat attendu avec un pandas.MultiIndexpour mon columnsobjet:

        A    B    C    D
d1 2  0.1  0.2  0.3  NaN
   3  0.1  0.2  0.3  NaN
d2 1  NaN  0.4  0.5  0.6
   2  NaN  0.4  0.5  0.6
d3 1  0.7  0.8  NaN  0.9
   3  0.7  0.8  NaN  0.9

Cependant, je voulais utiliser la levelsdocumentation des arguments :

niveaux : liste de séquences, par défaut Aucun. Niveaux spécifiques (valeurs uniques) à utiliser pour construire un MultiIndex. Sinon, ils seront déduits des clés.

Alors j'ai passé

pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2']])

Et obtenez un KeyError

ValueError: Key d3 not in level Index(['d1', 'd2'], dtype='object')

Cela avait du sens. Les niveaux que j'ai passés étaient insuffisants pour décrire les niveaux nécessaires indiqués par les touches. Si je n'avais rien réussi, comme je l'ai fait ci-dessus, les niveaux sont déduits (comme indiqué dans la documentation). Mais comment puis-je utiliser cet argument pour un meilleur effet?

Si j'ai essayé ceci à la place:

pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3']])

I et j'ai obtenu les mêmes résultats que ci-dessus. Mais quand j'ajoute une valeur de plus aux niveaux,

df = pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3', 'd4']])

Je me retrouve avec la même trame de données, mais le résultat MultiIndexa un niveau inutilisé.

df.index.levels[0]

Index(['d1', 'd2', 'd3', 'd4'], dtype='object')

Alors, quel est le point de l' levelargument et dois-je utiliser keysdifféremment?

J'utilise Python 3.6 et Pandas 0.22.

piRSquared
la source

Réponses:

124

En répondant moi-même à cette question, j'ai appris beaucoup de choses et je voulais rassembler un catalogue d'exemples et quelques explications.

La réponse spécifique au point de l' levelsargument viendra vers la fin.

pandas.concat: Le manuel manquant

Lien vers la documentation actuelle

Importation et définition d'objets

import pandas as pd

d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), index=[2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), index=[1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), index=[1, 3])

s1 = pd.Series([1, 2], index=[2, 3])
s2 = pd.Series([3, 4], index=[1, 2])
s3 = pd.Series([5, 6], index=[1, 3])

Arguments

objs

Le premier argument que nous rencontrons est objs:

objs : une séquence ou un mappage d'objets Series, DataFrame ou Panel Si un dict est passé, les clés triées seront utilisées comme argument de clés, à moins qu'il ne soit passé, auquel cas les valeurs seront sélectionnées (voir ci-dessous). Tous les objets None seront supprimés silencieusement à moins qu'ils ne soient tous None, auquel cas une ValueError sera déclenchée

  • Nous voyons généralement cela utilisé avec une liste d' objets Seriesou DataFrame.
  • Je vais montrer que cela dictpeut également être très utile.
  • Les générateurs peuvent également être utilisés et peuvent être utiles lors de l'utilisation mapcomme dansmap(f, list_of_df)

Pour l'instant, nous allons nous en tenir à une liste de certains des objets DataFrameet Seriesdéfinis ci-dessus. Je vais montrer comment les dictionnaires peuvent être exploités pour donner des MultiIndexrésultats très utiles plus tard.

pd.concat([d1, d2])

     A    B    C    D
2  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6

axis

Le deuxième argument que nous rencontrons est axisdont la valeur par défaut est 0:

axis : {0 / 'index', 1 / 'columns'}, par défaut 0 L'axe à concaténer le long.

Deux DataFrames avec axis=0(empilés)

Pour les valeurs de 0ou indexnous voulons dire: "Alignez le long des colonnes et ajoutez à l'index".

Comme indiqué ci-dessus où nous avons utilisé axis=0, car 0est la valeur par défaut, et nous voyons que l'index de d2étend l'index de d1malgré le chevauchement de la valeur 2:

pd.concat([d1, d2], axis=0)

     A    B    C    D
2  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6

Deux DataFrames avec axis=1(côte à côte)

Pour les valeurs 1ou columnsnous voulons dire: "Alignez le long de l'index et ajoutez aux colonnes",

pd.concat([d1, d2], axis=1)

     A    B    C    B    C    D
1  NaN  NaN  NaN  0.4  0.5  0.6
2  0.1  0.2  0.3  0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN

Nous pouvons voir que l'index résultant est l'union des indices et les colonnes résultantes sont l'extension des colonnes de d1par les colonnes ded2 .

Deux (ou trois) Seriesavecaxis=0 (empilés)

Lors de la combinaison le pandas.Serieslong axis=0, nous récupérons un pandas.Series. Le nom du résultat Seriessera Nonesauf si tous les éléments Seriescombinés portent le même nom. Faites attention au 'Name: A'moment où nous imprimons le résultat Series. Lorsqu'il n'est pas présent, nous pouvons supposer que le Seriesnom est None.

               |                       |                        |  pd.concat(
               |  pd.concat(           |  pd.concat(            |      [s1.rename('A'),
 pd.concat(    |      [s1.rename('A'), |      [s1.rename('A'),  |       s2.rename('B'),
     [s1, s2]) |       s2])            |       s2.rename('A')]) |       s3.rename('A')])
-------------- | --------------------- | ---------------------- | ----------------------
2    1         | 2    1                | 2    1                 | 2    1
3    2         | 3    2                | 3    2                 | 3    2
1    3         | 1    3                | 1    3                 | 1    3
2    4         | 2    4                | 2    4                 | 2    4
dtype: int64   | dtype: int64          | Name: A, dtype: int64  | 1    5
               |                       |                        | 3    6
               |                       |                        | dtype: int64

Deux (ou trois) Seriesavec axis=1(côte à côte)

Lors de la combinaison le pandas.Serieslong axis=1, il est l' nameattribut que nous appelons pour en déduire un nom de colonne dans la résultante pandas.DataFrame.

                       |                       |  pd.concat(
                       |  pd.concat(           |      [s1.rename('X'),
 pd.concat(            |      [s1.rename('X'), |       s2.rename('Y'),
     [s1, s2], axis=1) |       s2], axis=1)    |       s3.rename('Z')], axis=1)
---------------------- | --------------------- | ------------------------------
     0    1            |      X    0           |      X    Y    Z
1  NaN  3.0            | 1  NaN  3.0           | 1  NaN  3.0  5.0
2  1.0  4.0            | 2  1.0  4.0           | 2  1.0  4.0  NaN
3  2.0  NaN            | 3  2.0  NaN           | 3  2.0  NaN  6.0

Mixte Serieset DataFrameavec axis=0(empilé)

Lors de la concaténation de a Serieset DataFramelong axis=0, nous convertissons tout Seriesen une seule colonne DataFrames.

Notez tout particulièrement qu'il s'agit d'une concaténation axis=0; cela signifie étendre l'index (lignes) tout en alignant les colonnes. Dans les exemples ci-dessous, nous voyons que l'index devient [2, 3, 2, 3]qui est une adjonction indiscriminée d'indices. Les colonnes ne se chevauchent pas sauf si je force la dénomination de la Seriescolonne avec l'argument à to_frame:

 pd.concat(               |
     [s1.to_frame(), d1]) |  pd.concat([s1, d1])
------------------------- | ---------------------
     0    A    B    C     |      0    A    B    C
2  1.0  NaN  NaN  NaN     | 2  1.0  NaN  NaN  NaN
3  2.0  NaN  NaN  NaN     | 3  2.0  NaN  NaN  NaN
2  NaN  0.1  0.2  0.3     | 2  NaN  0.1  0.2  0.3
3  NaN  0.1  0.2  0.3     | 3  NaN  0.1  0.2  0.3

Vous pouvez voir les résultats de pd.concat([s1, d1]) sont les mêmes que si j'avais effectué le to_framemoi - même.

Cependant, je peux contrôler le nom de la colonne résultante avec un paramètre à to_frame. Renommer le Seriesavec la renameméthode ne contrôle pas le nom de la colonne dans le résultat DataFrame.

 # Effectively renames       |                            |
 # `s1` but does not align   |  # Does not rename.  So    |  # Renames to something
 # with columns in `d1`      |  # Pandas defaults to `0`  |  # that does align with `d1`
 pd.concat(                  |  pd.concat(                |  pd.concat(
     [s1.to_frame('X'), d1]) |      [s1.rename('X'), d1]) |      [s1.to_frame('B'), d1])
---------------------------- | -------------------------- | ----------------------------
     A    B    C    X        |      0    A    B    C      |      A    B    C
2  NaN  NaN  NaN  1.0        | 2  1.0  NaN  NaN  NaN      | 2  NaN  1.0  NaN
3  NaN  NaN  NaN  2.0        | 3  2.0  NaN  NaN  NaN      | 3  NaN  2.0  NaN
2  0.1  0.2  0.3  NaN        | 2  NaN  0.1  0.2  0.3      | 2  0.1  0.2  0.3
3  0.1  0.2  0.3  NaN        | 3  NaN  0.1  0.2  0.3      | 3  0.1  0.2  0.3

Mixte Serieset DataFrameavecaxis=1 (côte à côte)

C'est assez intuitif. SeriesLe nom de la colonne est par défaut une énumération de ces Seriesobjets lorsqu'un nameattribut n'est pas disponible.

                    |  pd.concat(
 pd.concat(         |      [s1.rename('X'),
     [s1, d1],      |       s2, s3, d1],
     axis=1)        |      axis=1)
------------------- | -------------------------------
   0    A    B    C |      X    0    1    A    B    C
2  1  0.1  0.2  0.3 | 1  NaN  3.0  5.0  NaN  NaN  NaN
3  2  0.1  0.2  0.3 | 2  1.0  4.0  NaN  0.1  0.2  0.3
                    | 3  2.0  NaN  6.0  0.1  0.2  0.3

join

Le troisième argument est joinqui décrit si la fusion résultante doit être une fusion externe (par défaut) ou une fusion interne.

join : {'interne', 'externe'}, par défaut 'externe'
Comment gérer les index sur d'autres axes.

Il s'avère qu'il n'y a pas d' option leftou rightqui pd.concatpeut gérer plus de deux objets à fusionner.

Dans le cas de d1et d2, les options ressemblent à:

outer

pd.concat([d1, d2], axis=1, join='outer')

     A    B    C    B    C    D
1  NaN  NaN  NaN  0.4  0.5  0.6
2  0.1  0.2  0.3  0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN

inner

pd.concat([d1, d2], axis=1, join='inner')

     A    B    C    B    C    D
2  0.1  0.2  0.3  0.4  0.5  0.6

join_axes

Le quatrième argument est la chose qui nous permet de faire notre leftfusion et plus encore.

join_axes : liste des objets Index Index
spécifiques à utiliser pour les autres n - 1 axes au lieu d'exécuter une logique d'ensemble interne / externe.

Fusion à gauche

pd.concat([d1, d2, d3], axis=1, join_axes=[d1.index])

     A    B    C    B    C    D    A    B    D
2  0.1  0.2  0.3  0.4  0.5  0.6  NaN  NaN  NaN
3  0.1  0.2  0.3  NaN  NaN  NaN  0.7  0.8  0.9

Fusion à droite

pd.concat([d1, d2, d3], axis=1, join_axes=[d3.index])

     A    B    C    B    C    D    A    B    D
1  NaN  NaN  NaN  0.4  0.5  0.6  0.7  0.8  0.9
3  0.1  0.2  0.3  NaN  NaN  NaN  0.7  0.8  0.9

ignore_index

ignore_index : boolean, par défaut False
Si True, n'utilisez pas les valeurs d'index le long de l'axe de concaténation. L'axe résultant sera étiqueté 0, ..., n - 1. Ceci est utile si vous concaténez des objets où l'axe de concaténation ne contient pas d'informations d'indexation significatives. Notez que les valeurs d'index sur les autres axes sont toujours respectées dans la jointure.

Comme quand je d1superpose d2, si je ne me soucie pas des valeurs d'index, je pourrais les réinitialiser ou les ignorer.

                      |  pd.concat(             |  pd.concat(
                      |      [d1, d2],          |      [d1, d2]
 pd.concat([d1, d2])  |      ignore_index=True) |  ).reset_index(drop=True)
--------------------- | ----------------------- | -------------------------
     A    B    C    D |      A    B    C    D   |      A    B    C    D
2  0.1  0.2  0.3  NaN | 0  0.1  0.2  0.3  NaN   | 0  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN | 1  0.1  0.2  0.3  NaN   | 1  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6 | 2  NaN  0.4  0.5  0.6   | 2  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6 | 3  NaN  0.4  0.5  0.6   | 3  NaN  0.4  0.5  0.6

Et lors de l'utilisation axis=1:

                                   |     pd.concat(
                                   |         [d1, d2], axis=1,
 pd.concat([d1, d2], axis=1)       |         ignore_index=True)
-------------------------------    |    -------------------------------
     A    B    C    B    C    D    |         0    1    2    3    4    5
1  NaN  NaN  NaN  0.4  0.5  0.6    |    1  NaN  NaN  NaN  0.4  0.5  0.6
2  0.1  0.2  0.3  0.4  0.5  0.6    |    2  0.1  0.2  0.3  0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN    |    3  0.1  0.2  0.3  NaN  NaN  NaN

keys

Nous pouvons passer une liste de valeurs scalaires ou de tuples afin d'attribuer des valeurs de tuple ou de scalaire au MultiIndex correspondant. La longueur de la liste transmise doit être de la même longueur que le nombre d'éléments concaténés.

keys : sequence, default None
Si plusieurs niveaux sont passés, doit contenir des tuples. Construire un index hiérarchique en utilisant les clés transmises comme niveau le plus externe

axis=0

Lors de la concaténation d' Seriesobjets le longaxis=0 (extension de l'index).

Ces clés deviennent un nouveau niveau initial d'un MultiIndexobjet dans l'attribut index.

 #           length 3             length 3           #         length 2        length 2
 #          /--------\         /-----------\         #          /----\         /------\
 pd.concat([s1, s2, s3], keys=['A', 'B', 'C'])       pd.concat([s1, s2], keys=['A', 'B'])
----------------------------------------------      -------------------------------------
A  2    1                                           A  2    1
   3    2                                              3    2
B  1    3                                           B  1    3
   2    4                                              2    4
C  1    5                                           dtype: int64
   3    6
dtype: int64

Cependant, nous pouvons utiliser plus que des valeurs scalaires dans l' keysargument pour créer un fichier encore plus profond MultiIndex. Ici, nous passons tuplesde longueur 2 le préfixe de deux nouveaux niveaux de a MultiIndex:

 pd.concat(
     [s1, s2, s3],
     keys=[('A', 'X'), ('A', 'Y'), ('B', 'X')])
-----------------------------------------------
A  X  2    1
      3    2
   Y  1    3
      2    4
B  X  1    5
      3    6
dtype: int64

axis=1

C'est un peu différent lors de l'extension le long des colonnes. Lorsque nous avons utilisé axis=0(voir ci-dessus), nous avons keysagi comme des MultiIndexniveaux en plus de l'index existant. Car axis=1, nous faisons référence à un axe quiSeries objets n'ont pas, à savoir l' columnsattribut.

Variations de deux Seriesavecaxis=1

Notez que nommer s1et s2importe tant que no keysest passé, mais il est remplacé s'il keysest passé.

               |                       |                        |  pd.concat(
               |  pd.concat(           |  pd.concat(            |      [s1.rename('U'),
 pd.concat(    |      [s1, s2],        |      [s1.rename('U'),  |       s2.rename('V')],
     [s1, s2], |      axis=1,          |       s2.rename('V')], |       axis=1,
     axis=1)   |      keys=['X', 'Y']) |       axis=1)          |       keys=['X', 'Y'])
-------------- | --------------------- | ---------------------- | ----------------------
     0    1    |      X    Y           |      U    V            |      X    Y
1  NaN  3.0    | 1  NaN  3.0           | 1  NaN  3.0            | 1  NaN  3.0
2  1.0  4.0    | 2  1.0  4.0           | 2  1.0  4.0            | 2  1.0  4.0
3  2.0  NaN    | 3  2.0  NaN           | 3  2.0  NaN            | 3  2.0  NaN
MultiIndexavec Seriesetaxis=1
 pd.concat(
     [s1, s2],
     axis=1,
     keys=[('W', 'X'), ('W', 'Y')])
-----------------------------------
     W
     X    Y
1  NaN  3.0
2  1.0  4.0
3  2.0  NaN
Deux DataFrameavecaxis=1

Comme pour les axis=0exemples, keysajoutez des niveaux à a MultiIndex, mais cette fois à l'objet stocké dans l' columnsattribut.

 pd.concat(                     |  pd.concat(
     [d1, d2],                  |      [d1, d2],
     axis=1,                    |      axis=1,
     keys=['X', 'Y'])           |      keys=[('First', 'X'), ('Second', 'X')])
------------------------------- | --------------------------------------------
     X              Y           |   First           Second
     A    B    C    B    C    D |       X                X
1  NaN  NaN  NaN  0.4  0.5  0.6 |       A    B    C      B    C    D
2  0.1  0.2  0.3  0.4  0.5  0.6 | 1   NaN  NaN  NaN    0.4  0.5  0.6
3  0.1  0.2  0.3  NaN  NaN  NaN | 2   0.1  0.2  0.3    0.4  0.5  0.6
                                | 3   0.1  0.2  0.3    NaN  NaN  NaN
Serieset DataFrameavecaxis=1

C'est délicat. Dans ce cas, une valeur de clé scalaire ne peut pas agir comme le seul niveau d'index pour l' Seriesobjet lorsqu'il devient une colonne tout en agissant également comme le premier niveau de a MultiIndexpour le DataFrame. Ainsi, Pandas utilisera à nouveau l' nameattribut de l' Seriesobjet comme source du nom de la colonne.

 pd.concat(           |  pd.concat(
     [s1, d1],        |      [s1.rename('Z'), d1],
     axis=1,          |      axis=1,
     keys=['X', 'Y']) |      keys=['X', 'Y'])
--------------------- | --------------------------
   X    Y             |    X    Y
   0    A    B    C   |    Z    A    B    C
2  1  0.1  0.2  0.3   | 2  1  0.1  0.2  0.3
3  2  0.1  0.2  0.3   | 3  2  0.1  0.2  0.3
Limitations keyset MultiIndexinférence.

Pandas semble uniquement déduire les noms de colonnes à partir du Seriesnom, mais il ne remplira pas les espaces vides lors d'une concaténation analogue entre des blocs de données avec un nombre différent de niveaux de colonne.

d1_ = pd.concat(
    [d1], axis=1,
    keys=['One'])
d1_

   One
     A    B    C
2  0.1  0.2  0.3
3  0.1  0.2  0.3

Puis concaténez ceci avec un autre bloc de données avec un seul niveau dans l'objet de colonnes et Pandas refusera d'essayer de créer des tuples de l' MultiIndexobjet et de combiner tous les blocs de données comme s'il s'agissait d'un seul niveau d'objets, de scalaires et de tuples.

pd.concat([d1_, d2], axis=1)

   (One, A)  (One, B)  (One, C)    B    C    D
1       NaN       NaN       NaN  0.4  0.5  0.6
2       0.1       0.2       0.3  0.4  0.5  0.6
3       0.1       0.2       0.3  NaN  NaN  NaN

Passer un dictau lieu d'unlist

Lors du passage d'un dictionnaire, pandas.concatutilisera les clés du dictionnaire comme keysparamètre.

 # axis=0               |  # axis=1
 pd.concat(             |  pd.concat(
     {0: d1, 1: d2})    |      {0: d1, 1: d2}, axis=1)
----------------------- | -------------------------------
       A    B    C    D |      0              1
0 2  0.1  0.2  0.3  NaN |      A    B    C    B    C    D
  3  0.1  0.2  0.3  NaN | 1  NaN  NaN  NaN  0.4  0.5  0.6
1 1  NaN  0.4  0.5  0.6 | 2  0.1  0.2  0.3  0.4  0.5  0.6
  2  NaN  0.4  0.5  0.6 | 3  0.1  0.2  0.3  NaN  NaN  NaN

levels

Ceci est utilisé en conjonction avec l' keysargument. Lorsqu'il levelsest laissé comme valeur par défaut de None, Pandas prendra les valeurs uniques de chaque niveau du résultat MultiIndexet l'utilisera comme objet utilisé dans l' index.levelsattribut résultant .

niveaux : liste de séquences, par défaut Aucun
Niveaux spécifiques (valeurs uniques) à utiliser pour construire un MultiIndex. Sinon, ils seront déduits des clés.

Si Pandas déduit déjà ce que devraient être ces niveaux, quel avantage y a-t-il à le spécifier nous-mêmes? Je vais vous montrer un exemple et vous laisser le soin de trouver d'autres raisons pour lesquelles cela pourrait être utile.

Exemple

Selon la documentation, l' levelsargument est une liste de séquences. Cela signifie que nous pouvons en utiliser une autre pandas.Indexcomme l'une de ces séquences.

Considérez le bloc de données dfqui est la concaténation de d1, d2et d3:

df = pd.concat(
    [d1, d2, d3], axis=1,
    keys=['First', 'Second', 'Fourth'])

df

  First           Second           Fourth
      A    B    C      B    C    D      A    B    D
1   NaN  NaN  NaN    0.4  0.5  0.6    0.7  0.8  0.9
2   0.1  0.2  0.3    0.4  0.5  0.6    NaN  NaN  NaN
3   0.1  0.2  0.3    NaN  NaN  NaN    0.7  0.8  0.9

Les niveaux de l'objet colonnes sont:

print(df, *df.columns.levels, sep='\n')

Index(['First', 'Second', 'Fourth'], dtype='object')
Index(['A', 'B', 'C', 'D'], dtype='object')

Si nous utilisons sumdans un groupbynous obtenons:

df.groupby(axis=1, level=0).sum()

   First  Fourth  Second
1    0.0     2.4     1.5
2    0.6     0.0     1.5
3    0.6     2.4     0.0

Mais que se passerait- ['First', 'Second', 'Fourth']il si à la place il y avait une autre catégorie manquante nommée Thirdet Fifth? Et je voulais les inclure dans les résultats d'une groupbyagrégation? Nous pouvons le faire si nous avions un fichier pandas.CategoricalIndex. Et nous pouvons spécifier cela à l'avance avec lelevels argument.

Alors, définissons plutôt dfcomme:

cats = ['First', 'Second', 'Third', 'Fourth', 'Fifth']
lvl = pd.CategoricalIndex(cats, categories=cats, ordered=True)

df = pd.concat(
    [d1, d2, d3], axis=1,
    keys=['First', 'Second', 'Fourth'],
    levels=[lvl]
)

df

   First  Fourth  Second
1    0.0     2.4     1.5
2    0.6     0.0     1.5
3    0.6     2.4     0.0

Mais le premier niveau de l'objet de colonnes est:

df.columns.levels[0]

CategoricalIndex(
    ['First', 'Second', 'Third', 'Fourth', 'Fifth'],
    categories=['First', 'Second', 'Third', 'Fourth', 'Fifth'],
    ordered=True, dtype='category')

Et notre groupbyrésumé ressemble à:

df.groupby(axis=1, level=0).sum()

   First  Second  Third  Fourth  Fifth
1    0.0     1.5    0.0     2.4    0.0
2    0.6     1.5    0.0     0.0    0.0
3    0.6     0.0    0.0     2.4    0.0

names

Ceci est utilisé pour nommer les niveaux d'un résultat MultiIndex. La longueur de la namesliste doit correspondre au nombre de niveaux dans le résultat MultiIndex.

names : liste, par défaut Aucun
Noms des niveaux dans l'index hiérarchique résultant

 # axis=0                     |  # axis=1
 pd.concat(                   |  pd.concat(
     [d1, d2],                |      [d1, d2],
     keys=[0, 1],             |      axis=1, keys=[0, 1],
     names=['lvl0', 'lvl1'])  |      names=['lvl0', 'lvl1'])
----------------------------- | ----------------------------------
             A    B    C    D | lvl0    0              1
lvl0 lvl1                     | lvl1    A    B    C    B    C    D
0    2     0.1  0.2  0.3  NaN | 1     NaN  NaN  NaN  0.4  0.5  0.6
     3     0.1  0.2  0.3  NaN | 2     0.1  0.2  0.3  0.4  0.5  0.6
1    1     NaN  0.4  0.5  0.6 | 3     0.1  0.2  0.3  NaN  NaN  NaN
     2     NaN  0.4  0.5  0.6 |

verify_integrity

Documentation explicite

verify_integrity : booléen, par défaut False
Vérifie si le nouvel axe concaténé contient des doublons. Cela peut être très coûteux par rapport à la concaténation réelle des données.

Étant donné que l'indice résultant de concaténer d1et d2n'est pas unique, il échouerait le contrôle d'intégrité.

pd.concat([d1, d2])

     A    B    C    D
2  0.1  0.2  0.3  NaN
3  0.1  0.2  0.3  NaN
1  NaN  0.4  0.5  0.6
2  NaN  0.4  0.5  0.6

Et

pd.concat([d1, d2], verify_integrity=True)

> ValueError: les index ont des valeurs qui se chevauchent: [2]

piRSquared
la source
23
il serait vraiment beaucoup plus utile pour la communauté de simplement faire une pull request pour ajouter quelques exemples manquants (juste quelques exemples) à la documentation principale; SO est uniquement consultable et ne peut pas être parcouru; il serait utile de mettre un lien vers les documents ici - la grande majorité est déjà bien et complètement documentée
Jeff
6
@Jeff il y a des dimensions de ma croissance qui ont été lentes. L'utilisation de git en fait partie. Je promets que c'est ce que je veux commencer à faire.
piRSquared
L'utilisation pd.concat(..., levels=[lvl]).groupby(axis=1, level=0).sum()produit un résultat différent de pd.concat(..., levels=[cats]).groupby(axis=1, level=0).sum(). Est-ce que tu sais pourquoi? Les documents indiquent seulement que levelsdevrait être une liste de séquences.
unutbu
1
Excellente réponse, mais je pense que la section sur a Passing a dict instead of a listbesoin d'un exemple utilisant un dict, pas une liste.
unutbu
1
@unutbu J'ai corrigé l' dictexemple, merci. La raison en est qu'il lvls'agit d'un index catégorique et catsn'est qu'une liste. Lors du regroupement par type catégoriel, les catégories manquantes sont remplies de zéros et de valeurs nulles, le cas échéant. Voir ça
piRSquared