numpyのndarray、pandasのSeriesとDataFrameの使い分け

分析する元データをcsvファイルとして取り込む際、pandasのDataFrameをよく使います。

大体、下記のような感じでとりあえずcsvファイルをDataFrame型変数に格納し、可視化や成型を行なっていきます。

import pandas as pd
df_train = pd.read_csv("___.csv")
df_train.head()


このDataFrame以外にも、同じようなデータ構造を持つクラスはいくつかあるのですが、正直それぞれの違いが整理できてなかったので、調べて整理してみます。


numpyのndarray

まず、ライブラリであるnumpyが持つクラス、ndarrayがあります。
このndarrayは、正確にいうと、多次元配列を扱うクラスです。
import numpy as npでライブラリを読み込んだ後、np.array(引数:多重リストか多重タプル)で作成するのが慣例です。

import numpy as np
a = np.array([1, 2, 3])
print(a)
print(type(a))
# 出力結果
#[1 2 3]


ndarrayには以下のルールがあります。

  1. 配列内要素の型は全て同じ
  2. 配列長は固定 (固定長配列)
  3. 配列の各次元の要素数は同じ

ちなみに、配列内要素のルールについて、いわゆる数字の型でなくてもよいみたいです。その点、上記のルールに漏れがないかと思ったのですが特にstring型の除外ルールとかは不要で、stringとかでもちゃんと定義できました。演算の挙動が気になりますが、ちょっと今回の内容からそれてしまうのでここでは深入りしません。

ndarrayの使い所

ListがネストされたList(要素がListのList、多重リスト)でも同じ配列は作れるのですが、numpyのndarrayはC言語で実装されており、Listよりも早く処理ができるという点が優れています。さらに、ndarray用の便利な関数が多く用意されており、多重リストよりも安易で高速な行列演算処理が可能です。

また、多重リストとndarrayの具体的な違いは下記です。

  1. 多重リストは動的に変更可能ですが、ndarrayは全体の削除、再生成が必要
  2. 多重リストはリスト内でその要素の型が違ってもよいが、ndarray内の要素の型は全て同じである必要がある
  3. 多重リストはネストされているListの要素数はバラバラでもよいが、ndarrayは行毎に列数が異なるようなことは許されない
  4. ndarrayは要素の型や要素数を揃えて固定してしまうことで行列演算用の関数を持つことができ、これを使うことで計算が楽になる

 
 
 
 

pandasのSeries

次にpandasライブラリの持つSeries。こちらはndarrayやDataFrameに比べるとあまり見かけないマイナーな印象。
このSeriesは、一次元の配列です。また、indexというラベルをつけることができます。
import pandas as pdでライブラリを読み込んだ後、pd.Series(引数[data]:dictionary、ndarray、スカラー値など)で作成するのが慣例です。
引数には、dataの他にindexも指定でき、これが値のラベルになります(dictionaryみたいに)。
あと、dictionaryと同じように、このSeriesとDataFrameも要素には数値型だけでなく、stringなども入れることができます。
 
 
このように書きます。
 

s = pd.Series(data, index=index)

 
 
 
■dictionaryを引数とする場合

import pandas as pd
d = {'a' : 1, 'b' : 2, 'c' : 3}
print(pd.Series(d))
# 出力結果
#a    1
#b    2
#c    3
#dtype: float64

 
 
 
■ndarrayを引数とする場合(indexなし)

import numpy as np
import pandas as pd
a = np.array([1, 2, 3])
print(pd.Series(a))
# 出力結果
#0    1
#1    2
#2    3
#dtype: int64

※自動で0,1,2のラベルが付与されている。



■ndarrayを引数とする場合(indexあり)

import numpy as np
import pandas as pd
a = np.array([1, 2, 3])
print(pd.Series(a,index=['a', 'b', 'c']))
# 出力結果
#a    1
#b    2
#c    3
#dtype: int64

※indexによりラベルを指定。



スカラー値を引数とする場合(indexは必須)

import pandas as pd
print(pd.Series(3,index=['a', 'b', 'c']))
# 出力結果
#a    3
#b    3
#c    3
#dtype: int64
Seriesの使い所

Seriesは、dnarrayとほぼ同じようにnumpyの関数の引数として扱えます。
また、1次元配列にラベルがついているという点でdictionaryと似ていますが、Seriesは順序を保存し、その点がdictionaryとの相違点です。よって、文字列のラベルをしていても、整数値で要素にアクセスすることが可能となっています。

import numpy as np
import pandas as pd
a = np.array([1, 2, 3])
s = pd.Series(a,index=['a', 'b', 'c'])
print(s['b'])
# 出力結果
#2
print(s[1])
# 出力結果
#2

Seriesだけだとそれほど便利というわけではないです。たぶん。。。「numpyのndarrayにラベルがつけられるようになったもの」という理解でよいかと。


pandasのDataFrame

冒頭に記載したように、分析だと、大体csvファイルをとりあえずこのDataFrameにぶち込んで、色々いじります。
DataFrameはカラムごとに異なる型の値を持つことができる2次元のラベル付けされたデータ配列です。sqlのテーブルやスプレッドシートのイメージです。



このように書きます


df = pd.DataFrame(data, index=index, columns=columns)


基本的には、List(もしくはSeries)を値とするdictionaryから生成できます。Listの場合は同じ要素数でないとダメなのですが、Seriesであれば、indexが行名となり、あるindexに値が入っていないSeriesでは、そのindexの要素を自動でNanにしてくれるため、要素数は気にしなくてよいです。
具体的な定義例は下記のような感じ。下記ではまず、dというdictionaryを定義します。このdは、ラベル(c_1とか)と、それに紐付く要素(pd.Series)で構成されます。このdを、pd.DataFrameの引数にすると、dのラベルがcolumnsに対応し、dの要素のpd.Seriesの要素([1,2,3])とそのラベルが、それぞれdataとindexに対応します。
各Seriesが各カラム毎の中身を表しており、indexは行を表しています。

d = {'c_1' : pd.Series([1, 2, 3], index=['a', 'b', 'c']), 'c_2' : pd.Series(['aaa', 'bbb', 'ccc', 'ddd'], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print(df)
# 出力結果
#   c_1   c_2
#a	1.0	aaa
#b	2.0	bbb
#c	3.0	ccc
#d	NaN	ddd

c_1とc_2がcolumnsのラベルの指定になっています。


DataFrameの使い所

ズバリ、sqlのテーブルをpythonでそのまま扱いたい場合。
ただ、一般的なデータ加工は基本的にsqlで加工した方が早いと思います。sqlpythonの業務分担は好みだったり、状況や案件次第だったりもするのですが、pythonsqlライクなことまでやってしまったほうが楽という場合には、pandasのDataFrameを使えばよいと思います。



まとめ

numpyのndarrayは、行列演算をするときに使う。
pandasのSeriesはDataFrameの1カラムを表し、DataFrameはsqlのテーブルをpythonでそのまま扱えるので、簡易的な(探索的な)分析におけるデータの可視化時に便利。
だと思いました。
 
 
 
特に情報がまとまっていた参照元
NumPy 配列の基礎 — 機械学習の Python との出会い
NumPy / pandas | hydroculのメモ