[PYTHON] Guide de l'utilisateur Pandas "Multi-Index / Advanced Index" (Document officiel traduction japonaise)

Cet article est une traduction automatique partielle de la documentation officielle de Pandas User Guide --MultiIndex / Advanced Indexing. C'est une modification d'une phrase non naturelle. La dernière version de Pandas est "0.25.3" au moment de la rédaction de cet article, mais le texte de cet article est basé sur le document de la version de développement "1.0.0" en tenant compte de l'avenir.

Si vous avez des erreurs de traduction, des traductions alternatives, des questions, etc., veuillez utiliser la section commentaires ou modifier la demande.

Index multi-index / index avancé

Ce chapitre décrit l'indexation avec multi-index (# Index hiérarchique (multi-index)) et d'autres fonctionnalités d'indexation avancées (#Other Advanced Indexing Features).

Pour la documentation de base sur l'indexation, consultez Indexation et sélection des données (https://qiita.com/nkay/items/d322ed9d9a14bdbf14cb).

: avertissement: ** Avertissement ** Le fait que l'opération d'affectation renvoie une copie ou une référence dépend du contexte. Cela s'appelle une affectation chaînée` et doit être évité. Voir [Retourner une vue ou une copie](https://qiita.com/nkay/items/d322ed9d9a14bdbf14cb#Returning a View ou Copy).

Voir aussi livre de recettes pour des opérations plus avancées.

Index hiérarchique (multi-index)

Les index hiérarchiques et à plusieurs niveaux sont très utiles pour l'analyse et la manipulation avancées des données, en particulier lorsqu'il s'agit de données de grande dimension. En substance, vous pouvez stocker et manipuler n'importe quel nombre de dimensions dans une structure de données de faible dimension telle que «Series» (1d) ou «DataFrame» (2d).

Cette section montre ce que signifie un index «hiérarchique» et comment il s'intègre à toutes les fonctionnalités d'indexation Pandas décrites ci-dessus et dans les chapitres précédents. Plus tard, regroupement (https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html#groupby) et pivotement et remodelage des données (https://pandas.pydata.org/) Lorsque nous parlons de pandas-docs / stable / user_guide / reshaping.html # reshaping), nous vous présenterons une application importante pour vous expliquer comment elle peut vous aider à structurer vos données à des fins d'analyse.

Voir aussi le livre de recettes pour des méthodes plus avancées.

_ Modifié dans la version 0.24.0 _: MultiIndex.labels a été changé en MultiIndex.codes et MultiIndex.set_labels a été changé en MultiIndex.set_codes.

Création d'un objet MultiIndex (index hiérarchique)

Dans les objets Pandas, l'objet ʻIndex est couramment utilisé pour stocker les étiquettes d'axe, tandis que l'objet MultiIndexen est une version hiérarchique. Vous pouvez considérerMultiIndexcomme un tableau de taples, où chaque tapple est unique.MultiIndex utilise une liste de tableaux (en utilisant MultiIndex.from_arrays ()), un tableau de taples (en utilisant MultiIndex.from_tuples () ) et ( MultiIndex.from_product () . Il peut être créé à partir d'un produit direct d'itérables, DataFrame (en utilisant MultiIndex.from_frame ()). Le constructeur ʻIndex tente de renvoyer un MultiIndex quand une liste de taples est passée. Vous trouverez ci-dessous différentes manières d'initialiser MultiIndex.

In [1]: arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
   ...:           ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
   ...:

In [2]: tuples = list(zip(*arrays))

In [3]: tuples
Out[3]:
[('bar', 'one'),
 ('bar', 'two'),
 ('baz', 'one'),
 ('baz', 'two'),
 ('foo', 'one'),
 ('foo', 'two'),
 ('qux', 'one'),
 ('qux', 'two')]

In [4]: index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])

In [5]: index
Out[5]:
MultiIndex([('bar', 'one'),
            ('bar', 'two'),
            ('baz', 'one'),
            ('baz', 'two'),
            ('foo', 'one'),
            ('foo', 'two'),
            ('qux', 'one'),
            ('qux', 'two')],
           names=['first', 'second'])

In [6]: s = pd.Series(np.random.randn(8), index=index)

In [7]: s
Out[7]:
first  second
bar    one       0.469112
       two      -0.282863
baz    one      -1.509059
       two      -1.135632
foo    one       1.212112
       two      -0.173215
qux    one       0.119209
       two      -1.044236
dtype: float64

Si vous voulez toutes les combinaisons (produits directs) des deux éléments itérables, il est pratique d'utiliser la méthode MultiIndex.from_product ().

In [8]: iterables = [['bar', 'baz', 'foo', 'qux'], ['one', 'two']]

In [9]: pd.MultiIndex.from_product(iterables, names=['first', 'second'])
Out[9]:
MultiIndex([('bar', 'one'),
            ('bar', 'two'),
            ('baz', 'one'),
            ('baz', 'two'),
            ('foo', 'one'),
            ('foo', 'two'),
            ('qux', 'one'),
            ('qux', 'two')],
           names=['first', 'second'])

Vous pouvez également utiliser la méthode MultiIndex.from_frame () pour créer un MultiIndex directement à partir du DataFrame. C'est une méthode qui complète MultiIndex.to_frame ().

_ À partir de la version 0.24.0 _

In [10]: df = pd.DataFrame([['bar', 'one'], ['bar', 'two'],
   ....:                    ['foo', 'one'], ['foo', 'two']],
   ....:                   columns=['first', 'second'])
   ....:

In [11]: pd.MultiIndex.from_frame(df)
Out[11]:
MultiIndex([('bar', 'one'),
            ('bar', 'two'),
            ('foo', 'one'),
            ('foo', 'two')],
           names=['first', 'second'])

Vous pouvez également créer automatiquement un MultiIndex en passant la liste des tableaux directement à la Series ou au DataFrame, comme indiqué ci-dessous.

In [12]: arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']),
   ....:           np.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])]
   ....:

In [13]: s = pd.Series(np.random.randn(8), index=arrays)

In [14]: s
Out[14]:
bar  one   -0.861849
     two   -2.104569
baz  one   -0.494929
     two    1.071804
foo  one    0.721555
     two   -0.706771
qux  one   -1.039575
     two    0.271860
dtype: float64

In [15]: df = pd.DataFrame(np.random.randn(8, 4), index=arrays)

In [16]: df
Out[16]:
                0         1         2         3
bar one -0.424972  0.567020  0.276232 -1.087401
    two -0.673690  0.113648 -1.478427  0.524988
baz one  0.404705  0.577046 -1.715002 -1.039268
    two -0.370647 -1.157892 -1.344312  0.844885
foo one  1.075770 -0.109050  1.643563 -1.469388
    two  0.357021 -0.674600 -1.776904 -0.968914
qux one -1.294524  0.413738  0.276662 -0.472035
    two -0.013960 -0.362543 -0.006154 -0.923061

Tous les constructeurs MultiIndex prennent un argument names qui stocke les propres noms de chaîne (étiquettes) du niveau. Si aucun nom n'est spécifié, «Aucun» sera attribué.

In [17]: df.index.names
Out[17]: FrozenList([None, None])

Cet index peut être défini sur l'axe dans n'importe quelle direction de l'objet Pandas, et le nombre de ** niveaux ** de l'index est arbitraire.

In [18]: df = pd.DataFrame(np.random.randn(3, 8), index=['A', 'B', 'C'], columns=index)

In [19]: df
Out[19]:
first        bar                 baz                 foo                 qux
second       one       two       one       two       one       two       one       two
A       0.895717  0.805244 -1.206412  2.565646  1.431256  1.340309 -1.170299 -0.226169
B       0.410835  0.813850  0.132003 -0.827317 -0.076467 -1.187678  1.130127 -1.436737
C      -1.413681  1.607920  1.024180  0.569605  0.875906 -2.211372  0.974466 -2.006747

In [20]: pd.DataFrame(np.random.randn(6, 6), index=index[:6], columns=index[:6])
Out[20]:
first              bar                 baz                 foo
second             one       two       one       two       one       two
first second
bar   one    -0.410001 -0.078638  0.545952 -1.219217 -1.226825  0.769804
      two    -1.281247 -0.727707 -0.121306 -0.097883  0.695775  0.341734
baz   one     0.959726 -1.110336 -0.619976  0.149748 -0.732339  0.687738
      two     0.176444  0.403310 -0.154951  0.301624 -2.179861 -1.369849
foo   one    -0.954208  1.462696 -1.743161 -0.826591 -0.345352  1.314232
      two     0.690579  0.995761  2.396780  0.014871  3.357427 -0.317441

Nous avons "épargné" un index de niveau supérieur pour rendre la sortie de la console plus facile à voir. Vous pouvez contrôler l'affichage de l'index en utilisant l'option multi_sparse de pandas.set_options ().

In [21]: with pd.option_context('display.multi_sparse', False):
   ....:     df
   ....:

Il convient de rappeler qu'il est normal d'utiliser des taples comme une étiquette unique et indivisible.

In [22]: pd.Series(np.random.randn(8), index=tuples)
Out[22]:
(bar, one)   -1.236269
(bar, two)    0.896171
(baz, one)   -0.487602
(baz, two)   -0.082240
(foo, one)   -2.182937
(foo, two)    0.380396
(qux, one)    0.084844
(qux, two)    0.432390
dtype: float64

La raison pour laquelle «MultiIndex» est important est que vous pouvez utiliser «MultiIndex» pour effectuer des opérations de regroupement, de sélection et de changement de forme, comme expliqué dans les chapitres suivants et suivants. Comme vous le verrez dans une section ultérieure, vous pouvez travailler avec des données indexées hiérarchiquement sans avoir à créer vous-même explicitement le MultiIndex. Cependant, si vous chargez des données à partir d'un fichier, vous pouvez générer votre propre MultiIndex lors de la préparation du jeu de données.

Reconstruction des étiquettes de niveau

get_level_values () La méthode est pour chaque niveau spécifique Renvoie un vecteur d'étiquettes de position.

In [23]: index.get_level_values(0)
Out[23]: Index(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], dtype='object', name='first')

In [24]: index.get_level_values('second')
Out[24]: Index(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'], dtype='object', name='second')

Indexation de base des axes à l'aide de MultiIndex

L'une des principales caractéristiques d'un index hiérarchique est la possibilité de sélectionner des données par une étiquette «partielle» qui identifie un sous-groupe dans les données. La sélection ** partielle ** "supprime" le niveau de l'index hiérarchique résultant exactement de la même manière que la sélection d'une colonne dans un DataFrame normal.

In [25]: df['bar']
Out[25]:
second       one       two
A       0.895717  0.805244
B       0.410835  0.813850
C      -1.413681  1.607920

In [26]: df['bar', 'one']
Out[26]:
A    0.895717
B    0.410835
C   -1.413681
Name: (bar, one), dtype: float64

In [27]: df['bar']['one']
Out[27]:
A    0.895717
B    0.410835
C   -1.413681
Name: one, dtype: float64

In [28]: s['qux']
Out[28]:
one   -1.039575
two    0.271860
dtype: float64

Voir les sections avec des index hiérarchiques pour plus d'informations sur la sélection à un niveau plus profond.

Définition du niveau

MultiIndex a été défini même s'il n'a pas été réellement utilisé Contient des index pour tous les niveaux. Vous remarquerez peut-être cela lors du découpage de l'index. Par exemple

In [29]: df.columns.levels  # original MultiIndex
Out[29]: FrozenList([['bar', 'baz', 'foo', 'qux'], ['one', 'two']])

In [30]: df[['foo','qux']].columns.levels  # sliced
Out[30]: FrozenList([['bar', 'baz', 'foo', 'qux'], ['one', 'two']])

Cela permet d'éviter les recalculs de niveau afin d'améliorer les performances de la tranche. Si vous voulez voir uniquement les niveaux utilisés, [get_level_values ()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.MultiIndex.get_level_values.html#pandas. Vous pouvez utiliser la méthode MultiIndex.get_level_values).

In [31]: df[['foo', 'qux']].columns.to_numpy()
Out[31]:
array([('foo', 'one'), ('foo', 'two'), ('qux', 'one'), ('qux', 'two')],
      dtype=object)

# for a specific level
In [32]: df[['foo', 'qux']].columns.get_level_values(0)
Out[32]: Index(['foo', 'foo', 'qux', 'qux'], dtype='object', name='first')

Pour reconstruire MultiIndex avec uniquement des niveaux d'utilisation, [remove_unused_levels ()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.MultiIndex.remove_unused_levels.html# Vous pouvez utiliser la méthode pandas.MultiIndex.remove_unused_levels).

In [33]: new_mi = df[['foo', 'qux']].columns.remove_unused_levels()

In [34]: new_mi.levels
Out[34]: FrozenList([['foo', 'qux'], ['one', 'two']])

Alignement des données et utilisation de la «réindexation»

Les opérations entre des objets avec différents index qui ont «MultiIndex» sur l'axe fonctionnent comme prévu. L'alignement des données fonctionne comme un index de tapple.

In [35]: s + s[:-2]
Out[35]:
bar  one   -1.723698
     two   -4.209138
baz  one   -0.989859
     two    2.143608
foo  one    1.443110
     two   -1.413542
qux  one         NaN
     two         NaN
dtype: float64

In [36]: s + s[::2]
Out[36]:
bar  one   -1.723698
     two         NaN
baz  one   -0.989859
     two         NaN
foo  one    1.443110
     two         NaN
qux  one   -2.079150
     two         NaN
dtype: float64

[Reindex ()] de Series / DataFrames (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.reindex.html#pandas.DataFrame.reindex) La méthode peut également recevoir un autre "MultiIndex", ou une liste ou un tableau de tuples.

In [37]: s.reindex(index[:3])
Out[37]:
first  second
bar    one      -0.861849
       two      -2.104569
baz    one      -0.494929
dtype: float64

In [38]: s.reindex([('foo', 'two'), ('bar', 'one'), ('qux', 'one'), ('baz', 'one')])
Out[38]:
foo  two   -0.706771
bar  one   -0.861849
qux  one   -1.039575
baz  one   -0.494929
dtype: float64

Indexation avancée avec index hiérarchiques

Obtenir MultiIndex pour faire une indexation avancée avec .loc est un peu difficile sur le plan syntaxique, mais nous avons fait tout notre possible pour y arriver. En général, les clés MultiIndex prennent la forme de taples. Par exemple, ce qui suit fonctionne comme prévu.

In [39]: df = df.T

In [40]: df
Out[40]:
                     A         B         C
first second
bar   one     0.895717  0.410835 -1.413681
      two     0.805244  0.813850  1.607920
baz   one    -1.206412  0.132003  1.024180
      two     2.565646 -0.827317  0.569605
foo   one     1.431256 -0.076467  0.875906
      two     1.340309 -1.187678 -2.211372
qux   one    -1.170299  1.130127  0.974466
      two    -0.226169 -1.436737 -2.006747

In [41]: df.loc[('bar', 'two')]
Out[41]:
A    0.805244
B    0.813850
C    1.607920
Name: (bar, two), dtype: float64

Pour cet exemple, df.loc ['bar', 'two'] fonctionnera également, mais sachez que cette abréviation peut être trompeuse en général.

Si vous utilisez .loc pour indexer à partir d'une colonne particulière, vous devez utiliser le taple comme suit:

In [42]: df.loc[('bar', 'two'), 'A']
Out[42]: 0.8052440253863785

Si vous ne passez que le premier élément du taple, vous n'avez pas à spécifier tous les niveaux de MultiIndex. Par exemple, vous pouvez utiliser l'index «partiel» pour obtenir tous les éléments qui ont «bar» dans le premier niveau comme suit:

df.loc['bar']

Ceci est un raccourci pour la notation plus redondante df.loc [('bar',),] (également équivalente à df.loc ['bar',] dans cet exemple).

La tranche «Partielle» fonctionne également très bien.

In [43]: df.loc['baz':'foo']
Out[43]:
                     A         B         C
first second
baz   one    -1.206412  0.132003  1.024180
      two     2.565646 -0.827317  0.569605
foo   one     1.431256 -0.076467  0.875906
      two     1.340309 -1.187678 -2.211372

Vous pouvez couper par la «plage» de valeurs en passant une tranche du taple.

In [44]: df.loc[('baz', 'two'):('qux', 'one')]
Out[44]:
                     A         B         C
first second
baz   two     2.565646 -0.827317  0.569605
foo   one     1.431256 -0.076467  0.875906
      two     1.340309 -1.187678 -2.211372
qux   one    -1.170299  1.130127  0.974466

In [45]: df.loc[('baz', 'two'):'foo']
Out[45]:
                     A         B         C
first second
baz   two     2.565646 -0.827317  0.569605
foo   one     1.431256 -0.076467  0.875906
      two     1.340309 -1.187678 -2.211372

Comme pour la réindexation, vous pouvez également transmettre une liste d'étiquettes ou de tuples.

In [46]: df.loc[[('bar', 'two'), ('qux', 'one')]]
Out[46]:
                     A         B         C
first second
bar   two     0.805244  0.813850  1.607920
qux   one    -1.170299  1.130127  0.974466

: ballot_box_with_check: ** Remarque ** Notez que les tapples et les listes ne sont pas traités de la même manière dans les pandas en matière d'indexation. Un taple est interprété comme une seule clé à plusieurs niveaux, mais une liste pointe vers plusieurs clés. En d'autres termes, le taple se déplace horizontalement (traversant le niveau) et la liste se déplace verticalement (balayant le niveau).

Surtout, une liste de taples tire plusieurs clés MultiIndex complètes, mais un tapple d'une liste fait référence à une valeur dans un niveau.

In [47]: s = pd.Series([1, 2, 3, 4, 5, 6],
   ....:               index=pd.MultiIndex.from_product([["A", "B"], ["c", "d", "e"]]))
   ....:

In [48]: s.loc[[("A", "c"), ("B", "d")]]  # list of tuples
Out[48]:
A  c    1
B  d    5
dtype: int64

In [49]: s.loc[(["A", "B"], ["c", "d"])]  # tuple of lists
Out[49]:
A  c    1
   d    2
B  c    4
   d    5
dtype: int64

Utilisation du slicer

Vous pouvez découper «MultiIndex» en fournissant plusieurs indexeurs.

Utilisez l'un des sélecteurs de liste d'étiquettes de tranches, d'étiquettes et de tableaux booléens que vous voyez dans Sélectionner par étiquette (https://qiita.com/nkay/items/d322ed9d9a14bdbf14cb#Select by Label) de la même manière. Je peux.

Vous pouvez utiliser slice (None) pour sélectionner tous les éléments à * ce * niveau. Il n'est pas nécessaire de spécifier tous les niveaux * plus profonds *. Ils sont impliqués par «slice (None)».

Comme les autres, il s'agit d'une indexation d'étiquette, donc elle inclut ** les deux côtés ** du slicer.

: avertissement: ** Avertissement ** Dans .loc, spécifiez tous les axes (** index ** et ** colonnes **). Il y a des cas ambigus où l'indexeur passé peut être mal interprété comme un index sur * les deux * axes plutôt que sur MultiIndex d'une ligne. Écrivez comme suit.

df.loc[(slice('A1', 'A3'), ...), :]             # noqa: E999

N'écrivez pas comme suit.

df.loc[(slice('A1', 'A3'), ...)]                # noqa: E999
In [50]: def mklbl(prefix, n):
   ....:     return ["%s%s" % (prefix, i) for i in range(n)]
   ....:

In [51]: miindex = pd.MultiIndex.from_product([mklbl('A', 4),
   ....:                                       mklbl('B', 2),
   ....:                                       mklbl('C', 4),
   ....:                                       mklbl('D', 2)])
   ....:

In [52]: micolumns = pd.MultiIndex.from_tuples([('a', 'foo'), ('a', 'bar'),
   ....:                                        ('b', 'foo'), ('b', 'bah')],
   ....:                                       names=['lvl0', 'lvl1'])
   ....:

In [53]: dfmi = pd.DataFrame(np.arange(len(miindex) * len(micolumns))
   ....:                       .reshape((len(miindex), len(micolumns))),
   ....:                     index=miindex,
   ....:                     columns=micolumns).sort_index().sort_index(axis=1)
   ....:

In [54]: dfmi
Out[54]:
lvl0           a         b
lvl1         bar  foo  bah  foo
A0 B0 C0 D0    1    0    3    2
         D1    5    4    7    6
      C1 D0    9    8   11   10
         D1   13   12   15   14
      C2 D0   17   16   19   18
...          ...  ...  ...  ...
A3 B1 C1 D1  237  236  239  238
      C2 D0  241  240  243  242
         D1  245  244  247  246
      C3 D0  249  248  251  250
         D1  253  252  255  254

[64 rows x 4 columns]

Tranchage multi-index de base avec des étiquettes de liste de tranches.

In [55]: dfmi.loc[(slice('A1', 'A3'), slice(None), ['C1', 'C3']), :]
Out[55]:
lvl0           a         b
lvl1         bar  foo  bah  foo
A1 B0 C1 D0   73   72   75   74
         D1   77   76   79   78
      C3 D0   89   88   91   90
         D1   93   92   95   94
   B1 C1 D0  105  104  107  106
...          ...  ...  ...  ...
A3 B0 C3 D1  221  220  223  222
   B1 C1 D0  233  232  235  234
         D1  237  236  239  238
      C3 D0  249  248  251  250
         D1  253  252  255  254

[24 rows x 4 columns]

Dans slice (None) en utilisant Pandas.IndexSlice Vous pouvez utiliser une syntaxe plus naturelle que d'utiliser :.

In [56]: idx = pd.IndexSlice

In [57]: dfmi.loc[idx[:, :, ['C1', 'C3']], idx[:, 'foo']]
Out[57]:
lvl0           a    b
lvl1         foo  foo
A0 B0 C1 D0    8   10
         D1   12   14
      C3 D0   24   26
         D1   28   30
   B1 C1 D0   40   42
...          ...  ...
A3 B0 C3 D1  220  222
   B1 C1 D0  232  234
         D1  236  238
      C3 D0  248  250
         D1  252  254

[32 rows x 2 columns]

Vous pouvez utiliser cette méthode pour effectuer des sélections très complexes sur plusieurs axes en même temps.

In [58]: dfmi.loc['A1', (slice(None), 'foo')]
Out[58]:
lvl0        a    b
lvl1      foo  foo
B0 C0 D0   64   66
      D1   68   70
   C1 D0   72   74
      D1   76   78
   C2 D0   80   82
...       ...  ...
B1 C1 D1  108  110
   C2 D0  112  114
      D1  116  118
   C3 D0  120  122
      D1  124  126

[16 rows x 2 columns]

In [59]: dfmi.loc[idx[:, :, ['C1', 'C3']], idx[:, 'foo']]
Out[59]:
lvl0           a    b
lvl1         foo  foo
A0 B0 C1 D0    8   10
         D1   12   14
      C3 D0   24   26
         D1   28   30
   B1 C1 D0   40   42
...          ...  ...
A3 B0 C3 D1  220  222
   B1 C1 D0  232  234
         D1  236  238
      C3 D0  248  250
         D1  252  254

[32 rows x 2 columns]

Vous pouvez utiliser l'indexeur booléen pour fournir des sélections liées aux * valeurs *.

In [60]: mask = dfmi[('a', 'foo')] > 200

In [61]: dfmi.loc[idx[mask, :, ['C1', 'C3']], idx[:, 'foo']]
Out[61]:
lvl0           a    b
lvl1         foo  foo
A3 B0 C1 D1  204  206
      C3 D0  216  218
         D1  220  222
   B1 C1 D0  232  234
         D1  236  238
      C3 D0  248  250
         D1  252  254

Vous pouvez également spécifier l'argument ʻaxisdans.loc` pour interpréter un slicer passé sur un seul axe.

In [62]: dfmi.loc(axis=0)[:, :, ['C1', 'C3']]
Out[62]:
lvl0           a         b
lvl1         bar  foo  bah  foo
A0 B0 C1 D0    9    8   11   10
         D1   13   12   15   14
      C3 D0   25   24   27   26
         D1   29   28   31   30
   B1 C1 D0   41   40   43   42
...          ...  ...  ...  ...
A3 B0 C3 D1  221  220  223  222
   B1 C1 D0  233  232  235  234
         D1  237  236  239  238
      C3 D0  249  248  251  250
         D1  253  252  255  254

[32 rows x 4 columns]

De plus, vous pouvez * attribuer * des valeurs * en utilisant les méthodes suivantes:

In [63]: df2 = dfmi.copy()

In [64]: df2.loc(axis=0)[:, :, ['C1', 'C3']] = -10

In [65]: df2
Out[65]:
lvl0           a         b
lvl1         bar  foo  bah  foo
A0 B0 C0 D0    1    0    3    2
         D1    5    4    7    6
      C1 D0  -10  -10  -10  -10
         D1  -10  -10  -10  -10
      C2 D0   17   16   19   18
...          ...  ...  ...  ...
A3 B1 C1 D1  -10  -10  -10  -10
      C2 D0  241  240  243  242
         D1  245  244  247  246
      C3 D0  -10  -10  -10  -10
         D1  -10  -10  -10  -10

[64 rows x 4 columns]

Vous pouvez également utiliser le côté droit de l'objet alignable.

In [66]: df2 = dfmi.copy()

In [67]: df2.loc[idx[:, :, ['C1', 'C3']], :] = df2 * 1000

In [68]: df2
Out[68]:
lvl0              a               b
lvl1            bar     foo     bah     foo
A0 B0 C0 D0       1       0       3       2
         D1       5       4       7       6
      C1 D0    9000    8000   11000   10000
         D1   13000   12000   15000   14000
      C2 D0      17      16      19      18
...             ...     ...     ...     ...
A3 B1 C1 D1  237000  236000  239000  238000
      C2 D0     241     240     243     242
         D1     245     244     247     246
      C3 D0  249000  248000  251000  250000
         D1  253000  252000  255000  254000

[64 rows x 4 columns]

Cross-section

La méthode xs () de DataFrame utilise également un argument de niveau pour faciliter la sélection de données à un niveau particulier de MultiIndex.

In [69]: df
Out[69]:
                     A         B         C
first second
bar   one     0.895717  0.410835 -1.413681
      two     0.805244  0.813850  1.607920
baz   one    -1.206412  0.132003  1.024180
      two     2.565646 -0.827317  0.569605
foo   one     1.431256 -0.076467  0.875906
      two     1.340309 -1.187678 -2.211372
qux   one    -1.170299  1.130127  0.974466
      two    -0.226169 -1.436737 -2.006747

In [70]: df.xs('one', level='second')
Out[70]:
              A         B         C
first
bar    0.895717  0.410835 -1.413681
baz   -1.206412  0.132003  1.024180
foo    1.431256 -0.076467  0.875906
qux   -1.170299  1.130127  0.974466
#Utilisation de tranches
In [71]: df.loc[(slice(None), 'one'), :]
Out[71]:
                     A         B         C
first second
bar   one     0.895717  0.410835 -1.413681
baz   one    -1.206412  0.132003  1.024180
foo   one     1.431256 -0.076467  0.875906
qux   one    -1.170299  1.130127  0.974466

Vous pouvez également sélectionner des colonnes avec «xs» en spécifiant l'argument Axis.

In [72]: df = df.T

In [73]: df.xs('one', level='second', axis=1)
Out[73]:
first       bar       baz       foo       qux
A      0.895717 -1.206412  1.431256 -1.170299
B      0.410835  0.132003 -0.076467  1.130127
C     -1.413681  1.024180  0.875906  0.974466
#Utilisation de tranches
In [74]: df.loc[:, (slice(None), 'one')]
Out[74]:
first        bar       baz       foo       qux
second       one       one       one       one
A       0.895717 -1.206412  1.431256 -1.170299
B       0.410835  0.132003 -0.076467  1.130127
C      -1.413681  1.024180  0.875906  0.974466

Avec xs, vous pouvez également sélectionner en utilisant plusieurs touches.

In [75]: df.xs(('one', 'bar'), level=('second', 'first'), axis=1)
Out[75]:
first        bar
second       one
A       0.895717
B       0.410835
C      -1.413681
#Utilisation de tranches
In [76]: df.loc[:, ('bar', 'one')]
Out[76]:
A    0.895717
B    0.410835
C   -1.413681
Name: (bar, one), dtype: float64

Vous pouvez conserver le niveau sélectionné en passant drop_level = False à xs.

In [77]: df.xs('one', level='second', axis=1, drop_level=False)
Out[77]:
first        bar       baz       foo       qux
second       one       one       one       one
A       0.895717 -1.206412  1.431256 -1.170299
B       0.410835  0.132003 -0.076467  1.130127
C      -1.413681  1.024180  0.875906  0.974466

Comparez le résultat ci-dessus avec le cas de drop_level = True (valeur par défaut).

In [78]: df.xs('one', level='second', axis=1, drop_level=True)
Out[78]:
first       bar       baz       foo       qux
A      0.895717 -1.206412  1.431256 -1.170299
B      0.410835  0.132003 -0.076467  1.130127
C     -1.413681  1.024180  0.875906  0.974466

Réindexation et alignement avancés

L'objet pandas reindex () et [ʻalign (ʻalign) ) ](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.align.html#pandas.DataFrame.align) Si vous utilisez l'argument level` dans la méthode, le niveau entier Utile pour diffuser des valeurs vers. Par exemple:

In [79]: midx = pd.MultiIndex(levels=[['zero', 'one'], ['x', 'y']],
   ....:                      codes=[[1, 1, 0, 0], [1, 0, 1, 0]])
   ....:

In [80]: df = pd.DataFrame(np.random.randn(4, 2), index=midx)

In [81]: df
Out[81]:
               0         1
one  y  1.519970 -0.493662
     x  0.600178  0.274230
zero y  0.132885 -0.023688
     x  2.410179  1.450520

In [82]: df2 = df.mean(level=0)

In [83]: df2
Out[83]:
             0         1
one   1.060074 -0.109716
zero  1.271532  0.713416

In [84]: df2.reindex(df.index, level=0)
Out[84]:
               0         1
one  y  1.060074 -0.109716
     x  1.060074 -0.109716
zero y  1.271532  0.713416
     x  1.271532  0.713416

#alignement
In [85]: df_aligned, df2_aligned = df.align(df2, level=0)

In [86]: df_aligned
Out[86]:
               0         1
one  y  1.519970 -0.493662
     x  0.600178  0.274230
zero y  0.132885 -0.023688
     x  2.410179  1.450520

In [87]: df2_aligned
Out[87]:
               0         1
one  y  1.060074 -0.109716
     x  1.060074 -0.109716
zero y  1.271532  0.713416
     x  1.271532  0.713416

Échange d'ordre de niveau par swaplevel

La méthode swaplevel () est un ordre à deux niveaux Peut être remplacé.

In [88]: df[:5]
Out[88]:
               0         1
one  y  1.519970 -0.493662
     x  0.600178  0.274230
zero y  0.132885 -0.023688
     x  2.410179  1.450520

In [89]: df[:5].swaplevel(0, 1, axis=0)
Out[89]:
               0         1
y one   1.519970 -0.493662
x one   0.600178  0.274230
y zero  0.132885 -0.023688
x zero  2.410179  1.450520

Tri des niveaux par reorder_levels

La méthode reorder_levels () est la méthode swaplevel. Généraliser pour vous permettre de remplacer le niveau d'un index hiérarchique en une seule étape.

In [90]: df[:5].reorder_levels([1, 0], axis=0)
Out[90]:
               0         1
y one   1.519970 -0.493662
x one   0.600178  0.274230
y zero  0.132885 -0.023688
x zero  2.410179  1.450520

Renommer ʻIndex ou MultiIndex`

[Rename ()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rename.html] généralement utilisé pour renommer les colonnes dans DataFrame La méthode # pandas.DataFrame.rename) peut également renommer l'étiquette MultiIndex. L'argument colonnes de renommer peut être un dictionnaire contenant uniquement les colonnes que vous souhaitez renommer.

In [91]: df.rename(columns={0: "col0", 1: "col1"})
Out[91]:
            col0      col1
one  y  1.519970 -0.493662
     x  0.600178  0.274230
zero y  0.132885 -0.023688
     x  2.410179  1.450520

Cette méthode peut également être utilisée pour renommer une étiquette particulière dans l'index principal de DataFrame.

In [92]: df.rename(index={"one": "two", "y": "z"})
Out[92]:
               0         1
two  z  1.519970 -0.493662
     x  0.600178  0.274230
zero z  0.132885 -0.023688
     x  2.410179  1.450520

rename_axis () La méthode est ʻIndex ou Renommer MultiIndex. En particulier, vous pouvez spécifier un nom de niveau pour le MultiIndex, ce qui sera utile plus tard lorsque vous utiliserez reset_index ()` pour déplacer la valeur du MultiIndex vers une colonne normale.

In [93]: df.rename_axis(index=['abc', 'def'])
Out[93]:
                 0         1
abc  def
one  y    1.519970 -0.493662
     x    0.600178  0.274230
zero y    0.132885 -0.023688
     x    2.410179  1.450520

Notez que les colonnes dans le DataFrame sont des index, donc utiliser rename_axis avec l'argument columns renommera l'index.

In [94]: df.rename_axis(columns="Cols").columns
Out[94]: RangeIndex(start=0, stop=2, step=1, name='Cols')

Rename et rename_axis prennent en charge la spécification d'un dictionnaire, Series et une fonction de mappage pour mapper une étiquette / un nom à une nouvelle valeur.

Si vous voulez travailler directement avec l'objet ʻIndex au lieu de via le DataFrame, alors [ʻIndex.set_names () ](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index) Vous pouvez le renommer en utilisant .set_names.html # pandas.Index.set_names).

In [95]: mi = pd.MultiIndex.from_product([[1, 2], ['a', 'b']], names=['x', 'y'])

In [96]: mi.names
Out[96]: FrozenList(['x', 'y'])

In [97]: mi2 = mi.rename("new name", level=0)

In [98]: mi2
Out[98]:
MultiIndex([(1, 'a'),
            (1, 'b'),
            (2, 'a'),
            (2, 'b')],
           names=['new name', 'y'])

: avertissement: ** Avertissement ** Avant pandas 1.0.0, il était également possible de définir le nom du MultiIndex en mettant à jour le nom du niveau.

>>> mi.levels[0].name = 'name via level'
>>> mi.names[0]  # only works for older panads
'name via level'

Depuis pandas 1.0, cela échoue implicitement à mettre à jour le nom de MultiIndex. Utilisez plutôt ʻIndex.set_names () ` S'il te plait donne moi.

Tri de MultiIndex

Les objets avec MultiIndex appliqué doivent être triés afin d'être efficacement indexés et découpés. Comme tout autre index, sort_index () Tu peux l'utiliser.

In [99]: import random

In [100]: random.shuffle(tuples)

In [101]: s = pd.Series(np.random.randn(8), index=pd.MultiIndex.from_tuples(tuples))

In [102]: s
Out[102]:
qux  one    0.206053
foo  two   -0.251905
bar  two   -2.213588
     one    1.063327
qux  two    1.266143
baz  one    0.299368
foo  one   -0.863838
baz  two    0.408204
dtype: float64

In [103]: s.sort_index()
Out[103]:
bar  one    1.063327
     two   -2.213588
baz  one    0.299368
     two    0.408204
foo  one   -0.863838
     two   -0.251905
qux  one    0.206053
     two    1.266143
dtype: float64

In [104]: s.sort_index(level=0)
Out[104]:
bar  one    1.063327
     two   -2.213588
baz  one    0.299368
     two    0.408204
foo  one   -0.863838
     two   -0.251905
qux  one    0.206053
     two    1.266143
dtype: float64

In [105]: s.sort_index(level=1)
Out[105]:
bar  one    1.063327
baz  one    0.299368
foo  one   -0.863838
qux  one    0.206053
bar  two   -2.213588
baz  two    0.408204
foo  two   -0.251905
qux  two    1.266143
dtype: float64

Si le niveau de MultiIndex est nommé, vous pouvez également passer le nom du niveau à sort_index.

In [106]: s.index.set_names(['L1', 'L2'], inplace=True)

In [107]: s.sort_index(level='L1')
Out[107]:
L1   L2
bar  one    1.063327
     two   -2.213588
baz  one    0.299368
     two    0.408204
foo  one   -0.863838
     two   -0.251905
qux  one    0.206053
     two    1.266143
dtype: float64

In [108]: s.sort_index(level='L2')
Out[108]:
L1   L2
bar  one    1.063327
baz  one    0.299368
foo  one   -0.863838
qux  one    0.206053
bar  two   -2.213588
baz  two    0.408204
foo  two   -0.251905
qux  two    1.266143
dtype: float64

Pour les objets de dimension supérieure, si vous avez «MultiIndex», vous pouvez trier par niveau sur des axes autres que l'index.

In [109]: df.T.sort_index(level=1, axis=1)
Out[109]:
        one      zero       one      zero
          x         x         y         y
0  0.600178  2.410179  1.519970  0.132885
1  0.274230  1.450520 -0.493662 -0.023688

L'indexation fonctionne même si les données ne sont pas triées, mais c'est assez inefficace (et vous verrez un PerformanceWarning). Il renvoie également une copie des données au lieu de la vue.

In [110]: dfm = pd.DataFrame({'jim': [0, 0, 1, 1],
   .....:                     'joe': ['x', 'x', 'z', 'y'],
   .....:                     'jolie': np.random.rand(4)})
   .....:

In [111]: dfm = dfm.set_index(['jim', 'joe'])

In [112]: dfm
Out[112]:
            jolie
jim joe
0   x    0.490671
    x    0.120248
1   z    0.537020
    y    0.110968
In [4]: dfm.loc[(1, 'z')]
PerformanceWarning: indexing past lexsort depth may impact performance.

Out[4]:
           jolie
jim joe
1   z    0.64094

De plus, l'indexation lorsqu'elle n'est pas complètement triée peut entraîner des erreurs similaires aux suivantes:

In [5]: dfm.loc[(0, 'y'):(1, 'z')]
UnsortedIndexError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'

La méthode [ʻis_lexsorted () ](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.MultiIndex.is_lexsorted.html#pandas.MultiIndex.is_lexsorted) de MultiIndexest un index. Indique si est trié et la propriétélexsort_depth` renvoie la profondeur de tri.

In [113]: dfm.index.is_lexsorted()
Out[113]: False

In [114]: dfm.index.lexsort_depth
Out[114]: 1
In [115]: dfm = dfm.sort_index()

In [116]: dfm
Out[116]:
            jolie
jim joe
0   x    0.490671
    x    0.120248
1   y    0.110968
    z    0.537020

In [117]: dfm.index.is_lexsorted()
Out[117]: True

In [118]: dfm.index.lexsort_depth
Out[118]: 2

Les choix fonctionnent maintenant comme prévu.

In [119]: dfm.loc[(0, 'y'):(1, 'z')]
Out[119]:
            jolie
jim joe
1   y    0.110968
    z    0.537020

prendre la méthode

Comme NumPy ndarrays, pandas ʻIndex·Series · DataFrame fournit également une méthode take () pour obtenir des éléments le long d'un axe particulier à un index particulier. L'index spécifié doit être un ndarray à la position d'index de liste ou d'entier. take` peut également accepter des entiers négatifs par rapport à la fin de l'objet.

In [120]: index = pd.Index(np.random.randint(0, 1000, 10))

In [121]: index
Out[121]: Int64Index([214, 502, 712, 567, 786, 175, 993, 133, 758, 329], dtype='int64')

In [122]: positions = [0, 9, 3]

In [123]: index[positions]
Out[123]: Int64Index([214, 329, 567], dtype='int64')

In [124]: index.take(positions)
Out[124]: Int64Index([214, 329, 567], dtype='int64')

In [125]: ser = pd.Series(np.random.randn(10))

In [126]: ser.iloc[positions]
Out[126]:
0   -0.179666
9    1.824375
3    0.392149
dtype: float64

In [127]: ser.take(positions)
Out[127]:
0   -0.179666
9    1.824375
3    0.392149
dtype: float64

Pour DataFrames, l'index spécifié doit être une liste unidimensionnelle ou un ndarray qui spécifie la position de la ligne ou de la colonne.

In [128]: frm = pd.DataFrame(np.random.randn(5, 3))

In [129]: frm.take([1, 4, 3])
Out[129]:
          0         1         2
1 -1.237881  0.106854 -1.276829
4  0.629675 -1.425966  1.857704
3  0.979542 -1.633678  0.615855

In [130]: frm.take([0, 2], axis=1)
Out[130]:
          0         2
0  0.595974  0.601544
1 -1.237881 -1.276829
2 -0.767101  1.499591
3  0.979542  0.615855
4  0.629675  1.857704

Notez que la méthode take de l'objet pandas n'est pas destinée à fonctionner avec des index booléens et peut renvoyer des résultats inattendus.

In [131]: arr = np.random.randn(10)

In [132]: arr.take([False, False, True, True])
Out[132]: array([-1.1935, -1.1935,  0.6775,  0.6775])

In [133]: arr[[0, 1]]
Out[133]: array([-1.1935,  0.6775])

In [134]: ser = pd.Series(np.random.randn(10))

In [135]: ser.take([False, False, True, True])
Out[135]:
0    0.233141
0    0.233141
1   -0.223540
1   -0.223540
dtype: float64

In [136]: ser.iloc[[0, 1]]
Out[136]:
0    0.233141
1   -0.223540
dtype: float64

Enfin, une petite note de performance, la méthode take gère une gamme plus étroite d'entrées, ce qui peut fournir des performances beaucoup plus rapides qu'un index sophistiqué. ]

In [137]: arr = np.random.randn(10000, 5)

In [138]: indexer = np.arange(10000)

In [139]: random.shuffle(indexer)

In [140]: %timeit arr[indexer]
   .....: %timeit arr.take(indexer, axis=0)
   .....:
219 us +- 1.23 us per loop (mean +- std. dev. of 7 runs, 1000 loops each)
72.3 us +- 727 ns per loop (mean +- std. dev. of 7 runs, 10000 loops each)
In [141]: ser = pd.Series(arr[:, 0])

In [142]: %timeit ser.iloc[indexer]
   .....: %timeit ser.take(indexer)
   .....:
179 us +- 1.54 us per loop (mean +- std. dev. of 7 runs, 10000 loops each)
162 us +- 1.6 us per loop (mean +- std. dev. of 7 runs, 10000 loops each)

Type d'index

Jusqu'à présent, nous avons couvert le «MultiIndex» de manière assez approfondie. La documentation pour DatetimeIndex et PeriodIndex est ici, la documentation pour TimedeltaIndex est [ici](https: / Voir /dev.pandas.io/docs/user_guide/timedeltas.html#timedeltas-index).

Les sous-sections suivantes mettent en évidence d'autres types d'index.

CategoricalIndex

CategoricalIndex est un index qui permet de prendre en charge les index en double. .. Il s'agit d'un conteneur qui entoure Categorical de nombreux éléments en double. Permet une indexation et un stockage efficaces des index contenant.

In [143]: from pandas.api.types import CategoricalDtype

In [144]: df = pd.DataFrame({'A': np.arange(6),
   .....:                    'B': list('aabbca')})
   .....:

In [145]: df['B'] = df['B'].astype(CategoricalDtype(list('cab')))

In [146]: df
Out[146]:
   A  B
0  0  a
1  1  a
2  2  b
3  3  b
4  4  c
5  5  a

In [147]: df.dtypes
Out[147]:
A       int64
B    category
dtype: object

In [148]: df['B'].cat.categories
Out[148]: Index(['c', 'a', 'b'], dtype='object')

La définition de l'index crée un CatégoricalIndex.

In [149]: df2 = df.set_index('B')

In [150]: df2.index
Out[150]: CategoricalIndex(['a', 'a', 'b', 'b', 'c', 'a'], categories=['c', 'a', 'b'], ordered=False, name='B', dtype='category')

Les index utilisant __getitem__ /. Iloc / .loc fonctionnent de la même manière que ʻIndex. L'indexeur doit ** appartenir à une catégorie **. Sinon, vous obtiendrez une KeyError`.

In [151]: df2.loc['a']
Out[151]:
   A
B
a  0
a  1
a  5

L'index «catégorique» est ** conservé ** après l'index.

In [152]: df2.loc['a'].index
Out[152]: CategoricalIndex(['a', 'a', 'a'], categories=['c', 'a', 'b'], ordered=False, name='B', dtype='category')

Lorsque vous triez les index, ils sont triés par catégorie (puisque vous avez créé l'index avec CategoricalDtype (list ('cab')), il est trié par cab).

In [153]: df2.sort_index()
Out[153]:
   A
B
c  4
a  0
a  1
a  5
b  2
b  3

Les opérations Groupby sur les index conservent également les propriétés de l'index.

In [154]: df2.groupby(level=0).sum()
Out[154]:
   A
B
c  4
a  6
b  5

In [155]: df2.groupby(level=0).sum().index
Out[155]: CategoricalIndex(['c', 'a', 'b'], categories=['c', 'a', 'b'], ordered=False, name='B', dtype='category')

L'opération de réindexation renvoie un index du résultat basé sur le type d'indexeur passé. Passer une liste renverra l''Index 'habituel. Passer un Catégorique renvoie unCategoricalIndex indexé selon la ** catégorie de type Catégorique transmise. Cela vous permet d'indexer arbitrairement des valeurs qui ** n'existent pas dans la catégorie, comme pour réindexer les pandas.

In [156]: df3 = pd.DataFrame({'A': np.arange(3),
   .....:                     'B': pd.Series(list('abc')).astype('category')})
   .....:

In [157]: df3 = df3.set_index('B')

In [158]: df3
Out[158]:
   A
B
a  0
b  1
c  2
In [159]: df3.reindex(['a', 'e'])
Out[159]:
     A
B
a  0.0
e  NaN

In [160]: df3.reindex(['a', 'e']).index
Out[160]: Index(['a', 'e'], dtype='object', name='B')

In [161]: df3.reindex(pd.Categorical(['a', 'e'], categories=list('abe')))
Out[161]:
     A
B
a  0.0
e  NaN

In [162]: df3.reindex(pd.Categorical(['a', 'e'], categories=list('abe'))).index
Out[162]: CategoricalIndex(['a', 'e'], categories=['a', 'b', 'e'], ordered=False, name='B', dtype='category')

: avertissement: ** Avertissement ** Les opérations de formatage et de comparaison dans CatégoricalIndex doivent être dans la même catégorie. Sinon, vous obtiendrez un TypeError.

In [163]: df4 = pd.DataFrame({'A': np.arange(2),
   .....:                     'B': list('ba')})
   .....:

In [164]: df4['B'] = df4['B'].astype(CategoricalDtype(list('ab')))

In [165]: df4 = df4.set_index('B')

In [166]: df4.index
Out[166]: CategoricalIndex(['b', 'a'], categories=['a', 'b'], ordered=False, name='B', dtype='category')

In [167]: df5 = pd.DataFrame({'A': np.arange(2),
   .....:                     'B': list('bc')})
   .....:

In [168]: df5['B'] = df5['B'].astype(CategoricalDtype(list('bc')))

In [169]: df5 = df5.set_index('B')

In [170]: df5.index
Out[170]: CategoricalIndex(['b', 'c'], categories=['b', 'c'], ordered=False, name='B', dtype='category')
In [1]: pd.concat([df4, df5])
TypeError: categories must match existing categories when appending

Int64Index et RangeIndex

ʻInt64Index` est le fondement de la fondation de l'indice pandas. Il s'agit d'un tableau immuable qui implémente un ensemble de tranches ordonnées.

RangeIndex fournit l'index par défaut pour tous les objets NDFrame ʻIl s'agit d'une sous-classe de Int64Index. RangeIndex est une version de ʻInt64Index optimisée pour représenter un ensemble d'ordres monotone. Ceux-ci sont similaires au [type de plage] de Python (https://docs.python.org/3/library/stdtypes.html#typesseq-range).

Float64Index

Par défaut, Float64Index est une minorité flottante ou un entier dans l'indexation. Créé automatiquement lors du passage d'une valeur mixte de fractions flottantes. Cela permet un paradigme de découpage purement basé sur les étiquettes qui fait que l'index scalaire et le découpage «[]», «x» et «loc» fonctionnent exactement de la même manière.

In [171]: indexf = pd.Index([1.5, 2, 3, 4.5, 5])

In [172]: indexf
Out[172]: Float64Index([1.5, 2.0, 3.0, 4.5, 5.0], dtype='float64')

In [173]: sf = pd.Series(range(5), index=indexf)

In [174]: sf
Out[174]:
1.5    0
2.0    1
3.0    2
4.5    3
5.0    4
dtype: int64

Les sélections scalaires pour «[]» et «.loc» sont toujours basées sur des étiquettes. La spécification d'un entier correspond à un index flottant égal (par exemple, «3» équivaut à «3.0»).

In [175]: sf[3]
Out[175]: 2

In [176]: sf[3.0]
Out[176]: 2

In [177]: sf.loc[3]
Out[177]: 2

In [178]: sf.loc[3.0]
Out[178]: 2

Le seul indice de position est via ʻiloc`.

In [179]: sf.iloc[3]
Out[179]: 3

L'index scalaire introuvable lève une KeyError. Les tranches sont principalement basées sur les valeurs d'index lors de l'utilisation de [], ʻixetloc, et ** toujours basées sur la position lors de l'utilisation de ʻiloc. L'exception est lorsque la tranche est une valeur booléenne. Dans ce cas, il est toujours basé sur la position.

In [180]: sf[2:4]
Out[180]:
2.0    1
3.0    2
dtype: int64

In [181]: sf.loc[2:4]
Out[181]:
2.0    1
3.0    2
dtype: int64

In [182]: sf.iloc[2:4]
Out[182]:
3.0    2
4.5    3
dtype: int64

L'index flottant vous permet d'utiliser des tranches avec des fractions flottantes.

In [183]: sf[2.1:4.6]
Out[183]:
3.0    2
4.5    3
dtype: int64

In [184]: sf.loc[2.1:4.6]
Out[184]:
3.0    2
4.5    3
dtype: int64

S'il ne s'agit pas d'un index flottant, les tranches utilisant des flottants lèveront un TypeError.

In [1]: pd.Series(range(5))[3.5]
TypeError: the label [3.5] is not a proper indexer for this index type (Int64Index)

In [1]: pd.Series(range(5))[3.5:4.5]
TypeError: the slice start [3.5] is not a proper indexer for this index type (Int64Index)

Voici quelques cas d'utilisation courants pour l'utilisation de ce type d'index: Imaginez un schéma d'indexation irrégulier de type timedelta où les données sont enregistrées sous forme de flottants. Cela peut être, par exemple, un décalage en millisecondes.

In [185]: dfir = pd.concat([pd.DataFrame(np.random.randn(5, 2),
   .....:                                index=np.arange(5) * 250.0,
   .....:                                columns=list('AB')),
   .....:                   pd.DataFrame(np.random.randn(6, 2),
   .....:                                index=np.arange(4, 10) * 250.1,
   .....:                                columns=list('AB'))])
   .....:

In [186]: dfir
Out[186]:
               A         B
0.0    -0.435772 -1.188928
250.0  -0.808286 -0.284634
500.0  -1.815703  1.347213
750.0  -0.243487  0.514704
1000.0  1.162969 -0.287725
1000.4 -0.179734  0.993962
1250.5 -0.212673  0.909872
1500.6 -0.733333 -0.349893
1750.7  0.456434 -0.306735
2000.8  0.553396  0.166221
2250.9 -0.101684 -0.734907

Les opérations de sélection fonctionnent toujours sur une base de valeur pour tous les opérateurs de sélection.

In [187]: dfir[0:1000.4]
Out[187]:
               A         B
0.0    -0.435772 -1.188928
250.0  -0.808286 -0.284634
500.0  -1.815703  1.347213
750.0  -0.243487  0.514704
1000.0  1.162969 -0.287725
1000.4 -0.179734  0.993962

In [188]: dfir.loc[0:1001, 'A']
Out[188]:
0.0      -0.435772
250.0    -0.808286
500.0    -1.815703
750.0    -0.243487
1000.0    1.162969
1000.4   -0.179734
Name: A, dtype: float64

In [189]: dfir.loc[1000.4]
Out[189]:
A   -0.179734
B    0.993962
Name: 1000.4, dtype: float64

Vous pouvez obtenir la première seconde (1000 millisecondes) des données comme suit:

In [190]: dfir[0:1000]
Out[190]:
               A         B
0.0    -0.435772 -1.188928
250.0  -0.808286 -0.284634
500.0  -1.815703  1.347213
750.0  -0.243487  0.514704
1000.0  1.162969 -0.287725

Si vous avez besoin d'une sélection basée sur une position entière, utilisez ʻiloc`.

In [191]: dfir.iloc[0:5]
Out[191]:
               A         B
0.0    -0.435772 -1.188928
250.0  -0.808286 -0.284634
500.0  -1.815703  1.347213
750.0  -0.243487  0.514704
1000.0  1.162969 -0.287725

IntervalIndex

[ʻIntervalIndex](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.IntervalIndex.html#pandas.IntervalIndex) et son propre dtype, ʻIntervalDtype, et ʻInterval` Le type scalaire permet aux pandas de fournir un support de première classe pour la notation d'intervalle. Je vais.

ʻInterval Index permet une indexation unique, [cut ()](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.cut.html#pandas En tant que type de retour des catégories .cut) et [qcut ()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.qcut.html#pandas.qcut) Est également utilisé.

Index par ʻInterval Index`

ʻIntervalIndex peut être utilisé comme index dans SeriesetDataFrame`.

In [192]: df = pd.DataFrame({'A': [1, 2, 3, 4]},
   .....:                   index=pd.IntervalIndex.from_breaks([0, 1, 2, 3, 4]))
   .....:

In [193]: df
Out[193]:
        A
(0, 1]  1
(1, 2]  2
(2, 3]  3
(3, 4]  4

Les index basés sur des étiquettes via .loc qui suivent les extrémités de l'intervalle fonctionnent comme prévu et sélectionnent cet intervalle particulier.

In [194]: df.loc[2]
Out[194]:
A    2
Name: (1, 2], dtype: int64

In [195]: df.loc[[2, 3]]
Out[195]:
        A
(1, 2]  2
(2, 3]  3

Si vous sélectionnez le libellé * inclus * dans un intervalle, il sera sélectionné pour chaque intervalle.

In [196]: df.loc[2.5]
Out[196]:
A    3
Name: (2, 3], dtype: int64

In [197]: df.loc[[2.5, 3.5]]
Out[197]:
        A
(2, 3]  3
(3, 4]  4

Lorsqu'elle est sélectionnée à l'aide d'intervalles, seules les correspondances exactes sont renvoyées (pandas 0.25.0 et versions ultérieures).

In [198]: df.loc[pd.Interval(1, 2)]
Out[198]:
A    2
Name: (1, 2], dtype: int64

Si vous essayez de sélectionner un intervalle qui n'est pas exactement inclus dans ʻIntervalIndex, vous obtiendrez uneKeyError`.

In [7]: df.loc[pd.Interval(0.5, 2.5)]
---------------------------------------------------------------------------
KeyError: Interval(0.5, 2.5, closed='right')

Pour sélectionner tous les ʻIntervals qui chevauchent un ʻInterval particulier, [ʻoverlaps () `](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.IntervalIndex. Créez un indexeur booléen à l'aide de la méthode overlaps.html # pandas.IntervalIndex.overlaps).

In [199]: idxr = df.index.overlaps(pd.Interval(0.5, 2.5))

In [200]: idxr
Out[200]: array([ True,  True,  True, False])

In [201]: df[idxr]
Out[201]:
        A
(0, 1]  1
(1, 2]  2
(2, 3]  3

Binning des données en utilisant cut et qcut

cut () et [qcut ()](https: / /pandas.pydata.org/pandas-docs/stable/reference/api/pandas.qcut.html#pandas.qcut) tous les deux renvoient des objets «catégoriques» et les bacs qu'ils créent sont «dans l'attribut« .categories ». Il est enregistré comme IntervalIndex`.

In [202]: c = pd.cut(range(4), bins=2)

In [203]: c
Out[203]:
[(-0.003, 1.5], (-0.003, 1.5], (1.5, 3.0], (1.5, 3.0]]
Categories (2, interval[float64]): [(-0.003, 1.5] < (1.5, 3.0]]

In [204]: c.categories
Out[204]:
IntervalIndex([(-0.003, 1.5], (1.5, 3.0]],
              closed='right',
              dtype='interval[float64]')

cut () transmet ʻIntervalIndex à l'argument binspeut aussi faire. Cela permet un idiome pandas pratique. Tout d'abord, définissez certaines données etbins sur un nombre fixe [ cut () ](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.cut. Appelez html # pandas.cut) pour créer un bac. Ensuite, la valeur de .categories a été appelée par la suite [ cut () ](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.cut.html# Vous pouvez regrouper de nouvelles données dans le même bac en les passant à l'argument bins` de pandas.cut).

In [205]: pd.cut([0, 3, 5, 1], bins=c.categories)
Out[205]:
[(-0.003, 1.5], (1.5, 3.0], NaN, (-0.003, 1.5]]
Categories (2, interval[float64]): [(-0.003, 1.5] < (1.5, 3.0]]

Les valeurs en dehors de toutes les classes reçoivent la valeur «NaN».

Créer une plage d'intervalles

Si vous avez besoin d'intervalles à fréquence normale, utilisez la fonction [ʻinterval_range () ](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.interval_range.html#pandas.interval_range). Vous pouvez l'utiliser pour créer un «index d'intervalle» en utilisant différentes combinaisons de «début», «fin» et «périodes». La période par défaut pour ʻinterval_range est 1 pour les intervalles numériques et les jours calendaires pour les intervalles de type datetime.

In [206]: pd.interval_range(start=0, end=5)
Out[206]:
IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]],
              closed='right',
              dtype='interval[int64]')

In [207]: pd.interval_range(start=pd.Timestamp('2017-01-01'), periods=4)
Out[207]:
IntervalIndex([(2017-01-01, 2017-01-02], (2017-01-02, 2017-01-03], (2017-01-03, 2017-01-04], (2017-01-04, 2017-01-05]],
              closed='right',
              dtype='interval[datetime64[ns]]')

In [208]: pd.interval_range(end=pd.Timedelta('3 days'), periods=3)
Out[208]:
IntervalIndex([(0 days 00:00:00, 1 days 00:00:00], (1 days 00:00:00, 2 days 00:00:00], (2 days 00:00:00, 3 days 00:00:00]],
              closed='right',
              dtype='interval[timedelta64[ns]]')

L'argument freq peut être utilisé pour spécifier une période autre que celle par défaut, avec divers alias de période pour des intervalles de type datetime. # timeseries-offset-aliases) est disponible.

In [209]: pd.interval_range(start=0, periods=5, freq=1.5)
Out[209]:
IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0], (6.0, 7.5]],
              closed='right',
              dtype='interval[float64]')

In [210]: pd.interval_range(start=pd.Timestamp('2017-01-01'), periods=4, freq='W')
Out[210]:
IntervalIndex([(2017-01-01, 2017-01-08], (2017-01-08, 2017-01-15], (2017-01-15, 2017-01-22], (2017-01-22, 2017-01-29]],
              closed='right',
              dtype='interval[datetime64[ns]]')

In [211]: pd.interval_range(start=pd.Timedelta('0 days'), periods=3, freq='9H')
Out[211]:
IntervalIndex([(0 days 00:00:00, 0 days 09:00:00], (0 days 09:00:00, 0 days 18:00:00], (0 days 18:00:00, 1 days 03:00:00]],
              closed='right',
              dtype='interval[timedelta64[ns]]')

De plus, vous pouvez utiliser l'argument closed pour spécifier qui ferme l'intervalle. Par défaut, l'intervalle est fermé à droite.

In [212]: pd.interval_range(start=0, end=4, closed='both')
Out[212]:
IntervalIndex([[0, 1], [1, 2], [2, 3], [3, 4]],
              closed='both',
              dtype='interval[int64]')

In [213]: pd.interval_range(start=0, end=4, closed='neither')
Out[213]:
IntervalIndex([(0, 1), (1, 2), (2, 3), (3, 4)],
              closed='neither',
              dtype='interval[int64]')

_ À partir de la version 0.23.0 _

Si vous spécifiez start ・ ʻend period, le résultat ʻIntervalIndex créera un intervalle de start à ʻendavec autant d'éléments que depériodes`.

In [214]: pd.interval_range(start=0, end=6, periods=4)
Out[214]:
IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]],
              closed='right',
              dtype='interval[float64]')

In [215]: pd.interval_range(pd.Timestamp('2018-01-01'),
   .....:                   pd.Timestamp('2018-02-28'), periods=3)
   .....:
Out[215]:
IntervalIndex([(2018-01-01, 2018-01-20 08:00:00], (2018-01-20 08:00:00, 2018-02-08 16:00:00], (2018-02-08 16:00:00, 2018-02-28]],
              closed='right',
              dtype='interval[datetime64[ns]]')

Autres FAQ sur l'indexation

Index par entier

L'indexation basée sur des étiquettes avec des étiquettes d'axe entier est un sujet délicat. Il est fréquemment discuté entre les différents membres de la liste de diffusion et la communauté scientifique Python. Chez les pandas, notre opinion générale est que les étiquettes sont plus importantes que les positions entières. Par conséquent, pour les index d'axes entiers, les outils standard tels que .loc autorisent l'indexation basée sur les étiquettes * uniquement *. Le code suivant lève une exception.

In [216]: s = pd.Series(range(5))

In [217]: s[-1]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-217-76c3dce40054> in <module>
----> 1 s[-1]

~/work/1/s/pandas/core/series.py in __getitem__(self, key)
   1076         key = com.apply_if_callable(key, self)
   1077         try:
-> 1078             result = self.index.get_value(self, key)
   1079
   1080             if not is_scalar(result):

~/work/1/s/pandas/core/indexes/base.py in get_value(self, series, key)
   4623         k = self._convert_scalar_indexer(k, kind="getitem")
   4624         try:
-> 4625             return self._engine.get_value(s, k, tz=getattr(series.dtype, "tz", None))
   4626         except KeyError as e1:
   4627             if len(self) > 0 and (self.holds_integer() or self.is_boolean()):

~/work/1/s/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()

~/work/1/s/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()

~/work/1/s/pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

~/work/1/s/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()

~/work/1/s/pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()

KeyError: -1

In [218]: df = pd.DataFrame(np.random.randn(5, 4))

In [219]: df
Out[219]:
          0         1         2         3
0 -0.130121 -0.476046  0.759104  0.213379
1 -0.082641  0.448008  0.656420 -1.051443
2  0.594956 -0.151360 -0.069303  1.221431
3 -0.182832  0.791235  0.042745  2.069775
4  1.446552  0.019814 -1.389212 -0.702312

In [220]: df.loc[-2:]
Out[220]:
          0         1         2         3
0 -0.130121 -0.476046  0.759104  0.213379
1 -0.082641  0.448008  0.656420 -1.051443
2  0.594956 -0.151360 -0.069303  1.221431
3 -0.182832  0.791235  0.042745  2.069775
4  1.446552  0.019814 -1.389212 -0.702312

Cette décision délibérée a été prise pour éviter toute ambiguïté et bogues subtils (de nombreux utilisateurs trouvent des bogues lorsqu'ils modifient l'API pour arrêter le «repli» dans l'indexation basée sur la position. J'ai signalé).

L'index non monotone nécessite une correspondance exacte

Si l'index de la Series ou DataFrame augmente ou diminue de manière monotone, il est possible que les bordures des tranches basées sur des étiquettes soient en dehors de la plage de l'index, comme dans l'indexation de tranche de liste Python normale. La monotonie de l'index est ʻis_monotonic_increasing () ](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index.is_monotonic_increasing.html#pandas.Index.is_monotonic_increasing) et [ ʻIs_monotonic_decreasing () Vous pouvez tester avec l'attribut.

In [221]: df = pd.DataFrame(index=[2, 3, 3, 4, 5], columns=['data'], data=list(range(5)))

In [222]: df.index.is_monotonic_increasing
Out[222]: True

#Les lignes 0 et 1 n'existent pas, mais renvoie les lignes 2 et 3 (les deux), 4
In [223]: df.loc[0:4, :]
Out[223]:
   data
2     0
3     1
3     2
4     3

#Un DataFrame vide est renvoyé car la tranche est hors d'index
In [224]: df.loc[13:15, :]
Out[224]:
Empty DataFrame
Columns: [data]
Index: []

D'autre part, si l'index n'est pas monotone, les deux limites de tranche doivent être des valeurs * uniques * de l'index.

In [225]: df = pd.DataFrame(index=[2, 3, 1, 4, 3, 5],
   .....:                   columns=['data'], data=list(range(6)))
   .....:

In [226]: df.index.is_monotonic_increasing
Out[226]: False

#Il n'y a pas de problème car 2 et 4 sont dans l'index
In [227]: df.loc[2:4, :]
Out[227]:
   data
2     0
3     1
1     2
4     3
#0 n'existe pas dans l'index
In [9]: df.loc[0:4, :]
KeyError: 0

#3 n'est pas un label unique
In [11]: df.loc[2:3, :]
KeyError: 'Cannot get right slice bound for non-unique label: 3'

ʻIndex.is_monotonic_increasing et ʻIndex.is_monotonic_decreasing vérifient légèrement si l'index est monotone. Pour voir la monotonie exacte, soit [ʻis_unique () `](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Index.is_unique.html# À combiner avec l'attribut pandas.Index.is_unique).

In [228]: weakly_monotonic = pd.Index(['a', 'b', 'c', 'c'])

In [229]: weakly_monotonic
Out[229]: Index(['a', 'b', 'c', 'c'], dtype='object')

In [230]: weakly_monotonic.is_monotonic_increasing
Out[230]: True

In [231]: weakly_monotonic.is_monotonic_increasing & weakly_monotonic.is_unique
Out[231]: False

Le point final est inclus

Contrairement aux tranches de séquence Python standard qui ne contiennent pas de points de terminaison, les tranches basées sur des étiquettes pandas le font. La raison principale en est qu'il n'est souvent pas facile de déterminer «l'étiquette suivante» ou l'élément suivant après une étiquette particulière dans l'index. Par exemple, considérez les «Seires» suivants.

In [232]: s = pd.Series(np.random.randn(6), index=list('abcdef'))

In [233]: s
Out[233]:
a    0.301379
b    1.240445
c   -0.846068
d   -0.043312
e   -1.658747
f   -0.819549
dtype: float64

Supposons que vous souhaitiez découper de c à e en utilisant un entier. Cela se fait comme suit:

In [234]: s[2:5]
Out[234]:
c   -0.846068
d   -0.043312
e   -1.658747
dtype: float64

Cependant, si vous spécifiez uniquement à partir de «c» et «e», la détermination de l'élément suivant dans l'index peut être un peu compliquée. Par exemple, ce qui suit ne fonctionne pas:

s.loc['c':'e' + 1]

Un cas d'utilisation très courant consiste à spécifier une série chronologique spécifique qui commence et se termine à deux dates spécifiques. Pour rendre cela possible, nous avons conçu la tranche basée sur les étiquettes pour inclure les deux points de terminaison.

In [235]: s.loc['c':'e']
Out[235]:
c   -0.846068
d   -0.043312
e   -1.658747
dtype: float64

C'est certainement "plus pratique que pur", mais soyez prudent si vous vous attendez à ce que les tranches basées sur des étiquettes se comportent exactement comme les tranches d'entiers Python standard.

Indexation qui modifie implicitement le type de série

Différentes opérations d'indexation peuvent changer le dtype de «Series».

In [236]: series1 = pd.Series([1, 2, 3])

In [237]: series1.dtype
Out[237]: dtype('int64')

In [238]: res = series1.reindex([0, 4])

In [239]: res.dtype
Out[239]: dtype('float64')

In [240]: res
Out[240]:
0    1.0
4    NaN
dtype: float64
In [241]: series2 = pd.Series([True])

In [242]: series2.dtype
Out[242]: dtype('bool')

In [243]: res = series2.reindex_like(series1)

In [244]: res.dtype
Out[244]: dtype('O')

In [245]: res
Out[245]:
0    True
1     NaN
2     NaN
dtype: object

En effet, l'opération de (ré) indexation ci-dessus insère implicitement "NaNs" et modifie le "type" en conséquence. Cela peut poser des problèmes lors de l'utilisation de numpy ufuncs comme numpy.logical_and.

Voir ce dernier numéro (https://github.com/pydata/pandas/issues/2388) pour plus d'informations.

Recommended Posts

Guide de l'utilisateur Pandas "Multi-Index / Advanced Index" (Document officiel traduction japonaise)
Guide de l'utilisateur Pandas "Manipulation des données manquantes" (Document officiel de traduction en japonais)
Guide de l'utilisateur de Pandas "Formatage de tableau et tableau croisé dynamique" (Document officiel traduction japonaise)
Guide de l'utilisateur Pandas "fusionner et joindre et concaténer" (document officiel traduction japonaise)
[Français] scikit-learn 0.18 Guide de l'utilisateur 4.5. Projection aléatoire
[Français] scikit-learn 0.18 Guide de l'utilisateur 1.11. Méthode Ensemble
[Français] scikit-learn 0.18 Guide de l'utilisateur 1.15. Régression isotonique
[Français] scikit-learn 0.18 Guide de l'utilisateur 4.2 Extraction de fonctionnalités
[Français] scikit-learn 0.18 Guide de l'utilisateur 1.16. Étalonnage des probabilités
[Français] scikit-learn 0.18 Guide de l'utilisateur 1.13 Sélection des fonctionnalités
[Français] scikit-learn 0.18 Guide de l'utilisateur 3.4. Persistance du modèle
[Français] scikit-learn 0.18 Guide de l'utilisateur 2.8. Estimation de la densité
[Français] scikit-learn 0.18 Guide de l'utilisateur 4.3. Prétraitement des données
Traduction japonaise de documents Apache Spark - Soumission d'applications
[Français] scikit-learn 0.18 Guide de l'utilisateur 4.4. Réduction de dimension non supervisée
[Français] scikit-learn 0.18 Guide de l'utilisateur Table des matières
[Français] scikit-learn 0.18 Guide de l'utilisateur 1.4. Support Vector Machine
Comment utiliser FastAPI ② Advanced - Guide de l'utilisateur
Traduction japonaise de documents Apache Spark - Démarrage rapide
[Google App Engine] Objets utilisateur (traduction en japonais)