NumPyと同様、pandasのSeriesオブジェクトとDataFrameオブジェクトではベクトル化された演算がサポートされています。ここではSeriesオブジェクトを例に取りますが、ベクトル化された演算とはつまり、2つのSeriesオブジェクトがあったときに、それらの要素同士を加減乗除したいときに、ループ処理をして個別の要素にアクセスするコードを書く必要がないということです。例えば、以下のようなコードは書く必要はありません。
s0 = pd.Series([0, 1, 2])
s1 = pd.Series([3, 4, 5])
result = pd.Series(0, index=range(3))
for idx, (x, y) in enumerate(zip(s0, s1)):
result[idx] = x + y
print(result)
# 出力結果:
#0 3
#1 5
#2 7
この例では、2つのSeriesオブジェクトを生成し、for文で対応する要素同士(インデックスが同じ要素同士)とそのインデックスを取り出して、加算した結果を別のSeriesオブジェクトの該当インデックスの位置に代入しています。
こんなコードを書くことなく、pandasでは次のようなコードを書くだけで2つのSeriesオブジェクトの要素同士を加算できます。
result = s0 + s1
print(result)
# 出力結果:
#0 3
#1 5
#2 7
もちろん、NumPyと同様にブロードキャストもサポートされています。ブロードキャストとは、2つのSeriesオブジェクト(およびDataFrameオブジェクト)の演算時に形状が異なっていたら、一方をもう一方の形状に合うように自動的に変換することです。以下は簡単な例です。
s = pd.Series([0, 1, 2, 4])
result = s + 1
print(result)
# 出力結果:
#0 1
#1 2
#2 3
#3 5
#dtype: int64
result = s * 2
print(result)
# 出力結果:
#0 0
#1 2
#2 4
#3 8
#dtype: int64
ここでは加算と乗算を例としています。どちらの例でも、Seriesオブジェクトに加算または乗算しているスカラー値は、実際の演算時にはSeriesオブジェクトの形状((4,))に合わせてブロードキャストされて、1次元配列同士の演算として処理されます。
ベクトル化された演算とブロードキャストは、NumPyやpandasのコードをシンプルで完結に記述するための重要な要素です。とはいえ、異なる点もあります。例えば、以下のコードはNumPyで作成した1次元配列のスライス同士を加算するコードです。
a = np.array(range(5))
print(a) # [0 1 2 3 4]
print(a[1:]) # [1 2 3 4]
print(a[:4]) # [0 1 2 3]
result = a[1:] + a[:4]
print(result) # [1 3 5 7]
ndarrayオブジェクトをスライスしたものを加算した結果は、スライスされたものの先頭から末尾までの要素同士が加算されたために、結果が[1 3 5 7]になっている点に注目してください。
これと同じことをSeriesオブジェクトでやるとどうなるでしょう。
s = pd.Series(range(5))
print(s[1:])
# 出力結果:
#1 1
#2 2
#3 3
#4 4
#dtype: int64
print(s[:4])
# 出力結果:
#0 0
#1 1
#2 2
#3 3
#dtype: int64
result = s[1:] + s[:4]
print(result)
上のコードをVS Codeで実行した結果を以下に示します。
どういうことでしょう。インデックス0と4ではデータが欠損していることを意味するNaNが値となり、他のインデックスについては対応する値が2倍(同じインデックスの値同士を加算した結果)になっています。
Seriesオブジェクト同士を演算するときには、それらのインデックスに沿って要素が取り出されると考えられます。s[1:]というスライスが持つインデックスは1、2、3、4で、s[:4]というスライスが持つインデックスは0、1、2、3であり、両者に共通する1、2、3については「s[インデックス]+s[インデックス]」が行われますが、両者に含まれていないインデックス0と4については「そんなインデックスはない」ことを表すNaNがセットされるというわけです。
NumPyとpandasでは同様に扱える点もありますが、差異もあることはよく覚えておきましょう。
SeriesオブジェクトやDataFrameオブジェクトに格納できるデータの型はさまざまですが、以下に幾つか紹介しておきます。
データ型 | データ型 | dtypeの文字列エイリアス |
---|---|---|
符号付き整数型 | Int64Dtypeなど | 'Int64'など |
符号なし整数型 | UInt64Dtypeなど | 'UInt64'など |
浮動小数点数型 | Float64Dtypeなど | 'Float64'など |
ブーリアン型 | BoolanDtype | 'boolean' |
文字列型 | StringDtype/object | 'string'/'object' |
SeriesオブジェクトやDataFrameオブジェクトに格納できるデータの種類(一部) |
実際には個別のデータを格納するのにどれだけのメモリを必要とするかに応じて、整数型であれば'Int8'や’Int64'のように「データ型+ビット数」といった形でデータ型(の文字列エイリアス)を指定します。また、文字列エイリアスに対応するデータ型が用意されています(例えばpandas.Int8Dtypeクラス)。
pandas.Seriesコンストラクタのパラメーターにはdtypeというものがあります。
pandas.Series(data=None, index=None, dtype=None, name=None, copy=None)
このdtypeパラメーターに上記表のデータ型や文字列エイリアスを指定することで、Seriesオブジェクト(やDataFrameオブジェクト)に格納するデータの種類を明示できます。以下に簡単な例を示します。
s = pd.Series([0, 1, 2], dtype='float64')
print(s.dtype) # float64
s = pd.Series(['foo', 'bar', 'baz'], dtype=pd.StringDtype())
print(s.dtype) # string
s = pd.Series(['foo', 'bar', 'baz'], dtype=object)
print(s.dtype) # object
1つのSeriesオブジェクトには基本的に単一のデータ型の値を格納すべきです。SeriesオブジェクトとDataFrameオブジェクトの関係を考えると、DataFrameオブジェクトにはさまざまな型のデータを格納できますが、その中でも1つの列については1種類のデータを格納すべきともいえるでしょう。
少し試してみましょう。
s = pd.Series(range(5))
print(s)
# 出力結果:
#0 0
#1 1
#2 2
#3 3
#4 4
#dtype: int64
print(s.dtype) # int64
s[0] = '0' # FutureWarning:
このコードをVS Codeで実行した結果を以下に示します。
上の画像では途中で切れてしまっていますが、FutureWarningではおおよそ「dtypeが表す型と互換性がない項目の代入は廃止されていて、将来のバージョンでは例外を発生する」といったメッセージが表示されます。このことからも1つのSeriesオブジェクトには特定の型の値だけを格納するようにすべきでしょう。
とはいえ、実際には上記コードの最後にある代入文はまだ実行可能です。
次のコードを見てください。
columns = ['col0', 'col1', 'col2']
df = pd.DataFrame([['foo', 1.0, 2.0], ['bar', 3.0, 4.0]], columns=columns)
s = df.iloc[0]
print(type(s)) # <class 'pandas.core.series.Series'>
print(s)
# 出力結果:
#col0 foo
#col1 1.0
#col2 2.0
#Name: 0, dtype: object
これはDataFrameオブジェクトを作成して、その先頭行を選択して、その内容を表示するコードです。1件(1行)のデータは文字列と2つの浮動小数点数値で構成されています。「print(type(s))」の出力結果からは、取り出した行のデータ型は「Series」です。そして、最下部を見ると、そのdtypeが「object」になっていることが分かります。
このように、1つのSeriesオブジェクトに複数の型の値を格納するのであれば、そのdtype属性をobjectに設定すればよいでしょう(上のコードではそうした部分は行の選択時に自動的に行われています)。ただし、明示的にそうしたコードを書くのがよいかどうかは時と場合によるでしょう。
上のコードはDataFrameオブジェクトの行を選択した場合でしたが、列を選択すると次のようになります。
print(df['col1'])
# 出力結果:
#0 1.0
#1 3.0
#Name: col1, dtype: float64
'col1'列には浮動小数点数値が格納されているので、選択した結果であるSeriesオブジェクトのdtypeも「float64」になりました。表形式のデータではこのように、行を選択して得られるSeriesオブジェクトは複数の型のデータを格納し、列を選択して得られるSeriesオブジェクトは特定の型のデータを格納するといったことがよくあると考えられます。
ここまでSeriesオブジェクトを作成しながら、その基本的な特徴について紹介してきました。次回はDataFrameオブジェクトについて見ていくことにしましょう。
Copyright© Digital Advantage Corp. All Rights Reserved.