必須の基本機能#

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

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オブジェクト(IndexSeriesDataFrame)は、実際のデータを保持し、実際の計算を行う配列のコンテナと考えることができます。多くのタイプの場合、基になる配列は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をある程度制御できます。たとえば、タイムゾーン付きの日付時刻を考えてみましょう。NumPyにはタイムゾーン対応の日付時刻を表すdtypeがないため、2つの有用な表現方法があります。

  1. 各々が正しいtzを持つTimestampオブジェクトを持つオブジェクト型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は、関連するすべてのデータに対応するように選択されます。たとえば、文字列が含まれる場合、結果はオブジェクトのdtypeになります。浮動小数点数と整数のみがある場合、結果の配列は浮動小数点数の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ライブラリを使用して、特定の種類の2項数値演算およびブール演算を高速化するサポートを提供しています。

これらのライブラリは、特に大規模なデータセットを扱う場合に役立ち、大幅な高速化を実現します。numexprは、スマートなチャンク分割、キャッシング、およびマルチコアを使用します。bottleneckは、特にnansを含む配列を扱う場合に高速な、特殊なCythonルーチンのセットです。

以下はサンプル(100列x100,000行のDataFrameを使用)です

操作

0.11.0 (ミリ秒)

以前のバージョン (ミリ秒)

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

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)

柔軟な2項演算#

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

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

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

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

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

DataFrameには、add()sub()mul()div() および関連する関数 radd()rsub()、… があり、2項演算を実行します。ブロードキャスト動作に関しては、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*を入力するオプションがあります。これは、ある位置の値が最大で1つ欠落している場合に代用する値です。たとえば、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の2項比較メソッドがあり、その動作は上記で説明した2項算術演算に類似しています。

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)
   1574     @final
   1575     def __nonzero__(self) -> NoReturn:
-> 1576         raise ValueError(
   1577             f"The truth value of a {type(self).__name__} is ambiguous. "
   1578             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
   1579         )

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)
   1574     @final
   1575     def __nonzero__(self) -> NoReturn:
-> 1576         raise ValueError(
   1577             f"The truth value of a {type(self).__name__} is ambiguous. "
   1578             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."
   1579         )

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など)には、対応する位置のNaNを等しいとして扱う、等価性をテストするためのequals()メソッドがあります。

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

等価性が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:6105, in Series._cmp_method(self, other, op)
   6102 res_name = ops.get_op_result_name(self, other)
   6104 if isinstance(other, Series) and not self._indexed_same(other):
-> 6105     raise ValueError("Can only compare identically-labeled Series objects")
   6107 lvalues = self._values
   6108 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:6105, in Series._cmp_method(self, other, op)
   6102 res_name = ops.get_op_result_name(self, other)
   6104 if isinstance(other, Series) and not self._indexed_same(other):
-> 6105     raise ValueError("Can only compare identically-labeled Series objects")
   6107 lvalues = self._values
   6108 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とcombiner関数を受け取り、入力DataFrameを揃え、combiner関数に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: axis引数は不要

  • 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パラメーターも取ります。

関数

説明

count

NAではない観測の数

sum

値の合計

mean

値の平均

median

値の算術中央値

min

最小値

max

最大値

mode

最頻値

abs

絶対値

prod

値の積

std

ベッセル補正されたサンプル標準偏差

var

不偏分散

sem

平均の標準誤差

skew

サンプル歪度(3次モーメント)

kurt

サンプル尖度(4次モーメント)

quantile

サンプル分位数(%での値)

cumsum

累積和

cumprod

累積積

cummax

累積最大値

cummin

累積最小値

NumPyのメソッド(meanstdsumなど)は、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#

便利なdescribe()関数があります。これは、SeriesまたはDataFrameの列に関するさまざまな要約統計量(もちろん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メソッドは、1次元の値配列のヒストグラムを計算します。通常の配列の関数としても使用できます。

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()

テーブル全体の関数適用#

DataFrameSeriesは関数に渡すことができます。ただし、関数をチェーン内で呼び出す必要がある場合は、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は、DataFrameを受け取って返す関数です。

ここで、以下を比較してください。

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は、メソッドチェーンとして知られる2番目のスタイルを推奨します。pipeを使用すると、pandasのメソッドとともに、独自の関数や他のライブラリの関数をメソッドチェーンで簡単に使用できます。

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

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

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()のソースコードを見てみることをお勧めします。

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

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

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です。

このデフォルトの動作は、result_typeを使用して上書きできます。これは、reducebroadcastexpandの3つのオプションを受け入れます。これらは、リストのような戻り値を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を使用すると、複数の集約操作を1つの簡潔な方法で表現できます。この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()を使用すると、組み込みのdescribe関数と同様に、カスタムの記述関数を簡単に作成できます。

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を使用すると、一度に1つずつではなく、複数の操作を提供できます。その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 / 高度なインデックス付け は、より簡潔な再インデックス付けの方法です。

注意

パフォーマンスが重要なコードを作成する場合は、再インデックス付けの達人になるために時間を費やす価値があります。多くの場合、事前配置されたデータに対する操作の方が高速です。整列されていない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)

DataFrameの場合、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)

Seriesを DataFrame.align() に渡す場合、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 を受け取ります。

メソッド

アクション

pad / ffill

値を前方へ埋める

bfill / backfill

値を後方へ埋める

nearest

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

これらの埋めるメソッドを単純な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

これらのメソッドでは、インデックスが昇順または降順に並べ替えられている必要があります。

同じ結果は ffillmethod='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オブジェクトには、(key, value)のペアを反復処理するための辞書のような items() メソッドもあります。

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

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

  • itertuples():DataFrameの行を値の名前付きタプルとして反復処理します。これは、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#

dictのようなインターフェースと一致するように、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を保持しません (dtypesはDataFrameの列全体で保持されます)。 たとえば、

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

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

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

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

行を反復処理中にdtypesを保持するには、itertuples()を使用する方が優れています。これは値の名前付きタプルを返し、一般的にiterrows()よりもはるかに高速です。

たとえば、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の各行の名前付きタプルを返すイテレータを返します。 タプルの最初の要素は、行に対応するインデックス値であり、残りの値は行の値です。

たとえば

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オブジェクトに変換しません。単に名前付きタプル内の値を返します。したがって、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アクセサーは、期間およびtimedelta dtypesで機能します。

# 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 はタイムゾーン対応の日付時刻をサポートしていないことに注意してください)。

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

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

データの種類

データ型

スカラー

配列

文字列エイリアス

タイムゾーン対応の日付時刻

DatetimeTZDtype

Timestamp

arrays.DatetimeArray

'datetime64[ns, <tz>]'

カテゴリ

CategoricalDtype

(なし)

カテゴリ

'category'

期間 (期間)

PeriodDtype

Period

arrays.PeriodArray 'Period[<freq>]'

'period[<freq>]',

スパース

SparseDtype

(なし)

arrays.SparseArray

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

インターバル

IntervalDtype

Interval

arrays.IntervalArray

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

null 可能な整数

Int64Dtype, …

(なし)

arrays.IntegerArray

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

null 可能な浮動小数点数

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 を使用して格納できますが、(パフォーマンスと他のライブラリやメソッドとの相互運用性のために) 可能な限り避ける必要があります。object 変換 を参照してください。

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

デフォルト#

デフォルトでは、整数型は int64 であり、浮動小数点型は、プラットフォーム (32 ビットまたは 64 ビット) *に関係なく* 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() に dict を渡すことで、特定の列を特定の 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

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()は、別の引数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]の型も定義します。