pythonでデータ分析するとき、pandasというモジュールを使うのが一般的である(らしい)。
pandasにおいて、データはSeriesやDataframeという型に収納できる。
Seriesは一次元、Dataframeは二次元のデータを収納するために使われる。
それぞれ、高機能の一次元の配列、二次元の配列のようなものである。
高機能というのは、横行や縦列にそれぞれ名前が付けられる他、多くのメソッドが用意されているのだ。
# | ハチマン | ユキノ | ユイ |
---|---|---|---|
数学 | 8 | 90 | 10 |
国語 | 88 | 100 | 50 |
英語 | 38 | 95 | 35 |
二次元配列でこれを表現するとき、「ハチマン」「ユキノ」「ユイ」や「数学」「国語」「英語」といった文字の扱いに困る。Dataframeではこれをcolumnsやindexで表せる。
しかし、この型はいろいろと厄介な仕様があり、私はのっけから躓いてしまった。
これはpandas超初心者の私が自分用に作った、超初歩的なpandas操作マニュアルである。
pythonのバージョンは3.5.2(標準pythonではなく、Anacondan4.2.0を使っている)
pandasのバージョンは0.18.1
コードはiPython5.1.0で実行している状況をイメージしている。
#pandasを使う前の準備
##pandasのインストール
私はAnacondaで一括で入れた。(Anacondaはpython+よく使われるライブラリみたいなものである。NumpyやiPythonも入れられる)
そのほかにも、pipやなんやで入れてもいい。
##pandasのインポート
pandasはモジュールであるため、インポートしなければならない。
In[1]: import pandas
しかし、どこの参考サイトを見ても、pandasはpdという名前で読み込まれているようであるから、ここでもそれに倣う。
In[2]: import pandas as pd
#Seriesの操作
##Seriesを作る
|#|ハチマン|
|:-:|:-:|:-:|:-:|
|数学| 8| 90| 10|
|国語| 88|100| 50|
|英語| 38| 95| 35|
例えば、こんな一次元の配列が与えられたとしよう。
まず、これを見て思いつくのは、Listを作ることである。
In[3]: HachimanList = [8, 88, 38]
これの要素にアクセスするのは簡単だ。
国語の点数がほしいのであれば、
In[4]: Hachiman[1]
とすれば
Out[4]: 88
と返ってくるだろう。
これの問題点は、「数学」の点数、といった情報が抜けてしまうことだ。
もちろん、辞書を作ったり、オブジェクトを作ったり、名前付きタプルを作ったりしてもいいが、いずれも大規模データ処理には向かない趣がある。
これを解決するのが、pandasのSeriesである。
In[5]: HachimanSeries = pd.Series(HachimanList, index = ["math", "japanese", "english"])
このように、
変数 = pd.Series(データの配列, index = 横行の名前の配列)
で指定できる。
これを出力してみると、
In[6]: HachimanSeries
Out[6]:
math 8
japanese 88
english 38
dtype: int64`
とそれぞれの項目に名前を与えられて、出力されているのがわかる。
なおdtype
は配列全体のデータ型である。(Numpyで整数型はいくつかの種類の異なる整数型に割り振られる。int64はその内の一つである)
indexを指定していなかったらどうなるのだろうか?エラーが返ってくるのだろうか?
In[7]: YukinoSeries = pd.Series([90, 100, 95])
In[8]: YukinoSeries
Out[8]:
0 90
1 100
2 95
dtype: int64
どうやら、index
は0から増えていくデフォルト値が設定されているようだ。
なお、index
は後から加えることができる。
In[9]: YukinoSeries.index = ["math", "japanese", "english"]
In[10]: YukinoSeries
Out[10]:
math 90
japanese 100
english 95
dtype: int64
辞書を用いる方法もある。
In[11]: YuiSeries = pd.Series({"math":10, "japanese":50, "english":35})
In[12]: YuiSeries
Out[12]:
english 35
japanese 50
math 10
dtype: int64
この場合、順番がバラバラになってしまうのは仕方ない。
##Seriesの値を取り出す
Series内の値を取り出すのは通常の配列と殆ど操作が変わらない。
####要素の指定
配列のn番目の要素は配列[n-1]
で取り出す。
同様に、Seriesのn番目の要素は
In[13]: HachimanSeries[2]
Out[14]: 38
また、これを変数に渡して、計算することも可能だ。
In[15]: HachimanMath = HachimanSeries[0]
In[16]: 40 <= HachimanMath
Out[16]: False
ただし、HachimanMathの型は普通のint
ではなく、numpy.int64
である。
In[17]: type(HachimanMath)
Out[17]: numpy.int64
配列[-1]
も使える。
複数の変数を取り出す事も簡単だ。
In[18]: HachimanSeries[0:2]
Out[18]:
[8, 38]
私の実行結果を鵜呑みのせず、自分自身の環境でサンプルコードを実行してきた読者なら、ここで私がとうとう馬脚を現して、うそを書いたことに気が付いたはずだ。
このコードHachimanSeries[0:2]
はpandas.core.series.Series
という見るからにSeries型の実行結果を返してくる。
In[18]: HachimanSeries[0:2]
Out[18]:
math 8
japanese 88
dtype: int64
In[19]: type(HachimanSeries[0:2])
Out[19]: pandas.core.series.Series
まとめると
- 要素を単独で指定すると、
numpy.int64
というint
型の様な実行結果が - 要素を範囲で指定すると
pandas.core.series.Series
というSeries
型と言うべきものが返ってくる
ちょっと気持ち悪いと感じる人もいるかも知れないが、よくよく考えると
-
int
->numpy.int64
-
list
->pandas.core.series.Series
という対応関係があるだけで、ごく普通の配列の操作と変わらない。
だからもちろん、単独の要素を持つSeries
型は、配列から単独の配列を取り出すときと同様に取り出せる。
In[20]: HachimanSeries[1:1+1]
Out[20]:
japanese 88
dtype: int64
In[21]: type(HachimanSeries[1:1+1])
Out[21]:
pandas.core.series.Series
要素を指定するには、辞書型のようにindex
名で指定する方法もある。
In[22]: HachimanSeries["math"]
Out[22]: 8
ちなみに配列[::-1]
を使うと、逆にした結果が返ってくる。
驚くべきことに、これはindex
名を使って、範囲指定までできる。
In[23]: HachimanSeries["math":"english"]
Out[23]:
math 8
japanese 88
english 38
dtype: int64
これは、普通の辞書はもちろん、collections.OrderedDictでもできないことであり、Seriesたんの高性能を示している。
なお、index
の名前にアクセスしたいときは、Series.index
を配列のように扱えばいい。
In[24]:HachimanSeries.index
Out[24]:Index(['math', 'japanese', 'english'], dtype='object')
In[25]: HachimanSeries.index[1]
Out[25]: 'japanese'
In[26]: HachimanSeries.index.[1:2]
Out[26]: Index(['japanese'], dtype='object')
ここまで、Seriesが普通の配列や辞書の様に要素を取り出せることを説明してきた。
####ほしい形のSeriesを手に入れる
Series
のなかでデータのつまみ食いをしたい場合はどうすればいいだろうか?
つまり、math
とjapanese
だけが欲しいとか、math
とenglish
だけが欲しいとか。
あるいは、math
が二か所に欲しいとか。
(今はそんな必要性を感じないか…)
math
とjapanese
なら、HachimanSeries[0:2]
でなんとかなる。しかし、math
とenglish
となると、なかなか悩ましい。
そこで思いつく。
In[27]: HachimanSeries["math"]+HachimanSeries["english"]
これでどうだ!!
Out[27]: 46
現実は非情であるが、この出力はうなずける。そもそも、HachimanSeries["math"]
の結果はnumpy.int64
なのだから。
ならばと、
In[28]: HachimanSeries[0:0 + 1] + HachimanSeries[2:2 + 1]
を試してみる。
Out[28]:
english NaN
math NaN
dtype: float64
見るからに産業廃棄物を吐いてきた。
これはおそらく、Series
における足し算が、「同じindex同士を足す」ものだからであろう。
そして、共通しない要素に関しては、とりあえずNaN
を埋めておくのだ。
現に
In[29]: HachimanSeries[0:0 + 2] + YukinoSeries[1:1 + 2]
Out[29]:
english NaN
japanese 188.0
math NaN
dtype: float64
となる。
では、同じSeries内のmath
とenglish
だけを贔屓するにはどうしたらいいだろうか。
答えは、二重に[]
を書く、である。
In[30]: HachimanSeries[[0, 2]]
Out[30]:
math 8
english 38
dtype: int64
たぶん、この[[]]
は二次配列の表記と関係ない。ただ単に[[]]
という表記が使いたかっただけなのだと思われる。
(HachimanSeries[(0,2)]
は通らないため、イテレーター的な何かである必要はなかったのだろう。しかし、HachimanSeries[list((0,2))]
は通るため、処理的には配列と同じであると考えられる。)
ひたすらに数学の得点を強調したければ、
In[31]: HachimanSeries[["math","math","math","math","math"]]
Out[31]:
math 8
math 8
math 8
math 8
math 8
dtype: int64
とすればいい。(ここではindex
名を直接指定した)
index
に関しても同様だ。
In [32]: HachimanSeries.index[[1,2]]
Out[32]: Index(['japanese', 'english'], dtype='object')
ここまで、[[]]
を用いて、欲しいindex
だけを抽出したSeriesを新しく作る方法を学んだ。
###Seriesの要素の書き換え
後からSeries
の中身やindex
名が間違っていたことに気が付くかもしれない。
同じ名前で修正版Series
を上書きする方法もあるが、実は、配列と同じように簡単に変えられる。
まずは一個だけ書き換えるコード。
In[33]:HachimanSeries[1]
Out[33]: 88
In[34]: HachimanSeries[1] = 98
In[35]: HachimanSeries[1]
Out[35]: 98
次に指定した範囲を書き換える
In[36]: HachimanSeries
Out[36]:
math 8
japanese 98
english 38
dtype: int64
In[37]: HachimanSeries[1:1+2] = [89,33]
In[38]: HachimanSeries
Out[38]:
math 8
japanese 89
english 33
dtype: int64
ここで、左辺と右辺の個数があってないと怒られる。
In[39]: HachimanSeries[1:1+1] = [88,38]
-------------------
ValueError
(略)
ValueError: cannot set using a slice indexer with a different length than the value
ただし、同じ値に揃える事は出来る。
In[40]: HachimanSeries[0:0+3] = 0
In[41]: HachimanSeries
Out[40]:
math 0
japanese 0
english 0
dtype: int64
最後に、index
の書き換え
In[42]: HachimanSeries.index[1] = "Japanese"
Out[42]: HachimanSeries
math 0
Japanese 0
english 0
dtype: int64
と実はこういかない。
TypeError: Index does not support mutable operations
とあるように、index
はイミュータブルであるようだ。(文字列で似たことをしても怒られる)
だから上書きするしかない。
In[43]: HachimanSeries.index = ["Math","Japanese","English"]
In[44]: HachimanSeries
Out[44]:
Math 0
Japanese 0
English 0
dtype: int64
さて、復習がてらリセットしておこう。
In[45]: HachimanSeries[0:0+3] = [8,88,38]
In[46]: HachimanSeries.index = ["math", "japanese", "english"]
Out[46]:
math 8
japanese 88
english 38
dtype: int64
以上、Seriesの基本的な操作である。
予想以上に長くなってしまったため、Dataframeについてや、Seriesのメソッドについては続く記事で記す。