データのインデックス付けと選択#
pandasオブジェクトの軸ラベル情報は、多くの目的を果たします。
既知の指標を使用してデータを識別し(つまり、メタデータを提供します)、分析、可視化、対話型コンソール表示に重要です。
自動的および明示的なデータの整合を可能にします。
データセットの部分集合の直感的な取得と設定を可能にします。
このセクションでは、最後の点、つまりpandasオブジェクトの部分集合のスライス、分割、および一般的な取得と設定方法に焦点を当てます。この分野ではSeriesとDataFrameがより多くの開発の注目を集めているため、主にそれらに焦点を当てます。
注記
PythonとNumPyのインデックス演算子[]
および属性演算子.
は、幅広いユースケースにおいてpandasデータ構造への迅速かつ容易なアクセスを提供します。これにより、Pythonの辞書やNumPy配列の扱い方を知っていれば、学ぶことはほとんどないため、対話型作業が直感的になります。しかし、アクセスされるデータの型が事前にわからないため、標準演算子を直接使用すると、最適化に限界があります。本番コードでは、この章で説明されている最適化されたpandasデータアクセス方法を活用することをお勧めします。
警告
設定操作に対してコピーが返されるか参照が返されるかは、コンテキストによって異なる場合があります。これは、chained assignment
と呼ばれることがあり、避けるべきです。ビューとコピーの返却を参照してください。
MultiIndex / 高度なインデックス付けでMultiIndex
およびより高度なインデックス付けのドキュメントを参照してください。
高度な戦略については、クックブックを参照してください。
インデックス付けの異なる選択肢#
オブジェクトの選択には、より明示的な位置ベースのインデックス付けをサポートするために、多くのユーザーからの要望に基づいた追加が行われています。pandasは現在、3種類の多次元軸インデックス付けをサポートしています。
.loc
は主にラベルベースですが、ブール配列でも使用できます。.loc
は、アイテムが見つからない場合にKeyError
を発生させます。許容される入力は次のとおりです。単一のラベル、例:
5
または'a'
(5
はインデックスのラベルとして解釈されます。この使い方は、インデックスに沿った整数位置ではありません)。ラベルのリストまたは配列
['a', 'b', 'c']
。ラベル付きのスライスオブジェクト
'a':'f'
(通常のPythonのスライスとは異なり、インデックスに存在する場合、両方の開始と終了が含められます!ラベルによるスライスと端点は包含的ですを参照してください)。ブール配列(すべての
NA
値はFalse
として扱われます)。1つの引数(呼び出し元のSeriesまたはDataFrame)を持つ関数で、インデックス付けに対して有効な出力を返す関数(上記いずれか)。
上記入力のいずれかである要素を持つ行(および列)インデックスのタプル。
ラベルによる選択で詳細を参照してください。
.iloc
は主に整数位置ベース(軸の長さ-1までの0
から)ですが、ブール配列でも使用できます。.iloc
は、要求されたインデックス付け子が範囲外の場合、IndexError
を発生させますが、スライスインデックス付け子は範囲外インデックス付けを許可します。(これはPython/NumPyのスライスセマンティクスに従います)。許容される入力は次のとおりです。整数、例:
5
。整数のリストまたは配列
[4, 3, 0]
。整数付きのスライスオブジェクト
1:7
。ブール配列(すべての
NA
値はFalse
として扱われます)。1つの引数(呼び出し元のSeriesまたはDataFrame)を持つ関数で、インデックス付けに対して有効な出力を返す関数(上記いずれか)。
上記入力のいずれかである要素を持つ行(および列)インデックスのタプル。
位置による選択、高度なインデックス付け、および高度な階層で詳細を参照してください。
.loc
、.iloc
、および[]
インデックス付けは、callable
をインデックス付け子として受け入れることができます。呼び出し可能オブジェクトによる選択で詳細を参照してください。注記
行(および列)インデックスへのタプルキーのデストラクチャリングは、呼び出し可能オブジェクトが適用される前に発生するため、行と列の両方をインデックス付けする呼び出し可能オブジェクトからタプルを返すことはできません。
多次元軸選択を使用してオブジェクトから値を取得するには、次の表記法を使用します(例として.loc
を使用しますが、以下は.iloc
にも適用されます)。軸アクセッサのいずれかは、ヌルスライス:
にすることができます。仕様から除外された軸は:
であるとみなされます。例:p.loc['a']
はp.loc['a', :]
と同等です。
オブジェクトの種類 |
インデックス付け子 |
---|---|
Series |
|
DataFrame |
|
基本#
前のセクションでデータ構造を紹介した際に述べたように、[]
(Pythonでクラスの動作を実装することに慣れている人にとっては__getitem__
とも呼ばれます)によるインデックス付けの主な機能は、低次元のスライスを選択することです。次の表は、pandasオブジェクトを[]
でインデックス付けしたときの戻り値の種類を示しています。
オブジェクトの種類 |
選択 |
戻り値の種類 |
---|---|---|
Series |
|
スカラー値 |
DataFrame |
|
colnameに対応する |
ここでは、インデックス付け機能を示すために使用する単純な時系列データセットを作成します。
In [1]: dates = pd.date_range('1/1/2000', periods=8)
In [2]: df = pd.DataFrame(np.random.randn(8, 4),
...: index=dates, columns=['A', 'B', 'C', 'D'])
...:
In [3]: df
Out[3]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
注記
特に明記されていない限り、インデックス付け機能のどれも時系列固有のものではありません。
したがって、上記のように、[]
を使用した最も基本的なインデックス付けがあります。
In [4]: s = df['A']
In [5]: s[dates[5]]
Out[5]: -0.6736897080883706
[]
に列のリストを渡して、その順序で列を選択できます。DataFrameに列が含まれていない場合、例外が発生します。複数の列もこのように設定できます。
In [6]: df
Out[6]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
In [7]: df[['B', 'A']] = df[['A', 'B']]
In [8]: df
Out[8]:
A B C D
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632
2000-01-02 -0.173215 1.212112 0.119209 -1.044236
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804
2000-01-04 -0.706771 0.721555 -1.039575 0.271860
2000-01-05 0.567020 -0.424972 0.276232 -1.087401
2000-01-06 0.113648 -0.673690 -1.478427 0.524988
2000-01-07 0.577046 0.404705 -1.715002 -1.039268
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885
これは、列のサブセットにトランスフォーム(インプレース)を適用する場合に役立つ場合があります。
警告
pandasは、.loc
からSeries
とDataFrame
を設定する際にすべての軸を整合させます。
列の整合は値の代入の前に行われるため、これはdf
を変更しません。
In [9]: df[['A', 'B']]
Out[9]:
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
In [10]: df.loc[:, ['B', 'A']] = df[['A', 'B']]
In [11]: df[['A', 'B']]
Out[11]:
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
列の値を入れ替える正しい方法は、生の値を使用することです。
In [12]: df.loc[:, ['B', 'A']] = df[['A', 'B']].to_numpy()
In [13]: df[['A', 'B']]
Out[13]:
A B
2000-01-01 0.469112 -0.282863
2000-01-02 1.212112 -0.173215
2000-01-03 -0.861849 -2.104569
2000-01-04 0.721555 -0.706771
2000-01-05 -0.424972 0.567020
2000-01-06 -0.673690 0.113648
2000-01-07 0.404705 0.577046
2000-01-08 -0.370647 -1.157892
ただし、pandasは.iloc
からSeries
とDataFrame
を設定する際に軸を整合させません。.iloc
は位置によって動作するためです。
列の整合は値の代入の前に実行されないため、これはdf
を変更します。
In [14]: df[['A', 'B']]
Out[14]:
A B
2000-01-01 0.469112 -0.282863
2000-01-02 1.212112 -0.173215
2000-01-03 -0.861849 -2.104569
2000-01-04 0.721555 -0.706771
2000-01-05 -0.424972 0.567020
2000-01-06 -0.673690 0.113648
2000-01-07 0.404705 0.577046
2000-01-08 -0.370647 -1.157892
In [15]: df.iloc[:, [1, 0]] = df[['A', 'B']]
In [16]: df[['A','B']]
Out[16]:
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
属性アクセス#
Series
のインデックスまたはDataFrame
の列に、属性として直接アクセスできます。
In [17]: sa = pd.Series([1, 2, 3], index=list('abc'))
In [18]: dfa = df.copy()
In [19]: sa.b
Out[19]: 2
In [20]: dfa.A
Out[20]:
2000-01-01 -0.282863
2000-01-02 -0.173215
2000-01-03 -2.104569
2000-01-04 -0.706771
2000-01-05 0.567020
2000-01-06 0.113648
2000-01-07 0.577046
2000-01-08 -1.157892
Freq: D, Name: A, dtype: float64
In [21]: sa.a = 5
In [22]: sa
Out[22]:
a 5
b 2
c 3
dtype: int64
In [23]: dfa.A = list(range(len(dfa.index))) # ok if A already exists
In [24]: dfa
Out[24]:
A B C D
2000-01-01 0 0.469112 -1.509059 -1.135632
2000-01-02 1 1.212112 0.119209 -1.044236
2000-01-03 2 -0.861849 -0.494929 1.071804
2000-01-04 3 0.721555 -1.039575 0.271860
2000-01-05 4 -0.424972 0.276232 -1.087401
2000-01-06 5 -0.673690 -1.478427 0.524988
2000-01-07 6 0.404705 -1.715002 -1.039268
2000-01-08 7 -0.370647 -1.344312 0.844885
In [25]: dfa['A'] = list(range(len(dfa.index))) # use this form to create a new column
In [26]: dfa
Out[26]:
A B C D
2000-01-01 0 0.469112 -1.509059 -1.135632
2000-01-02 1 1.212112 0.119209 -1.044236
2000-01-03 2 -0.861849 -0.494929 1.071804
2000-01-04 3 0.721555 -1.039575 0.271860
2000-01-05 4 -0.424972 0.276232 -1.087401
2000-01-06 5 -0.673690 -1.478427 0.524988
2000-01-07 6 0.404705 -1.715002 -1.039268
2000-01-08 7 -0.370647 -1.344312 0.844885
警告
インデックス要素が有効なPython識別子の場合にのみ、このアクセスを使用できます。例:
s.1
は許可されません。有効な識別子の説明はこちらを参照してください。属性は、既存のメソッド名と競合する場合には使用できません。例:
s.min
は許可されませんが、s['min']
は可能です。同様に、属性は、以下のリストのいずれかと競合する場合、使用できません。
index
、major_axis
、minor_axis
、items
。これらのいずれの場合でも、標準的なインデックス付けは引き続き機能します。例:
s['1']
、s['min']
、およびs['index']
は、対応する要素または列にアクセスします。
IPython環境を使用している場合は、タブ補完を使用して、これらのアクセス可能な属性を確認することもできます。
dict
をDataFrame
の行に割り当てることもできます。
In [27]: x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
In [28]: x.iloc[1] = {'x': 9, 'y': 99}
In [29]: x
Out[29]:
x y
0 1 3
1 9 99
2 3 5
属性アクセスを使用して、Seriesの既存の要素またはDataFrameの列を変更できますが、注意が必要です。属性アクセスを使用して新しい列を作成しようとすると、新しい列ではなく新しい属性が作成され、UserWarning
が発生します。
In [30]: df_new = pd.DataFrame({'one': [1., 2., 3.]})
In [31]: df_new.two = [4, 5, 6]
In [32]: df_new
Out[32]:
one
0 1.0
1 2.0
2 3.0
範囲のスライス#
任意の軸に沿った範囲のスライスを行う最も堅牢で一貫性のある方法は、.iloc
メソッドについて詳述する位置による選択セクションで説明されています。ここでは、[]
演算子を使用したスライスのセマンティクスについて説明します。
Seriesでは、構文はndarrayの場合とまったく同じように機能し、値のスライスと対応するラベルが返されます。
In [33]: s[:5]
Out[33]:
2000-01-01 0.469112
2000-01-02 1.212112
2000-01-03 -0.861849
2000-01-04 0.721555
2000-01-05 -0.424972
Freq: D, Name: A, dtype: float64
In [34]: s[::2]
Out[34]:
2000-01-01 0.469112
2000-01-03 -0.861849
2000-01-05 -0.424972
2000-01-07 0.404705
Freq: 2D, Name: A, dtype: float64
In [35]: s[::-1]
Out[35]:
2000-01-08 -0.370647
2000-01-07 0.404705
2000-01-06 -0.673690
2000-01-05 -0.424972
2000-01-04 0.721555
2000-01-03 -0.861849
2000-01-02 1.212112
2000-01-01 0.469112
Freq: -1D, Name: A, dtype: float64
設定も同様に機能することに注意してください。
In [36]: s2 = s.copy()
In [37]: s2[:5] = 0
In [38]: s2
Out[38]:
2000-01-01 0.000000
2000-01-02 0.000000
2000-01-03 0.000000
2000-01-04 0.000000
2000-01-05 0.000000
2000-01-06 -0.673690
2000-01-07 0.404705
2000-01-08 -0.370647
Freq: D, Name: A, dtype: float64
DataFrameでは、[]
内のスライスは**行をスライス**します。これは非常に一般的な操作であるため、主に利便性のために提供されています。
In [39]: df[:3]
Out[39]:
A B C D
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632
2000-01-02 -0.173215 1.212112 0.119209 -1.044236
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804
In [40]: df[::-1]
Out[40]:
A B C D
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885
2000-01-07 0.577046 0.404705 -1.715002 -1.039268
2000-01-06 0.113648 -0.673690 -1.478427 0.524988
2000-01-05 0.567020 -0.424972 0.276232 -1.087401
2000-01-04 -0.706771 0.721555 -1.039575 0.271860
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804
2000-01-02 -0.173215 1.212112 0.119209 -1.044236
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632
ラベルによる選択#
警告
設定操作に対してコピーが返されるか参照が返されるかは、コンテキストによって異なる場合があります。これは、chained assignment
と呼ばれることがあり、避けるべきです。ビューとコピーの返却を参照してください。
警告
.loc
は、インデックスタイプと互換性のない(または変換できない)スライサーが提示された場合、厳格です。たとえば、DatetimeIndex
で整数を使用する場合。これらはTypeError
を発生させます。In [41]: dfl = pd.DataFrame(np.random.randn(5, 4), ....: columns=list('ABCD'), ....: index=pd.date_range('20130101', periods=5)) ....: In [42]: dfl Out[42]: A B C D 2013-01-01 1.075770 -0.109050 1.643563 -1.469388 2013-01-02 0.357021 -0.674600 -1.776904 -0.968914 2013-01-03 -1.294524 0.413738 0.276662 -0.472035 2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061 2013-01-05 0.895717 0.805244 -1.206412 2.565646 In [43]: dfl.loc[2:3] --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[43], line 1 ----> 1 dfl.loc[2:3] File ~/work/pandas/pandas/pandas/core/indexing.py:1191, in _LocationIndexer.__getitem__(self, key) 1189 maybe_callable = com.apply_if_callable(key, self.obj) 1190 maybe_callable = self._check_deprecated_callable_usage(key, maybe_callable) -> 1191 return self._getitem_axis(maybe_callable, axis=axis) File ~/work/pandas/pandas/pandas/core/indexing.py:1411, in _LocIndexer._getitem_axis(self, key, axis) 1409 if isinstance(key, slice): 1410 self._validate_key(key, axis) -> 1411 return self._get_slice_axis(key, axis=axis) 1412 elif com.is_bool_indexer(key): 1413 return self._getbool_axis(key, axis=axis) File ~/work/pandas/pandas/pandas/core/indexing.py:1443, in _LocIndexer._get_slice_axis(self, slice_obj, axis) 1440 return obj.copy(deep=False) 1442 labels = obj._get_axis(axis) -> 1443 indexer = labels.slice_indexer(slice_obj.start, slice_obj.stop, slice_obj.step) 1445 if isinstance(indexer, slice): 1446 return self.obj._slice(indexer, axis=axis) File ~/work/pandas/pandas/pandas/core/indexes/datetimes.py:682, in DatetimeIndex.slice_indexer(self, start, end, step) 674 # GH#33146 if start and end are combinations of str and None and Index is not 675 # monotonic, we can not use Index.slice_indexer because it does not honor the 676 # actual elements, is only searching for start and end 677 if ( 678 check_str_or_none(start) 679 or check_str_or_none(end) 680 or self.is_monotonic_increasing 681 ): --> 682 return Index.slice_indexer(self, start, end, step) 684 mask = np.array(True) 685 in_index = True File ~/work/pandas/pandas/pandas/core/indexes/base.py:6662, in Index.slice_indexer(self, start, end, step) 6618 def slice_indexer( 6619 self, 6620 start: Hashable | None = None, 6621 end: Hashable | None = None, 6622 step: int | None = None, 6623 ) -> slice: 6624 """ 6625 Compute the slice indexer for input labels and step. 6626 (...) 6660 slice(1, 3, None) 6661 """ -> 6662 start_slice, end_slice = self.slice_locs(start, end, step=step) 6664 # return a slice 6665 if not is_scalar(start_slice): File ~/work/pandas/pandas/pandas/core/indexes/base.py:6879, in Index.slice_locs(self, start, end, step) 6877 start_slice = None 6878 if start is not None: -> 6879 start_slice = self.get_slice_bound(start, "left") 6880 if start_slice is None: 6881 start_slice = 0 File ~/work/pandas/pandas/pandas/core/indexes/base.py:6794, in Index.get_slice_bound(self, label, side) 6790 original_label = label 6792 # For datetime indices label may be a string that has to be converted 6793 # to datetime boundary according to its resolution. -> 6794 label = self._maybe_cast_slice_bound(label, side) 6796 # we need to look up the label 6797 try: File ~/work/pandas/pandas/pandas/core/indexes/datetimes.py:642, in DatetimeIndex._maybe_cast_slice_bound(self, label, side) 637 if isinstance(label, dt.date) and not isinstance(label, dt.datetime): 638 # Pandas supports slicing with dates, treated as datetimes at midnight. 639 # https://github.com/pandas-dev/pandas/issues/31501 640 label = Timestamp(label).to_pydatetime() --> 642 label = super()._maybe_cast_slice_bound(label, side) 643 self._data._assert_tzawareness_compat(label) 644 return Timestamp(label) File ~/work/pandas/pandas/pandas/core/indexes/datetimelike.py:378, in DatetimeIndexOpsMixin._maybe_cast_slice_bound(self, label, side) 376 return lower if side == "left" else upper 377 elif not isinstance(label, self._data._recognized_scalars): --> 378 self._raise_invalid_indexer("slice", label) 380 return label File ~/work/pandas/pandas/pandas/core/indexes/base.py:4301, in Index._raise_invalid_indexer(self, form, key, reraise) 4299 if reraise is not lib.no_default: 4300 raise TypeError(msg) from reraise -> 4301 raise TypeError(msg) TypeError: cannot do slice indexing on DatetimeIndex with these indexers [2] of type int
スライス内の文字列のようなものは、インデックスタイプに変換でき、自然なスライスにつながる可能性があります。
In [44]: dfl.loc['20130102':'20130104']
Out[44]:
A B C D
2013-01-02 0.357021 -0.674600 -1.776904 -0.968914
2013-01-03 -1.294524 0.413738 0.276662 -0.472035
2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061
pandasは、**純粋にラベルベースのインデックス付け**を行うためのメソッド一式を提供します。これは厳格な包含ベースのプロトコルです。要求されたすべてのラベルはインデックスに存在する必要があります。そうでない場合、KeyError
が発生します。スライスする場合、開始境界と終了境界の両方が、インデックスに存在する場合は含まれます。整数は有効なラベルですが、位置ではなくラベルを参照します。
.loc
属性は、主要なアクセス方法です。有効な入力は次のとおりです。
単一のラベル、例:
5
または'a'
(5
はインデックスのラベルとして解釈されます。この使い方は、インデックスに沿った整数位置ではありません)。ラベルのリストまたは配列
['a', 'b', 'c']
。ラベル付きのスライスオブジェクト
'a':'f'
(通常のPythonスライスとは異なり、インデックスに存在する場合は、開始と終了の**両方**が含まれます! ラベルによるスライスを参照してください)。ブール値の配列。
呼び出し可能オブジェクト、呼び出し可能オブジェクトによる選択を参照してください。
In [45]: s1 = pd.Series(np.random.randn(6), index=list('abcdef'))
In [46]: s1
Out[46]:
a 1.431256
b 1.340309
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [47]: s1.loc['c':]
Out[47]:
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [48]: s1.loc['b']
Out[48]: 1.3403088497993827
設定も同様に機能することに注意してください。
In [49]: s1.loc['c':] = 0
In [50]: s1
Out[50]:
a 1.431256
b 1.340309
c 0.000000
d 0.000000
e 0.000000
f 0.000000
dtype: float64
DataFrameの場合
In [51]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [52]: df1
Out[52]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
c 1.024180 0.569605 0.875906 -2.211372
d 0.974466 -2.006747 -0.410001 -0.078638
e 0.545952 -1.219217 -1.226825 0.769804
f -1.281247 -0.727707 -0.121306 -0.097883
In [53]: df1.loc[['a', 'b', 'd'], :]
Out[53]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
d 0.974466 -2.006747 -0.410001 -0.078638
ラベルスライスによるアクセス
In [54]: df1.loc['d':, 'A':'C']
Out[54]:
A B C
d 0.974466 -2.006747 -0.410001
e 0.545952 -1.219217 -1.226825
f -1.281247 -0.727707 -0.121306
ラベルを使用してクロスセクションを取得する場合(df.xs('a')
と同等)
In [55]: df1.loc['a']
Out[55]:
A 0.132003
B -0.827317
C -0.076467
D -1.187678
Name: a, dtype: float64
ブール値の配列で値を取得する場合
In [56]: df1.loc['a'] > 0
Out[56]:
A True
B False
C False
D False
Name: a, dtype: bool
In [57]: df1.loc[:, df1.loc['a'] > 0]
Out[57]:
A
a 0.132003
b 1.130127
c 1.024180
d 0.974466
e 0.545952
f -1.281247
ブール値の配列内のNA値はFalse
として伝播します。
In [58]: mask = pd.array([True, False, True, False, pd.NA, False], dtype="boolean")
In [59]: mask
Out[59]:
<BooleanArray>
[True, False, True, False, <NA>, False]
Length: 6, dtype: boolean
In [60]: df1[mask]
Out[60]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
c 1.024180 0.569605 0.875906 -2.211372
値を明示的に取得する場合
# this is also equivalent to ``df1.at['a','A']``
In [61]: df1.loc['a', 'A']
Out[61]: 0.13200317033032932
ラベルによるスライス#
.loc
をスライスで使用する場合、開始ラベルと終了ラベルの両方がインデックスに存在する場合は、2つの間にある要素(それらを含む)が返されます。
In [62]: s = pd.Series(list('abcde'), index=[0, 3, 2, 5, 4])
In [63]: s.loc[3:5]
Out[63]:
3 b
2 c
5 d
dtype: object
2つのうち少なくとも1つが存在しないが、インデックスがソートされており、開始ラベルと終了ラベルと比較できる場合は、2つの間にランクするラベルを選択することで、スライスは期待どおりに機能します。
In [64]: s.sort_index()
Out[64]:
0 a
2 c
3 b
4 e
5 d
dtype: object
In [65]: s.sort_index().loc[1:6]
Out[65]:
2 c
3 b
4 e
5 d
dtype: object
ただし、2つのうち少なくとも1つが存在せず、インデックスがソートされていない場合は、エラーが発生します(そうでないことを行うと、計算コストが高くなるだけでなく、混合タイプのインデックスではあいまいになる可能性があるためです)。上記の例では、s.loc[1:6]
はKeyError
を発生させます。
この動作の理由については、エンドポイントは包含的ですを参照してください。
In [66]: s = pd.Series(list('abcdef'), index=[0, 3, 2, 5, 4, 2])
In [67]: s.loc[3:5]
Out[67]:
3 b
2 c
5 d
dtype: object
また、インデックスに重複ラベルがあり、開始ラベルまたは終了ラベルのどちらかが重複している場合も、エラーが発生します。上記の例では、s.loc[2:5]
はKeyError
を発生させます。
重複ラベルの詳細については、重複ラベルを参照してください。
位置による選択#
警告
設定操作に対してコピーが返されるか参照が返されるかは、コンテキストによって異なる場合があります。これは、chained assignment
と呼ばれることがあり、避けるべきです。ビューとコピーの返却を参照してください。
pandasは、**純粋に整数ベースのインデックス付け**を行うためのメソッド一式を提供します。セマンティクスはPythonとNumPyのスライスに非常に近いです。これらは0-based
インデックス付けです。スライスする場合、開始境界は含まれ、上限は除外されます。たとえ**有効な**ラベルであっても、非整数を用いるとIndexError
が発生します。
.iloc
属性は、主要なアクセス方法です。有効な入力は次のとおりです。
整数、例:
5
。整数のリストまたは配列
[4, 3, 0]
。整数付きのスライスオブジェクト
1:7
。ブール値の配列。
呼び出し可能オブジェクト、呼び出し可能オブジェクトによる選択を参照してください。
行(および列)インデックスのタプル。その要素は上記のいずれかのタイプです。
In [68]: s1 = pd.Series(np.random.randn(5), index=list(range(0, 10, 2)))
In [69]: s1
Out[69]:
0 0.695775
2 0.341734
4 0.959726
6 -1.110336
8 -0.619976
dtype: float64
In [70]: s1.iloc[:3]
Out[70]:
0 0.695775
2 0.341734
4 0.959726
dtype: float64
In [71]: s1.iloc[3]
Out[71]: -1.110336102891167
設定も同様に機能することに注意してください。
In [72]: s1.iloc[:3] = 0
In [73]: s1
Out[73]:
0 0.000000
2 0.000000
4 0.000000
6 -1.110336
8 -0.619976
dtype: float64
DataFrameの場合
In [74]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list(range(0, 12, 2)),
....: columns=list(range(0, 8, 2)))
....:
In [75]: df1
Out[75]:
0 2 4 6
0 0.149748 -0.732339 0.687738 0.176444
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
6 -0.826591 -0.345352 1.314232 0.690579
8 0.995761 2.396780 0.014871 3.357427
10 -0.317441 -1.236269 0.896171 -0.487602
整数スライスによる選択
In [76]: df1.iloc[:3]
Out[76]:
0 2 4 6
0 0.149748 -0.732339 0.687738 0.176444
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [77]: df1.iloc[1:5, 2:4]
Out[77]:
4 6
2 0.301624 -2.179861
4 1.462696 -1.743161
6 1.314232 0.690579
8 0.014871 3.357427
整数リストによる選択
In [78]: df1.iloc[[1, 3, 5], [1, 3]]
Out[78]:
2 6
2 -0.154951 -2.179861
6 -0.345352 0.690579
10 -1.236269 -0.487602
In [79]: df1.iloc[1:3, :]
Out[79]:
0 2 4 6
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [80]: df1.iloc[:, 1:3]
Out[80]:
2 4
0 -0.732339 0.687738
2 -0.154951 0.301624
4 -0.954208 1.462696
6 -0.345352 1.314232
8 2.396780 0.014871
10 -1.236269 0.896171
# this is also equivalent to ``df1.iat[1,1]``
In [81]: df1.iloc[1, 1]
Out[81]: -0.1549507744249032
整数位置を使用してクロスセクションを取得する場合(df.xs(1)
と同等)
In [82]: df1.iloc[1]
Out[82]:
0 0.403310
2 -0.154951
4 0.301624
6 -2.179861
Name: 2, dtype: float64
範囲外の範囲インデックスは、Python/NumPyと同様に適切に処理されます。
# these are allowed in Python/NumPy.
In [83]: x = list('abcdef')
In [84]: x
Out[84]: ['a', 'b', 'c', 'd', 'e', 'f']
In [85]: x[4:10]
Out[85]: ['e', 'f']
In [86]: x[8:10]
Out[86]: []
In [87]: s = pd.Series(x)
In [88]: s
Out[88]:
0 a
1 b
2 c
3 d
4 e
5 f
dtype: object
In [89]: s.iloc[4:10]
Out[89]:
4 e
5 f
dtype: object
In [90]: s.iloc[8:10]
Out[90]: Series([], dtype: object)
範囲外の範囲を使用すると、空の軸(例:空のDataFrameが返される)になる可能性があることに注意してください。
In [91]: dfl = pd.DataFrame(np.random.randn(5, 2), columns=list('AB'))
In [92]: dfl
Out[92]:
A B
0 -0.082240 -2.182937
1 0.380396 0.084844
2 0.432390 1.519970
3 -0.493662 0.600178
4 0.274230 0.132885
In [93]: dfl.iloc[:, 2:3]
Out[93]:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4]
In [94]: dfl.iloc[:, 1:3]
Out[94]:
B
0 -2.182937
1 0.084844
2 1.519970
3 0.600178
4 0.132885
In [95]: dfl.iloc[4:6]
Out[95]:
A B
4 0.27423 0.132885
範囲外の単一インデクサーはIndexError
を発生させます。いずれかの要素が範囲外のインデクサーのリストはIndexError
を発生させます。
In [96]: dfl.iloc[[4, 5, 6]]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
File ~/work/pandas/pandas/pandas/core/indexing.py:1714, in _iLocIndexer._get_list_axis(self, key, axis)
1713 try:
-> 1714 return self.obj._take_with_is_copy(key, axis=axis)
1715 except IndexError as err:
1716 # re-raise with different error message, e.g. test_getitem_ndarray_3d
File ~/work/pandas/pandas/pandas/core/generic.py:4150, in NDFrame._take_with_is_copy(self, indices, axis)
4141 """
4142 Internal version of the `take` method that sets the `_is_copy`
4143 attribute to keep track of the parent dataframe (using in indexing
(...)
4148 See the docstring of `take` for full explanation of the parameters.
4149 """
-> 4150 result = self.take(indices=indices, axis=axis)
4151 # Maybe set copy if we didn't actually change the index.
File ~/work/pandas/pandas/pandas/core/generic.py:4130, in NDFrame.take(self, indices, axis, **kwargs)
4126 indices = np.arange(
4127 indices.start, indices.stop, indices.step, dtype=np.intp
4128 )
-> 4130 new_data = self._mgr.take(
4131 indices,
4132 axis=self._get_block_manager_axis(axis),
4133 verify=True,
4134 )
4135 return self._constructor_from_mgr(new_data, axes=new_data.axes).__finalize__(
4136 self, method="take"
4137 )
File ~/work/pandas/pandas/pandas/core/internals/managers.py:891, in BaseBlockManager.take(self, indexer, axis, verify)
890 n = self.shape[axis]
--> 891 indexer = maybe_convert_indices(indexer, n, verify=verify)
893 new_labels = self.axes[axis].take(indexer)
File ~/work/pandas/pandas/pandas/core/indexers/utils.py:282, in maybe_convert_indices(indices, n, verify)
281 if mask.any():
--> 282 raise IndexError("indices are out-of-bounds")
283 return indices
IndexError: indices are out-of-bounds
The above exception was the direct cause of the following exception:
IndexError Traceback (most recent call last)
Cell In[96], line 1
----> 1 dfl.iloc[[4, 5, 6]]
File ~/work/pandas/pandas/pandas/core/indexing.py:1191, in _LocationIndexer.__getitem__(self, key)
1189 maybe_callable = com.apply_if_callable(key, self.obj)
1190 maybe_callable = self._check_deprecated_callable_usage(key, maybe_callable)
-> 1191 return self._getitem_axis(maybe_callable, axis=axis)
File ~/work/pandas/pandas/pandas/core/indexing.py:1743, in _iLocIndexer._getitem_axis(self, key, axis)
1741 # a list of integers
1742 elif is_list_like_indexer(key):
-> 1743 return self._get_list_axis(key, axis=axis)
1745 # a single integer
1746 else:
1747 key = item_from_zerodim(key)
File ~/work/pandas/pandas/pandas/core/indexing.py:1717, in _iLocIndexer._get_list_axis(self, key, axis)
1714 return self.obj._take_with_is_copy(key, axis=axis)
1715 except IndexError as err:
1716 # re-raise with different error message, e.g. test_getitem_ndarray_3d
-> 1717 raise IndexError("positional indexers are out-of-bounds") from err
IndexError: positional indexers are out-of-bounds
In [97]: dfl.iloc[:, 4]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[97], line 1
----> 1 dfl.iloc[:, 4]
File ~/work/pandas/pandas/pandas/core/indexing.py:1184, in _LocationIndexer.__getitem__(self, key)
1182 if self._is_scalar_access(key):
1183 return self.obj._get_value(*key, takeable=self._takeable)
-> 1184 return self._getitem_tuple(key)
1185 else:
1186 # we by definition only have the 0th axis
1187 axis = self.axis or 0
File ~/work/pandas/pandas/pandas/core/indexing.py:1690, in _iLocIndexer._getitem_tuple(self, tup)
1689 def _getitem_tuple(self, tup: tuple):
-> 1690 tup = self._validate_tuple_indexer(tup)
1691 with suppress(IndexingError):
1692 return self._getitem_lowerdim(tup)
File ~/work/pandas/pandas/pandas/core/indexing.py:966, in _LocationIndexer._validate_tuple_indexer(self, key)
964 for i, k in enumerate(key):
965 try:
--> 966 self._validate_key(k, i)
967 except ValueError as err:
968 raise ValueError(
969 "Location based indexing can only have "
970 f"[{self._valid_types}] types"
971 ) from err
File ~/work/pandas/pandas/pandas/core/indexing.py:1592, in _iLocIndexer._validate_key(self, key, axis)
1590 return
1591 elif is_integer(key):
-> 1592 self._validate_integer(key, axis)
1593 elif isinstance(key, tuple):
1594 # a tuple should already have been caught by this point
1595 # so don't treat a tuple as a valid indexer
1596 raise IndexingError("Too many indexers")
File ~/work/pandas/pandas/pandas/core/indexing.py:1685, in _iLocIndexer._validate_integer(self, key, axis)
1683 len_axis = len(self.obj._get_axis(axis))
1684 if key >= len_axis or key < -len_axis:
-> 1685 raise IndexError("single positional indexer is out-of-bounds")
IndexError: single positional indexer is out-of-bounds
呼び出し可能オブジェクトによる選択#
.loc
、.iloc
、および[]
インデックス付けは、呼び出し可能オブジェクトをインデクサーとして受け入れることができます。呼び出し可能オブジェクトは、1つの引数(呼び出し元のSeriesまたはDataFrame)を持つ関数で、インデックス付けに有効な出力を返す必要があります。
注記
.iloc
インデックス付けでは、呼び出し可能オブジェクトからタプルを返すことはサポートされていません。行と列のインデックスのタプル分解は、呼び出し可能オブジェクトを適用する前に発生するためです。
In [98]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [99]: df1
Out[99]:
A B C D
a -0.023688 2.410179 1.450520 0.206053
b -0.251905 -2.213588 1.063327 1.266143
c 0.299368 -0.863838 0.408204 -1.048089
d -0.025747 -0.988387 0.094055 1.262731
e 1.289997 0.082423 -0.055758 0.536580
f -0.489682 0.369374 -0.034571 -2.484478
In [100]: df1.loc[lambda df: df['A'] > 0, :]
Out[100]:
A B C D
c 0.299368 -0.863838 0.408204 -1.048089
e 1.289997 0.082423 -0.055758 0.536580
In [101]: df1.loc[:, lambda df: ['A', 'B']]
Out[101]:
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
In [102]: df1.iloc[:, lambda df: [0, 1]]
Out[102]:
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
In [103]: df1[lambda df: df.columns[0]]
Out[103]:
a -0.023688
b -0.251905
c 0.299368
d -0.025747
e 1.289997
f -0.489682
Name: A, dtype: float64
Seriesで呼び出し可能インデックスを使用できます。
In [104]: df1['A'].loc[lambda s: s > 0]
Out[104]:
c 0.299368
e 1.289997
Name: A, dtype: float64
これらのメソッド/インデクサーを使用すると、一時変数を使用せずにデータ選択操作をチェーンできます。
In [105]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [106]: (bb.groupby(['year', 'team']).sum(numeric_only=True)
.....: .loc[lambda df: df['r'] > 100])
.....:
Out[106]:
stint g ab r h X2b ... so ibb hbp sh sf gidp
year team ...
2007 CIN 6 379 745 101 203 35 ... 127.0 14.0 1.0 1.0 15.0 18.0
DET 5 301 1062 162 283 54 ... 176.0 3.0 10.0 4.0 8.0 28.0
HOU 4 311 926 109 218 47 ... 212.0 3.0 9.0 16.0 6.0 17.0
LAN 11 413 1021 153 293 61 ... 141.0 8.0 9.0 3.0 8.0 29.0
NYN 13 622 1854 240 509 101 ... 310.0 24.0 23.0 18.0 15.0 48.0
SFN 5 482 1305 198 337 67 ... 188.0 51.0 8.0 16.0 6.0 41.0
TEX 2 198 729 115 200 40 ... 140.0 4.0 5.0 2.0 8.0 16.0
TOR 4 459 1408 187 378 96 ... 265.0 16.0 12.0 4.0 16.0 38.0
[8 rows x 18 columns]
位置ベースのインデックスとラベルベースのインデックスの組み合わせ#
「A」列のインデックスから0番目と2番目の要素を取得する場合は、次のように行うことができます。
In [107]: dfd = pd.DataFrame({'A': [1, 2, 3],
.....: 'B': [4, 5, 6]},
.....: index=list('abc'))
.....:
In [108]: dfd
Out[108]:
A B
a 1 4
b 2 5
c 3 6
In [109]: dfd.loc[dfd.index[[0, 2]], 'A']
Out[109]:
a 1
c 3
Name: A, dtype: int64
これは.iloc
を使用して表現することもできます。インデクサーの位置を明示的に取得し、位置ベースのインデックス付けを使用して要素を選択します。
In [110]: dfd.iloc[[0, 2], dfd.columns.get_loc('A')]
Out[110]:
a 1
c 3
Name: A, dtype: int64
複数のインデクサーを取得するには、.get_indexer
を使用します。
In [111]: dfd.iloc[[0, 2], dfd.columns.get_indexer(['A', 'B'])]
Out[111]:
A B
a 1 4
c 3 6
インデックスの再設定#
見つけられない可能性のある要素を選択するための慣用的な方法は、.reindex()
を使用することです。インデックスの再設定に関するセクションも参照してください。
In [112]: s = pd.Series([1, 2, 3])
In [113]: s.reindex([1, 2, 3])
Out[113]:
1 2.0
2 3.0
3 NaN
dtype: float64
あるいは、有効なキーのみを選択する場合は、次の方法が慣用的で効率的です。選択のdtypeは確実に保持されます。
In [114]: labels = [1, 2, 3]
In [115]: s.loc[s.index.intersection(labels)]
Out[115]:
1 2
2 3
dtype: int64
重複したインデックスがある場合、.reindex()
はエラーを発生させます。
In [116]: s = pd.Series(np.arange(4), index=['a', 'a', 'b', 'c'])
In [117]: labels = ['c', 'd']
In [118]: s.reindex(labels)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[118], line 1
----> 1 s.reindex(labels)
File ~/work/pandas/pandas/pandas/core/series.py:5144, in Series.reindex(self, index, axis, method, copy, level, fill_value, limit, tolerance)
5127 @doc(
5128 NDFrame.reindex, # type: ignore[has-type]
5129 klass=_shared_doc_kwargs["klass"],
(...)
5142 tolerance=None,
5143 ) -> Series:
-> 5144 return super().reindex(
5145 index=index,
5146 method=method,
5147 copy=copy,
5148 level=level,
5149 fill_value=fill_value,
5150 limit=limit,
5151 tolerance=tolerance,
5152 )
File ~/work/pandas/pandas/pandas/core/generic.py:5607, in NDFrame.reindex(self, labels, index, columns, axis, method, copy, level, fill_value, limit, tolerance)
5604 return self._reindex_multi(axes, copy, fill_value)
5606 # perform the reindex on the axes
-> 5607 return self._reindex_axes(
5608 axes, level, limit, tolerance, method, fill_value, copy
5609 ).__finalize__(self, method="reindex")
File ~/work/pandas/pandas/pandas/core/generic.py:5630, in NDFrame._reindex_axes(self, axes, level, limit, tolerance, method, fill_value, copy)
5627 continue
5629 ax = self._get_axis(a)
-> 5630 new_index, indexer = ax.reindex(
5631 labels, level=level, limit=limit, tolerance=tolerance, method=method
5632 )
5634 axis = self._get_axis_number(a)
5635 obj = obj._reindex_with_indexers(
5636 {axis: [new_index, indexer]},
5637 fill_value=fill_value,
5638 copy=copy,
5639 allow_dups=False,
5640 )
File ~/work/pandas/pandas/pandas/core/indexes/base.py:4429, in Index.reindex(self, target, method, level, limit, tolerance)
4426 raise ValueError("cannot handle a non-unique multi-index!")
4427 elif not self.is_unique:
4428 # GH#42568
-> 4429 raise ValueError("cannot reindex on an axis with duplicate labels")
4430 else:
4431 indexer, _ = self.get_indexer_non_unique(target)
ValueError: cannot reindex on an axis with duplicate labels
一般的に、目的のラベルと現在の軸を交差させ、インデックスを再設定できます。
In [119]: s.loc[s.index.intersection(labels)].reindex(labels)
Out[119]:
c 3.0
d NaN
dtype: float64
ただし、結果のインデックスが重複している場合でも、エラーが発生します。
In [120]: labels = ['a', 'd']
In [121]: s.loc[s.index.intersection(labels)].reindex(labels)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[121], line 1
----> 1 s.loc[s.index.intersection(labels)].reindex(labels)
File ~/work/pandas/pandas/pandas/core/series.py:5144, in Series.reindex(self, index, axis, method, copy, level, fill_value, limit, tolerance)
5127 @doc(
5128 NDFrame.reindex, # type: ignore[has-type]
5129 klass=_shared_doc_kwargs["klass"],
(...)
5142 tolerance=None,
5143 ) -> Series:
-> 5144 return super().reindex(
5145 index=index,
5146 method=method,
5147 copy=copy,
5148 level=level,
5149 fill_value=fill_value,
5150 limit=limit,
5151 tolerance=tolerance,
5152 )
File ~/work/pandas/pandas/pandas/core/generic.py:5607, in NDFrame.reindex(self, labels, index, columns, axis, method, copy, level, fill_value, limit, tolerance)
5604 return self._reindex_multi(axes, copy, fill_value)
5606 # perform the reindex on the axes
-> 5607 return self._reindex_axes(
5608 axes, level, limit, tolerance, method, fill_value, copy
5609 ).__finalize__(self, method="reindex")
File ~/work/pandas/pandas/pandas/core/generic.py:5630, in NDFrame._reindex_axes(self, axes, level, limit, tolerance, method, fill_value, copy)
5627 continue
5629 ax = self._get_axis(a)
-> 5630 new_index, indexer = ax.reindex(
5631 labels, level=level, limit=limit, tolerance=tolerance, method=method
5632 )
5634 axis = self._get_axis_number(a)
5635 obj = obj._reindex_with_indexers(
5636 {axis: [new_index, indexer]},
5637 fill_value=fill_value,
5638 copy=copy,
5639 allow_dups=False,
5640 )
File ~/work/pandas/pandas/pandas/core/indexes/base.py:4429, in Index.reindex(self, target, method, level, limit, tolerance)
4426 raise ValueError("cannot handle a non-unique multi-index!")
4427 elif not self.is_unique:
4428 # GH#42568
-> 4429 raise ValueError("cannot reindex on an axis with duplicate labels")
4430 else:
4431 indexer, _ = self.get_indexer_non_unique(target)
ValueError: cannot reindex on an axis with duplicate labels
ランダムサンプルの選択#
sample()
メソッドを使用して、SeriesまたはDataFrameから行または列のランダムな選択を行います。このメソッドはデフォルトで行をサンプリングし、返す行/列の特定の数、または行の割合を受け入れます。
In [122]: s = pd.Series([0, 1, 2, 3, 4, 5])
# When no arguments are passed, returns 1 row.
In [123]: s.sample()
Out[123]:
4 4
dtype: int64
# One may specify either a number of rows:
In [124]: s.sample(n=3)
Out[124]:
0 0
4 4
1 1
dtype: int64
# Or a fraction of the rows:
In [125]: s.sample(frac=0.5)
Out[125]:
5 5
3 3
1 1
dtype: int64
デフォルトでは、sample
は各行を最大で1回返しますが、replace
オプションを使用して置換を伴うサンプリングを行うこともできます。
In [126]: s = pd.Series([0, 1, 2, 3, 4, 5])
# Without replacement (default):
In [127]: s.sample(n=6, replace=False)
Out[127]:
0 0
1 1
5 5
3 3
2 2
4 4
dtype: int64
# With replacement:
In [128]: s.sample(n=6, replace=True)
Out[128]:
0 0
4 4
3 3
2 2
4 4
4 4
dtype: int64
デフォルトでは、各行が選択される確率は均等ですが、行に異なる確率を持たせたい場合は、sample
関数にサンプリングウェイトをweights
として渡すことができます。これらのウェイトはリスト、NumPy配列、またはSeriesにすることができますが、サンプリングするオブジェクトと同じ長さである必要があります。欠損値はウェイト0として処理され、inf値は許可されません。ウェイトの合計が1にならない場合、すべてのウェイトをウェイトの合計で割ることで再正規化されます。例:
In [129]: s = pd.Series([0, 1, 2, 3, 4, 5])
In [130]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4]
In [131]: s.sample(n=3, weights=example_weights)
Out[131]:
5 5
4 4
3 3
dtype: int64
# Weights will be re-normalized automatically
In [132]: example_weights2 = [0.5, 0, 0, 0, 0, 0]
In [133]: s.sample(n=1, weights=example_weights2)
Out[133]:
0 0
dtype: int64
DataFrameに適用する場合、DataFrameの列をサンプリングウェイトとして使用できます(行をサンプリングしていて列をサンプリングしていない場合)。列の名前を文字列として渡すだけで済みます。
In [134]: df2 = pd.DataFrame({'col1': [9, 8, 7, 6],
.....: 'weight_column': [0.5, 0.4, 0.1, 0]})
.....:
In [135]: df2.sample(n=3, weights='weight_column')
Out[135]:
col1 weight_column
1 8 0.4
0 9 0.5
2 7 0.1
sample
では、axis
引数を使用して、行ではなく列をサンプリングすることもできます。
In [136]: df3 = pd.DataFrame({'col1': [1, 2, 3], 'col2': [2, 3, 4]})
In [137]: df3.sample(n=1, axis=1)
Out[137]:
col1
0 1
1 2
2 3
最後に、random_state
引数を使用して、sample
の乱数ジェネレータのシードを設定することもできます。整数(シードとして)またはNumPyのRandomStateオブジェクトのいずれかを受け入れます。
In [138]: df4 = pd.DataFrame({'col1': [1, 2, 3], 'col2': [2, 3, 4]})
# With a given seed, the sample will always draw the same rows.
In [139]: df4.sample(n=2, random_state=2)
Out[139]:
col1 col2
2 3 4
1 2 3
In [140]: df4.sample(n=2, random_state=2)
Out[140]:
col1 col2
2 3 4
1 2 3
拡張による設定#
.loc/[]
操作は、その軸に存在しないキーを設定する場合に拡張を実行できます。
Seriesの場合、これは事実上追加操作です。
In [141]: se = pd.Series([1, 2, 3])
In [142]: se
Out[142]:
0 1
1 2
2 3
dtype: int64
In [143]: se[5] = 5.
In [144]: se
Out[144]:
0 1.0
1 2.0
2 3.0
5 5.0
dtype: float64
DataFrame
は、.loc
を使用していずれかの軸で拡張できます。
In [145]: dfi = pd.DataFrame(np.arange(6).reshape(3, 2),
.....: columns=['A', 'B'])
.....:
In [146]: dfi
Out[146]:
A B
0 0 1
1 2 3
2 4 5
In [147]: dfi.loc[:, 'C'] = dfi.loc[:, 'A']
In [148]: dfi
Out[148]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
これはDataFrame
に対するappend
操作に似ています。
In [149]: dfi.loc[3] = 5
In [150]: dfi
Out[150]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
3 5 5 5
高速なスカラー値の取得と設定#
[]
を用いたインデックス付けは、(単一ラベルへのアクセス、スライシング、ブールインデックスなど)多くのケースに対応する必要があるため、何が要求されているかを判断するために多少のオーバーヘッドがあります。スカラー値にアクセスするだけの場合、最も高速な方法は、すべてデータ構造に実装されている at
メソッドと iat
メソッドを使用することです。
loc
と同様に、at
は**ラベル**ベースのスカラー検索を提供し、iat
はiloc
と同様に**整数**ベースの検索を提供します。
In [151]: s.iat[5]
Out[151]: 5
In [152]: df.at[dates[5], 'A']
Out[152]: 0.1136484096888855
In [153]: df.iat[3, 0]
Out[153]: -0.7067711336300845
これらのインデクサを使用して設定することもできます。
In [154]: df.at[dates[5], 'E'] = 7
In [155]: df.iat[3, 0] = 7
インデクサが見つからない場合、上記のように at
はオブジェクトをインプレースで拡大する可能性があります。
In [156]: df.at[dates[-1] + pd.Timedelta('1 day'), 0] = 7
In [157]: df
Out[157]:
A B C D E 0
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632 NaN NaN
2000-01-02 -0.173215 1.212112 0.119209 -1.044236 NaN NaN
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804 NaN NaN
2000-01-04 7.000000 0.721555 -1.039575 0.271860 NaN NaN
2000-01-05 0.567020 -0.424972 0.276232 -1.087401 NaN NaN
2000-01-06 0.113648 -0.673690 -1.478427 0.524988 7.0 NaN
2000-01-07 0.577046 0.404705 -1.715002 -1.039268 NaN NaN
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885 NaN NaN
2000-01-09 NaN NaN NaN NaN NaN 7.0
ブールインデックス#
もう一つの一般的な操作は、ブールベクトルを使用してデータをフィルタリングすることです。演算子は、or
には |
、and
には &
、not
には ~
です。これらの演算子は、括弧を使用してグループ化する**必要**があります。なぜなら、デフォルトではPythonはdf['A'] > 2 & df['B'] < 3
のような式を df['A'] > (2 & df['B']) < 3
として評価しますが、望ましい評価順序は (df['A'] > 2) & (df['B'] < 3)
であるためです。
ブールベクトルを使用してSeriesをインデックス付けすることは、NumPyのndarrayとまったく同じように機能します。
In [158]: s = pd.Series(range(-3, 4))
In [159]: s
Out[159]:
0 -3
1 -2
2 -1
3 0
4 1
5 2
6 3
dtype: int64
In [160]: s[s > 0]
Out[160]:
4 1
5 2
6 3
dtype: int64
In [161]: s[(s < -1) | (s > 0.5)]
Out[161]:
0 -3
1 -2
4 1
5 2
6 3
dtype: int64
In [162]: s[~(s < 0)]
Out[162]:
3 0
4 1
5 2
6 3
dtype: int64
DataFrameのインデックスと同じ長さのブールベクトルを使用して、DataFrameから行を選択できます(たとえば、DataFrameの列の1つから導き出されたものなど)。
In [163]: df[df['A'] > 0]
Out[163]:
A B C D E 0
2000-01-04 7.000000 0.721555 -1.039575 0.271860 NaN NaN
2000-01-05 0.567020 -0.424972 0.276232 -1.087401 NaN NaN
2000-01-06 0.113648 -0.673690 -1.478427 0.524988 7.0 NaN
2000-01-07 0.577046 0.404705 -1.715002 -1.039268 NaN NaN
リスト内包表記とSeriesのmap
メソッドを使用して、より複雑な条件を作成することもできます。
In [164]: df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
.....: 'b': ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
.....: 'c': np.random.randn(7)})
.....:
# only want 'two' or 'three'
In [165]: criterion = df2['a'].map(lambda x: x.startswith('t'))
In [166]: df2[criterion]
Out[166]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# equivalent but slower
In [167]: df2[[x.startswith('t') for x in df2['a']]]
Out[167]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# Multiple criteria
In [168]: df2[criterion & (df2['b'] == 'x')]
Out[168]:
a b c
3 three x 0.361719
選択メソッドラベルによる選択、位置による選択、および高度なインデックス付けを使用すると、ブールベクトルと他のインデックス式を組み合わせて、複数の軸に沿って選択できます。
In [169]: df2.loc[criterion & (df2['b'] == 'x'), 'b':'c']
Out[169]:
b c
3 x 0.361719
警告
iloc
は2種類のブールインデックス付けをサポートしています。インデクサがブールSeries
の場合、エラーが発生します。たとえば、次の例では、df.iloc[s.values, 1]
は問題ありません。ブールインデクサは配列です。しかし、df.iloc[s, 1]
はValueError
を発生させます。
In [170]: df = pd.DataFrame([[1, 2], [3, 4], [5, 6]],
.....: index=list('abc'),
.....: columns=['A', 'B'])
.....:
In [171]: s = (df['A'] > 2)
In [172]: s
Out[172]:
a False
b True
c True
Name: A, dtype: bool
In [173]: df.loc[s, 'B']
Out[173]:
b 4
c 6
Name: B, dtype: int64
In [174]: df.iloc[s.values, 1]
Out[174]:
b 4
c 6
Name: B, dtype: int64
isinを使用したインデックス付け#
Series
のisin()
メソッドは、渡されたリストにSeries
の要素が存在する場所で真となるブールベクトルを返します。これにより、1つ以上の列に目的の値を持つ行を選択できます。
In [175]: s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64')
In [176]: s
Out[176]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [177]: s.isin([2, 4, 6])
Out[177]:
4 False
3 False
2 True
1 False
0 True
dtype: bool
In [178]: s[s.isin([2, 4, 6])]
Out[178]:
2 2
0 4
dtype: int64
同じメソッドはIndex
オブジェクトでも使用でき、検索するラベルのうち実際に存在するラベルがどれであるかわからない場合に便利です。
In [179]: s[s.index.isin([2, 4, 6])]
Out[179]:
4 0
2 2
dtype: int64
# compare it to the following
In [180]: s.reindex([2, 4, 6])
Out[180]:
2 2.0
4 0.0
6 NaN
dtype: float64
さらに、MultiIndex
では、メンバーシップチェックで使用する別々のレベルを選択できます。
In [181]: s_mi = pd.Series(np.arange(6),
.....: index=pd.MultiIndex.from_product([[0, 1], ['a', 'b', 'c']]))
.....:
In [182]: s_mi
Out[182]:
0 a 0
b 1
c 2
1 a 3
b 4
c 5
dtype: int64
In [183]: s_mi.iloc[s_mi.index.isin([(1, 'a'), (2, 'b'), (0, 'c')])]
Out[183]:
0 c 2
1 a 3
dtype: int64
In [184]: s_mi.iloc[s_mi.index.isin(['a', 'c', 'e'], level=1)]
Out[184]:
0 a 0
c 2
1 a 3
c 5
dtype: int64
DataFrameにもisin()
メソッドがあります。isin
を呼び出す際には、配列または辞書として値の集合を渡します。値が配列の場合、isin
は元のDataFrameと同じ形状のブール値のDataFrameを返し、要素が値のシーケンスに含まれる場合はTrueになります。
In [185]: df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
.....: 'ids2': ['a', 'n', 'c', 'n']})
.....:
In [186]: values = ['a', 'b', 1, 3]
In [187]: df.isin(values)
Out[187]:
vals ids ids2
0 True True True
1 False True False
2 True False False
3 False False False
多くの場合、特定の値を特定の列と一致させる必要があります。値をキーが列で、値がチェックするアイテムのリストである辞書にするだけです。
In [188]: values = {'ids': ['a', 'b'], 'vals': [1, 3]}
In [189]: df.isin(values)
Out[189]:
vals ids ids2
0 True True False
1 False True False
2 True False False
3 False False False
値が元のDataFrameに含まれていないブール値のDataFrameを返すには、~
演算子を使用します。
In [190]: values = {'ids': ['a', 'b'], 'vals': [1, 3]}
In [191]: ~df.isin(values)
Out[191]:
vals ids ids2
0 False False True
1 True False True
2 False True True
3 True True True
DataFrameのisin
とany()
およびall()
メソッドを組み合わせて、特定の条件を満たすデータのサブセットをすばやく選択します。各列が独自の条件を満たす行を選択するには
In [192]: values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
In [193]: row_mask = df.isin(values).all(1)
In [194]: df[row_mask]
Out[194]:
vals ids ids2
0 1 a a
where()
メソッドとマスキング#
ブールベクトルを使用してSeriesから値を選択すると、一般的にデータのサブセットが返されます。選択結果が元のデータと同じ形状になるようにするには、Series
とDataFrame
のwhere
メソッドを使用できます。
選択された行のみを返すには
In [195]: s[s > 0]
Out[195]:
3 1
2 2
1 3
0 4
dtype: int64
元のデータと同じ形状のSeriesを返すには
In [196]: s.where(s > 0)
Out[196]:
4 NaN
3 1.0
2 2.0
1 3.0
0 4.0
dtype: float64
ブール条件を使用してDataFrameから値を選択すると、入力データの形状も保持されるようになりました。where
は実装として内部で使用されます。以下のコードはdf.where(df < 0)
と同等です。
In [197]: dates = pd.date_range('1/1/2000', periods=8)
In [198]: df = pd.DataFrame(np.random.randn(8, 4),
.....: index=dates, columns=['A', 'B', 'C', 'D'])
.....:
In [199]: df[df < 0]
Out[199]:
A B C D
2000-01-01 -2.104139 -1.309525 NaN NaN
2000-01-02 -0.352480 NaN -1.192319 NaN
2000-01-03 -0.864883 NaN -0.227870 NaN
2000-01-04 NaN -1.222082 NaN -1.233203
2000-01-05 NaN -0.605656 -1.169184 NaN
2000-01-06 NaN -0.948458 NaN -0.684718
2000-01-07 -2.670153 -0.114722 NaN -0.048048
2000-01-08 NaN NaN -0.048788 -0.808838
さらに、where
は、条件がFalseである値の置換に使用するオプションのother
引数を取ります。
In [200]: df.where(df < 0, -df)
Out[200]:
A B C D
2000-01-01 -2.104139 -1.309525 -0.485855 -0.245166
2000-01-02 -0.352480 -0.390389 -1.192319 -1.655824
2000-01-03 -0.864883 -0.299674 -0.227870 -0.281059
2000-01-04 -0.846958 -1.222082 -0.600705 -1.233203
2000-01-05 -0.669692 -0.605656 -1.169184 -0.342416
2000-01-06 -0.868584 -0.948458 -2.297780 -0.684718
2000-01-07 -2.670153 -0.114722 -0.168904 -0.048048
2000-01-08 -0.801196 -1.392071 -0.048788 -0.808838
ブール条件に基づいて値を設定したい場合があります。これは次のように直感的に行うことができます。
In [201]: s2 = s.copy()
In [202]: s2[s2 < 0] = 0
In [203]: s2
Out[203]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [204]: df2 = df.copy()
In [205]: df2[df2 < 0] = 0
In [206]: df2
Out[206]:
A B C D
2000-01-01 0.000000 0.000000 0.485855 0.245166
2000-01-02 0.000000 0.390389 0.000000 1.655824
2000-01-03 0.000000 0.299674 0.000000 0.281059
2000-01-04 0.846958 0.000000 0.600705 0.000000
2000-01-05 0.669692 0.000000 0.000000 0.342416
2000-01-06 0.868584 0.000000 2.297780 0.000000
2000-01-07 0.000000 0.000000 0.168904 0.000000
2000-01-08 0.801196 1.392071 0.000000 0.000000
where
はデータの変更されたコピーを返します。
注記
DataFrame.where()
のシグネチャはnumpy.where()
とは異なります。df1.where(m, df2)
はおおよそnp.where(m, df1, df2)
と同等です。
In [207]: df.where(df < 0, -df) == np.where(df < 0, df, -df)
Out[207]:
A B C D
2000-01-01 True True True True
2000-01-02 True True True True
2000-01-03 True True True True
2000-01-04 True True True True
2000-01-05 True True True True
2000-01-06 True True True True
2000-01-07 True True True True
2000-01-08 True True True True
整列
さらに、where
は入力ブール条件(ndarrayまたはDataFrame)を整列するため、設定による部分的な選択が可能です。これは.loc
による部分的な設定と類似していますが(軸ラベルではなく内容に対して)。
In [208]: df2 = df.copy()
In [209]: df2[df2[1:4] > 0] = 3
In [210]: df2
Out[210]:
A B C D
2000-01-01 -2.104139 -1.309525 0.485855 0.245166
2000-01-02 -0.352480 3.000000 -1.192319 3.000000
2000-01-03 -0.864883 3.000000 -0.227870 3.000000
2000-01-04 3.000000 -1.222082 3.000000 -1.233203
2000-01-05 0.669692 -0.605656 -1.169184 0.342416
2000-01-06 0.868584 -0.948458 2.297780 -0.684718
2000-01-07 -2.670153 -0.114722 0.168904 -0.048048
2000-01-08 0.801196 1.392071 -0.048788 -0.808838
where
はwhere
を実行する際に、入力の整列を行うaxis
およびlevel
パラメータを受け入れることができます。
In [211]: df2 = df.copy()
In [212]: df2.where(df2 > 0, df2['A'], axis='index')
Out[212]:
A B C D
2000-01-01 -2.104139 -2.104139 0.485855 0.245166
2000-01-02 -0.352480 0.390389 -0.352480 1.655824
2000-01-03 -0.864883 0.299674 -0.864883 0.281059
2000-01-04 0.846958 0.846958 0.600705 0.846958
2000-01-05 0.669692 0.669692 0.669692 0.342416
2000-01-06 0.868584 0.868584 2.297780 0.868584
2000-01-07 -2.670153 -2.670153 0.168904 -2.670153
2000-01-08 0.801196 1.392071 0.801196 0.801196
これは次のものと同等ですが(ただし、それよりも高速です)。
In [213]: df2 = df.copy()
In [214]: df.apply(lambda x, y: x.where(x > 0, y), y=df['A'])
Out[214]:
A B C D
2000-01-01 -2.104139 -2.104139 0.485855 0.245166
2000-01-02 -0.352480 0.390389 -0.352480 1.655824
2000-01-03 -0.864883 0.299674 -0.864883 0.281059
2000-01-04 0.846958 0.846958 0.600705 0.846958
2000-01-05 0.669692 0.669692 0.669692 0.342416
2000-01-06 0.868584 0.868584 2.297780 0.868584
2000-01-07 -2.670153 -2.670153 0.168904 -2.670153
2000-01-08 0.801196 1.392071 0.801196 0.801196
where
は、条件とother
引数として呼び出し可能なオブジェクトを受け入れることができます。関数は、1つの引数(呼び出し元のSeriesまたはDataFrame)を持ち、条件とother
引数として有効な出力を返す必要があります。
In [215]: df3 = pd.DataFrame({'A': [1, 2, 3],
.....: 'B': [4, 5, 6],
.....: 'C': [7, 8, 9]})
.....:
In [216]: df3.where(lambda x: x > 4, lambda x: x + 10)
Out[216]:
A B C
0 11 14 7
1 12 5 8
2 13 6 9
マスク#
mask()
はwhere
の逆ブール演算です。
In [217]: s.mask(s >= 0)
Out[217]:
4 NaN
3 NaN
2 NaN
1 NaN
0 NaN
dtype: float64
In [218]: df.mask(df >= 0)
Out[218]:
A B C D
2000-01-01 -2.104139 -1.309525 NaN NaN
2000-01-02 -0.352480 NaN -1.192319 NaN
2000-01-03 -0.864883 NaN -0.227870 NaN
2000-01-04 NaN -1.222082 NaN -1.233203
2000-01-05 NaN -0.605656 -1.169184 NaN
2000-01-06 NaN -0.948458 NaN -0.684718
2000-01-07 -2.670153 -0.114722 NaN -0.048048
2000-01-08 NaN NaN -0.048788 -0.808838
numpy()
を使用した条件付き拡大による設定#
where()
の代替手段としてnumpy.where()
を使用できます。新しい列の設定と組み合わせて使用することで、値が条件付きで決定されるDataFrameを拡大できます。
次のDataFrameで2つの選択肢から選択する必要があるとします。そして、2番目の列に「Z」がある場合に新しい列colorを「green」に設定したいとします。次のように行うことができます。
In [219]: df = pd.DataFrame({'col1': list('ABBC'), 'col2': list('ZZXY')})
In [220]: df['color'] = np.where(df['col2'] == 'Z', 'green', 'red')
In [221]: df
Out[221]:
col1 col2 color
0 A Z green
1 B Z green
2 B X red
3 C Y red
複数の条件がある場合は、numpy.select()
を使用してこれを実現できます。3つの条件に対応して3つの色の選択肢があり、4番目の色はフォールバックとして使用する場合、次のように行うことができます。
In [222]: conditions = [
.....: (df['col2'] == 'Z') & (df['col1'] == 'A'),
.....: (df['col2'] == 'Z') & (df['col1'] == 'B'),
.....: (df['col1'] == 'B')
.....: ]
.....:
In [223]: choices = ['yellow', 'blue', 'purple']
In [224]: df['color'] = np.select(conditions, choices, default='black')
In [225]: df
Out[225]:
col1 col2 color
0 A Z yellow
1 B Z blue
2 B X purple
3 C Y black
query()
メソッド#
DataFrame
オブジェクトには、式を使用して選択を可能にするquery()
メソッドがあります。
列b
の値が列a
とc
の値の間にあるフレームの値を取得できます。例:
In [226]: n = 10
In [227]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [228]: df
Out[228]:
a b c
0 0.438921 0.118680 0.863670
1 0.138138 0.577363 0.686602
2 0.595307 0.564592 0.520630
3 0.913052 0.926075 0.616184
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
6 0.792342 0.216974 0.564056
7 0.397890 0.454131 0.915716
8 0.074315 0.437913 0.019794
9 0.559209 0.502065 0.026437
# pure python
In [229]: df[(df['a'] < df['b']) & (df['b'] < df['c'])]
Out[229]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
# query
In [230]: df.query('(a < b) & (b < c)')
Out[230]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
同じことを行いますが、列名a
がない場合は、名前付きインデックスにフォールバックします。
In [231]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
In [232]: df.index.name = 'a'
In [233]: df
Out[233]:
b c
a
0 0 4
1 0 1
2 3 4
3 4 3
4 1 4
5 0 3
6 0 1
7 3 4
8 2 3
9 1 1
In [234]: df.query('a < b and b < c')
Out[234]:
b c
a
2 3 4
代わりにインデックスに名前を付けたくない場合、または付けられない場合は、クエリ式でindex
という名前を使用できます。
In [235]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
In [236]: df
Out[236]:
b c
0 3 1
1 3 0
2 5 6
3 5 2
4 7 4
5 0 1
6 2 5
7 0 1
8 6 0
9 7 9
In [237]: df.query('index < b < c')
Out[237]:
b c
2 5 6
注記
インデックスの名前が列名と重複している場合、列名が優先されます。例:
In [238]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})
In [239]: df.index.name = 'a'
In [240]: df.query('a > 2') # uses the column 'a', not the index
Out[240]:
a
a
1 3
3 3
特別な識別子「index」を使用することで、クエリ式でインデックスを引き続き使用できます。
In [241]: df.query('index > 2')
Out[241]:
a
a
3 3
4 2
何らかの理由でindex
という名前の列がある場合、インデックスはilevel_0
としても参照できますが、この時点で列名をより曖昧性の少ない名前に変更することを検討する必要があります。
MultiIndex
query()
構文#
MultiIndex
を持つDataFrame
のレベルを、フレーム内の列であるかのように使用することもできます。
In [242]: n = 10
In [243]: colors = np.random.choice(['red', 'green'], size=n)
In [244]: foods = np.random.choice(['eggs', 'ham'], size=n)
In [245]: colors
Out[245]:
array(['red', 'red', 'red', 'green', 'green', 'green', 'green', 'green',
'green', 'green'], dtype='<U5')
In [246]: foods
Out[246]:
array(['ham', 'ham', 'eggs', 'eggs', 'eggs', 'ham', 'ham', 'eggs', 'eggs',
'eggs'], dtype='<U4')
In [247]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [248]: df = pd.DataFrame(np.random.randn(n, 2), index=index)
In [249]: df
Out[249]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [250]: df.query('color == "red"')
Out[250]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
MultiIndex
のレベルに名前がない場合は、特別な名前を使用して参照できます。
In [251]: df.index.names = [None, None]
In [252]: df
Out[252]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [253]: df.query('ilevel_0 == "red"')
Out[253]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
慣例ではilevel_0
は「インデックスレベル0」を意味し、index
の0番目のレベルを表します。
query()
の使用例#
query()
の使用例としては、共通の列名(またはインデックスレベル/名)のサブセットを持つDataFrame
オブジェクトのコレクションがある場合が挙げられます。クエリ対象のフレームを指定しなくても、両方のフレームに同じクエリを渡すことができます。
In [254]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [255]: df
Out[255]:
a b c
0 0.224283 0.736107 0.139168
1 0.302827 0.657803 0.713897
2 0.611185 0.136624 0.984960
3 0.195246 0.123436 0.627712
4 0.618673 0.371660 0.047902
5 0.480088 0.062993 0.185760
6 0.568018 0.483467 0.445289
7 0.309040 0.274580 0.587101
8 0.258993 0.477769 0.370255
9 0.550459 0.840870 0.304611
In [256]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
In [257]: df2
Out[257]:
a b c
0 0.357579 0.229800 0.596001
1 0.309059 0.957923 0.965663
2 0.123102 0.336914 0.318616
3 0.526506 0.323321 0.860813
4 0.518736 0.486514 0.384724
5 0.190804 0.505723 0.614533
6 0.891939 0.623977 0.676639
7 0.480559 0.378528 0.460858
8 0.420223 0.136404 0.141295
9 0.732206 0.419540 0.604675
10 0.604466 0.848974 0.896165
11 0.589168 0.920046 0.732716
In [258]: expr = '0.0 <= a <= c <= 0.5'
In [259]: map(lambda frame: frame.query(expr), [df, df2])
Out[259]: <map at 0x7fac68ab7580>
query()
:Pythonとpandasの構文比較#
完全なNumPyライクな構文
In [260]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
In [261]: df
Out[261]:
a b c
0 7 8 9
1 1 0 7
2 2 7 2
3 6 2 2
4 2 6 3
5 3 8 2
6 1 7 2
7 5 1 5
8 9 8 0
9 1 5 0
In [262]: df.query('(a < b) & (b < c)')
Out[262]:
a b c
0 7 8 9
In [263]: df[(df['a'] < df['b']) & (df['b'] < df['c'])]
Out[263]:
a b c
0 7 8 9
(比較演算子は&
と|
よりも強くバインドされるため)括弧を削除することで、少し見やすくなります。
In [264]: df.query('a < b & b < c')
Out[264]:
a b c
0 7 8 9
記号の代わりに英語を使用します。
In [265]: df.query('a < b and b < c')
Out[265]:
a b c
0 7 8 9
紙に書く方法に非常に近いです。
In [266]: df.query('a < b < c')
Out[266]:
a b c
0 7 8 9
in
とnot in
演算子#
query()
は、Pythonのin
とnot in
比較演算子の特殊な使用方法もサポートしており、Series
またはDataFrame
のisin
メソッドを呼び出すための簡潔な構文を提供します。
# get all rows where columns "a" and "b" have overlapping values
In [267]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
.....: 'c': np.random.randint(5, size=12),
.....: 'd': np.random.randint(9, size=12)})
.....:
In [268]: df
Out[268]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [269]: df.query('a in b')
Out[269]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
# How you'd do it in pure Python
In [270]: df[df['a'].isin(df['b'])]
Out[270]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
In [271]: df.query('a not in b')
Out[271]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# pure Python
In [272]: df[~df['a'].isin(df['b'])]
Out[272]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
これを他の式と組み合わせることで、非常に簡潔なクエリを作成できます。
# rows where cols a and b have overlapping values
# and col c's values are less than col d's
In [273]: df.query('a in b and c < d')
Out[273]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
# pure Python
In [274]: df[df['b'].isin(df['a']) & (df['c'] < df['d'])]
Out[274]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
10 f c 0 6
11 f c 1 2
注記
numexpr
にはこの演算の等価物が存在しないため、in
とnot in
はPythonで評価されます。ただし、**in
/not in
式自体のみ**が通常のPythonで評価されます。たとえば、式
df.query('a in b + c + d')
(b + c + d)
はnumexpr
によって評価され、その後in
演算が通常のPythonで評価されます。一般的に、numexpr
を使用して評価できる演算はすべて評価されます。
list
オブジェクトでの==
演算子の特殊な使用方法#
==
/!=
を使用して値のlist
を列と比較する方法は、in
/not in
と同様です。
In [275]: df.query('b == ["a", "b", "c"]')
Out[275]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# pure Python
In [276]: df[df['b'].isin(["a", "b", "c"])]
Out[276]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [277]: df.query('c == [1, 2]')
Out[277]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [278]: df.query('c != [1, 2]')
Out[278]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# using in/not in
In [279]: df.query('[1, 2] in c')
Out[279]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [280]: df.query('[1, 2] not in c')
Out[280]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# pure Python
In [281]: df[df['c'].isin([1, 2])]
Out[281]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
ブール演算子#
not
という単語または~
演算子を使用して、ブール式を否定できます。
In [282]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [283]: df['bools'] = np.random.rand(len(df)) > 0.5
In [284]: df.query('~bools')
Out[284]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [285]: df.query('not bools')
Out[285]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [286]: df.query('not bools') == df[~df['bools']]
Out[286]:
a b c bools
2 True True True True
7 True True True True
8 True True True True
もちろん、式は任意に複雑にすることもできます。
# short query syntax
In [287]: shorter = df.query('a < b < c and (not bools) or bools > 2')
# equivalent in pure Python
In [288]: longer = df[(df['a'] < df['b'])
.....: & (df['b'] < df['c'])
.....: & (~df['bools'])
.....: | (df['bools'] > 2)]
.....:
In [289]: shorter
Out[289]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [290]: longer
Out[290]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [291]: shorter == longer
Out[291]:
a b c bools
7 True True True True
query()
のパフォーマンス#
numexpr
を使用するDataFrame.query()
は、大規模なフレームではPythonよりもわずかに高速です。

DataFrame.query()
でnumexpr
エンジンを使用することによるパフォーマンス上の利点は、フレームの行数が約10万行を超えている場合にのみ見られます。
このプロットは、numpy.random.randn()
を使用して生成された浮動小数点値をそれぞれ含む3つの列を持つDataFrame
を使用して作成されました。
In [292]: df = pd.DataFrame(np.random.randn(8, 4),
.....: index=dates, columns=['A', 'B', 'C', 'D'])
.....:
In [293]: df2 = df.copy()
重複データ#
DataFrameで重複行を特定して削除する場合、duplicated
とdrop_duplicates
という2つのメソッドが役立ちます。それぞれに、重複行の特定に使用する列を引数として渡します。
duplicated
は、行数を長さとするブールベクトルを返し、行が重複しているかどうかを示します。drop_duplicates
は重複行を削除します。
デフォルトでは、重複セットで最初に観測された行が一意と見なされますが、各メソッドには、保持するターゲットを指定するためのkeep
パラメータがあります。
keep='first'
(デフォルト):最初の出現を除く重複をマーク/削除します。keep='last'
:最後の出現を除く重複をマーク/削除します。keep=False
:すべての重複をマーク/削除します。
In [294]: df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'two', 'two', 'three', 'four'],
.....: 'b': ['x', 'y', 'x', 'y', 'x', 'x', 'x'],
.....: 'c': np.random.randn(7)})
.....:
In [295]: df2
Out[295]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [296]: df2.duplicated('a')
Out[296]:
0 False
1 True
2 False
3 True
4 True
5 False
6 False
dtype: bool
In [297]: df2.duplicated('a', keep='last')
Out[297]:
0 True
1 False
2 True
3 True
4 False
5 False
6 False
dtype: bool
In [298]: df2.duplicated('a', keep=False)
Out[298]:
0 True
1 True
2 True
3 True
4 True
5 False
6 False
dtype: bool
In [299]: df2.drop_duplicates('a')
Out[299]:
a b c
0 one x -1.067137
2 two x -0.211056
5 three x -1.964475
6 four x 1.298329
In [300]: df2.drop_duplicates('a', keep='last')
Out[300]:
a b c
1 one y 0.309500
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [301]: df2.drop_duplicates('a', keep=False)
Out[301]:
a b c
5 three x -1.964475
6 four x 1.298329
また、重複を識別するための列のリストを渡すこともできます。
In [302]: df2.duplicated(['a', 'b'])
Out[302]:
0 False
1 False
2 False
3 False
4 True
5 False
6 False
dtype: bool
In [303]: df2.drop_duplicates(['a', 'b'])
Out[303]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
5 three x -1.964475
6 four x 1.298329
インデックス値で重複を削除するには、Index.duplicated
を使用してスライスを実行します。keep
パラメータには、同じオプションセットを使用できます。
In [304]: df3 = pd.DataFrame({'a': np.arange(6),
.....: 'b': np.random.randn(6)},
.....: index=['a', 'a', 'b', 'c', 'b', 'a'])
.....:
In [305]: df3
Out[305]:
a b
a 0 1.440455
a 1 2.456086
b 2 1.038402
c 3 -0.894409
b 4 0.683536
a 5 3.082764
In [306]: df3.index.duplicated()
Out[306]: array([False, True, False, False, True, True])
In [307]: df3[~df3.index.duplicated()]
Out[307]:
a b
a 0 1.440455
b 2 1.038402
c 3 -0.894409
In [308]: df3[~df3.index.duplicated(keep='last')]
Out[308]:
a b
c 3 -0.894409
b 4 0.683536
a 5 3.082764
In [309]: df3[~df3.index.duplicated(keep=False)]
Out[309]:
a b
c 3 -0.894409
辞書のようなget()
メソッド#
SeriesとDataFrameのそれぞれに、デフォルト値を返すことができるget
メソッドがあります。
In [310]: s = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
In [311]: s.get('a') # equivalent to s['a']
Out[311]: 1
In [312]: s.get('x', default=-1)
Out[312]: -1
インデックス/列ラベルによる値の参照#
行ラベルと列ラベルのシーケンスを指定して値のセットを抽出したい場合があります。これは、pandas.factorize
とNumPyインデックス付けによって実現できます。例えば
In [313]: df = pd.DataFrame({'col': ["A", "A", "B", "B"],
.....: 'A': [80, 23, np.nan, 22],
.....: 'B': [80, 55, 76, 67]})
.....:
In [314]: df
Out[314]:
col A B
0 A 80.0 80
1 A 23.0 55
2 B NaN 76
3 B 22.0 67
In [315]: idx, cols = pd.factorize(df['col'])
In [316]: df.reindex(cols, axis=1).to_numpy()[np.arange(len(df)), idx]
Out[316]: array([80., 23., 76., 67.])
以前は、バージョン1.2.0で非推奨となり、バージョン2.0.0で削除された専用のDataFrame.lookup
メソッドを使用して実現できました。
インデックスオブジェクト#
pandasのIndex
クラスとそのサブクラスは、順序付けられたマルチセットを実装していると見なすことができます。重複は許容されます。
Index
は、ルックアップ、データの整列、およびインデックスの再設定に必要なインフラストラクチャも提供します。Index
を直接作成する最も簡単な方法は、list
またはその他のシーケンスをIndex
に渡すことです。
In [317]: index = pd.Index(['e', 'd', 'a', 'b'])
In [318]: index
Out[318]: Index(['e', 'd', 'a', 'b'], dtype='object')
In [319]: 'd' in index
Out[319]: True
または数値を使用します。
In [320]: index = pd.Index([1, 5, 12])
In [321]: index
Out[321]: Index([1, 5, 12], dtype='int64')
In [322]: 5 in index
Out[322]: True
dtypeが指定されていない場合、Index
はデータからdtypeを推測しようとします。Index
をインスタンス化するときに、明示的なdtypeを指定することも可能です。
In [323]: index = pd.Index(['e', 'd', 'a', 'b'], dtype="string")
In [324]: index
Out[324]: Index(['e', 'd', 'a', 'b'], dtype='string')
In [325]: index = pd.Index([1, 5, 12], dtype="int8")
In [326]: index
Out[326]: Index([1, 5, 12], dtype='int8')
In [327]: index = pd.Index([1, 5, 12], dtype="float32")
In [328]: index
Out[328]: Index([1.0, 5.0, 12.0], dtype='float32')
インデックスに保存するname
を渡すこともできます。
In [329]: index = pd.Index(['e', 'd', 'a', 'b'], name='something')
In [330]: index.name
Out[330]: 'something'
名前が設定されている場合、コンソール表示に表示されます。
In [331]: index = pd.Index(list(range(5)), name='rows')
In [332]: columns = pd.Index(['A', 'B', 'C'], name='cols')
In [333]: df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns)
In [334]: df
Out[334]:
cols A B C
rows
0 1.295989 -1.051694 1.340429
1 -2.366110 0.428241 0.387275
2 0.433306 0.929548 0.278094
3 2.154730 -0.315628 0.264223
4 1.126818 1.132290 -0.353310
In [335]: df['A']
Out[335]:
rows
0 1.295989
1 -2.366110
2 0.433306
3 2.154730
4 1.126818
Name: A, dtype: float64
メタデータの設定#
インデックスは「ほとんど不変」ですが、name
属性を設定および変更することは可能です。rename
、set_names
を使用してこれらの属性を直接設定できます。デフォルトでは、コピーが返されます。
MultiIndexの使用については、「高度なインデックス付け」を参照してください。
In [336]: ind = pd.Index([1, 2, 3])
In [337]: ind.rename("apple")
Out[337]: Index([1, 2, 3], dtype='int64', name='apple')
In [338]: ind
Out[338]: Index([1, 2, 3], dtype='int64')
In [339]: ind = ind.set_names(["apple"])
In [340]: ind.name = "bob"
In [341]: ind
Out[341]: Index([1, 2, 3], dtype='int64', name='bob')
set_names
、set_levels
、およびset_codes
は、オプションのlevel
引数も受け付けます。
In [342]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'])
In [343]: index
Out[343]:
MultiIndex([(0, 'one'),
(0, 'two'),
(1, 'one'),
(1, 'two'),
(2, 'one'),
(2, 'two')],
names=['first', 'second'])
In [344]: index.levels[1]
Out[344]: Index(['one', 'two'], dtype='object', name='second')
In [345]: index.set_levels(["a", "b"], level=1)
Out[345]:
MultiIndex([(0, 'a'),
(0, 'b'),
(1, 'a'),
(1, 'b'),
(2, 'a'),
(2, 'b')],
names=['first', 'second'])
Indexオブジェクトに対する集合演算#
2つの主要な演算は、union
とintersection
です。差分は.difference()
メソッドで提供されます。
In [346]: a = pd.Index(['c', 'b', 'a'])
In [347]: b = pd.Index(['c', 'e', 'd'])
In [348]: a.difference(b)
Out[348]: Index(['a', 'b'], dtype='object')
また、symmetric_difference
演算も利用できます。これは、idx1
またはidx2
のどちらか一方にのみ出現する要素を返します。これは、idx1.difference(idx2).union(idx2.difference(idx1))
によって作成されたIndexと同等であり、重複は削除されます。
In [349]: idx1 = pd.Index([1, 2, 3, 4])
In [350]: idx2 = pd.Index([2, 3, 4, 5])
In [351]: idx1.symmetric_difference(idx2)
Out[351]: Index([1, 5], dtype='int64')
注記
集合演算の結果として得られるIndexは、昇順にソートされます。
異なるdtypeを持つIndex間でIndex.union()
を実行する場合、Indexは共通のdtypeにキャストする必要があります。通常はobject dtypeですが、必ずしもそうではありません。例外は、整数データと浮動小数点データの結合を実行する場合です。この場合、整数値は浮動小数点値に変換されます。
In [352]: idx1 = pd.Index([0, 1, 2])
In [353]: idx2 = pd.Index([0.5, 1.5])
In [354]: idx1.union(idx2)
Out[354]: Index([0.0, 0.5, 1.0, 1.5, 2.0], dtype='float64')
欠損値#
重要
Index
は欠損値(NaN
)を保持できますが、予期しない結果を避けたい場合は、それを避けるべきです。たとえば、一部の演算では暗黙的に欠損値が除外されます。
Index.fillna
は、指定したスカラー値で欠損値を埋めます。
In [355]: idx1 = pd.Index([1, np.nan, 3, 4])
In [356]: idx1
Out[356]: Index([1.0, nan, 3.0, 4.0], dtype='float64')
In [357]: idx1.fillna(2)
Out[357]: Index([1.0, 2.0, 3.0, 4.0], dtype='float64')
In [358]: idx2 = pd.DatetimeIndex([pd.Timestamp('2011-01-01'),
.....: pd.NaT,
.....: pd.Timestamp('2011-01-03')])
.....:
In [359]: idx2
Out[359]: DatetimeIndex(['2011-01-01', 'NaT', '2011-01-03'], dtype='datetime64[ns]', freq=None)
In [360]: idx2.fillna(pd.Timestamp('2011-01-02'))
Out[360]: DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], dtype='datetime64[ns]', freq=None)
インデックスの設定/リセット#
DataFrameにデータセットを読み込んだり作成したりした後、インデックスを追加したい場合があります。いくつかの方法があります。
インデックスの設定#
DataFrameには、列名(通常のIndex
の場合)または列名のリスト(MultiIndex
の場合)を受け取るset_index()
メソッドがあります。新しい、再インデックスされたDataFrameを作成するには
In [361]: data = pd.DataFrame({'a': ['bar', 'bar', 'foo', 'foo'],
.....: 'b': ['one', 'two', 'one', 'two'],
.....: 'c': ['z', 'y', 'x', 'w'],
.....: 'd': [1., 2., 3, 4]})
.....:
In [362]: data
Out[362]:
a b c d
0 bar one z 1.0
1 bar two y 2.0
2 foo one x 3.0
3 foo two w 4.0
In [363]: indexed1 = data.set_index('c')
In [364]: indexed1
Out[364]:
a b d
c
z bar one 1.0
y bar two 2.0
x foo one 3.0
w foo two 4.0
In [365]: indexed2 = data.set_index(['a', 'b'])
In [366]: indexed2
Out[366]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
append
キーワードオプションを使用すると、既存のインデックスを維持し、指定された列をMultiIndexに追加できます。
In [367]: frame = data.set_index('c', drop=False)
In [368]: frame = frame.set_index(['a', 'b'], append=True)
In [369]: frame
Out[369]:
c d
c a b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
set_index
の他のオプションでは、インデックス列を削除しないようにできます。
In [370]: data.set_index('c', drop=False)
Out[370]:
a b c d
c
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
インデックスのリセット#
便宜上、DataFrameにはreset_index()
という新しい関数があり、これによりインデックス値をDataFrameの列に転送し、単純な整数インデックスを設定します。これはset_index()
の逆演算です。
In [371]: data
Out[371]:
a b c d
0 bar one z 1.0
1 bar two y 2.0
2 foo one x 3.0
3 foo two w 4.0
In [372]: data.reset_index()
Out[372]:
index a b c d
0 0 bar one z 1.0
1 1 bar two y 2.0
2 2 foo one x 3.0
3 3 foo two w 4.0
出力は、SQLテーブルやレコード配列とより似ています。インデックスから派生した列の名前は、names
属性に格納されている名前です。
level
キーワードを使用して、インデックスの一部のみを削除できます。
In [373]: frame
Out[373]:
c d
c a b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
In [374]: frame.reset_index(level=1)
Out[374]:
a c d
c b
z one bar z 1.0
y two bar y 2.0
x one foo x 3.0
w two foo w 4.0
reset_index
は、オプションのパラメータdrop
を受け付けます。これがtrueの場合、DataFrameの列にインデックス値を入れる代わりに、インデックスを単純に破棄します。
アドホックインデックスの追加#
index
属性にカスタムインデックスを割り当てることができます。
In [375]: df_idx = pd.DataFrame(range(4))
In [376]: df_idx.index = pd.Index([10, 20, 30, 40], name="a")
In [377]: df_idx
Out[377]:
0
a
10 0
20 1
30 2
40 3
ビューとコピーの返却#
警告
コピーオンライトは、pandas 3.0で新しいデフォルトになります。これは、連鎖インデックスが機能しなくなることを意味します。その結果、SettingWithCopyWarning
は不要になります。このセクションで詳細を確認してください。pandas 3.0が利用可能になる前にでも、以下の設定でコピーオンライトを有効にして改善点を活用することをお勧めします。
` pd.options.mode.copy_on_write = True `
pandas 3.0が利用可能になる前でも。
pandasオブジェクトの値を設定する際には、「連鎖インデックス」と呼ばれるものを避けるために注意が必要です。例を以下に示します。
In [378]: dfmi = pd.DataFrame([list('abcd'),
.....: list('efgh'),
.....: list('ijkl'),
.....: list('mnop')],
.....: columns=pd.MultiIndex.from_product([['one', 'two'],
.....: ['first', 'second']]))
.....:
In [379]: dfmi
Out[379]:
one two
first second first second
0 a b c d
1 e f g h
2 i j k l
3 m n o p
これら2つのアクセス方法を比較します。
In [380]: dfmi['one']['second']
Out[380]:
0 b
1 f
2 j
3 n
Name: second, dtype: object
In [381]: dfmi.loc[:, ('one', 'second')]
Out[381]:
0 b
1 f
2 j
3 n
Name: (one, second), dtype: object
どちらも同じ結果になりますが、どちらを使用するべきでしょうか? これらの操作順序と、メソッド2(.loc
)がメソッド1(連鎖[]
)よりもはるかに好まれる理由を理解することは有益です。
dfmi['one']
は列の最初のレベルを選択し、単一インデックスのDataFrameを返します。次に、別のPython操作dfmi_with_one['second']
が'second'
でインデックス付けされたシリーズを選択します。これは、pandasがこれらの操作を別々のイベント(つまり、__getitem__
への別々の呼び出し)と見なすため、変数dfmi_with_one
によって示されます。そのため、線形操作として扱う必要があり、順番に実行されます。
これに対して、df.loc[:,('one','second')]
は(slice(None),('one','second'))
のネストされたタプルを__getitem__
への単一の呼び出しに渡します。これにより、pandasはこれを単一のエンティティとして処理できます。さらに、この操作順序は大幅に高速になる可能性があり、必要に応じて両方の軸をインデックス付けできます。
連鎖インデックスを使用すると、代入が失敗するのはなぜですか?#
警告
コピーオンライトは、pandas 3.0で新しいデフォルトになります。これは、連鎖インデックスが機能しなくなることを意味します。その結果、SettingWithCopyWarning
は不要になります。このセクションで詳細を確認してください。pandas 3.0が利用可能になる前にでも、以下の設定でコピーオンライトを有効にして改善点を活用することをお勧めします。
` pd.options.mode.copy_on_write = True `
pandas 3.0が利用可能になる前でも。
前のセクションの問題は、単なるパフォーマンスの問題です。SettingWithCopy
警告はどういうことですか?数ミリ秒余計にかかる可能性がある操作をした場合に警告を出すことは、通常は行いません!
しかし、連鎖インデックスの結果への代入は、本質的に予測不可能な結果をもたらすことが判明しました。これを確認するために、Pythonインタープリターがこのコードを実行する方法を考えてみてください。
dfmi.loc[:, ('one', 'second')] = value
# becomes
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)
しかし、このコードは異なる方法で処理されます。
dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)
そこに__getitem__
がありますね?単純なケースを除いて、ビューとコピーのどちらを返すかは予測するのが非常に困難です(配列のメモリレイアウトに依存し、pandasはそれについて保証しません)。したがって、__setitem__
がdfmi
を変更するのか、直後に破棄される一時オブジェクトを変更するのかが不明です。これが、SettingWithCopy
が警告していることです!
注記
最初の例ではloc
プロパティを気にするべきかどうか疑問に思うかもしれません。しかし、dfmi.loc
は、変更されたインデックス動作を持つdfmi
自体であることが保証されているため、dfmi.loc.__getitem__
/ dfmi.loc.__setitem__
はdfmi
を直接操作します。もちろん、dfmi.loc.__getitem__(idx)
は、dfmi
のビューまたはコピーである可能性があります。
明らかな連鎖インデックスがない場合でも、SettingWithCopy
警告が発生することがあります。これらは、SettingWithCopy
が検出するように設計されているバグです。pandasはおそらく、あなたがこれを行ったことを警告しようとしています。
def do_something(df):
foo = df[['bar', 'baz']] # Is foo a view? A copy? Nobody knows!
# ... many lines here ...
# We don't know whether this will modify df or not!
foo['quux'] = value
return foo
大変だ!
評価順序が重要#
警告
コピーオンライトは、pandas 3.0で新しいデフォルトになります。これは、連鎖インデックスが機能しなくなることを意味します。その結果、SettingWithCopyWarning
は不要になります。このセクションで詳細を確認してください。pandas 3.0が利用可能になる前にでも、以下の設定でコピーオンライトを有効にして改善点を活用することをお勧めします。
` pd.options.mode.copy_on_write = True `
pandas 3.0が利用可能になる前でも。
連鎖インデックスを使用する場合、インデックス操作の順序と種類によって、結果が元のオブジェクトのスライスになるか、スライスのコピーになるかが部分的に決定されます。
pandasにはSettingWithCopyWarning
があります。これは、スライスのコピーへの代入が頻繁に意図的なものではなく、連鎖インデックスがスライスを期待した場所にコピーを返すことによって発生する間違いであるためです。
連鎖インデックス式への代入について、pandasをより信頼するか、あるいは信頼しないようにしたい場合は、オプションmode.chained_assignment
を次のいずれかの値に設定できます。
'warn'
(デフォルト)は、SettingWithCopyWarning
が出力されることを意味します。'raise'
は、pandasが対処する必要があるSettingWithCopyError
を発生させることを意味します。None
は警告を完全に抑制します。
In [382]: dfb = pd.DataFrame({'a': ['one', 'one', 'two',
.....: 'three', 'two', 'one', 'six'],
.....: 'c': np.arange(7)})
.....:
# This will show the SettingWithCopyWarning
# but the frame values will be set
In [383]: dfb['c'][dfb['a'].str.startswith('o')] = 42
ただし、これはコピーを操作しており、機能しません。
In [384]: with pd.option_context('mode.chained_assignment','warn'):
.....: dfb[dfb['a'].str.startswith('o')]['c'] = 42
.....:
連鎖代入は、混合dtypeフレームの設定にも発生する可能性があります。
注記
これらの設定ルールは、.loc/.iloc
すべてに適用されます。
以下は、複数の項目(mask
を使用)と固定インデックスを使用する単一項目に対して.loc
を使用する推奨されるアクセス方法です。
In [385]: dfc = pd.DataFrame({'a': ['one', 'one', 'two',
.....: 'three', 'two', 'one', 'six'],
.....: 'c': np.arange(7)})
.....:
In [386]: dfd = dfc.copy()
# Setting multiple items using a mask
In [387]: mask = dfd['a'].str.startswith('o')
In [388]: dfd.loc[mask, 'c'] = 42
In [389]: dfd
Out[389]:
a c
0 one 42
1 one 42
2 two 2
3 three 3
4 two 4
5 one 42
6 six 6
# Setting a single item
In [390]: dfd = dfc.copy()
In [391]: dfd.loc[2, 'a'] = 11
In [392]: dfd
Out[392]:
a c
0 one 0
1 one 1
2 11 2
3 three 3
4 two 4
5 one 5
6 six 6
以下は、場合によっては機能する可能性がありますが、保証されておらず、そのため避けるべきです。
In [393]: dfd = dfc.copy()
In [394]: dfd['a'][2] = 111
In [395]: dfd
Out[395]:
a c
0 one 0
1 one 1
2 111 2
3 three 3
4 two 4
5 one 5
6 six 6
最後に、次の例はまったく機能しないため、避けるべきです。
In [396]: with pd.option_context('mode.chained_assignment','raise'):
.....: dfd.loc[0]['a'] = 1111
.....:
---------------------------------------------------------------------------
SettingWithCopyError Traceback (most recent call last)
<ipython-input-396-32ce785aaa5b> in ?()
1 with pd.option_context('mode.chained_assignment','raise'):
----> 2 dfd.loc[0]['a'] = 1111
~/work/pandas/pandas/pandas/core/series.py in ?(self, key, value)
1275 )
1276
1277 check_dict_or_set_indexers(key)
1278 key = com.apply_if_callable(key, self)
-> 1279 cacher_needs_updating = self._check_is_chained_assignment_possible()
1280
1281 if key is Ellipsis:
1282 key = slice(None)
~/work/pandas/pandas/pandas/core/series.py in ?(self)
1480 ref = self._get_cacher()
1481 if ref is not None and ref._is_mixed_type:
1482 self._check_setitem_copy(t="referent", force=True)
1483 return True
-> 1484 return super()._check_is_chained_assignment_possible()
~/work/pandas/pandas/pandas/core/generic.py in ?(self)
4392 single-dtype meaning that the cacher should be updated following
4393 setting.
4394 """
4395 if self._is_copy:
-> 4396 self._check_setitem_copy(t="referent")
4397 return False
~/work/pandas/pandas/pandas/core/generic.py in ?(self, t, force)
4466 "indexing.html#returning-a-view-versus-a-copy"
4467 )
4468
4469 if value == "raise":
-> 4470 raise SettingWithCopyError(t)
4471 if value == "warn":
4472 warnings.warn(t, SettingWithCopyWarning, stacklevel=find_stack_level())
SettingWithCopyError:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.dokyumento.jp/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
警告
連鎖代入に関する警告/例外は、ユーザーに無効な代入の可能性について通知することを目的としています。誤検知、つまり連鎖代入が誤って報告される状況が発生する可能性があります。