Pandas 集中講座 その1 オブジェクト作成とデータ参照

Pandasライブラリを覚えたい!公式「10 Minutes to pandas」をモチーフに使って実際に動作させ学習する。第1回 オブジェクト作成とデータ参照

Pandas 集中講座(1)

Pandas 集中講座 その1 オブジェクト作成とデータ参照 - chiyoh’s blog
Pandas 集中講座 その2 選択 - chiyoh’s blog
Pandas 集中講座 その3 欠陥データと操作 - chiyoh’s blog
Pandas 集中講座 その4 マージ(結合) - chiyoh’s blog
Pandas 集中講座 その5 グループ、再構成 - chiyoh’s blog
Pandas 集中講座 その6 時系列、カテゴリ、プロット、データ入出力 - chiyoh’s blog

Pandasとは、なにか?

 初め考えていたのは、EXCELシートも扱えるPythonスプレッドシートライブラリ。 VBAの代わりにマクロをPython上で実行できる便利なものと考えていた。

Pandasを実際に使ってみると

 始めて使ったのは、テキストデータをEXCELのxlsxに変換しておきたかったから。実際に変換してみると思ったより難しい(実際には、我々がEXCEL表計算としてとらえていなくて計算機能と印刷機能を持つワープロとして使っているのが原因)。また、Pandasも高機能なので出来ることが多く大いに迷ったが、それでもEXCELファイルの書き出しに成功したのでこれは便利だ!ということでこれを機に少し使ってみようと考えた。

公式「10 Minutes to pandas」を実際にやってみる

 Pandasを覚える!さて、グーグル先生に教えてもらえば大抵のことは分かるのだが、それが今の最適解なのか?と考えるとそうではない。グーグル先生のサーチ機能は、カンニングペーパーであり、単語帳である。欲しい答えを最小ステップでたどり着くための方法。例えるなら、望遠鏡、又は顕微鏡で拡大されたエリアで見つけた希望(答え)。なぜそうなるのか?なぜそう書けば動くのか?など、答えに対する成り立ちがすっぽり抜けてしまうのである。最近のPython環境は、Jupyter Notebookで敷地も下がって来ている。入門書や学習本を眺めて図を見てなんとなくへーとか言って分かった気になって何も残らないパターンになりかねない。今回選んだ方法は、グーグル先生の支援のもと公式のチュートリアル以前のPandas概要説明『10 Minutes to pandas』を上から順にJupyter Notebookを使い実行していってみようと思う。これにより、学習講座のWorkshop形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。

pandas.pydata.org

【解説】公式「10 Minutes to pandas」をJupyter Notebookを使って動作させながら確認し、突っ込みを入れる!(この文章を見る価値としては、原書が何をやっているのかが分かる、、、分かるはずである)

公式「10 Minutes to pandas」を実際にやってみる (1/6)

約10分でPandasを覚える!

これはパンダの簡単な紹介で、主に新規ユーザーを対象としています。あなたはクックブックにもっと複雑なレシピを見ることができます。

import numpy as np
import pandas as pd

【実況】importこれがないと始まらない。matplotlibもそうだがpandasもnumpyライブラリを使用して作られている。Numpyすごいな。

1.オブジェクト作成

Pandasの基本オブジェクトSeriesオブジェクトの作成

データ構造の紹介セクションを参照してください。

【実況】ハマった。もともと、10分で何とかPandasの概要が分かればいいなぁの感覚なのに。リンク先のデータ構造紹介セレクションと書かれていることは「数学」の代数幾何紹介で行列についてうんたらかんたらとそう。中間試験範囲より広い内容じゃねーかよ!ってな感じで学習にはなったがおモッキリPandasの全体の概要が知りたかったんだよ!に対しては、大きく外れてしまった。要は、ここだけですでに数時間かかっている。
しかし、Numpyでもそうだったけど初めは、np.array()なんだよね。とにかくNumpyオブジェクトを作ってそれから使う方法を覚える。PandasもSeriesとDataFrameが基本だからそのオブジェクトの作り方いろいろが学べたのは非常に良かったかもしれない。

pandas.pydata.org

そうなんだと持ったら上記のリンクを参照するかこっちのリンクを chiyoh.hatenablog.com

Series値のリストを渡してを作成し、パンダにデフォルトの整数インデックスを作成させます。

Series(1次元格納庫なシーズ)

s = pd.Series([1, 3, 5, np.nan, 6, 8])
s
0    1.0
1    3.0
2    5.0
3    NaN
4    6.0
5    8.0
dtype: float64

【解説】数字の羅列がデータ化されてPandasライブラリのSeriesオブジェクトが作成された。0~5の番号はindexと呼ばれる。データ識別番号。データとは、オブジェクトを作るときに使った引数でここでは(1, 3, 5, NaN , 6, 8)のこと。dtypeは、(Pandas内部でNumpyライブラリ互換を使っている)DataTypeのことでfloat64として代入されたみたいだ。え?Intじゃねーの?(np.NaNがfloatなので浮動小数点側にキャストされている)

s = pd.Series([1, 3, 5, 6, 8])
s
0    1
1    3
2    5
3    6
4    8
dtype: int64

【解説】NaNのせいで、int64からfloat64にクラスチェンジしたみたいだ。NaNは、Not a Numberで非数。数字じゃない何か、Series(リスト、1次元配列のようなもの)に、途中でデータが無かった場合や、削除したデータなど、データ無を強引に0やマイナス値で誤魔化すことをしないでPandasでは、欠陥データとしてNaNを使って処理をしている。ということを紹介するためにnp.nanをリストに潜り込ませたと思われる。ちょっと強引だな。
 Windows OS でも、int32にならないで、ディフォルトがint64なのね。(Numpyとちょっと違う)

a = np.array([1, 3, 5, 6, 8])
a.dtype
dtype('int32')
s = pd.Series([1, 3, 5, 6, 8])
s.dtype
dtype('int64')

【雑談】Windowsパソコンではマイク〇ソフトの陰謀でint32が標準になってしまった。64bitパソコンなのになぁ。ちなみに、他のOSだとディフォルトint64である。

DataFrame(2次元格納庫)

DataFrame日付時刻インデックスとラベル付きの列を持つNumPy配列を渡してを作成します。

dates = pd.date_range('20190501', periods=6)
dates
DatetimeIndex(['2019-05-01', '2019-05-02', '2019-05-03', '2019-05-04',
               '2019-05-05', '2019-05-06'],
              dtype='datetime64[ns]', freq='D')
type(dates)
pandas.core.indexes.datetimes.DatetimeIndex
dates[0]
Timestamp('2019-05-01 00:00:00', freq='D')
type(dates[0])
pandas._libs.tslibs.timestamps.Timestamp

【解説】date_rangeは、日付専用のrange。作られるのは、「DatetimeIndex」つまり日付用Indexだ。よってindexとして使用できる。その要素は、Timestampで定義されている

df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))
df
A B C D
2019-05-01 -0.149943 0.785767 0.622169 1.553862
2019-05-02 0.108830 1.195339 1.791636 1.337328
2019-05-03 -0.627742 -0.601345 0.201793 -0.685570
2019-05-04 -0.000103 -0.482461 -1.541615 -0.710261
2019-05-05 -0.064446 0.856883 -0.745213 2.190018
2019-05-06 -0.438960 -1.841473 1.083706 -1.413025

【解説】numpyの乱数を使って6x4の行列を作る。先ほど作った日付6日Index(行ラベル)と、['A','B','C','D']の列ラベルを使ってデータフレームを作成

辞書形式を渡すことによってSeriesのようにDataFrame作成できます。

【雑談】Seriesの辞書形式の例題やってないじゃん!

pd.Series({"壱":1,"弐":2,"参":3})
壱    1
弐    2
参    3
dtype: int64

【解説】こんなやつ。DataFrameの列が1列限定はある意味Seriesと変わらないかな。逆もかSeriesが1つの系列にか扱えない複数あるとDataFrameになるともいえる

df2 = pd.DataFrame({'A': 1.,
                    'B': pd.Timestamp('20130102'),
                    'C': pd.Series(1, index=list(range(4)), dtype='float32'),
                    'D': np.array([3] * 4, dtype='int32'),
                    'E': pd.Categorical(["test", "train", "test", "train"]),
                    'F': 'foo'})
df2
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 1.0 3 train foo
2 1.0 2013-01-02 1.0 3 test foo
3 1.0 2013-01-02 1.0 3 train foo

【解説】辞書形式を使ってDataFrameのオブジェクトを作る。ここでは、列毎に違うdtypeが入れられることをデモしている。 A列が、浮動小数点のスカラーで固定値入力の場合ほかのindexサイズに合わせて同じ値がコピーされてデータとして扱われるNumpyのブロードキャスト的な使い方。B列、日付タイプ、さっきはrangeタイプで変化していったけどここでは固定値の日付のPandas形式を紹介している。C列、Seriesオブジェクトも列指定できることをしめしている。ここで、indexの縛りをいれている。そしてdtype指定もできるよ宣言だ。D列は、Numpyの1次元配列も扱えることを示している。E列Pandasのカテゴリオブジェクトを何気なく紹介している?F列、文字列オブジェクトもブロードキャストできるよ的な。ほんと、てんこ盛りな内容ですね。

結果の列DataFrameは異なる dtypeを持ちます。

【雑談】まあ、そうでしょうよ

df2.dtypes
A           float64
B    datetime64[ns]
C           float32
D             int32
E          category
F            object
dtype: object

IPythonを使用している場合、列名(およびパブリック属性)のタブ補完は自動的に有効になります。完成する属性のサブセットは次のとおりです。

In [12]: df2.<TAB>  # noqa: E225, E999
df2.A                  df2.bool
df2.abs                df2.boxplot
df2.add                df2.C
df2.add_prefix         df2.clip
df2.add_suffix         df2.clip_lower
df2.align              df2.clip_upper
df2.all                df2.columns
df2.any                df2.combine
df2.append             df2.combine_first
df2.apply              df2.compound
df2.applymap           df2.consolidate
df2.D

あなたが見ることができるように、列はA、B、C、とD自動的にタブが完成されています。E同様にあります。残りの属性は簡潔にするために切り捨てられています。

【解説】上で作ったdf2作成したときにcolumn列A~Fしれ~っとメンバー登録されています。つまり

df2.B
0   2013-01-02
1   2013-01-02
2   2013-01-02
3   2013-01-02
Name: B, dtype: datetime64[ns]

【解説】こんな感じに使えます。

df3 = pd.DataFrame({"壱":1,"弐":2,"参":3})
ValueError: If using all scalar values, you must pass an index

【実況】英語理解してなかったので10分にしたぜ!

(ValueError:すべてのスカラー値を使用する場合は、インデックスを渡す必要があります)

df3 = pd.DataFrame({"壱":1,"弐":2,"参":[1,2,3]})
df3
0 1 2 1
1 1 2 2
2 1 2 3

【解説】うー。DataFrameとSeriesにはスカラー値(固定値)が使えます。正確にはブロードキャスト的な使い方ができます。Numpyみたいな感じです。これは、他の配列の大きさに自動調整してくれる機能ですが。全部が全部スカラー値の場合長さが決まらないのでエラーになったみたいですね。長さ1でもよさそうですがね。長さ1は[5.5]等のリスト形式にするのが正しいやり方なんでしょうね。

df3.参
0    1
1    2
2    3
Name: 参, dtype: int64

【実況】columnにもUTF-8が使えるみたいでアルファベット専用かよ!と思ったら漢字でもメンバー呼び出しできました。そうなんだぁー

2.データを見る(Series、DataFrameを参照する)

基本セクションを参照してください。

【実況】騙されません。これは、後で見るもので今見たら10分でおわりません!

これはフレームの上と下の行を表示する方法です。

df.head()
A B C D
2019-05-01 -0.149943 0.785767 0.622169 1.553862
2019-05-02 0.108830 1.195339 1.791636 1.337328
2019-05-03 -0.627742 -0.601345 0.201793 -0.685570
2019-05-04 -0.000103 -0.482461 -1.541615 -0.710261
2019-05-05 -0.064446 0.856883 -0.745213 2.190018
df.tail(3)
A B C D
2019-05-04 -0.000103 -0.482461 -1.541615 -0.710261
2019-05-05 -0.064446 0.856883 -0.745213 2.190018
2019-05-06 -0.438960 -1.841473 1.083706 -1.413025

【実況】なるほど、headとtailで頭と尻尾。つまり、行の始めと終わりを部分的に取り出す関数ですね。

行(index)、列(columns)を表示します。

df.index
DatetimeIndex(['2019-05-01', '2019-05-02', '2019-05-03', '2019-05-04',
               '2019-05-05', '2019-05-06'],
              dtype='datetime64[ns]', freq='D')
df.columns
Index(['A', 'B', 'C', 'D'], dtype='object')
type(df.index),type(df.columns)
(pandas.core.indexes.datetimes.DatetimeIndex, pandas.core.indexes.base.Index)
list(df.columns)
['A', 'B', 'C', 'D']
np.array(df.columns)
array(['A', 'B', 'C', 'D'], dtype=object)

【解説】Indexとcolumnsを取り出す方法ですね。取り出しても独自オブジェクトタイプになっております。list()化、ndarray化はできるのでいろいろ使えると思います。

Numpy配列に変換

DataFrame.to_numpy()は、NumPy配列に変換します。DataFrameデータ型が異なる列があると、操作が高負荷になることがあります。PandasとNumPyの基本的な違いは、NumPy配列は配列全体に対して1つのdtypeを持ち、一方、pandas DataFrameは列ごとに1つのdtypeを持ちます。DataFrame.to_numpy()を呼び出すと 、パンダは DataFrame内のすべての dtypeを保持できるNumPy dtypeを見つけます。結局これはobjectPythonオブジェクトにすべての値をキャストすることを必要とします。これはDataFrameすべての浮動小数点値のDataFrame.to_numpy()が高速であり、データをコピーする必要がないためです。

【解説】書いてある通りですが、ここにはNumpyとDataFrameの住み分けが書かれているように思います。Numpyは中身を統一して高速に処理が目的でDataFrameはいろいろなデータを整理しながら使うのが目的。

%time df.to_numpy()
Wall time: 0 ns





array([[-1.49942925e-01,  7.85767116e-01,  6.22168717e-01,
         1.55386197e+00],
       [ 1.08830080e-01,  1.19533852e+00,  1.79163625e+00,
         1.33732841e+00],
       [-6.27742453e-01, -6.01344643e-01,  2.01792873e-01,
        -6.85569839e-01],
       [-1.03391437e-04, -4.82460968e-01, -1.54161499e+00,
        -7.10261450e-01],
       [-6.44463926e-02,  8.56883212e-01, -7.45212590e-01,
         2.19001751e+00],
       [-4.38960300e-01, -1.84147291e+00,  1.08370615e+00,
        -1.41302507e+00]])

以下のためにdf2、DataFrame複数のdtypesで、 DataFrame.to_numpy()比較的高価(処理が手間で時間がかかるの意味)です。

%time df2.to_numpy()
Wall time: 0 ns





array([[1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
       [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo'],
       [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'test', 'foo'],
       [1.0, Timestamp('2013-01-02 00:00:00'), 1.0, 3, 'train', 'foo']],
      dtype=object)

【解説】dfは、float64で固定なのでデータを取り出しNumpyにcastすれば終わり。それに対してdf2はいろいろなdtypeがあり齟齬が出ないように調整しつつ、objectタイプにして1つ1つの要素毎にcastしてオブジェクトを生成していくので処理コストがかかっているという意味です。

【注意】Numpyは、オブジェクト型は許容しても、ndarrayは、値のdtypeがバラバラなのは許しません。**

注意:DataFrame.to_numpy()出力にインデックスラベルや列ラベルを含めません。

describe() データの簡単な統計要約を表示します。

df.describe()
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean -0.195394 -0.014548 0.235413 0.378725
std 0.281479 1.163155 1.217505 1.490613
min -0.627742 -1.841473 -1.541615 -1.413025
25% -0.366706 -0.571624 -0.508461 -0.704089
50% -0.107195 0.151653 0.411981 0.325879
75% -0.016189 0.839104 0.968322 1.499729
max 0.108830 1.195339 1.791636 2.190018

【実況】へーこういうのすごいね。(データ解析って感じ)

df2.describe()
A C D
count 4.0 4.0 4.0
mean 1.0 1.0 3.0
std 0.0 0.0 0.0
min 1.0 1.0 3.0
25% 1.0 1.0 3.0
50% 1.0 1.0 3.0
75% 1.0 1.0 3.0
max 1.0 1.0 3.0

【解説】df2は、いろんなdtypeが入っているDataFrameですね。実行すると数値のカラムのみ表示されてます

データを転置する:

df.T
2019-05-01 00:00:00 2019-05-02 00:00:00 2019-05-03 00:00:00 2019-05-04 00:00:00 2019-05-05 00:00:00 2019-05-06 00:00:00
A -0.149943 0.108830 -0.627742 -0.000103 -0.064446 -0.438960
B 0.785767 1.195339 -0.601345 -0.482461 0.856883 -1.841473
C 0.622169 1.791636 0.201793 -1.541615 -0.745213 1.083706
D 1.553862 1.337328 -0.685570 -0.710261 2.190018 -1.413025

【解説】転置行列ですね。行と列の並びが入れ替わります。
【雑談】何気に使いそう。Numpyでもlistが1次元配列に対して、indexとcolumnって1次元であってshape表記にすると(1,n),(n,1)なんだよね。(n,1)とか結構扱いにくい。無意味に'[]'を繰り返したりして。早々、df.TのTは転置(Tanchi)のTではありません。TransposeのTです。

df.transpose()
2019-05-01 00:00:00 2019-05-02 00:00:00 2019-05-03 00:00:00 2019-05-04 00:00:00 2019-05-05 00:00:00 2019-05-06 00:00:00
A -0.149943 0.108830 -0.627742 -0.000103 -0.064446 -0.438960
B 0.785767 1.195339 -0.601345 -0.482461 0.856883 -1.841473
C 0.622169 1.791636 0.201793 -1.541615 -0.745213 1.083706
D 1.553862 1.337328 -0.685570 -0.710261 2.190018 -1.413025
lst = df.T.index
lst
Index(['A', 'B', 'C', 'D'], dtype='object')
for i in lst:
    print(i,type(i))
A <class 'str'>
B <class 'str'>
C <class 'str'>
D <class 'str'>

【解説】indexは、object形式ですが中身はstrです

lst = df.T.columns
lst
DatetimeIndex(['2019-05-01', '2019-05-02', '2019-05-03', '2019-05-04',
               '2019-05-05', '2019-05-06'],
              dtype='datetime64[ns]', freq='D')
for i in lst:
    print(i,type(i))
2019-05-01 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
2019-05-02 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
2019-05-03 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
2019-05-04 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
2019-05-05 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
2019-05-06 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>


dir(df.T)   

【解説】転置した結果indexとcolumnsが入れ替わりました。indexのDatetimeIndexがcolumnsでもDatetimeIndexを保持しています。そして要素を取り出すとTimestampに代わるみたいですね。

軸によるソート

【解説】axis=1(or 'columns'),axis=0(or 'index') 、ascending=True(昇順,上り順,ABC順,123順)、ascending=Flase(降順,下り順,ZYX順,987順)

df.sort_index(axis=1, ascending=False)
D C B A
2019-05-01 1.553862 0.622169 0.785767 -0.149943
2019-05-02 1.337328 1.791636 1.195339 0.108830
2019-05-03 -0.685570 0.201793 -0.601345 -0.627742
2019-05-04 -0.710261 -1.541615 -0.482461 -0.000103
2019-05-05 2.190018 -0.745213 0.856883 -0.064446
2019-05-06 -1.413025 1.083706 -1.841473 -0.438960
df.sort_index(axis='index', ascending=False)
A B C D
2019-05-06 -0.438960 -1.841473 1.083706 -1.413025
2019-05-05 -0.064446 0.856883 -0.745213 2.190018
2019-05-04 -0.000103 -0.482461 -1.541615 -0.710261
2019-05-03 -0.627742 -0.601345 0.201793 -0.685570
2019-05-02 0.108830 1.195339 1.791636 1.337328
2019-05-01 -0.149943 0.785767 0.622169 1.553862

値によるソート

【解説】第一パラメータがソート対象。columnのラベルが指定できる。ascending=True(昇順,上り順,ABC順,123順)、ascending=Flase(降順,下り順,ZYX順,987順)

df.sort_values(by='B')
A B C D
2019-05-06 -0.438960 -1.841473 1.083706 -1.413025
2019-05-03 -0.627742 -0.601345 0.201793 -0.685570
2019-05-04 -0.000103 -0.482461 -1.541615 -0.710261
2019-05-01 -0.149943 0.785767 0.622169 1.553862
2019-05-05 -0.064446 0.856883 -0.745213 2.190018
2019-05-02 0.108830 1.195339 1.791636 1.337328
df.sort_values(by='B',ascending=False)
A B C D
2019-05-02 0.108830 1.195339 1.791636 1.337328
2019-05-05 -0.064446 0.856883 -0.745213 2.190018
2019-05-01 -0.149943 0.785767 0.622169 1.553862
2019-05-04 -0.000103 -0.482461 -1.541615 -0.710261
2019-05-03 -0.627742 -0.601345 0.201793 -0.685570
2019-05-06 -0.438960 -1.841473 1.083706 -1.413025

【解説】DataFrameの場合、column毎に単位その他が違う可能性大なので、方向でのソートはできない。同じ形式なのになぁというなら転置してソートするというてもある。というか、転置して列毎に意味合いを持たせるのが正しい使い方

df.T.sort_values(by='2019-05-04',ascending=False)
2019-05-01 00:00:00 2019-05-02 00:00:00 2019-05-03 00:00:00 2019-05-04 00:00:00 2019-05-05 00:00:00 2019-05-06 00:00:00
A -0.149943 0.108830 -0.627742 -0.000103 -0.064446 -0.438960
B 0.785767 1.195339 -0.601345 -0.482461 0.856883 -1.841473
D 1.553862 1.337328 -0.685570 -0.710261 2.190018 -1.413025
C 0.622169 1.791636 0.201793 -1.541615 -0.745213 1.083706

【雑談】お、いけた。str指定でOKそうだ。

df.sort_values(by='B',ascending=False).index
DatetimeIndex(['2019-05-02', '2019-05-05', '2019-05-01', '2019-05-04',
               '2019-05-03', '2019-05-06'],
              dtype='datetime64[ns]', freq=None)

【解説】ソートされたDatetimeIndexは、どうなっているかと思ったら。freq=Noneになって並び替えされているみたいだ。

df.sort_values(['B','C'])
A B C D
2019-05-06 -0.438960 -1.841473 1.083706 -1.413025
2019-05-03 -0.627742 -0.601345 0.201793 -0.685570
2019-05-04 -0.000103 -0.482461 -1.541615 -0.710261
2019-05-01 -0.149943 0.785767 0.622169 1.553862
2019-05-05 -0.064446 0.856883 -0.745213 2.190018
2019-05-02 0.108830 1.195339 1.791636 1.337328

【解説】複数列指定することにより、同着のデータでの順位決めのために第二keyなどを設定できる

chiyoh.hatenablog.com