必須の基本機能#
ここでは、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オブジェクト(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.ndarray
のdtype
をある程度制御できます。たとえば、タイムゾーン付きの日付時刻を考えてみましょう。NumPyにはタイムゾーン対応の日付時刻を表すdtypeがないため、2つの有用な表現方法があります。
各々が正しい
tz
を持つTimestamp
オブジェクトを持つオブジェクト型numpy.ndarray
値がUTCに変換され、タイムゾーンが破棄された
datetime64[ns]
-dtypenumpy.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
には次の欠点があります。
Seriesに拡張型が含まれている場合、
Series.values
がNumPy配列を返すのか、拡張配列を返すのかは不明確です。Series.array
は常にExtensionArray
を返し、データをコピーすることはありません。Series.to_numpy()
は常にNumPy配列を返しますが、値のコピー/強制変換が必要になる可能性があります。DataFrameに複数のデータ型が混在している場合、
DataFrame.values
はデータのコピーや値を共通のdtypeに強制変換する必要がある場合があり、比較的高コストな操作になります。DataFrame.to_numpy()
はメソッドであるため、返されるNumPy配列がDataFrame内の同じデータのビューではない可能性があることをより明確に示します。
高速化された演算#
pandasは、numexpr
ライブラリとbottleneck
ライブラリを使用して、特定の種類の2項数値演算およびブール演算を高速化するサポートを提供しています。
これらのライブラリは、特に大規模なデータセットを扱う場合に役立ち、大幅な高速化を実現します。numexpr
は、スマートなチャンク分割、キャッシング、およびマルチコアを使用します。bottleneck
は、特にnans
を含む配列を扱う場合に高速な、特殊なCythonルーチンのセットです。
以下はサンプル(100列x100,000行のDataFrame
を使用)です
操作 |
0.11.0 (ミリ秒) |
以前のバージョン (ミリ秒) |
以前のバージョンとの比率 |
---|---|---|---|
|
13.32 |
125.35 |
0.1063 |
|
21.71 |
36.63 |
0.5928 |
|
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には、eq
、ne
、lt
、gt
、le
、ge
の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
オブジェクトは、インデックス作成操作で使用できます。詳細については、ブールインデックスに関するセクションを参照してください。
ブール削減#
削減を適用できます:empty
、any()
、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 + df
と df * 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
記述統計#
記述統計やその他の関連操作をSeriesやDataFrameに対して計算するための方法は数多く存在します。これらのほとんどは、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
パラメーターも取ります。
関数 |
説明 |
---|---|
|
NAではない観測の数 |
|
値の合計 |
|
値の平均 |
|
値の算術中央値 |
|
最小値 |
|
最大値 |
|
最頻値 |
|
絶対値 |
|
値の積 |
|
ベッセル補正されたサンプル標準偏差 |
|
不偏分散 |
|
平均の標準誤差 |
|
サンプル歪度(3次モーメント) |
|
サンプル尖度(4次モーメント) |
|
サンプル分位数(%での値) |
|
累積和 |
|
累積積 |
|
累積最大値 |
|
累積最小値 |
NumPyのメソッド(mean
、std
、sum
など)は、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'
注意
idxmin
とidxmax
は、NumPyではargmin
とargmax
と呼ばれます。
値のカウント(ヒストグラム)/最頻値#
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
全体、行ごとまたは列ごと、あるいは要素ごとに操作することを期待するかどうかによって異なります。
テーブル全体の関数適用#
DataFrame
と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_name
とadd_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_name
とadd_country_name
は、それぞれ最初の位置引数としてDataFrame
を期待していました。適用したい関数が、たとえば2番目の引数としてデータを受け取る場合はどうすればよいでしょうか?この場合は、pipe
に(callable, data_keyword)
のタプルを渡します。.pipe
は、タプルで指定された引数にDataFrame
をルーティングします。
例えば、statsmodelsを使って回帰を当てはめることができます。そのAPIは、最初に式を、2番目の引数としてDataFrame
、data
を期待します。関数とキーワードのペア(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パイプと、最近ではdplyrとmagrittrに触発されています。これらは、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
を使用して上書きできます。これは、reduce
、broadcast
、expand
の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 API、window 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
これらのメソッドでは、インデックスが昇順または降順に並べ替えられている必要があります。
同じ結果は 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
DatetimeIndex
、TimedeltaIndex
、または 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 は float
、int
、bool
、timedelta64[ns]
、および datetime64[ns]
のサポートを提供しています (NumPy はタイムゾーン対応の日付時刻をサポートしていないことに注意してください)。
pandas およびサードパーティライブラリは、いくつかの場所で NumPy の型システムを *拡張* しています。このセクションでは、pandas が内部で行った拡張について説明します。pandas と連携する独自の拡張機能の作成方法については、拡張型 を参照してください。拡張機能を実装したサードパーティライブラリのリストについては、エコシステムのページ を参照してください。
次の表に、pandas のすべての拡張型を示します。dtype
引数を必要とするメソッドについては、指定された文字列を使用できます。各型の詳細については、それぞれのドキュメントセクションを参照してください。
データの種類 |
データ型 |
スカラー |
配列 |
文字列エイリアス |
||
---|---|---|---|---|---|---|
|
||||||
(なし) |
|
|||||
|
|
|||||
(なし) |
|
|||||
|
||||||
|
(なし) |
|
||||
|
(なし) |
|
||||
|
||||||
|
pandas には、文字列を格納する 2 つの方法があります。
文字列を含む任意の Python オブジェクトを保持できる
object
dtype。文字列専用の
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()
には、include
とexclude
の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]
の型も定義します。