必要不可欠な基本機能#

ここでは、pandas データ構造に共通する多くの基本的な機能について説明します。まず、10 minutes to pandas セクションで行ったように、いくつかのサンプルオブジェクトを作成しましょう。

In [1]: index = pd.date_range("1/1/2000", periods=8)

In [2]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

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

先頭と末尾#

Series または DataFrame オブジェクトの小さなサンプルを表示するには、head()tail() メソッドを使用します。表示する要素のデフォルト数は 5 ですが、カスタム数を渡すこともできます。

In [4]: long_series = pd.Series(np.random.randn(1000))

In [5]: long_series.head()
Out[5]: 
0   -1.157892
1   -1.344312
2    0.844885
3    1.075770
4   -0.109050
dtype: float64

In [6]: long_series.tail(3)
Out[6]: 
997   -0.289388
998   -1.020544
999    0.589993
dtype: float64

属性と基礎となるデータ#

pandas オブジェクトには、メタデータにアクセスできる多数の属性があります。

  • shape: ndarray と一貫性のある、オブジェクトの軸の次元を返します。

  • 軸ラベル
    • Series: index (軸のみ)

    • DataFrame: index (行) と columns

なお、これらの属性は安全に割り当てることができます

In [7]: df[:2]
Out[7]: 
                   A         B         C
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929

In [8]: df.columns = [x.lower() for x in df.columns]

In [9]: df
Out[9]: 
                   a         b         c
2000-01-01 -0.173215  0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
2000-01-03  1.071804  0.721555 -0.706771
2000-01-04 -1.039575  0.271860 -0.424972
2000-01-05  0.567020  0.276232 -1.087401
2000-01-06 -0.673690  0.113648 -1.478427
2000-01-07  0.524988  0.404705  0.577046
2000-01-08 -1.715002 -1.039268 -0.370647

pandas オブジェクト (Index, Series, DataFrame) は、実際のデータを保持し、実際の計算を行う配列のコンテナと考えることができます。多くのタイプの場合、基礎となる配列は numpy.ndarray です。ただし、pandas およびサードパーティライブラリは、カスタム配列のサポートを追加するために NumPy の型システムを拡張する場合があります (dtypes を参照)。

Index または Series 内の実際のデータを取得するには、.array プロパティを使用します。

In [10]: s.array
Out[10]: 
<NumpyExtensionArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
 -1.1356323710171934,  1.2121120250208506]
Length: 5, dtype: float64

In [11]: s.index.array
Out[11]: 
<NumpyExtensionArray>
['a', 'b', 'c', 'd', 'e']
Length: 5, dtype: object

array は常に ExtensionArray になります。ExtensionArray が何であり、なぜ pandas がそれらを使用するのかの正確な詳細は、この導入の範囲を少し超えています。dtypes を参照してください。

NumPy 配列が必要であることがわかっている場合は、to_numpy() または numpy.asarray() を使用します。

In [12]: s.to_numpy()
Out[12]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])

In [13]: np.asarray(s)
Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356,  1.2121])

Series または Index が ExtensionArray によってバックアップされている場合、to_numpy() はデータのコピーと値の強制変換を伴う場合があります。dtypes を参照してください。

to_numpy() は、結果の numpy.ndarraydtype をある程度制御できます。例えば、タイムゾーンを持つ datetime を考えてみましょう。NumPy にはタイムゾーン対応の datetime を表す dtype がないため、2つの有用な表現があります。

  1. 正しい tz を持つ Timestamp オブジェクトを含む object-dtype の numpy.ndarray

  2. 値が UTC に変換され、タイムゾーンが破棄された datetime64[ns] -dtype の numpy.ndarray

タイムゾーンは dtype=object で保持できます。

In [14]: ser = pd.Series(pd.date_range("2000", periods=2, tz="CET"))

In [15]: ser.to_numpy(dtype=object)
Out[15]: 
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET'),
       Timestamp('2000-01-02 00:00:00+0100', tz='CET')], dtype=object)

または dtype='datetime64[ns]' で破棄されます。

In [16]: ser.to_numpy(dtype="datetime64[ns]")
Out[16]: 
array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'],
      dtype='datetime64[ns]')

DataFrame 内の「生データ」を取得するのは、おそらく少し複雑です。DataFrame のすべての列に単一のデータ型のみが含まれている場合、DataFrame.to_numpy() は基礎となるデータを返します。

In [17]: df.to_numpy()
Out[17]: 
array([[-0.1732,  0.1192, -1.0442],
       [-0.8618, -2.1046, -0.4949],
       [ 1.0718,  0.7216, -0.7068],
       [-1.0396,  0.2719, -0.425 ],
       [ 0.567 ,  0.2762, -1.0874],
       [-0.6737,  0.1136, -1.4784],
       [ 0.525 ,  0.4047,  0.577 ],
       [-1.715 , -1.0393, -0.3706]])

DataFrame に同種のデータが含まれている場合、ndarray は実際にその場で変更でき、変更はデータ構造に反映されます。異種データ (例えば、DataFrame の一部の列がすべて同じ dtype ではない場合) の場合、これは当てはまりません。値属性自体は、軸ラベルとは異なり、割り当てることはできません。

異種データを扱う場合、結果の ndarray の dtype は、関係するすべてのデータを収容するように選択されます。たとえば、文字列が関係する場合、結果は object dtype になります。浮動小数点数と整数のみの場合、結果の配列は float dtype になります。

以前は、pandas は Series または DataFrame からデータを抽出するために Series.values または DataFrame.values を推奨していました。古いコードベースやオンラインでは、これらの参照がまだ見つかるでしょう。今後は、.values を避け、.array または .to_numpy() を使用することをお勧めします。.values には以下の欠点があります。

  1. Series に 拡張タイプ が含まれている場合、Series.values が NumPy 配列を返すのか拡張配列を返すのかは不明確です。Series.array は常に ExtensionArray を返し、データをコピーすることはありません。Series.to_numpy() は常に NumPy 配列を返しますが、値のコピー/強制変換のコストがかかる場合があります。

  2. DataFrame にさまざまなデータ型が混在している場合、DataFrame.values はデータをコピーしたり、値を共通の dtype に強制変換したりする可能性があり、比較的コストのかかる操作です。DataFrame.to_numpy() はメソッドであるため、返される NumPy 配列が DataFrame 内の同じデータのビューではない可能性があることを明確にします。

高速化された操作#

pandas は、numexpr ライブラリと bottleneck ライブラリを使用して、特定の種類の二項数値演算とブール演算を高速化する機能をサポートしています。

これらのライブラリは、大規模なデータセットを扱う場合に特に有用で、大幅な高速化を提供します。numexpr はスマートなチャンク処理、キャッシング、複数のコアを使用します。bottleneck は、nans を持つ配列を扱う場合に特に高速な Cython ルーチンのセットです。

以下にサンプルを示します (100列 x 100,000行の DataFrames を使用)。

操作

0.11.0 (ms)

以前のバージョン (ms)

以前のバージョンとの比率

df1 > df2

13.32

125.35

0.1063

df1 * df2

21.71

36.63

0.5928

df1 + df2

22.04

36.50

0.6039

両方のライブラリをインストールすることを強くお勧めします。インストールに関する詳細情報は、推奨される依存関係 セクションを参照してください。

これらは両方ともデフォルトで使用可能になっており、オプションを設定することで制御できます。

pd.set_option("compute.use_bottleneck", False)
pd.set_option("compute.use_numexpr", False)

柔軟な二項演算#

pandas データ構造間の二項演算には、2つの重要なポイントがあります。

  • 高次元 (例: DataFrame) と低次元 (例: Series) オブジェクト間のブロードキャスト動作。

  • 計算における欠損データ。

これらの問題は同時に処理することもできますが、ここでは個別に管理する方法を示します。

マッチング/ブロードキャスト動作#

DataFrame には、二項演算を実行するためのメソッド add()sub()mul()div() と、関連する関数 radd()rsub()、... があります。ブロードキャスト動作の場合、Series 入力が主な関心事です。これらの関数を使用すると、axis キーワードを介してインデックスまたはのいずれかで一致させることができます。

In [18]: df = pd.DataFrame(
   ....:     {
   ....:         "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
   ....:         "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
   ....:         "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
   ....:     }
   ....: )
   ....: 

In [19]: df
Out[19]: 
        one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [20]: row = df.iloc[1]

In [21]: column = df["two"]

In [22]: df.sub(row, axis="columns")
Out[22]: 
        one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [23]: df.sub(row, axis=1)
Out[23]: 
        one       two     three
a  1.051928 -0.139606       NaN
b  0.000000  0.000000  0.000000
c  0.352192 -0.433754  1.277825
d       NaN -1.632779 -0.562782

In [24]: df.sub(column, axis="index")
Out[24]: 
        one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516

In [25]: df.sub(column, axis=0)
Out[25]: 
        one  two     three
a -0.377535  0.0       NaN
b -1.569069  0.0 -1.962513
c -0.783123  0.0 -0.250933
d       NaN  0.0 -0.892516

さらに、MultiIndexed DataFrame のレベルを Series に合わせることもできます。

In [26]: dfmi = df.copy()

In [27]: dfmi.index = pd.MultiIndex.from_tuples(
   ....:     [(1, "a"), (1, "b"), (1, "c"), (2, "a")], names=["first", "second"]
   ....: )
   ....: 

In [28]: dfmi.sub(column, axis=0, level="second")
Out[28]: 
                   one       two     three
first second                              
1     a      -0.377535  0.000000       NaN
      b      -1.569069  0.000000 -1.962513
      c      -0.783123  0.000000 -0.250933
2     a            NaN -1.493173 -2.385688

Series と Index は、組み込みの divmod() もサポートしています。この関数は、切り捨て除算と剰余演算を同時に行い、左辺と同じ型の2要素タプルを返します。例えば

In [29]: s = pd.Series(np.arange(10))

In [30]: s
Out[30]: 
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
dtype: int64

In [31]: div, rem = divmod(s, 3)

In [32]: div
Out[32]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    2
7    2
8    2
9    3
dtype: int64

In [33]: rem
Out[33]: 
0    0
1    1
2    2
3    0
4    1
5    2
6    0
7    1
8    2
9    0
dtype: int64

In [34]: idx = pd.Index(np.arange(10))

In [35]: idx
Out[35]: Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')

In [36]: div, rem = divmod(idx, 3)

In [37]: div
Out[37]: Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')

In [38]: rem
Out[38]: Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')

要素ごとの divmod() も可能です。

In [39]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])

In [40]: div
Out[40]: 
0    0
1    0
2    0
3    1
4    1
5    1
6    1
7    1
8    1
9    1
dtype: int64

In [41]: rem
Out[41]: 
0    0
1    1
2    2
3    0
4    0
5    1
6    1
7    2
8    2
9    3
dtype: int64

欠損データ / 埋め込み値を使用した操作#

Series と DataFrame の算術関数には、オプションで *fill_value* を入力できます。これは、指定された位置の値の多くて一つが欠損している場合に代わりに使用する値です。たとえば、2つの DataFrame オブジェクトを追加する場合、両方の DataFrame がその値を欠損している場合を除き、NaN を 0 として扱いたい場合があります。その場合、結果は NaN になります (後で必要に応じて fillna を使用して NaN を他の値に置き換えることができます)。

In [42]: df2 = df.copy()

In [43]: df2.loc["a", "three"] = 1.0

In [44]: df
Out[44]: 
        one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [45]: df2
Out[45]: 
        one       two     three
a  1.394981  1.772517  1.000000
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [46]: df + df2
Out[46]: 
        one       two     three
a  2.789963  3.545034       NaN
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343

In [47]: df.add(df2, fill_value=0)
Out[47]: 
        one       two     three
a  2.789963  3.545034  1.000000
b  0.686107  3.824246 -0.100780
c  1.390491  2.956737  2.454870
d       NaN  0.558688 -1.226343

柔軟な比較#

Series と DataFrame には、eqneltgtlege の二項比較メソッドがあり、それらの動作は上記の二項算術演算と同様です。

In [48]: df.gt(df2)
Out[48]: 
     one    two  three
a  False  False  False
b  False  False  False
c  False  False  False
d  False  False  False

In [49]: df2.ne(df)
Out[49]: 
     one    two  three
a  False  False   True
b  False  False  False
c  False  False  False
d   True  False  False

これらの操作は、左側の入力と同じ型の pandas オブジェクトを生成し、その dtype は bool です。これらの boolean オブジェクトはインデックス操作で使用できます。ブールインデックス のセクションを参照してください。

ブール削減#

emptyany()all()bool() といった削減を適用することで、ブール結果を要約する方法を提供できます。

In [50]: (df > 0).all()
Out[50]: 
one      False
two       True
three    False
dtype: bool

In [51]: (df > 0).any()
Out[51]: 
one      True
two      True
three    True
dtype: bool

最終的なブール値に還元できます。

In [52]: (df > 0).any().any()
Out[52]: True

empty プロパティを介して、pandas オブジェクトが空であるかどうかをテストできます。

In [53]: df.empty
Out[53]: False

In [54]: pd.DataFrame(columns=list("ABC")).empty
Out[54]: True

警告

pandas オブジェクトの真偽をアサートすると、空であるか値のテストがあいまいであるため、エラーが発生します。

In [55]: if df:
   ....:     print(True)
   ....: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-55-318d08b2571a> in ?()
----> 1 if df:
      2     print(True)

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
   1575     @final
   1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
   1578             f"The truth value of a {type(self).__name__} is ambiguous. "
   1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
   1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
In [56]: df and df2
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-56-b241b64bb471> in ?()
----> 1 df and df2

~/work/pandas/pandas/pandas/core/generic.py in ?(self)
   1575     @final
   1576     def __nonzero__(self) -> NoReturn:
-> 1577         raise ValueError(
   1578             f"The truth value of a {type(self).__name__} is ambiguous. "
   1579             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
   1580         )

ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

詳細な議論については、落とし穴 を参照してください。

オブジェクトが同等であるかどうかの比較#

多くの場合、同じ結果を計算する方法が複数あることに気づくかもしれません。簡単な例として、df + dfdf * 2 を考えてみましょう。これらの2つの計算が同じ結果を生成するかどうかをテストするには、上記のツールを使用して (df + df == df * 2).all() を使用するかもしれませんが、実際には、この式は False です。

In [57]: df + df == df * 2
Out[57]: 
     one   two  three
a   True  True  False
b   True  True   True
c   True  True   True
d  False  True   True

In [58]: (df + df == df * 2).all()
Out[58]: 
one      False
two       True
three    False
dtype: bool

ブール値の DataFrame df + df == df * 2 にはいくつかの False 値が含まれていることに注意してください!これは、NaN が等しいものとして比較されないためです。

In [59]: np.nan == np.nan
Out[59]: False

したがって、NDFrame (Series や DataFrame など) には、等価性をテストするための equals() メソッドがあり、対応する位置の NaN は等しいものとして扱われます。

In [60]: (df + df).equals(df * 2)
Out[60]: True

Series または DataFrame のインデックスが等価であるためには、同じ順序である必要があることに注意してください。

In [61]: df1 = pd.DataFrame({"col": ["foo", 0, np.nan]})

In [62]: df2 = pd.DataFrame({"col": [np.nan, 0, "foo"]}, index=[2, 1, 0])

In [63]: df1.equals(df2)
Out[63]: False

In [64]: df1.equals(df2.sort_index())
Out[64]: True

配列のようなオブジェクトの比較#

pandas データ構造とスカラー値を比較する場合、要素ごとの比較を簡単に行うことができます。

In [65]: pd.Series(["foo", "bar", "baz"]) == "foo"
Out[65]: 
0     True
1    False
2    False
dtype: bool

In [66]: pd.Index(["foo", "bar", "baz"]) == "foo"
Out[66]: array([ True, False, False])

pandas は、同じ長さの異なる配列状オブジェクト間の要素ごとの比較も処理します。

In [67]: pd.Series(["foo", "bar", "baz"]) == pd.Index(["foo", "bar", "qux"])
Out[67]: 
0     True
1     True
2    False
dtype: bool

In [68]: pd.Series(["foo", "bar", "baz"]) == np.array(["foo", "bar", "qux"])
Out[68]: 
0     True
1     True
2    False
dtype: bool

異なる長さの Index または Series オブジェクトを比較しようとすると、ValueError が発生します。

In [69]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[69], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
     72             return NotImplemented
     74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
     38 @unpack_zerodim_and_defer("__eq__")
     39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6125, in Series._cmp_method(self, other, op)
   6122 res_name = ops.get_op_result_name(self, other)
   6124 if isinstance(other, Series) and not self._indexed_same(other):
-> 6125     raise ValueError("Can only compare identically-labeled Series objects")
   6127 lvalues = self._values
   6128 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects

In [70]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[70], line 1
----> 1 pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])

File ~/work/pandas/pandas/pandas/core/ops/common.py:76, in _unpack_zerodim_and_defer.<locals>.new_method(self, other)
     72             return NotImplemented
     74 other = item_from_zerodim(other)
---> 76 return method(self, other)

File ~/work/pandas/pandas/pandas/core/arraylike.py:40, in OpsMixin.__eq__(self, other)
     38 @unpack_zerodim_and_defer("__eq__")
     39 def __eq__(self, other):
---> 40     return self._cmp_method(other, operator.eq)

File ~/work/pandas/pandas/pandas/core/series.py:6125, in Series._cmp_method(self, other, op)
   6122 res_name = ops.get_op_result_name(self, other)
   6124 if isinstance(other, Series) and not self._indexed_same(other):
-> 6125     raise ValueError("Can only compare identically-labeled Series objects")
   6127 lvalues = self._values
   6128 rvalues = extract_array(other, extract_numpy=True, extract_range=True)

ValueError: Can only compare identically-labeled Series objects

重複するデータセットの結合#

時折発生する問題は、一方の値が他方よりも優先される2つの類似したデータセットを結合することです。例としては、特定の経済指標を表す2つのデータ系列があり、一方が「高品質」と見なされる場合です。しかし、品質の低い系列は、歴史的にさらに遡ったり、より完全なデータカバレッジを持っていたりするかもしれません。そのため、ある DataFrame の欠損値が、もう一方の DataFrame からの同じラベルの値で条件付きで埋められるように、2つの DataFrame オブジェクトを結合したいとします。この操作を実装する関数は combine_first() であり、以下で説明します。

In [71]: df1 = pd.DataFrame(
   ....:     {"A": [1.0, np.nan, 3.0, 5.0, np.nan], "B": [np.nan, 2.0, 3.0, np.nan, 6.0]}
   ....: )
   ....: 

In [72]: df2 = pd.DataFrame(
   ....:     {
   ....:         "A": [5.0, 2.0, 4.0, np.nan, 3.0, 7.0],
   ....:         "B": [np.nan, np.nan, 3.0, 4.0, 6.0, 8.0],
   ....:     }
   ....: )
   ....: 

In [73]: df1
Out[73]: 
     A    B
0  1.0  NaN
1  NaN  2.0
2  3.0  3.0
3  5.0  NaN
4  NaN  6.0

In [74]: df2
Out[74]: 
     A    B
0  5.0  NaN
1  2.0  NaN
2  4.0  3.0
3  NaN  4.0
4  3.0  6.0
5  7.0  8.0

In [75]: df1.combine_first(df2)
Out[75]: 
     A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0

一般的な DataFrame の結合#

上記の combine_first() メソッドは、より一般的な DataFrame.combine() を呼び出します。このメソッドは別の DataFrame とコンバイナー関数を受け取り、入力 DataFrame をアラインしてから、コンバイナー関数に Series のペア (つまり、同じ名前の列) を渡します。

したがって、例えば、上記のように combine_first() を再現するには

In [76]: def combiner(x, y):
   ....:     return np.where(pd.isna(x), y, x)
   ....: 

In [77]: df1.combine(df2, combiner)
Out[77]: 
     A    B
0  1.0  NaN
1  2.0  2.0
2  3.0  3.0
3  5.0  4.0
4  3.0  6.0
5  7.0  8.0

記述統計#

SeriesDataFrame に対して記述統計量やその他の関連操作を計算するための多数のメソッドが存在します。これらのほとんどは、sum()mean()quantile() のような集計 (結果として次元が減少する) ですが、cumsum()cumprod() のように同じサイズのオブジェクトを生成するものもあります。一般的に、これらのメソッドは ndarray.{sum, std, …} と同様に axis 引数を受け取りますが、軸は名前または整数で指定できます。

  • Series: 軸引数は不要

  • DataFrame: "index" (axis=0, デフォルト), "columns" (axis=1)

例えば

In [78]: df
Out[78]: 
        one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [79]: df.mean(0)
Out[79]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [80]: df.mean(1)
Out[80]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

このようなすべてのメソッドには、欠損データを除外するかどうかを示す skipna オプションがあります (デフォルトは True)。

In [81]: df.sum(0, skipna=False)
Out[81]: 
one           NaN
two      5.442353
three         NaN
dtype: float64

In [82]: df.sum(axis=1, skipna=True)
Out[82]: 
a    3.167498
b    2.204786
c    3.401050
d   -0.333828
dtype: float64

ブロードキャスト/算術動作と組み合わせることで、標準化 (データを平均 0、標準偏差 1 に変換) のようなさまざまな統計的手順を非常に簡潔に記述できます。

In [83]: ts_stand = (df - df.mean()) / df.std()

In [84]: ts_stand.std()
Out[84]: 
one      1.0
two      1.0
three    1.0
dtype: float64

In [85]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)

In [86]: xs_stand.std(1)
Out[86]: 
a    1.0
b    1.0
c    1.0
d    1.0
dtype: float64

cumsum()cumprod() のようなメソッドは、NaN 値の位置を保持することに注意してください。これは、expanding()rolling() とは少し異なります。NaN の動作は、さらに min_periods パラメーターによって規定されるためです。

In [87]: df.cumsum()
Out[87]: 
        one       two     three
a  1.394981  1.772517       NaN
b  1.738035  3.684640 -0.050390
c  2.433281  5.163008  1.177045
d       NaN  5.442353  0.563873

以下に、一般的な関数の簡単な参照まとめ表を示します。それぞれにオプションの level パラメーターも指定できます。これは、オブジェクトが 階層インデックス を持っている場合にのみ適用されます。

関数

説明

カウント

非NA観測の数

合計

値の合計

平均

値の平均

中央値

値の算術中央値

最小値

最小

最大値

最大

最頻値

最頻値

絶対値

絶対値

値の積

標準偏差

ベッセル補正済み標本標準偏差

分散

不偏分散

標準誤差

平均の標準誤差

歪度

標本歪度 (3次モーメント)

尖度

標本尖度 (4次モーメント)

分位点

標本分位点 (%の値)

累積和

累積和

累積積

累積積

累積最大値

累積最大値

累積最小値

累積最小値

たまたま、meanstdsum のような一部の NumPy メソッドは、Series 入力でデフォルトで NA を除外することに注意してください。

In [88]: np.mean(df["one"])
Out[88]: 0.8110935116651192

In [89]: np.mean(df["one"].to_numpy())
Out[89]: nan

Series.nunique() は、Series 内の一意の非NA値の数を返します。

In [90]: series = pd.Series(np.random.randn(500))

In [91]: series[20:500] = np.nan

In [92]: series[10:20] = 5

In [93]: series.nunique()
Out[93]: 11

データの要約: describe#

Series または DataFrame の列に関するさまざまな要約統計量を計算する便利な describe() 関数があります (もちろん NA は除外されます)。

In [94]: series = pd.Series(np.random.randn(1000))

In [95]: series[::2] = np.nan

In [96]: series.describe()
Out[96]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
25%       -0.699070
50%       -0.069718
75%        0.714483
max        3.160915
dtype: float64

In [97]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=["a", "b", "c", "d", "e"])

In [98]: frame.iloc[::2] = np.nan

In [99]: frame.describe()
Out[99]: 
                a           b           c           d           e
count  500.000000  500.000000  500.000000  500.000000  500.000000
mean     0.033387    0.030045   -0.043719   -0.051686    0.005979
std      1.017152    0.978743    1.025270    1.015988    1.006695
min     -3.000951   -2.637901   -3.303099   -3.159200   -3.188821
25%     -0.647623   -0.576449   -0.712369   -0.691338   -0.691115
50%      0.047578   -0.021499   -0.023888   -0.032652   -0.025363
75%      0.729907    0.775880    0.618896    0.670047    0.649748
max      2.740139    2.752332    3.004229    2.728702    3.240991

出力に含める特定のパーセンタイルを選択できます。

In [100]: series.describe(percentiles=[0.05, 0.25, 0.75, 0.95])
Out[100]: 
count    500.000000
mean      -0.021292
std        1.015906
min       -2.683763
5%        -1.645423
25%       -0.699070
50%       -0.069718
75%        0.714483
95%        1.711409
max        3.160915
dtype: float64

デフォルトでは、中央値は常に含まれます。

非数値 Series オブジェクトの場合、describe() は、一意の値の数と最も頻繁に発生する値の簡単な要約を提供します。

In [101]: s = pd.Series(["a", "a", "b", "b", "a", "a", np.nan, "c", "d", "a"])

In [102]: s.describe()
Out[102]: 
count     9
unique    4
top       a
freq      5
dtype: object

混合型の DataFrame オブジェクトの場合、describe() は、数値列のみ、または数値列がない場合はカテゴリカル列のみを含むように要約を制限することに注意してください。

In [103]: frame = pd.DataFrame({"a": ["Yes", "Yes", "No", "No"], "b": range(4)})

In [104]: frame.describe()
Out[104]: 
              b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

この動作は、include/exclude 引数として型のリストを指定することで制御できます。特別な値 all も使用できます。

In [105]: frame.describe(include=["object"])
Out[105]: 
          a
count     4
unique    2
top     Yes
freq      2

In [106]: frame.describe(include=["number"])
Out[106]: 
              b
count  4.000000
mean   1.500000
std    1.290994
min    0.000000
25%    0.750000
50%    1.500000
75%    2.250000
max    3.000000

In [107]: frame.describe(include="all")
Out[107]: 
          a         b
count     4  4.000000
unique    2       NaN
top     Yes       NaN
freq      2       NaN
mean    NaN  1.500000
std     NaN  1.290994
min     NaN  0.000000
25%     NaN  0.750000
50%     NaN  1.500000
75%     NaN  2.250000
max     NaN  3.000000

この機能は select_dtypes に依存しています。受け入れられる入力の詳細についてはそちらを参照してください。

最小/最大値のインデックス#

Series と DataFrame の idxmin()idxmax() 関数は、対応する値が最小および最大となるインデックスラベルを計算します。

In [108]: s1 = pd.Series(np.random.randn(5))

In [109]: s1
Out[109]: 
0    1.118076
1   -0.352051
2   -1.242883
3   -1.277155
4   -0.641184
dtype: float64

In [110]: s1.idxmin(), s1.idxmax()
Out[110]: (3, 0)

In [111]: df1 = pd.DataFrame(np.random.randn(5, 3), columns=["A", "B", "C"])

In [112]: df1
Out[112]: 
          A         B         C
0 -0.327863 -0.946180 -0.137570
1 -0.186235 -0.257213 -0.486567
2 -0.507027 -0.871259 -0.111110
3  2.000339 -2.430505  0.089759
4 -0.321434 -0.033695  0.096271

In [113]: df1.idxmin(axis=0)
Out[113]: 
A    2
B    3
C    1
dtype: int64

In [114]: df1.idxmax(axis=1)
Out[114]: 
0    C
1    A
2    C
3    A
4    C
dtype: object

最小値または最大値に一致する複数の行 (または列) がある場合、idxmin()idxmax() は最初に一致するインデックスを返します。

In [115]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=["A"], index=list("edcba"))

In [116]: df3
Out[116]: 
     A
e  2.0
d  1.0
c  1.0
b  3.0
a  NaN

In [117]: df3["A"].idxmin()
Out[117]: 'd'

idxminidxmax は NumPy では argminargmax と呼ばれます。

値のカウント (ヒストグラム化) / 最頻値#

value_counts() Series メソッドは、1D 配列の値のヒストグラムを計算します。通常の配列の関数としても使用できます。

In [118]: data = np.random.randint(0, 7, size=50)

In [119]: data
Out[119]: 
array([6, 6, 2, 3, 5, 3, 2, 5, 4, 5, 4, 3, 4, 5, 0, 2, 0, 4, 2, 0, 3, 2,
       2, 5, 6, 5, 3, 4, 6, 4, 3, 5, 6, 4, 3, 6, 2, 6, 6, 2, 3, 4, 2, 1,
       6, 2, 6, 1, 5, 4])

In [120]: s = pd.Series(data)

In [121]: s.value_counts()
Out[121]: 
6    10
2    10
4     9
3     8
5     8
0     3
1     2
Name: count, dtype: int64

value_counts() メソッドは、複数の列にわたる組み合わせをカウントするために使用できます。デフォルトではすべての列が使用されますが、subset 引数を使用してサブセットを選択することもできます。

In [122]: data = {"a": [1, 2, 3, 4], "b": ["x", "x", "y", "y"]}

In [123]: frame = pd.DataFrame(data)

In [124]: frame.value_counts()
Out[124]: 
a  b
1  x    1
2  x    1
3  y    1
4  y    1
Name: count, dtype: int64

同様に、Series または DataFrame の値で最も頻繁に発生する値、つまり最頻値を取得できます。

In [125]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])

In [126]: s5.mode()
Out[126]: 
0    3
1    7
dtype: int64

In [127]: df5 = pd.DataFrame(
   .....:     {
   .....:         "A": np.random.randint(0, 7, size=50),
   .....:         "B": np.random.randint(-10, 15, size=50),
   .....:     }
   .....: )
   .....: 

In [128]: df5.mode()
Out[128]: 
     A   B
0  1.0  -9
1  NaN  10
2  NaN  13

離散化と分位点化#

連続値は、cut() (値に基づくビン) と qcut() (標本分位点に基づくビン) 関数を使用して離散化できます。

In [129]: arr = np.random.randn(20)

In [130]: factor = pd.cut(arr, 4)

In [131]: factor
Out[131]: 
[(-0.251, 0.464], (-0.968, -0.251], (0.464, 1.179], (-0.251, 0.464], (-0.968, -0.251], ..., (-0.251, 0.464], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251], (-0.968, -0.251]]
Length: 20
Categories (4, interval[float64, right]): [(-0.968, -0.251] < (-0.251, 0.464] < (0.464, 1.179] <
                                           (1.179, 1.893]]

In [132]: factor = pd.cut(arr, [-5, -1, 0, 1, 5])

In [133]: factor
Out[133]: 
[(0, 1], (-1, 0], (0, 1], (0, 1], (-1, 0], ..., (-1, 0], (-1, 0], (-1, 0], (-1, 0], (-1, 0]]
Length: 20
Categories (4, interval[int64, right]): [(-5, -1] < (-1, 0] < (0, 1] < (1, 5]]

qcut() は標本分位点を計算します。たとえば、正規分布データを次のように等サイズの四分位に分割できます。

In [134]: arr = np.random.randn(30)

In [135]: factor = pd.qcut(arr, [0, 0.25, 0.5, 0.75, 1])

In [136]: factor
Out[136]: 
[(0.569, 1.184], (-2.278, -0.301], (-2.278, -0.301], (0.569, 1.184], (0.569, 1.184], ..., (-0.301, 0.569], (1.184, 2.346], (1.184, 2.346], (-0.301, 0.569], (-2.278, -0.301]]
Length: 30
Categories (4, interval[float64, right]): [(-2.278, -0.301] < (-0.301, 0.569] < (0.569, 1.184] <
                                           (1.184, 2.346]]

無限大の値を渡してビンを定義することもできます。

In [137]: arr = np.random.randn(20)

In [138]: factor = pd.cut(arr, [-np.inf, 0, np.inf])

In [139]: factor
Out[139]: 
[(-inf, 0.0], (0.0, inf], (0.0, inf], (-inf, 0.0], (-inf, 0.0], ..., (-inf, 0.0], (-inf, 0.0], (-inf, 0.0], (0.0, inf], (0.0, inf]]
Length: 20
Categories (2, interval[float64, right]): [(-inf, 0.0] < (0.0, inf]]

関数適用#

ご自身の関数や他のライブラリの関数を pandas オブジェクトに適用するには、以下の3つのメソッドを認識しておく必要があります。適切なメソッドは、関数が DataFrame または Series 全体、行または列ごと、あるいは要素ごとに操作することを期待するかどうかによって異なります。

  1. テーブルごとの関数適用: pipe()

  2. 行または列ごとの関数適用: apply()

  3. 集計 API: agg()transform()

  4. 要素ごとの関数の適用: map()

テーブルごとの関数適用#

DataFrames および Series は関数に渡すことができます。しかし、関数がチェーンで呼び出される必要がある場合は、pipe() メソッドの使用を検討してください。

まず、いくつかセットアップします。

In [140]: def extract_city_name(df):
   .....:     """
   .....:     Chicago, IL -> Chicago for city_name column
   .....:     """
   .....:     df["city_name"] = df["city_and_code"].str.split(",").str.get(0)
   .....:     return df
   .....: 

In [141]: def add_country_name(df, country_name=None):
   .....:     """
   .....:     Chicago -> Chicago-US for city_name column
   .....:     """
   .....:     col = "city_name"
   .....:     df["city_and_country"] = df[col] + country_name
   .....:     return df
   .....: 

In [142]: df_p = pd.DataFrame({"city_and_code": ["Chicago, IL"]})

extract_city_nameadd_country_name は、DataFrames を受け取り、返す関数です。

次に、以下を比較します。

In [143]: add_country_name(extract_city_name(df_p), country_name="US")
Out[143]: 
  city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS

以下と同等です。

In [144]: df_p.pipe(extract_city_name).pipe(add_country_name, country_name="US")
Out[144]: 
  city_and_code city_name city_and_country
0   Chicago, IL   Chicago        ChicagoUS

pandas は、メソッドチェーンとして知られる後者のスタイルを推奨しています。pipe を使用すると、pandas のメソッドと並行して、独自の関数や他のライブラリの関数をメソッドチェーンで簡単に使用できます。

上記の例では、関数 extract_city_nameadd_country_name はそれぞれ、最初の位置引数として DataFrame を期待していました。適用したい関数が、たとえば2番目の引数としてデータを取る場合はどうでしょうか?この場合、pipe(callable, data_keyword) のタプルを提供します。.pipe は、DataFrame をタプルで指定された引数にルーティングします。

例えば、statsmodels を使って回帰を当てはめることができます。彼らの API は、まず式を期待し、2番目の引数として data という DataFrame を期待します。pipe に関数とキーワードのペア (sm.ols, 'data') を渡します。

In [147]: import statsmodels.formula.api as sm

In [148]: bb = pd.read_csv("data/baseball.csv", index_col="id")

In [149]: (
   .....:     bb.query("h > 0")
   .....:     .assign(ln_h=lambda df: np.log(df.h))
   .....:     .pipe((sm.ols, "data"), "hr ~ ln_h + year + g + C(lg)")
   .....:     .fit()
   .....:     .summary()
   .....: )
   .....:
Out[149]:
<class 'statsmodels.iolib.summary.Summary'>
"""
                           OLS Regression Results
==============================================================================
Dep. Variable:                     hr   R-squared:                       0.685
Model:                            OLS   Adj. R-squared:                  0.665
Method:                 Least Squares   F-statistic:                     34.28
Date:                Tue, 22 Nov 2022   Prob (F-statistic):           3.48e-15
Time:                        05:34:17   Log-Likelihood:                -205.92
No. Observations:                  68   AIC:                             421.8
Df Residuals:                      63   BIC:                             432.9
Df Model:                           4
Covariance Type:            nonrobust
===============================================================================
                  coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------
Intercept   -8484.7720   4664.146     -1.819      0.074   -1.78e+04     835.780
C(lg)[T.NL]    -2.2736      1.325     -1.716      0.091      -4.922       0.375
ln_h           -1.3542      0.875     -1.547      0.127      -3.103       0.395
year            4.2277      2.324      1.819      0.074      -0.417       8.872
g               0.1841      0.029      6.258      0.000       0.125       0.243
==============================================================================
Omnibus:                       10.875   Durbin-Watson:                   1.999
Prob(Omnibus):                  0.004   Jarque-Bera (JB):               17.298
Skew:                           0.537   Prob(JB):                     0.000175
Kurtosis:                       5.225   Cond. No.                     1.49e+07
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
"""

pipe メソッドは、Unix パイプ、そして最近では dplyrmagrittr にインスパイアされています。これらは、R 用の人気のある (%>%) (読み取りパイプ) 演算子を導入しました。ここでの pipe の実装は非常にクリーンで、Python にしっくりと馴染みます。pipe() のソースコードをぜひご覧ください。

行または列ごとの関数適用#

apply() メソッドを使用すると、DataFrame の軸に沿って任意の関数を適用できます。これは、記述統計メソッドと同様に、オプションの axis 引数を取ります。

In [145]: df.apply(lambda x: np.mean(x))
Out[145]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [146]: df.apply(lambda x: np.mean(x), axis=1)
Out[146]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

In [147]: df.apply(lambda x: x.max() - x.min())
Out[147]: 
one      1.051928
two      1.632779
three    1.840607
dtype: float64

In [148]: df.apply(np.cumsum)
Out[148]: 
        one       two     three
a  1.394981  1.772517       NaN
b  1.738035  3.684640 -0.050390
c  2.433281  5.163008  1.177045
d       NaN  5.442353  0.563873

In [149]: df.apply(np.exp)
Out[149]: 
        one       two     three
a  4.034899  5.885648       NaN
b  1.409244  6.767440  0.950858
c  2.004201  4.385785  3.412466
d       NaN  1.322262  0.541630

apply() メソッドは、文字列メソッド名でもディスパッチします。

In [150]: df.apply("mean")
Out[150]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64

In [151]: df.apply("mean", axis=1)
Out[151]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

apply() に渡される関数の戻り値の型は、デフォルトの動作における DataFrame.apply からの最終出力の型に影響します。

  • 適用された関数が Series を返す場合、最終出力は DataFrame になります。列は、適用された関数によって返された Series のインデックスと一致します。

  • 適用された関数が他の型を返す場合、最終出力は Series になります。

このデフォルトの動作は、3つのオプション (reducebroadcast、および expand) を受け入れる result_type を使用してオーバーライドできます。これらは、リストのような戻り値が DataFrame にどのように展開されるか (または展開されないか) を決定します。

apply() を賢く組み合わせることで、データセットに関する多くの質問に答えることができます。たとえば、各列の最大値が発生した日付を抽出したいとします。

In [152]: tsdf = pd.DataFrame(
   .....:     np.random.randn(1000, 3),
   .....:     columns=["A", "B", "C"],
   .....:     index=pd.date_range("1/1/2000", periods=1000),
   .....: )
   .....: 

In [153]: tsdf.apply(lambda x: x.idxmax())
Out[153]: 
A   2000-08-06
B   2001-01-18
C   2001-07-18
dtype: datetime64[ns]

apply() メソッドには、追加の引数とキーワード引数を渡すこともできます。

In [154]: def subtract_and_divide(x, sub, divide=1):
   .....:     return (x - sub) / divide
   .....: 

In [155]: df_udf = pd.DataFrame(np.ones((2, 2)))

In [156]: df_udf.apply(subtract_and_divide, args=(5,), divide=3)
Out[156]: 
          0         1
0 -1.333333 -1.333333
1 -1.333333 -1.333333

もう1つの便利な機能は、各列または行で Series 操作を実行するために Series メソッドを渡す機能です。

In [157]: tsdf = pd.DataFrame(
   .....:     np.random.randn(10, 3),
   .....:     columns=["A", "B", "C"],
   .....:     index=pd.date_range("1/1/2000", periods=10),
   .....: )
   .....: 

In [158]: tsdf.iloc[3:7] = np.nan

In [159]: tsdf
Out[159]: 
                   A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950

In [160]: tsdf.apply(pd.Series.interpolate)
Out[160]: 
                   A         B         C
2000-01-01 -0.158131 -0.232466  0.321604
2000-01-02 -1.810340 -3.105758  0.433834
2000-01-03 -1.209847 -1.156793 -0.136794
2000-01-04 -1.098598 -0.889659  0.092225
2000-01-05 -0.987349 -0.622526  0.321243
2000-01-06 -0.876100 -0.355392  0.550262
2000-01-07 -0.764851 -0.088259  0.779280
2000-01-08 -0.653602  0.178875  1.008298
2000-01-09  1.007996  0.462824  0.254472
2000-01-10  0.307473  0.600337  1.643950

最後に、apply() は、デフォルトでは False である raw 引数を受け取ります。これは、関数を適用する前に各行または列を Series に変換します。True に設定すると、渡された関数は代わりに ndarray オブジェクトを受け取ります。これにより、インデックス作成機能が必要ない場合は、パフォーマンスが向上します。

集計 API#

集計 API を使用すると、おそらく複数の集計操作を単一の簡潔な方法で表現できます。この API は pandas オブジェクト全体で類似しており、groupby APIwindow API、および resample API を参照してください。集計のエントリーポイントは DataFrame.aggregate()、またはエイリアス DataFrame.agg() です。

上記で使用したフレームと同様のフレームを以下で使用します。

In [161]: tsdf = pd.DataFrame(
   .....:     np.random.randn(10, 3),
   .....:     columns=["A", "B", "C"],
   .....:     index=pd.date_range("1/1/2000", periods=10),
   .....: )
   .....: 

In [162]: tsdf.iloc[3:7] = np.nan

In [163]: tsdf
Out[163]: 
                   A         B         C
2000-01-01  1.257606  1.004194  0.167574
2000-01-02 -0.749892  0.288112 -0.757304
2000-01-03 -0.207550 -0.298599  0.116018
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.814347 -0.257623  0.869226
2000-01-09 -0.250663 -1.206601  0.896839
2000-01-10  2.169758 -1.333363  0.283157

単一の関数を使用することは apply() と同等です。名前付きメソッドを文字列として渡すこともできます。これらは、集計された出力の Series を返します。

In [164]: tsdf.agg(lambda x: np.sum(x))
Out[164]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

In [165]: tsdf.agg("sum")
Out[165]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

# these are equivalent to a ``.sum()`` because we are aggregating
# on a single function
In [166]: tsdf.sum()
Out[166]: 
A    3.033606
B   -1.803879
C    1.575510
dtype: float64

Series に対する単一の集計はスカラー値を返します。

In [167]: tsdf["A"].agg("sum")
Out[167]: 3.033606102414146

複数の関数での集計#

複数の集計引数をリストとして渡すことができます。渡された各関数の結果は、結果の DataFrame の行になります。これらは集計関数から自然に名前が付けられます。

In [168]: tsdf.agg(["sum"])
Out[168]: 
            A         B        C
sum  3.033606 -1.803879  1.57551

複数の関数は複数の行を生成します。

In [169]: tsdf.agg(["sum", "mean"])
Out[169]: 
             A         B         C
sum   3.033606 -1.803879  1.575510
mean  0.505601 -0.300647  0.262585

Series では、複数の関数は関数名でインデックス付けされた Series を返します。

In [170]: tsdf["A"].agg(["sum", "mean"])
Out[170]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64

lambda 関数を渡すと、<lambda> という名前の行が生成されます。

In [171]: tsdf["A"].agg(["sum", lambda x: x.mean()])
Out[171]: 
sum         3.033606
<lambda>    0.505601
Name: A, dtype: float64

名前付き関数を渡すと、その名前が行に付けられます。

In [172]: def mymean(x):
   .....:     return x.mean()
   .....: 

In [173]: tsdf["A"].agg(["sum", mymean])
Out[173]: 
sum       3.033606
mymean    0.505601
Name: A, dtype: float64

辞書を使用した集計#

DataFrame.agg に、列名からスカラーまたはスカラーのリストへの辞書を渡すことで、どの列にどの関数を適用するかをカスタマイズできます。結果は特定の順序にならないことに注意してください。順序を保証するには、代わりに OrderedDict を使用できます。

In [174]: tsdf.agg({"A": "mean", "B": "sum"})
Out[174]: 
A    0.505601
B   -1.803879
dtype: float64

リストのようなオブジェクトを渡すと、DataFrame の出力が生成されます。すべてのアグリゲーターの行列のような出力が得られます。出力はすべてのユニークな関数で構成されます。特定の列に対して指定されていないものは NaN になります。

In [175]: tsdf.agg({"A": ["mean", "min"], "B": "sum"})
Out[175]: 
             A         B
mean  0.505601       NaN
min  -0.749892       NaN
sum        NaN -1.803879

カスタム記述#

.agg() を使用すると、組み込みの 記述関数 と同様のカスタム記述関数を簡単に作成できます。

In [176]: from functools import partial

In [177]: q_25 = partial(pd.Series.quantile, q=0.25)

In [178]: q_25.__name__ = "25%"

In [179]: q_75 = partial(pd.Series.quantile, q=0.75)

In [180]: q_75.__name__ = "75%"

In [181]: tsdf.agg(["count", "mean", "std", "min", q_25, "median", q_75, "max"])
Out[181]: 
               A         B         C
count   6.000000  6.000000  6.000000
mean    0.505601 -0.300647  0.262585
std     1.103362  0.887508  0.606860
min    -0.749892 -1.333363 -0.757304
25%    -0.239885 -0.979600  0.128907
median  0.303398 -0.278111  0.225365
75%     1.146791  0.151678  0.722709
max     2.169758  1.004194  0.896839

変換 API#

transform() メソッドは、元のオブジェクトと同じインデックス (同じサイズ) を持つオブジェクトを返します。この API を使用すると、一度に複数の操作を個別にではなく提供できます。その API は .agg API と非常によく似ています。

上記のセクションで使用したフレームに似たフレームを作成します。

In [182]: tsdf = pd.DataFrame(
   .....:     np.random.randn(10, 3),
   .....:     columns=["A", "B", "C"],
   .....:     index=pd.date_range("1/1/2000", periods=10),
   .....: )
   .....: 

In [183]: tsdf.iloc[3:7] = np.nan

In [184]: tsdf
Out[184]: 
                   A         B         C
2000-01-01 -0.428759 -0.864890 -0.675341
2000-01-02 -0.168731  1.338144 -1.279321
2000-01-03 -1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -1.240447 -0.201052
2000-01-09 -0.157795  0.791197 -1.144209
2000-01-10 -0.030876  0.371900  0.061932

フレーム全体を変換します。.transform() は、NumPy 関数、文字列関数名、またはユーザー定義関数として入力関数を受け入れます。

In [185]: tsdf.transform(np.abs)
Out[185]: 
                   A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [186]: tsdf.transform("abs")
Out[186]: 
                   A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

In [187]: tsdf.transform(lambda x: x.abs())
Out[187]: 
                   A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

ここでは、transform() が単一の関数を受け取りました。これは ufunc の適用と同等です。

In [188]: np.abs(tsdf)
Out[188]: 
                   A         B         C
2000-01-01  0.428759  0.864890  0.675341
2000-01-02  0.168731  1.338144  1.279321
2000-01-03  1.621034  0.438107  0.903794
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374  1.240447  0.201052
2000-01-09  0.157795  0.791197  1.144209
2000-01-10  0.030876  0.371900  0.061932

Series に対して単一の関数を .transform() に渡すと、単一の Series が返されます。

In [189]: tsdf["A"].transform(np.abs)
Out[189]: 
2000-01-01    0.428759
2000-01-02    0.168731
2000-01-03    1.621034
2000-01-04         NaN
2000-01-05         NaN
2000-01-06         NaN
2000-01-07         NaN
2000-01-08    0.254374
2000-01-09    0.157795
2000-01-10    0.030876
Freq: D, Name: A, dtype: float64

複数の関数での変換#

複数の関数を渡すと、列の MultiIndexed DataFrame が生成されます。最初のレベルは元のフレームの列名になり、2番目のレベルは変換関数の名前になります。

In [190]: tsdf.transform([np.abs, lambda x: x + 1])
Out[190]: 
                   A                   B                   C          
            absolute  <lambda>  absolute  <lambda>  absolute  <lambda>
2000-01-01  0.428759  0.571241  0.864890  0.135110  0.675341  0.324659
2000-01-02  0.168731  0.831269  1.338144  2.338144  1.279321 -0.279321
2000-01-03  1.621034 -0.621034  0.438107  1.438107  0.903794  1.903794
2000-01-04       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN       NaN       NaN       NaN
2000-01-08  0.254374  1.254374  1.240447 -0.240447  0.201052  0.798948
2000-01-09  0.157795  0.842205  0.791197  1.791197  1.144209 -0.144209
2000-01-10  0.030876  0.969124  0.371900  1.371900  0.061932  1.061932

Series に複数の関数を渡すと、DataFrame が生成されます。結果の列名は変換関数になります。

In [191]: tsdf["A"].transform([np.abs, lambda x: x + 1])
Out[191]: 
            absolute  <lambda>
2000-01-01  0.428759  0.571241
2000-01-02  0.168731  0.831269
2000-01-03  1.621034 -0.621034
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374  1.254374
2000-01-09  0.157795  0.842205
2000-01-10  0.030876  0.969124

辞書による変換#

関数の辞書を渡すと、列ごとに選択的な変換が可能になります。

In [192]: tsdf.transform({"A": np.abs, "B": lambda x: x + 1})
Out[192]: 
                   A         B
2000-01-01  0.428759  0.135110
2000-01-02  0.168731  2.338144
2000-01-03  1.621034  1.438107
2000-01-04       NaN       NaN
2000-01-05       NaN       NaN
2000-01-06       NaN       NaN
2000-01-07       NaN       NaN
2000-01-08  0.254374 -0.240447
2000-01-09  0.157795  1.791197
2000-01-10  0.030876  1.371900

リストの辞書を渡すと、これらの選択的な変換を含むMultiIndexed DataFrameが生成されます。

In [193]: tsdf.transform({"A": np.abs, "B": [lambda x: x + 1, "sqrt"]})
Out[193]: 
                   A         B          
            absolute  <lambda>      sqrt
2000-01-01  0.428759  0.135110       NaN
2000-01-02  0.168731  2.338144  1.156782
2000-01-03  1.621034  1.438107  0.661897
2000-01-04       NaN       NaN       NaN
2000-01-05       NaN       NaN       NaN
2000-01-06       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN
2000-01-08  0.254374 -0.240447       NaN
2000-01-09  0.157795  1.791197  0.889493
2000-01-10  0.030876  1.371900  0.609836

要素ごとの関数の適用#

すべての関数がベクトル化できるわけではないため (NumPy 配列を受け入れて別の配列または値を返す)、DataFrame の map() メソッド、および同様に Series の map() メソッドは、単一の値を受け取り、単一の値を返す任意の Python 関数を受け入れます。例として

In [194]: df4 = df.copy()

In [195]: df4
Out[195]: 
        one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [196]: def f(x):
   .....:     return len(str(x))
   .....: 

In [197]: df4["one"].map(f)
Out[197]: 
a    18
b    19
c    18
d     3
Name: one, dtype: int64

In [198]: df4.map(f)
Out[198]: 
   one  two  three
a   18   17      3
b   19   18     20
c   18   18     16
d    3   19     19

Series.map() には追加機能があります。これは、セカンダリシリーズによって定義された値を簡単に「リンク」または「マップ」するために使用できます。これは マージ/結合機能 に密接に関連しています。

In [199]: s = pd.Series(
   .....:     ["six", "seven", "six", "seven", "six"], index=["a", "b", "c", "d", "e"]
   .....: )
   .....: 

In [200]: t = pd.Series({"six": 6.0, "seven": 7.0})

In [201]: s
Out[201]: 
a      six
b    seven
c      six
d    seven
e      six
dtype: object

In [202]: s.map(t)
Out[202]: 
a    6.0
b    7.0
c    6.0
d    7.0
e    6.0
dtype: float64

インデックスの再設定とラベルの変更#

reindex() は pandas の基本的なデータアラインメントメソッドです。ラベルアラインメント機能に依存する他のほとんどすべての機能を実装するために使用されます。インデックスの再設定とは、特定の軸に沿って指定されたラベルセットにデータを適合させることを意味します。これにより、いくつかのことが達成されます。

  • 既存のデータを新しいラベルセットと一致するように並べ替えます。

  • そのラベルのデータが存在しないラベル位置に欠損値 (NA) マーカーを挿入します。

  • 指定した場合、論理を使用して欠損ラベルのデータを埋めます (時系列データを扱う場合に非常に重要です)。

以下に簡単な例を示します。

In [203]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [204]: s
Out[204]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
e   -1.326508
dtype: float64

In [205]: s.reindex(["e", "b", "f", "d"])
Out[205]: 
e   -1.326508
b    1.328614
f         NaN
d   -0.385845
dtype: float64

ここでは、f ラベルが Series に含まれていなかったため、結果には NaN として表示されます。

DataFrame を使用すると、インデックスと列を同時に再インデックスできます。

In [206]: df
Out[206]: 
        one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [207]: df.reindex(index=["c", "f", "b"], columns=["three", "two", "one"])
Out[207]: 
      three       two       one
c  1.227435  1.478369  0.695246
f       NaN       NaN       NaN
b -0.050390  1.912123  0.343054

実際の軸ラベルを含む Index オブジェクトは、オブジェクト間で共有できることに注意してください。したがって、Series と DataFrame がある場合、次のことができます。

In [208]: rs = s.reindex(df.index)

In [209]: rs
Out[209]: 
a    1.695148
b    1.328614
c    1.234686
d   -0.385845
dtype: float64

In [210]: rs.index is df.index
Out[210]: True

これは、再インデックスされた Series のインデックスが、DataFrame のインデックスと同じ Python オブジェクトであることを意味します。

DataFrame.reindex() は、「軸スタイル」の呼び出し規則もサポートしています。これは、単一の labels 引数と、それが適用される axis を指定するものです。

In [211]: df.reindex(["c", "f", "b"], axis="index")
Out[211]: 
        one       two     three
c  0.695246  1.478369  1.227435
f       NaN       NaN       NaN
b  0.343054  1.912123 -0.050390

In [212]: df.reindex(["three", "two", "one"], axis="columns")
Out[212]: 
      three       two       one
a       NaN  1.772517  1.394981
b -0.050390  1.912123  0.343054
c  1.227435  1.478369  0.695246
d -0.613172  0.279344       NaN

参照

MultiIndex / Advanced Indexing は、インデックスの再設定を行うさらに簡潔な方法です。

パフォーマンスに敏感なコードを書く際には、リインデックスの達人になるために時間をかけるべき正当な理由があります。それは、**事前にアラインされたデータでは多くの操作が高速になる**からです。アラインされていない2つの DataFrame を追加すると、内部的にリインデックスのステップがトリガーされます。探索的分析ではほとんど違いに気づかないでしょう (なぜなら reindex は大幅に最適化されているからです) が、CPU サイクルが重要な場合は、いくつかの明示的な reindex 呼び出しを散りばめることで影響を与えることができます。

他のオブジェクトとアラインするための再インデックス#

あるオブジェクトの軸を、別のオブジェクトと同じラベルで再インデックスしたい場合があります。これは構文は簡単ですが冗長なため、この操作を簡単にするために reindex_like() メソッドが利用できます。

In [213]: df2 = df.reindex(["a", "b", "c"], columns=["one", "two"])

In [214]: df3 = df2 - df2.mean()

In [215]: df2
Out[215]: 
        one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369

In [216]: df3
Out[216]: 
        one       two
a  0.583888  0.051514
b -0.468040  0.191120
c -0.115848 -0.242634

In [217]: df.reindex_like(df2)
Out[217]: 
        one       two
a  1.394981  1.772517
b  0.343054  1.912123
c  0.695246  1.478369

align でオブジェクトを互いにアラインする#

align() メソッドは、2つのオブジェクトを同時にアラインする最も高速な方法です。join 引数をサポートしています (結合とマージ に関連)。

  • join='outer': インデックスの和集合を取る (デフォルト)

  • join='left': 呼び出し元オブジェクトのインデックスを使用する

  • join='right': 渡されたオブジェクトのインデックスを使用する

  • join='inner': インデックスを交差させる

再インデックスされた両方の Series を含むタプルを返します。

In [218]: s = pd.Series(np.random.randn(5), index=["a", "b", "c", "d", "e"])

In [219]: s1 = s[:4]

In [220]: s2 = s[1:]

In [221]: s1.align(s2)
Out[221]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e         NaN
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 e    1.114285
 dtype: float64)

In [222]: s1.align(s2, join="inner")
Out[222]: 
(b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64)

In [223]: s1.align(s2, join="left")
Out[223]: 
(a   -0.186646
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64,
 a         NaN
 b   -1.692424
 c   -0.303893
 d   -1.425662
 dtype: float64)

DataFrames の場合、join メソッドはデフォルトでインデックスと列の両方に適用されます。

In [224]: df.align(df2, join="inner")
Out[224]: 
(        one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369,
         one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369)

また、指定された軸のみでアラインするために axis オプションを渡すこともできます。

In [225]: df.align(df2, join="inner", axis=0)
Out[225]: 
(        one       two     three
 a  1.394981  1.772517       NaN
 b  0.343054  1.912123 -0.050390
 c  0.695246  1.478369  1.227435,
         one       two
 a  1.394981  1.772517
 b  0.343054  1.912123
 c  0.695246  1.478369)

DataFrame.align() に Series を渡す場合、axis 引数を使用して、DataFrame のインデックスまたは列のいずれかで両方のオブジェクトをアラインできます。

In [226]: df.align(df2.iloc[0], axis=1)
Out[226]: 
(        one     three       two
 a  1.394981       NaN  1.772517
 b  0.343054 -0.050390  1.912123
 c  0.695246  1.227435  1.478369
 d       NaN -0.613172  0.279344,
 one      1.394981
 three         NaN
 two      1.772517
 Name: a, dtype: float64)

再インデックス時の埋め込み#

reindex() は、オプションのパラメーター method を受け取ります。これは、以下の表から選択される埋め込みメソッドです。

メソッド

アクション

パッド / ffill

値を前方に埋める

bfill / backfill

値を後方に埋める

最近隣

最も近いインデックス値から埋める

これらの埋め込み方法を簡単な Series で説明します。

In [227]: rng = pd.date_range("1/3/2000", periods=8)

In [228]: ts = pd.Series(np.random.randn(8), index=rng)

In [229]: ts2 = ts.iloc[[0, 3, 6]]

In [230]: ts
Out[230]: 
2000-01-03    0.183051
2000-01-04    0.400528
2000-01-05   -0.015083
2000-01-06    2.395489
2000-01-07    1.414806
2000-01-08    0.118428
2000-01-09    0.733639
2000-01-10   -0.936077
Freq: D, dtype: float64

In [231]: ts2
Out[231]: 
2000-01-03    0.183051
2000-01-06    2.395489
2000-01-09    0.733639
Freq: 3D, dtype: float64

In [232]: ts2.reindex(ts.index)
Out[232]: 
2000-01-03    0.183051
2000-01-04         NaN
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07         NaN
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [233]: ts2.reindex(ts.index, method="ffill")
Out[233]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

In [234]: ts2.reindex(ts.index, method="bfill")
Out[234]: 
2000-01-03    0.183051
2000-01-04    2.395489
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    0.733639
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10         NaN
Freq: D, dtype: float64

In [235]: ts2.reindex(ts.index, method="nearest")
Out[235]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    2.395489
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    0.733639
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

これらのメソッドは、インデックスが順序付けられている (昇順または降順) 必要があります。

同じ結果は ffill (ただし method='nearest' は除く) または interpolate を使用しても達成できたことに注意してください。

In [236]: ts2.reindex(ts.index).ffill()
Out[236]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05    0.183051
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08    2.395489
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

インデックスが単調増加または減少していない場合、reindex() は ValueError を発生させます。fillna()interpolate() は、インデックスの順序に関するチェックを行いません。

再インデックス時の埋め込みの制限#

limit および tolerance 引数は、再インデックス時に埋め込みに対する追加の制御を提供します。limit は連続する一致の最大数を指定します。

In [237]: ts2.reindex(ts.index, method="ffill", limit=1)
Out[237]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

対照的に、tolerance はインデックスとインデクサ値間の最大距離を指定します。

In [238]: ts2.reindex(ts.index, method="ffill", tolerance="1 day")
Out[238]: 
2000-01-03    0.183051
2000-01-04    0.183051
2000-01-05         NaN
2000-01-06    2.395489
2000-01-07    2.395489
2000-01-08         NaN
2000-01-09    0.733639
2000-01-10    0.733639
Freq: D, dtype: float64

DatetimeIndexTimedeltaIndex、または PeriodIndex で使用される場合、tolerance は可能であれば Timedelta に強制変換されることに注意してください。これにより、適切な文字列で tolerance を指定できます。

軸からのラベルの削除#

reindex に密接に関連するメソッドは drop() 関数です。これは軸から一連のラベルを削除します。

In [239]: df
Out[239]: 
        one       two     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [240]: df.drop(["a", "d"], axis=0)
Out[240]: 
        one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435

In [241]: df.drop(["one"], axis=1)
Out[241]: 
        two     three
a  1.772517       NaN
b  1.912123 -0.050390
c  1.478369  1.227435
d  0.279344 -0.613172

以下の方法でも機能しますが、少し分かりにくく、簡潔ではありません。

In [242]: df.reindex(df.index.difference(["a", "d"]))
Out[242]: 
        one       two     three
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435

ラベルの名称変更 / マッピング#

rename() メソッドを使用すると、マッピング (辞書または Series) または任意の関数に基づいて軸のラベルを変更できます。

In [243]: s
Out[243]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
dtype: float64

In [244]: s.rename(str.upper)
Out[244]: 
A   -0.186646
B   -1.692424
C   -0.303893
D   -1.425662
E    1.114285
dtype: float64

関数を渡す場合、その関数は任意のラベルで呼び出されたときに値を返す必要があり (かつ、一意の値のセットを生成する必要があります)、辞書または Series も使用できます。

In [245]: df.rename(
   .....:     columns={"one": "foo", "two": "bar"},
   .....:     index={"a": "apple", "b": "banana", "d": "durian"},
   .....: )
   .....: 
Out[245]: 
             foo       bar     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172

マッピングに列/インデックスラベルが含まれていない場合、それは名前変更されません。マッピング内の余分なラベルはエラーを発生させません。

DataFrame.rename() は、「軸スタイル」の呼び出し規則もサポートしています。これは、単一の mapper と、そのマッピングを適用する axis を指定するものです。

In [246]: df.rename({"one": "foo", "two": "bar"}, axis="columns")
Out[246]: 
        foo       bar     three
a  1.394981  1.772517       NaN
b  0.343054  1.912123 -0.050390
c  0.695246  1.478369  1.227435
d       NaN  0.279344 -0.613172

In [247]: df.rename({"a": "apple", "b": "banana", "d": "durian"}, axis="index")
Out[247]: 
             one       two     three
apple   1.394981  1.772517       NaN
banana  0.343054  1.912123 -0.050390
c       0.695246  1.478369  1.227435
durian       NaN  0.279344 -0.613172

最後に、rename() は、Series.name 属性を変更するために、スカラーまたはリストのようなものも受け入れます。

In [248]: s.rename("scalar-name")
Out[248]: 
a   -0.186646
b   -1.692424
c   -0.303893
d   -1.425662
e    1.114285
Name: scalar-name, dtype: float64

DataFrame.rename_axis()Series.rename_axis() メソッドを使用すると、MultiIndex の特定の名前 (ラベルとは対照的に) を変更できます。

In [249]: df = pd.DataFrame(
   .....:     {"x": [1, 2, 3, 4, 5, 6], "y": [10, 20, 30, 40, 50, 60]},
   .....:     index=pd.MultiIndex.from_product(
   .....:         [["a", "b", "c"], [1, 2]], names=["let", "num"]
   .....:     ),
   .....: )
   .....: 

In [250]: df
Out[250]: 
         x   y
let num       
a   1    1  10
    2    2  20
b   1    3  30
    2    4  40
c   1    5  50
    2    6  60

In [251]: df.rename_axis(index={"let": "abc"})
Out[251]: 
         x   y
abc num       
a   1    1  10
    2    2  20
b   1    3  30
    2    4  40
c   1    5  50
    2    6  60

In [252]: df.rename_axis(index=str.upper)
Out[252]: 
         x   y
LET NUM       
a   1    1  10
    2    2  20
b   1    3  30
    2    4  40
c   1    5  50
    2    6  60

イテレーション#

pandas オブジェクトの基本的な反復の動作は、その型によって異なります。Series を反復する場合、それは配列のようなものと見なされ、基本的な反復は値を生成します。DataFrame はオブジェクトの「キー」を反復する辞書のような規則に従います。

つまり、基本的なイテレーション (for i in object) は以下を生成します。

  • Series: 値

  • DataFrame: 列ラベル

したがって、例えば DataFrame を反復すると、列名が得られます。

In [253]: df = pd.DataFrame(
   .....:     {"col1": np.random.randn(3), "col2": np.random.randn(3)}, index=["a", "b", "c"]
   .....: )
   .....: 

In [254]: for col in df:
   .....:     print(col)
   .....: 
col1
col2

pandas オブジェクトには、(キー、値) のペアを反復処理するための辞書のような items() メソッドもあります。

DataFrame の行を反復処理するには、次のメソッドを使用できます。

  • iterrows(): DataFrame の行を (インデックス、Series) ペアとして反復処理します。これにより、行が Series オブジェクトに変換され、dtypes が変更される可能性があり、パフォーマンスに影響を与えます。

  • itertuples(): DataFrame の行を値の namedtuple として反復処理します。これは iterrows() よりもはるかに高速であり、ほとんどの場合、DataFrame の値を反復処理するために推奨されます。

警告

pandas オブジェクトを介した反復処理は、一般的に遅いです。多くの場合、行を手動で反復処理する必要はなく、次のいずれかの方法で回避できます。

  • ベクトル化されたソリューションを探す: 多くの操作は、組み込みメソッドまたは NumPy 関数、(ブール) インデックスなどを使用して実行できます。

  • DataFrame/Series 全体に一度に適用できない関数がある場合は、値を反復処理するよりも apply() を使用する方が良いです。関数適用 のドキュメントを参照してください。

  • 値に対する反復的な操作が必要で、パフォーマンスが重要である場合は、Cython または Numba で内部ループを記述することを検討してください。このアプローチの例については、パフォーマンスの向上 セクションを参照してください。

警告

イテレーション中のオブジェクトを決して変更してはいけません。これはすべてのケースで機能する保証はありません。データ型によっては、イテレータがビューではなくコピーを返し、そこに書き込んでも効果がありません!

例えば、以下のケースでは値を設定しても効果はありません。

In [255]: df = pd.DataFrame({"a": [1, 2, 3], "b": ["a", "b", "c"]})

In [256]: for index, row in df.iterrows():
   .....:     row["a"] = 10
   .....: 

In [257]: df
Out[257]: 
   a  b
0  1  a
1  2  b
2  3  c

アイテム#

辞書のようなインターフェースと一貫して、items() はキーと値のペアを反復処理します。

  • Series: (インデックス、スカラー値) のペア

  • DataFrame: (列、Series) のペア

例えば

In [258]: for label, ser in df.items():
   .....:     print(label)
   .....:     print(ser)
   .....: 
a
0    1
1    2
2    3
Name: a, dtype: int64
b
0    a
1    b
2    c
Name: b, dtype: object

iterrows#

iterrows() を使用すると、DataFrame の行を Series オブジェクトとして反復処理できます。これは、各インデックス値と、各行のデータを含む Series を返すイテレータです。

In [259]: for row_index, row in df.iterrows():
   .....:     print(row_index, row, sep="\n")
   .....: 
0
a    1
b    a
Name: 0, dtype: object
1
a    2
b    b
Name: 1, dtype: object
2
a    3
b    c
Name: 2, dtype: object

iterrows() は各行に対して Series を返すため、行全体で dtypes を保持**しません** (DataFrame の場合は列全体で dtypes が保持されます)。たとえば、

In [260]: df_orig = pd.DataFrame([[1, 1.5]], columns=["int", "float"])

In [261]: df_orig.dtypes
Out[261]: 
int        int64
float    float64
dtype: object

In [262]: row = next(df_orig.iterrows())[1]

In [263]: row
Out[263]: 
int      1.0
float    1.5
Name: 0, dtype: float64

row 内のすべての値は Series として返され、元の整数値 x も含めて、すべて浮動小数点数にアップキャストされます。

In [264]: row["int"].dtype
Out[264]: dtype('float64')

In [265]: df_orig["int"].dtype
Out[265]: dtype('int64')

行を反復処理しながら dtype を保持するには、値の namedtuple を返し、一般的に iterrows() よりもはるかに高速な itertuples() を使用する方が良いでしょう。

例えば、DataFrame を転置する不自然な方法は次のようになります。

In [266]: df2 = pd.DataFrame({"x": [1, 2, 3], "y": [4, 5, 6]})

In [267]: print(df2)
   x  y
0  1  4
1  2  5
2  3  6

In [268]: print(df2.T)
   0  1  2
x  1  2  3
y  4  5  6

In [269]: df2_t = pd.DataFrame({idx: values for idx, values in df2.iterrows()})

In [270]: print(df2_t)
   0  1  2
x  1  2  3
y  4  5  6

itertuples#

itertuples() メソッドは、DataFrame の各行に対して namedtuple を生成するイテレータを返します。タプルの最初の要素は行に対応するインデックス値であり、残りの値は行の値です。

例えば

In [271]: for row in df.itertuples():
   .....:     print(row)
   .....: 
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c')

このメソッドは行を Series オブジェクトに変換しません。単に namedtuple 内の値を返します。したがって、itertuples() は値のデータ型を保持し、通常 iterrows() と同様に高速です。

列名が有効な Python 識別子でない場合、重複している場合、またはアンダースコアで始まる場合は、位置名に名前が変更されます。列数が多い場合 (>255)、通常のタプルが返されます。

.dt アクセサ#

Series は、 datetime/period のような Series である場合、Series の「値」の datetime のようなプロパティを簡潔に返すためのアクセサを持っています。これは、既存の Series と同じインデックスを持つ Series を返します。

# datetime
In [272]: s = pd.Series(pd.date_range("20130101 09:10:12", periods=4))

In [273]: s
Out[273]: 
0   2013-01-01 09:10:12
1   2013-01-02 09:10:12
2   2013-01-03 09:10:12
3   2013-01-04 09:10:12
dtype: datetime64[ns]

In [274]: s.dt.hour
Out[274]: 
0    9
1    9
2    9
3    9
dtype: int32

In [275]: s.dt.second
Out[275]: 
0    12
1    12
2    12
3    12
dtype: int32

In [276]: s.dt.day
Out[276]: 
0    1
1    2
2    3
3    4
dtype: int32

これにより、次のような素晴らしい式が可能になります。

In [277]: s[s.dt.day == 2]
Out[277]: 
1   2013-01-02 09:10:12
dtype: datetime64[ns]

簡単にタイムゾーンを考慮した変換を行うことができます。

In [278]: stz = s.dt.tz_localize("US/Eastern")

In [279]: stz
Out[279]: 
0   2013-01-01 09:10:12-05:00
1   2013-01-02 09:10:12-05:00
2   2013-01-03 09:10:12-05:00
3   2013-01-04 09:10:12-05:00
dtype: datetime64[ns, US/Eastern]

In [280]: stz.dt.tz
Out[280]: <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>

これらの種類の操作を連結することもできます。

In [281]: s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")
Out[281]: 
0   2013-01-01 04:10:12-05:00
1   2013-01-02 04:10:12-05:00
2   2013-01-03 04:10:12-05:00
3   2013-01-04 04:10:12-05:00
dtype: datetime64[ns, US/Eastern]

Series.dt.strftime() を使用して datetime 値を文字列としてフォーマットすることもできます。これは標準の strftime() と同じフォーマットをサポートしています。

# DatetimeIndex
In [282]: s = pd.Series(pd.date_range("20130101", periods=4))

In [283]: s
Out[283]: 
0   2013-01-01
1   2013-01-02
2   2013-01-03
3   2013-01-04
dtype: datetime64[ns]

In [284]: s.dt.strftime("%Y/%m/%d")
Out[284]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object
# PeriodIndex
In [285]: s = pd.Series(pd.period_range("20130101", periods=4))

In [286]: s
Out[286]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

In [287]: s.dt.strftime("%Y/%m/%d")
Out[287]: 
0    2013/01/01
1    2013/01/02
2    2013/01/03
3    2013/01/04
dtype: object

.dt アクセサは period および timedelta の dtype で動作します。

# period
In [288]: s = pd.Series(pd.period_range("20130101", periods=4, freq="D"))

In [289]: s
Out[289]: 
0    2013-01-01
1    2013-01-02
2    2013-01-03
3    2013-01-04
dtype: period[D]

In [290]: s.dt.year
Out[290]: 
0    2013
1    2013
2    2013
3    2013
dtype: int64

In [291]: s.dt.day
Out[291]: 
0    1
1    2
2    3
3    4
dtype: int64
# timedelta
In [292]: s = pd.Series(pd.timedelta_range("1 day 00:00:05", periods=4, freq="s"))

In [293]: s
Out[293]: 
0   1 days 00:00:05
1   1 days 00:00:06
2   1 days 00:00:07
3   1 days 00:00:08
dtype: timedelta64[ns]

In [294]: s.dt.days
Out[294]: 
0    1
1    1
2    1
3    1
dtype: int64

In [295]: s.dt.seconds
Out[295]: 
0    5
1    6
2    7
3    8
dtype: int32

In [296]: s.dt.components
Out[296]: 
   days  hours  minutes  seconds  milliseconds  microseconds  nanoseconds
0     1      0        0        5             0             0            0
1     1      0        0        6             0             0            0
2     1      0        0        7             0             0            0
3     1      0        0        8             0             0            0

Series.dt は、datetime のような値でないものでアクセスすると TypeError を発生させます。

ベクトル化された文字列メソッド#

Series には、配列の各要素に対して操作を簡単に行うための文字列処理メソッドが用意されています。おそらく最も重要なのは、これらのメソッドが欠損値/NA 値を自動的に除外することです。これらは Series の str 属性を介してアクセスされ、一般的に同等の (スカラ) 組み込み文字列メソッドと一致する名前を持っています。例えば

In [297]: s = pd.Series(
   .....:     ["A", "B", "C", "Aaba", "Baca", np.nan, "CABA", "dog", "cat"], dtype="string"
   .....: )
   .....: 

In [298]: s.str.lower()
Out[298]: 
0       a
1       b
2       c
3    aaba
4    baca
5    <NA>
6    caba
7     dog
8     cat
dtype: string

強力なパターンマッチングメソッドも提供されていますが、パターンマッチングは通常、デフォルトで (場合によっては常に) 正規表現を使用することに注意してください。

pandas 1.0 以前は、文字列メソッドは object -dtype の Series でのみ利用可能でした。pandas 1.0 で文字列専用の StringDtype が追加されました。詳細については、テキストデータ型を参照してください。

完全な説明については、ベクトル化された文字列メソッドを参照してください。

ソート#

pandas は、インデックスラベルによるソート、列値によるソート、および両方の組み合わせによるソートの3種類のソートをサポートしています。

インデックスによるソート#

Series.sort_index() および DataFrame.sort_index() メソッドは、pandas オブジェクトをそのインデックスレベルでソートするために使用されます。

In [299]: df = pd.DataFrame(
   .....:     {
   .....:         "one": pd.Series(np.random.randn(3), index=["a", "b", "c"]),
   .....:         "two": pd.Series(np.random.randn(4), index=["a", "b", "c", "d"]),
   .....:         "three": pd.Series(np.random.randn(3), index=["b", "c", "d"]),
   .....:     }
   .....: )
   .....: 

In [300]: unsorted_df = df.reindex(
   .....:     index=["a", "d", "c", "b"], columns=["three", "two", "one"]
   .....: )
   .....: 

In [301]: unsorted_df
Out[301]: 
      three       two       one
a       NaN -1.152244  0.562973
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504

# DataFrame
In [302]: unsorted_df.sort_index()
Out[302]: 
      three       two       one
a       NaN -1.152244  0.562973
b -0.098217  0.009797 -1.299504
c  1.273388 -0.167123  0.640382
d -0.252916 -0.109597       NaN

In [303]: unsorted_df.sort_index(ascending=False)
Out[303]: 
      three       two       one
d -0.252916 -0.109597       NaN
c  1.273388 -0.167123  0.640382
b -0.098217  0.009797 -1.299504
a       NaN -1.152244  0.562973

In [304]: unsorted_df.sort_index(axis=1)
Out[304]: 
        one     three       two
a  0.562973       NaN -1.152244
d       NaN -0.252916 -0.109597
c  0.640382  1.273388 -0.167123
b -1.299504 -0.098217  0.009797

# Series
In [305]: unsorted_df["three"].sort_index()
Out[305]: 
a         NaN
b   -0.098217
c    1.273388
d   -0.252916
Name: three, dtype: float64

インデックスによるソートは、ソートされるインデックスに適用する呼び出し可能関数を受け取る key パラメータもサポートしています。MultiIndex オブジェクトの場合、キーは level で指定されたレベルごとに適用されます。

In [306]: s1 = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3], "c": [2, 3, 4]}).set_index(
   .....:     list("ab")
   .....: )
   .....: 

In [307]: s1
Out[307]: 
     c
a b   
B 1  2
a 2  3
C 3  4
In [308]: s1.sort_index(level="a")
Out[308]: 
     c
a b   
B 1  2
C 3  4
a 2  3

In [309]: s1.sort_index(level="a", key=lambda idx: idx.str.lower())
Out[309]: 
     c
a b   
a 2  3
B 1  2
C 3  4

値によるキーソートの詳細については、値によるソートを参照してください。

値によるソート#

Series.sort_values() メソッドは Series をその値でソートするために使用されます。DataFrame.sort_values() メソッドは DataFrame をその列または行の値でソートするために使用されます。DataFrame.sort_values() のオプションの by パラメータは、ソート順を決定するために使用する1つ以上の列を指定するために使用できます。

In [310]: df1 = pd.DataFrame(
   .....:     {"one": [2, 1, 1, 1], "two": [1, 3, 2, 4], "three": [5, 4, 3, 2]}
   .....: )
   .....: 

In [311]: df1.sort_values(by="two")
Out[311]: 
   one  two  three
0    2    1      5
2    1    2      3
1    1    3      4
3    1    4      2

by パラメータは、列名のリストを受け取ることができます。例:

In [312]: df1[["one", "two", "three"]].sort_values(by=["one", "two"])
Out[312]: 
   one  two  three
2    1    2      3
1    1    3      4
3    1    4      2
0    2    1      5

これらのメソッドは na_position 引数を使用して NA 値を特別に処理します。

In [313]: s[2] = np.nan

In [314]: s.sort_values()
Out[314]: 
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
2    <NA>
5    <NA>
dtype: string

In [315]: s.sort_values(na_position="first")
Out[315]: 
2    <NA>
5    <NA>
0       A
3    Aaba
1       B
4    Baca
6    CABA
8     cat
7     dog
dtype: string

ソートは、ソートされる値に適用する呼び出し可能関数を受け取る key パラメータもサポートしています。

In [316]: s1 = pd.Series(["B", "a", "C"])
In [317]: s1.sort_values()
Out[317]: 
0    B
2    C
1    a
dtype: object

In [318]: s1.sort_values(key=lambda x: x.str.lower())
Out[318]: 
1    a
0    B
2    C
dtype: object

key には値の Series が与えられ、変換された値を持つ同じ形状の Series または配列を返すべきです。DataFrame オブジェクトの場合、キーは列ごとに適用されるため、キーは Series を期待し Series を返す必要があります。例えば、

In [319]: df = pd.DataFrame({"a": ["B", "a", "C"], "b": [1, 2, 3]})
In [320]: df.sort_values(by="a")
Out[320]: 
   a  b
0  B  1
2  C  3
1  a  2

In [321]: df.sort_values(by="a", key=lambda col: col.str.lower())
Out[321]: 
   a  b
1  a  2
0  B  1
2  C  3

各列の名前または型を使用して、異なる列に異なる関数を適用できます。

インデックスと値によるソート#

DataFrame.sort_values()by パラメータとして渡される文字列は、列またはインデックスレベル名を参照できます。

# Build MultiIndex
In [322]: idx = pd.MultiIndex.from_tuples(
   .....:     [("a", 1), ("a", 2), ("a", 2), ("b", 2), ("b", 1), ("b", 1)]
   .....: )
   .....: 

In [323]: idx.names = ["first", "second"]

# Build DataFrame
In [324]: df_multi = pd.DataFrame({"A": np.arange(6, 0, -1)}, index=idx)

In [325]: df_multi
Out[325]: 
              A
first second   
a     1       6
      2       5
      2       4
b     2       3
      1       2
      1       1

「second」(インデックス)と「A」(列)でソート

In [326]: df_multi.sort_values(by=["second", "A"])
Out[326]: 
              A
first second   
b     1       1
      1       2
a     1       6
b     2       3
a     2       4
      2       5

文字列が列名とインデックスレベル名の両方に一致する場合、警告が発行され、列が優先されます。これは将来のバージョンで曖昧性エラーを引き起こします。

searchsorted#

Series には searchsorted() メソッドがあり、numpy.ndarray.searchsorted() と同様に機能します。

In [327]: ser = pd.Series([1, 2, 3])

In [328]: ser.searchsorted([0, 3])
Out[328]: array([0, 2])

In [329]: ser.searchsorted([0, 4])
Out[329]: array([0, 3])

In [330]: ser.searchsorted([1, 3], side="right")
Out[330]: array([1, 3])

In [331]: ser.searchsorted([1, 3], side="left")
Out[331]: array([0, 2])

In [332]: ser = pd.Series([3, 1, 2])

In [333]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[333]: array([0, 2])

最小値 / 最大値#

Series には、最小または最大の \(n\) 個の値を返す nsmallest() および nlargest() メソッドがあります。大規模な Series の場合、Series 全体をソートして結果に対して head(n) を呼び出すよりもはるかに高速になる可能性があります。

In [334]: s = pd.Series(np.random.permutation(10))

In [335]: s
Out[335]: 
0    2
1    0
2    3
3    7
4    1
5    5
6    9
7    6
8    8
9    4
dtype: int64

In [336]: s.sort_values()
Out[336]: 
1    0
4    1
0    2
2    3
9    4
5    5
7    6
3    7
8    8
6    9
dtype: int64

In [337]: s.nsmallest(3)
Out[337]: 
1    0
4    1
0    2
dtype: int64

In [338]: s.nlargest(3)
Out[338]: 
6    9
8    8
3    7
dtype: int64

DataFrame にも nlargest および nsmallest メソッドがあります。

In [339]: df = pd.DataFrame(
   .....:     {
   .....:         "a": [-2, -1, 1, 10, 8, 11, -1],
   .....:         "b": list("abdceff"),
   .....:         "c": [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0],
   .....:     }
   .....: )
   .....: 

In [340]: df.nlargest(3, "a")
Out[340]: 
    a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN

In [341]: df.nlargest(5, ["a", "c"])
Out[341]: 
    a  b    c
5  11  f  3.0
3  10  c  3.2
4   8  e  NaN
2   1  d  4.0
6  -1  f  4.0

In [342]: df.nsmallest(3, "a")
Out[342]: 
   a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0

In [343]: df.nsmallest(5, ["a", "c"])
Out[343]: 
   a  b    c
0 -2  a  1.0
1 -1  b  2.0
6 -1  f  4.0
2  1  d  4.0
4  8  e  NaN

MultiIndex 列によるソート#

列が MultiIndex である場合、ソート時に明示的に by にすべてのレベルを完全に指定する必要があります。

In [344]: df1.columns = pd.MultiIndex.from_tuples(
   .....:     [("a", "one"), ("a", "two"), ("b", "three")]
   .....: )
   .....: 

In [345]: df1.sort_values(by=("a", "two"))
Out[345]: 
    a         b
  one two three
0   2   1     5
2   1   2     3
1   1   3     4
3   1   4     2

コピー#

pandas オブジェクトの copy() メソッドは、基になるデータをコピーし (軸インデックスは不変なのでコピーされません)、新しいオブジェクトを返します。ただし、**オブジェクトをコピーする必要はめったにありません**。たとえば、DataFrame を*インプレースで*変更する方法はほんのわずかしかありません。

  • 列の挿入、削除、または変更。

  • index または columns 属性への割り当て。

  • 均質なデータの場合、values 属性または高度なインデックス付けを介して値を直接変更する。

明確にするために、pandas のメソッドにはデータを変更する副作用はありません。ほとんどすべてのメソッドは新しいオブジェクトを返し、元のオブジェクトには手を加えません。データが変更された場合、それは明示的に行ったためです。

dtypes#

ほとんどの場合、pandas は Series または DataFrame の個々の列に NumPy 配列と dtype を使用します。NumPy は floatintbooltimedelta64[ns]、および datetime64[ns] をサポートしています (NumPy はタイムゾーンを考慮した datetime をサポートしていません)。

pandas およびサードパーティライブラリは、いくつかの箇所で NumPy の型システムを*拡張*しています。このセクションでは、pandas が内部的に行った拡張について説明します。pandas で動作する独自の拡張機能を記述する方法については、拡張型を参照してください。拡張機能を実装したサードパーティライブラリのリストについては、エコシステムページを参照してください。

次の表に、pandas のすべての拡張型を示します。dtype 引数を必要とするメソッドの場合、示されているように文字列を指定できます。各型の詳細については、それぞれのドキュメントセクションを参照してください。

データの種類

データ型

スカラ

配列

文字列エイリアス

タイムゾーン対応 datetime

DatetimeTZDtype

Timestamp

arrays.DatetimeArray

'datetime64[ns, <tz>]'

カテゴリ

CategoricalDtype

(なし)

カテゴリ

'category'

期間 (時間範囲)

PeriodDtype

期間

arrays.PeriodArray 'Period[<freq>]'

'period[<freq>]',

スパース

SparseDtype

(なし)

arrays.SparseArray

'Sparse', 'Sparse[int]', 'Sparse[float]'

間隔

IntervalDtype

インターバル

arrays.IntervalArray

'interval', 'Interval', 'Interval[<numpy_dtype>]', 'Interval[datetime64[ns, <tz>]]', 'Interval[timedelta64[<freq>]]'

ヌル許容整数

Int64Dtype, …

(なし)

arrays.IntegerArray

'Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64'

ヌル許容浮動小数点

Float64Dtype, …

(なし)

arrays.FloatingArray

'Float32', 'Float64'

文字列

StringDtype

str

arrays.StringArray

'string'

ブール (NA を含む)

BooleanDtype

bool

arrays.BooleanArray

'boolean'

pandas には文字列を格納する2つの方法があります。

  1. 文字列を含む任意の Python オブジェクトを保持できる object dtype。

  2. 文字列専用の StringDtype

一般的に、StringDtype の使用をお勧めします。詳細については、テキストデータ型を参照してください。

最後に、任意のオブジェクトは object dtype を使用して格納できますが、可能な限り避けるべきです (パフォーマンスおよび他のライブラリやメソッドとの相互運用性のため)。オブジェクト変換を参照してください。

DataFrame の便利な dtypes 属性は、各列のデータ型を持つ Series を返します。

In [346]: dft = pd.DataFrame(
   .....:     {
   .....:         "A": np.random.rand(3),
   .....:         "B": 1,
   .....:         "C": "foo",
   .....:         "D": pd.Timestamp("20010102"),
   .....:         "E": pd.Series([1.0] * 3).astype("float32"),
   .....:         "F": False,
   .....:         "G": pd.Series([1] * 3, dtype="int8"),
   .....:     }
   .....: )
   .....: 

In [347]: dft
Out[347]: 
          A  B    C          D    E      F  G
0  0.035962  1  foo 2001-01-02  1.0  False  1
1  0.701379  1  foo 2001-01-02  1.0  False  1
2  0.281885  1  foo 2001-01-02  1.0  False  1

In [348]: dft.dtypes
Out[348]: 
A          float64
B            int64
C           object
D    datetime64[s]
E          float32
F             bool
G             int8
dtype: object

Series オブジェクトでは、dtype 属性を使用します。

In [349]: dft["A"].dtype
Out[349]: dtype('float64')

pandas オブジェクトが*単一の列*に複数の dtype を含むデータを含んでいる場合、その列の dtype はすべてのデータ型を収容できるように選択されます (object が最も一般的です)。

# these ints are coerced to floats
In [350]: pd.Series([1, 2, 3, 4, 5, 6.0])
Out[350]: 
0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
5    6.0
dtype: float64

# string data forces an ``object`` dtype
In [351]: pd.Series([1, 2, 3, 6.0, "foo"])
Out[351]: 
0      1
1      2
2      3
3    6.0
4    foo
dtype: object

DataFrame 内の各型の列数は、DataFrame.dtypes.value_counts() を呼び出すことで見つけることができます。

In [352]: dft.dtypes.value_counts()
Out[352]: 
float64          1
int64            1
object           1
datetime64[s]    1
float32          1
bool             1
int8             1
Name: count, dtype: int64

数値 dtype は伝播し、DataFrame 内で共存できます。dtype が渡された場合 (dtype キーワードを直接使用するか、渡された ndarray または渡された Series を使用する)、DataFrame 操作で保持されます。さらに、異なる数値 dtype は**結合されません**。次の例は、その一端を示します。

In [353]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=["A"], dtype="float32")

In [354]: df1
Out[354]: 
          A
0  0.224364
1  1.890546
2  0.182879
3  0.787847
4 -0.188449
5  0.667715
6 -0.011736
7 -0.399073

In [355]: df1.dtypes
Out[355]: 
A    float32
dtype: object

In [356]: df2 = pd.DataFrame(
   .....:     {
   .....:         "A": pd.Series(np.random.randn(8), dtype="float16"),
   .....:         "B": pd.Series(np.random.randn(8)),
   .....:         "C": pd.Series(np.random.randint(0, 255, size=8), dtype="uint8"),  # [0,255] (range of uint8)
   .....:     }
   .....: )
   .....: 

In [357]: df2
Out[357]: 
          A         B    C
0  0.823242  0.256090   26
1  1.607422  1.426469   86
2 -0.333740 -0.416203   46
3 -0.063477  1.139976  212
4 -1.014648 -1.193477   26
5  0.678711  0.096706    7
6 -0.040863 -1.956850  184
7 -0.357422 -0.714337  206

In [358]: df2.dtypes
Out[358]: 
A    float16
B    float64
C      uint8
dtype: object

デフォルト#

デフォルトでは、プラットフォーム (32ビットまたは64ビット) に*関係なく*、整数型は int64、浮動小数点型は float64 です。以下はすべて int64 dtype になります。

In [359]: pd.DataFrame([1, 2], columns=["a"]).dtypes
Out[359]: 
a    int64
dtype: object

In [360]: pd.DataFrame({"a": [1, 2]}).dtypes
Out[360]: 
a    int64
dtype: object

In [361]: pd.DataFrame({"a": 1}, index=list(range(2))).dtypes
Out[361]: 
a    int64
dtype: object

Numpy は配列を作成するときに*プラットフォーム依存の*型を選択することに注意してください。以下は32ビットプラットフォームでは int32 になります。

In [362]: frame = pd.DataFrame(np.array([1, 2]))

アップキャスティング#

型は、他の型と組み合わせると*アップキャスト*される可能性があり、これは現在の型 (例: int から float) から昇格することを意味します。

In [363]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2

In [364]: df3
Out[364]: 
          A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [365]: df3.dtypes
Out[365]: 
A    float32
B    float64
C    float64
dtype: object

DataFrame.to_numpy() は、dtype の*最小公倍数*を返します。これは、結果として得られる均質な dtype の NumPy 配列内の**すべて**の型を収容できる dtype を意味します。これにより、一部の*アップキャスト*が強制される可能性があります。

In [366]: df3.to_numpy().dtype
Out[366]: dtype('float64')

astype#

astype() メソッドを使用して、dtype を明示的に相互変換できます。これらはデフォルトでコピーを返します。dtype が変更されなかった場合でも同様です (copy=False を渡すとこの動作が変更されます)。さらに、astype 操作が無効な場合は例外が発生します。

アップキャスティングは常に**NumPy**のルールに従います。2つの異なる dtype が操作に関与する場合、より*一般的な*方が操作の結果として使用されます。

In [367]: df3
Out[367]: 
          A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2 -0.150862 -0.416203   46.0
3  0.724370  1.139976  212.0
4 -1.203098 -1.193477   26.0
5  1.346426  0.096706    7.0
6 -0.052599 -1.956850  184.0
7 -0.756495 -0.714337  206.0

In [368]: df3.dtypes
Out[368]: 
A    float32
B    float64
C    float64
dtype: object

# conversion of dtypes
In [369]: df3.astype("float32").dtypes
Out[369]: 
A    float32
B    float32
C    float32
dtype: object

astype() を使用して、列のサブセットを指定された型に変換します。

In [370]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [371]: dft[["a", "b"]] = dft[["a", "b"]].astype(np.uint8)

In [372]: dft
Out[372]: 
   a  b  c
0  1  4  7
1  2  5  8
2  3  6  9

In [373]: dft.dtypes
Out[373]: 
a    uint8
b    uint8
c    int64
dtype: object

astype() に辞書を渡すことで、特定の列を特定の dtype に変換します。

In [374]: dft1 = pd.DataFrame({"a": [1, 0, 1], "b": [4, 5, 6], "c": [7, 8, 9]})

In [375]: dft1 = dft1.astype({"a": np.bool_, "c": np.float64})

In [376]: dft1
Out[376]: 
       a  b    c
0   True  4  7.0
1  False  5  8.0
2   True  6  9.0

In [377]: dft1.dtypes
Out[377]: 
a       bool
b      int64
c    float64
dtype: object

astype()loc() を使用して列のサブセットを指定された型に変換しようとすると、アップキャスティングが発生します。

loc() は、現在 dtype に割り当てているものを収めようとしますが、[] は右辺から dtype を取得して上書きします。したがって、次のコードは意図しない結果を生成します。

In [378]: dft = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})

In [379]: dft.loc[:, ["a", "b"]].astype(np.uint8).dtypes
Out[379]: 
a    uint8
b    uint8
dtype: object

In [380]: dft.loc[:, ["a", "b"]] = dft.loc[:, ["a", "b"]].astype(np.uint8)

In [381]: dft.dtypes
Out[381]: 
a    int64
b    int64
c    int64
dtype: object

オブジェクト変換#

pandas には、object dtype から他の型への型変換を強制しようとするさまざまな関数が用意されています。データがすでに正しい型であるが object 配列に格納されている場合、DataFrame.infer_objects() および Series.infer_objects() メソッドを使用して、正しい型にソフト変換できます。

In [382]: import datetime

In [383]: df = pd.DataFrame(
   .....:     [
   .....:         [1, 2],
   .....:         ["a", "b"],
   .....:         [datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)],
   .....:     ]
   .....: )
   .....: 

In [384]: df = df.T

In [385]: df
Out[385]: 
   0  1                    2
0  1  a  2016-03-02 00:00:00
1  2  b  2016-03-02 00:00:00

In [386]: df.dtypes
Out[386]: 
0    object
1    object
2    object
dtype: object

データが転置されたため、元の推論ではすべての列がオブジェクトとして格納されていましたが、infer_objects がこれを修正します。

In [387]: df.infer_objects().dtypes
Out[387]: 
0             int64
1            object
2    datetime64[ns]
dtype: object

次の関数は、1次元オブジェクト配列またはスカラを対象としており、オブジェクトを指定された型にハード変換するために使用できます。

  • to_numeric() (数値 dtype への変換)

    In [388]: m = ["1.1", 2, 3]
    
    In [389]: pd.to_numeric(m)
    Out[389]: array([1.1, 2. , 3. ])
    
  • to_datetime() (datetime オブジェクトへの変換)

    In [390]: import datetime
    
    In [391]: m = ["2016-07-09", datetime.datetime(2016, 3, 2)]
    
    In [392]: pd.to_datetime(m)
    Out[392]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
    
  • to_timedelta() (timedelta オブジェクトへの変換)

    In [393]: m = ["5us", pd.Timedelta("1day")]
    
    In [394]: pd.to_timedelta(m)
    Out[394]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
    

変換を強制するために、errors 引数を渡すことができます。これは、pandas が目的の dtype またはオブジェクトに変換できない要素をどのように処理するかを指定します。デフォルトでは、errors='raise' であり、変換プロセス中に発生したエラーはすべてスローされます。ただし、errors='coerce' の場合、これらのエラーは無視され、pandas は問題のある要素を pd.NaT (datetime および timedelta の場合) または np.nan (数値の場合) に変換します。これは、データのほとんどが目的の dtype (例: 数値、datetime) であるが、時折、欠損値として表現したい非準拠要素が混ざっている場合に役立つ可能性があります。

In [395]: import datetime

In [396]: m = ["apple", datetime.datetime(2016, 3, 2)]

In [397]: pd.to_datetime(m, errors="coerce")
Out[397]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None)

In [398]: m = ["apple", 2, 3]

In [399]: pd.to_numeric(m, errors="coerce")
Out[399]: array([nan,  2.,  3.])

In [400]: m = ["apple", pd.Timedelta("1day")]

In [401]: pd.to_timedelta(m, errors="coerce")
Out[401]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)

オブジェクト変換に加えて、to_numeric() にはもう1つの引数 downcast があり、新しく (またはすでに) 数値のデータをより小さな dtype にダウンキャストしてメモリを節約するオプションが与えられます。

In [402]: m = ["1", 2, 3]

In [403]: pd.to_numeric(m, downcast="integer")  # smallest signed int dtype
Out[403]: array([1, 2, 3], dtype=int8)

In [404]: pd.to_numeric(m, downcast="signed")  # same as 'integer'
Out[404]: array([1, 2, 3], dtype=int8)

In [405]: pd.to_numeric(m, downcast="unsigned")  # smallest unsigned int dtype
Out[405]: array([1, 2, 3], dtype=uint8)

In [406]: pd.to_numeric(m, downcast="float")  # smallest float dtype
Out[406]: array([1., 2., 3.], dtype=float32)

これらのメソッドは1次元配列、リスト、またはスカラにのみ適用されるため、DataFrame のような多次元オブジェクトに直接使用することはできません。しかし、apply() を使用すると、各列に対して関数を効率的に「適用」できます。

In [407]: import datetime

In [408]: df = pd.DataFrame([["2016-07-09", datetime.datetime(2016, 3, 2)]] * 2, dtype="O")

In [409]: df
Out[409]: 
            0                    1
0  2016-07-09  2016-03-02 00:00:00
1  2016-07-09  2016-03-02 00:00:00

In [410]: df.apply(pd.to_datetime)
Out[410]: 
           0          1
0 2016-07-09 2016-03-02
1 2016-07-09 2016-03-02

In [411]: df = pd.DataFrame([["1.1", 2, 3]] * 2, dtype="O")

In [412]: df
Out[412]: 
     0  1  2
0  1.1  2  3
1  1.1  2  3

In [413]: df.apply(pd.to_numeric)
Out[413]: 
     0  1  2
0  1.1  2  3
1  1.1  2  3

In [414]: df = pd.DataFrame([["5us", pd.Timedelta("1day")]] * 2, dtype="O")

In [415]: df
Out[415]: 
     0                1
0  5us  1 days 00:00:00
1  5us  1 days 00:00:00

In [416]: df.apply(pd.to_timedelta)
Out[416]: 
                       0      1
0 0 days 00:00:00.000005 1 days
1 0 days 00:00:00.000005 1 days

注意点#

integer 型のデータに対して選択操作を実行すると、データを簡単に floating にアップキャストすることができます。nans が導入されない場合、入力データの dtype は保持されます。整数の NA のサポートも参照してください。

In [417]: dfi = df3.astype("int32")

In [418]: dfi["E"] = 1

In [419]: dfi
Out[419]: 
   A  B    C  E
0  1  0   26  1
1  3  1   86  1
2  0  0   46  1
3  0  1  212  1
4 -1 -1   26  1
5  1  0    7  1
6  0 -1  184  1
7  0  0  206  1

In [420]: dfi.dtypes
Out[420]: 
A    int32
B    int32
C    int32
E    int64
dtype: object

In [421]: casted = dfi[dfi > 0]

In [422]: casted
Out[422]: 
     A    B    C  E
0  1.0  NaN   26  1
1  3.0  1.0   86  1
2  NaN  NaN   46  1
3  NaN  1.0  212  1
4  NaN  NaN   26  1
5  1.0  NaN    7  1
6  NaN  NaN  184  1
7  NaN  NaN  206  1

In [423]: casted.dtypes
Out[423]: 
A    float64
B    float64
C      int32
E      int64
dtype: object

一方、float dtype は変更されません。

In [424]: dfa = df3.copy()

In [425]: dfa["A"] = dfa["A"].astype("float32")

In [426]: dfa.dtypes
Out[426]: 
A    float32
B    float64
C    float64
dtype: object

In [427]: casted = dfa[df2 > 0]

In [428]: casted
Out[428]: 
          A         B      C
0  1.047606  0.256090   26.0
1  3.497968  1.426469   86.0
2       NaN       NaN   46.0
3       NaN  1.139976  212.0
4       NaN       NaN   26.0
5  1.346426  0.096706    7.0
6       NaN       NaN  184.0
7       NaN       NaN  206.0

In [429]: casted.dtypes
Out[429]: 
A    float32
B    float64
C    float64
dtype: object

dtype に基づく列の選択#

select_dtypes() メソッドは、列の dtype に基づく列のサブセット化を実装します。

まず、さまざまな dtype を持つ DataFrame を作成してみましょう。

In [430]: df = pd.DataFrame(
   .....:     {
   .....:         "string": list("abc"),
   .....:         "int64": list(range(1, 4)),
   .....:         "uint8": np.arange(3, 6).astype("u1"),
   .....:         "float64": np.arange(4.0, 7.0),
   .....:         "bool1": [True, False, True],
   .....:         "bool2": [False, True, False],
   .....:         "dates": pd.date_range("now", periods=3),
   .....:         "category": pd.Series(list("ABC")).astype("category"),
   .....:     }
   .....: )
   .....: 

In [431]: df["tdeltas"] = df.dates.diff()

In [432]: df["uint64"] = np.arange(3, 6).astype("u8")

In [433]: df["other_dates"] = pd.date_range("20130101", periods=3)

In [434]: df["tz_aware_dates"] = pd.date_range("20130101", periods=3, tz="US/Eastern")

In [435]: df
Out[435]: 
  string  int64  uint8  ...  uint64  other_dates            tz_aware_dates
0      a      1      3  ...       3   2013-01-01 2013-01-01 00:00:00-05:00
1      b      2      4  ...       4   2013-01-02 2013-01-02 00:00:00-05:00
2      c      3      5  ...       5   2013-01-03 2013-01-03 00:00:00-05:00

[3 rows x 12 columns]

そして dtype は

In [436]: df.dtypes
Out[436]: 
string                                object
int64                                  int64
uint8                                  uint8
float64                              float64
bool1                                   bool
bool2                                   bool
dates                         datetime64[ns]
category                            category
tdeltas                      timedelta64[ns]
uint64                                uint64
other_dates                   datetime64[ns]
tz_aware_dates    datetime64[ns, US/Eastern]
dtype: object

select_dtypes() には includeexclude という2つのパラメータがあり、「これらの dtype *を持つ*列をください」(include)および/または「これらの dtype *ではない*列をください」(exclude)と言うことができます。

例えば、bool 型の列を選択するには

In [437]: df.select_dtypes(include=[bool])
Out[437]: 
   bool1  bool2
0   True  False
1  False   True
2   True  False

NumPy dtype 階層の dtype の名前を渡すこともできます。

In [438]: df.select_dtypes(include=["bool"])
Out[438]: 
   bool1  bool2
0   True  False
1  False   True
2   True  False

select_dtypes() は汎用 dtype でも動作します。

例えば、符号なし整数を除外してすべての数値列とブール列を選択するには

In [439]: df.select_dtypes(include=["number", "bool"], exclude=["unsignedinteger"])
Out[439]: 
   int64  float64  bool1  bool2 tdeltas
0      1      4.0   True  False     NaT
1      2      5.0  False   True  1 days
2      3      6.0   True  False  1 days

文字列列を選択するには object dtype を使用する必要があります。

In [440]: df.select_dtypes(include=["object"])
Out[440]: 
  string
0      a
1      b
2      c

numpy.number のような汎用 dtype のすべての子 dtype を表示するには、子 dtype のツリーを返す関数を定義できます。

In [441]: def subdtypes(dtype):
   .....:     subs = dtype.__subclasses__()
   .....:     if not subs:
   .....:         return dtype
   .....:     return [dtype, [subdtypes(dt) for dt in subs]]
   .....: 

すべての NumPy dtype は numpy.generic のサブクラスです。

In [442]: subdtypes(np.generic)
Out[442]: 
[numpy.generic,
 [[numpy.number,
   [[numpy.integer,
     [[numpy.signedinteger,
       [numpy.int8,
        numpy.int16,
        numpy.int32,
        numpy.int64,
        numpy.longlong,
        numpy.timedelta64]],
      [numpy.unsignedinteger,
       [numpy.uint8,
        numpy.uint16,
        numpy.uint32,
        numpy.uint64,
        numpy.ulonglong]]]],
    [numpy.inexact,
     [[numpy.floating,
       [numpy.float16, numpy.float32, numpy.float64, numpy.longdouble]],
      [numpy.complexfloating,
       [numpy.complex64, numpy.complex128, numpy.clongdouble]]]]]],
  [numpy.flexible,
   [[numpy.character, [numpy.bytes_, numpy.str_]],
    [numpy.void, [numpy.record]]]],
  numpy.bool_,
  numpy.datetime64,
  numpy.object_]]

pandas は、通常の NumPy 階層には統合されておらず、上記の関数では表示されない category および datetime64[ns, tz] 型も定義しています。