Pandas 集中講座 その2 選択
Pandasライブラリを覚えたい!公式「10 Minutes to pandas」をモチーフに使って実際に動作させ学習する。第2回 選択
Pandas 集中講座(2)
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」を実際にやってみる (2/6)
Pandasを覚える!さて、グーグル先生に教えてもらえば大抵のことは分かるのだが、それが今の最適解なのか?と考えるとそうではない。グーグル先生のサーチ機能は、カンニングペーパーであり、単語帳である。欲しい答えを最小ステップでたどり着くための方法。例えるなら、望遠鏡、又は顕微鏡で拡大されたエリアで見つけた希望(答え)。なぜそうなるのか?なぜそう書けば動くのか?など、答えに対する成り立ちがすっぽり抜けてしまうのである。最近のPython環境は、Jupyter Notebookで敷地も下がって来ている。入門書や学習本を眺めて図を見てなんとなくへーとか言って分かった気になって何も残らないパターンになりかねない。今回選んだ方法は、グーグル先生の支援のもと公式のチュートリアル以前のPandas概要説明『10 Minutes to pandas』を上から順にJupyter Notebookを使い実行していってみようと思う。これにより、学習講座のWorkshop形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。
【解説】公式「10 Minutes to pandas」をJupyter Notebookを使って動作させながら確認し、突っ込みを入れる!(この文章を見る価値としては、原書が何をやっているのかが分かる、、、分かるはずである)
公式「10 Minutes to pandas」を実際にやってみる
3.選択(Selection)
注意:選択と設定のための標準的なPython / Numpy式は直感的でインタラクティブな作業には便利ですが、プロダクションコードのためには、最適化されたpandasデータアクセスメソッド、.at、.iat、.locおよび.ilocをお勧めします。
インデックス作成のドキュメントインデックス作成とデータの選択およびMultiIndex / Advanced Indexingを参照してください。
【解説】Pandasで最適化されたアクセス方法を用意してあるのでそっちを使ってね。ということか、Python / Numpy式が何かは分かりにくいが今私たちが思い浮かぶアクセスだよね?付けて選択する方法とかでx,y座標的につかったりするやりかた。[::-1]とかもかな
配列形式な選択 ()
単一の列を選択すると、単一Seriesオブジェクトとなりdf.Aでもあります。
df['A']
2019-05-01 -0.149943
2019-05-02 0.108830
2019-05-03 -0.627742
2019-05-04 -0.000103
2019-05-05 -0.064446
2019-05-06 -0.438960
Freq: D, Name: A, dtype: float64
df.A
2019-05-01 -0.149943
2019-05-02 0.108830
2019-05-03 -0.627742
2019-05-04 -0.000103
2019-05-05 -0.064446
2019-05-06 -0.438960
Freq: D, Name: A, dtype: float64
type(df['A'])
pandas.core.series.Series
【実況】Numpy形式か?
間を選択すると行がスライスされます。
df[0:3]
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 |
df['20190502':'20190504']
A | B | C | D | |
---|---|---|---|---|
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 |
【解説】column列を選択するのには、[column名]でよく、index行選択には[index行:index行]でする
a=[0,1,2,3,4,5] a[0:2]
[0, 1]
【解説】だよね?Numpyと少し動作が違うNumpyでは[start:end:step]がイタレーション的forループで作られているから条件式部分がcount<endで定義されているのでendは含まれない。[start:end:step]分かりにくい元か[init:times:step]が適切init初期値、times何回ループするか、stepループ時の増減値なのでPandasの直感的なここからここまでな[from:to]みたいなとは別になる
【余談】なんで別にした?と思ったがindexが数値で連続性を持っているとは限らないので次のデータが+1で指定できないためか
df['20190503']
KeyError: '20190503'
【解説】DatetimeIndexなんて特別なクラスを使っているから'2019-05-03'を'20190503'と省略して書けるのね。それは置いておいて、1つ選ぶのに直接呼び出したらエラーになった。これはスライスして同じ値を入れることで回避できる
df['2019-05-03':'2019-05-03']
A | B | C | D | |
---|---|---|---|---|
2019-05-03 | -0.627742 | -0.601345 | 0.201793 | -0.68557 |
ラベルによる選択(.loc[]) 行選択
ラベルによる選択の詳細を参照してください。ラベルを使って断面を取得するには:
df.loc[dates[0]]
A -0.149943
B 0.785767
C 0.622169
D 1.553862
Name: 2019-05-01 00:00:00, dtype: float64
【実況】dates[0]ってなんだ?dfを定義したときに使った変数だけど。10分講座だから覚えているよね?むりー
dates = pd.date_range('20190501', periods=6) df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD')) df
A | B | C | D | |
---|---|---|---|---|
2019-05-01 | 0.451080 | 1.378920 | -0.897954 | 0.382555 |
2019-05-02 | -0.250102 | -1.663388 | 0.010251 | -1.531969 |
2019-05-03 | -1.061661 | 0.248446 | -1.246751 | 1.907569 |
2019-05-04 | 0.064499 | 0.386742 | -0.841549 | -1.483970 |
2019-05-05 | -0.694038 | -0.496932 | 0.070118 | -0.056977 |
2019-05-06 | -0.422955 | 0.824024 | 0.654380 | 0.202406 |
df.loc[dates[0]]
A 0.451080
B 1.378920
C -0.897954
D 0.382555
Name: 2019-05-01 00:00:00, dtype: float64
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')
dates[0]
Timestamp('2019-05-01 00:00:00', freq='D')
【解説】indexに使ったDatetimeIndex形式はインスタンス化するときに'20190501'として日まで入力しているが実際は秒まで認識している。EXCELでいうところのシリアル値(int64)で内部処理されている
df.loc[dates[-1]]
A -0.422955
B 0.824024
C 0.654380
D 0.202406
Name: 2019-05-06 00:00:00, dtype: float64
df.loc['2019-05-03']
A -1.061661
B 0.248446
C -1.246751
D 1.907569
Name: 2019-05-03 00:00:00, dtype: float64
【解説】[]では、ダメでしたが.loc[]では、OKのようです
df.loc[:]
A | B | C | D | |
---|---|---|---|---|
2019-05-01 | 0.451080 | 1.378920 | -0.897954 | 0.382555 |
2019-05-02 | -0.250102 | -1.663388 | 0.010251 | -1.531969 |
2019-05-03 | -1.061661 | 0.248446 | -1.246751 | 1.907569 |
2019-05-04 | 0.064499 | 0.386742 | -0.841549 | -1.483970 |
2019-05-05 | -0.694038 | -0.496932 | 0.070118 | -0.056977 |
2019-05-06 | -0.422955 | 0.824024 | 0.654380 | 0.202406 |
【解説】[:]は、index全部
ラベルで多軸を選択: 行と列選択 (.loc[ix,col] ラベルで行と列のグループにアクセス)
df.loc[:, ['A', 'B']]
A | B | |
---|---|---|
2019-05-01 | 0.451080 | 1.378920 |
2019-05-02 | -0.250102 | -1.663388 |
2019-05-03 | -1.061661 | 0.248446 |
2019-05-04 | 0.064499 | 0.386742 |
2019-05-05 | -0.694038 | -0.496932 |
2019-05-06 | -0.422955 | 0.824024 |
【実況】分かりにくいdf.loc[row_indexer] または、df.loc[row_indexer,column_indexer]なのか?
df.loc[:, ['A', 'C']]
A | C | |
---|---|---|
2019-05-01 | 0.451080 | -0.897954 |
2019-05-02 | -0.250102 | 0.010251 |
2019-05-03 | -1.061661 | -1.246751 |
2019-05-04 | 0.064499 | -0.841549 |
2019-05-05 | -0.694038 | 0.070118 |
2019-05-06 | -0.422955 | 0.654380 |
【解説】リスト形式にすることで、columnを複数選択できる。スライスとは違う
ラベルスライスを表示すると、両方のエンドポイントが含まれます。
df.loc['20190502':'20190504', ['A', 'B']]
A | B | |
---|---|---|
2019-05-02 | -0.250102 | -1.663388 |
2019-05-03 | -1.061661 | 0.248446 |
2019-05-04 | 0.064499 | 0.386742 |
df.loc[:, 'A':'B']
A | B | |
---|---|---|
2019-05-01 | 0.451080 | 1.378920 |
2019-05-02 | -0.250102 | -1.663388 |
2019-05-03 | -1.061661 | 0.248446 |
2019-05-04 | 0.064499 | 0.386742 |
2019-05-05 | -0.694038 | -0.496932 |
2019-05-06 | -0.422955 | 0.824024 |
【解説】indexだけでなくcolumnもスライスできます
返されるオブジェクトのサイズを縮小します。
df.loc['20190502', ['A', 'B']]
A -0.250102
B -1.663388
Name: 2019-05-02 00:00:00, dtype: float64
type(df.loc['20190502', ['A', 'B']])
pandas.core.series.Series
【解説】indexが、一つの場合column列のSeriesを返してくる。
スカラー値を取得するには
df.loc[dates[0], 'A']
0.45107999031214707
type(df.loc[dates[0], 'A'])
numpy.float64
【解説】Index行とcolumn列を1ずつ選択するとスカラー値で戻ってくる
df.loc['20190502', ['A']]
A -0.250102
Name: 2019-05-02 00:00:00, dtype: float64
type(df.loc['20190502', ['A']])
pandas.core.series.Series
df.loc['20190502', ['A']][0]
-0.25010196341642593
【解説】1ずつ選択だが、 ['A']が複数選択形式、indexが1つなのでSeriesで戻してくる
df.loc['20190502':, ['A']]
A | |
---|---|
2019-05-02 | -0.250102 |
2019-05-03 | -1.061661 |
2019-05-04 | 0.064499 |
2019-05-05 | -0.694038 |
2019-05-06 | -0.422955 |
【解説】複数指定方式同士にするとDataFrameになる
スカラーへの高速アクセスを得るために(前のメソッドと同等): (.at 単一アクセス)
df.at[dates[0], 'A']
0.45107999031214707
位置による選択 (.iloc[])
位置による選択の詳細を参照してください。渡された整数の位置で選択します。
a = np.array(range(100)).reshape(10,10).T[:6,:4] df = pd.DataFrame(a, columns=list('ABCD')) df
A | B | C | D | |
---|---|---|---|---|
0 | 0 | 10 | 20 | 30 |
1 | 1 | 11 | 21 | 31 |
2 | 2 | 12 | 22 | 32 |
3 | 3 | 13 | 23 | 33 |
4 | 4 | 14 | 24 | 34 |
5 | 5 | 15 | 25 | 35 |
【解説】表が分かりにくいので、行が1の位、列が10の位が変化する表に差し替えた
df.iloc[3]
A 3
B 13
C 23
D 33
Name: 3, dtype: int32
【解説】[ ]指定なので配列風,行列風な指定方法です。[index]でカラムに対するデータを選択します
整数スライスで、numpy / pythonに似た動作をします。
df.iloc[3:5, 0:2]
A | B | |
---|---|---|
3 | 3 | 13 |
4 | 4 | 14 |
【解説】こっちが、Numpyと同じスライスの終わりが含まれないという動作ですね
整数位置のリストで、numpy / pythonスタイルに似ています。
df.iloc[[1, 2, 4], [0, 2]]
A | C | |
---|---|---|
1 | 1 | 21 |
2 | 2 | 22 |
4 | 4 | 24 |
【実況】え?そうなん?
【解説】内容に付いて、indexの1, 2, 4を選択 colの0列目と2列目を選択交差した部分を選択
list_a = [[ 0, 10, 20, 30],[ 1, 11, 21, 31], [ 2, 12, 22, 32],[ 3, 13, 23, 33], [ 4, 14, 24, 34],[ 5, 15, 25, 35]] list_a
[[0, 10, 20, 30],
[1, 11, 21, 31],
[2, 12, 22, 32],
[3, 13, 23, 33],
[4, 14, 24, 34],
[5, 15, 25, 35]]
list_a[[1, 2, 4], [0, 2]]
TypeError: list indices must be integers or slices, not tuple
a[[1, 2, 4], [0, 2]]
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (3,) (2,)
【解説】どの辺がnumpy/python風なのか分かりません
行を明示的にスライスする場合:
df.iloc[1:3, :]
A | B | C | D | |
---|---|---|---|---|
1 | 1 | 11 | 21 | 31 |
2 | 2 | 12 | 22 | 32 |
列を明示的にスライスする場合
df.iloc[:, 1:3]
B | C | |
---|---|---|
0 | 10 | 20 |
1 | 11 | 21 |
2 | 12 | 22 |
3 | 13 | 23 |
4 | 14 | 24 |
5 | 15 | 25 |
【解説】:(スライス)のみで全体選択をして、もう片方をIndexかcolumnを選ぶやり方ですね
明示的に値を取得するには
df.iloc[1, 1]
11
【解説】Indexとcolumnを1つに限定するとスカラー値として取り出せる
スカラーへの高速アクセスを得るために(前のメソッドと同等):
df.iat[1, 1]
11
【解説】loc[]と同様にat[]の代わりにiat[]が単一選択の高速版になります
ブール索引付け
単一列の値を使用してデータを選択する
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD')) df
A | B | C | D | |
---|---|---|---|---|
2019-05-01 | -0.106726 | -1.762983 | -1.265036 | -0.404320 |
2019-05-02 | -0.690402 | -0.045117 | -0.684309 | 1.756284 |
2019-05-03 | -0.479369 | 0.419347 | 1.396591 | -0.762011 |
2019-05-04 | 0.497103 | 0.760560 | -2.488575 | -0.670358 |
2019-05-05 | 0.974668 | -1.191495 | -1.674198 | 1.348520 |
2019-05-06 | -1.740956 | 0.539032 | 0.709385 | -1.034903 |
df[df.A > 0]
A | B | C | D | |
---|---|---|---|---|
2019-05-04 | 0.497103 | 0.760560 | -2.488575 | -0.670358 |
2019-05-05 | 0.974668 | -1.191495 | -1.674198 | 1.348520 |
【解説】A列で0より大きいindex行を選択しなさい
df.A
2019-05-01 -0.106726
2019-05-02 -0.690402
2019-05-03 -0.479369
2019-05-04 0.497103
2019-05-05 0.974668
2019-05-06 -1.740956
Freq: D, Name: A, dtype: float64
df.A > 0
2019-05-01 False
2019-05-02 False
2019-05-03 False
2019-05-04 True
2019-05-05 True
2019-05-06 False
Freq: D, Name: A, dtype: bool
【解説】内容的には、df.A
(df['A'])でA列を選択 df.A >0
A列の中で0以上のものをTrueとして判定。df[ index ]なので、dfのindexの並び順に照らし合わせてTrueの行のみを選択した
ブール条件が満たされるDataFrameから値を選択する。
df[df > 0]
A | B | C | D | |
---|---|---|---|---|
2019-05-01 | NaN | NaN | NaN | NaN |
2019-05-02 | NaN | NaN | NaN | 1.756284 |
2019-05-03 | NaN | 0.419347 | 1.396591 | NaN |
2019-05-04 | 0.497103 | 0.760560 | NaN | NaN |
2019-05-05 | 0.974668 | NaN | NaN | 1.348520 |
2019-05-06 | NaN | 0.539032 | 0.709385 | NaN |
df > 0
A | B | C | D | |
---|---|---|---|---|
2019-05-01 | False | False | False | False |
2019-05-02 | False | False | False | True |
2019-05-03 | False | True | True | False |
2019-05-04 | True | True | False | False |
2019-05-05 | True | False | False | True |
2019-05-06 | False | True | True | False |
【解説】さらに分かりにくいdf>0なので全体に足して0より大きいがどうかを判定している。これはいい。それをdf[ index,colum ]で全体適応したのでTrueのところは選択されて数値をFlaseのところはMaskされてnp.NaN表記になったという感じか?
df.loc[pd.Timestamp(2019, 5, 7, 0)] = -10 df.loc["2019-05-08"] = -10 df['E'] = -10 df
A | B | C | D | E | |
---|---|---|---|---|---|
2019-05-01 00:00:00 | -0.106726 | -1.762983 | -1.265036 | -0.404320 | -10 |
2019-05-02 00:00:00 | -0.690402 | -0.045117 | -0.684309 | 1.756284 | -10 |
2019-05-03 00:00:00 | -0.479369 | 0.419347 | 1.396591 | -0.762011 | -10 |
2019-05-04 00:00:00 | 0.497103 | 0.760560 | -2.488575 | -0.670358 | -10 |
2019-05-05 00:00:00 | 0.974668 | -1.191495 | -1.674198 | 1.348520 | -10 |
2019-05-06 00:00:00 | -1.740956 | 0.539032 | 0.709385 | -1.034903 | -10 |
2019-05-07 00:00:00 | -10.000000 | -10.000000 | -10.000000 | -10.000000 | -10 |
2019-05-08 | -10.000000 | -10.000000 | -10.000000 | -10.000000 | -10 |
【解説】"2019-05-08"だけでは、strのindexが挟み込まれたとして識別されてしまうみたいだ
df[df > 0]
A | B | C | D | E | |
---|---|---|---|---|---|
2019-05-01 00:00:00 | NaN | NaN | NaN | NaN | NaN |
2019-05-02 00:00:00 | NaN | NaN | NaN | 1.756284 | NaN |
2019-05-03 00:00:00 | NaN | 0.419347 | 1.396591 | NaN | NaN |
2019-05-04 00:00:00 | 0.497103 | 0.760560 | NaN | NaN | NaN |
2019-05-05 00:00:00 | 0.974668 | NaN | NaN | 1.348520 | NaN |
2019-05-06 00:00:00 | NaN | 0.539032 | 0.709385 | NaN | NaN |
2019-05-07 00:00:00 | NaN | NaN | NaN | NaN | NaN |
2019-05-08 | NaN | NaN | NaN | NaN | NaN |
【解説】df[df > 0]
適応するときA列から順に適応して結果をSeriesにしてそれを総合してindexとcolumを決めているのではなく単純に全体を判断して表示しているだけっぽい(indexとE列が削除されてない)
isin()フィルタリング方法を使用する:
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD')) df
A | B | C | D | |
---|---|---|---|---|
2019-05-01 | 1.111420 | -0.179004 | 1.799883 | 0.569833 |
2019-05-02 | 1.040001 | -0.743061 | 0.310026 | 0.121585 |
2019-05-03 | -0.987081 | -1.032944 | -0.367010 | -1.180204 |
2019-05-04 | 0.164185 | -0.573081 | -0.544974 | 0.859824 |
2019-05-05 | 0.769549 | 3.183677 | -0.426652 | 0.700605 |
2019-05-06 | -1.005111 | 0.095534 | -0.842821 | 1.181250 |
df2 = df.copy() df2['E'] = ['one', 'one', 'two', 'three', 'four', 'three'] df2
A | B | C | D | E | |
---|---|---|---|---|---|
2019-05-01 | 1.111420 | -0.179004 | 1.799883 | 0.569833 | one |
2019-05-02 | 1.040001 | -0.743061 | 0.310026 | 0.121585 | one |
2019-05-03 | -0.987081 | -1.032944 | -0.367010 | -1.180204 | two |
2019-05-04 | 0.164185 | -0.573081 | -0.544974 | 0.859824 | three |
2019-05-05 | 0.769549 | 3.183677 | -0.426652 | 0.700605 | four |
2019-05-06 | -1.005111 | 0.095534 | -0.842821 | 1.181250 | three |
df2[df2['E'].isin(['two', 'four'])]
A | B | C | D | E | |
---|---|---|---|---|---|
2019-05-03 | -0.987081 | -1.032944 | -0.367010 | -1.180204 | two |
2019-05-05 | 0.769549 | 3.183677 | -0.426652 | 0.700605 | four |
df2['E'].isin(['two', 'four'])
2019-05-01 False
2019-05-02 False
2019-05-03 True
2019-05-04 False
2019-05-05 True
2019-05-06 False
Freq: D, Name: E, dtype: bool
df2['E']
2019-05-01 one
2019-05-02 one
2019-05-03 two
2019-05-04 three
2019-05-05 four
2019-05-06 three
Freq: D, Name: E, dtype: object
type(df2['E'])
pandas.core.series.Series
【解説】E列を使ってフィルタする。df2['E'].isin(['two', 'four'])
は、E列に対してisinを適応isinは、中にある?の関数なので'two'と'four'があるindexのみTrueして判定1列なのでSeriesが戻ってくる。それをdf2[ ]全体に適応するとTrueの行のみが選択される。
df2[df2.isin(['two', 'four'])]
A | B | C | D | E | |
---|---|---|---|---|---|
2019-05-01 | NaN | NaN | NaN | NaN | NaN |
2019-05-02 | NaN | NaN | NaN | NaN | NaN |
2019-05-03 | NaN | NaN | NaN | NaN | two |
2019-05-04 | NaN | NaN | NaN | NaN | NaN |
2019-05-05 | NaN | NaN | NaN | NaN | four |
2019-05-06 | NaN | NaN | NaN | NaN | NaN |
df2[df2.isin(['two', 'four'])].fillna(value='')
A | B | C | D | E | |
---|---|---|---|---|---|
2019-05-01 | |||||
2019-05-02 | |||||
2019-05-03 | two | ||||
2019-05-04 | |||||
2019-05-05 | four | ||||
2019-05-06 |
【解説】全体適応させるとこんな感じ
設定 (追加,更新)
新しい列を設定すると、インデックスによってデータが自動的に整列されます。(追加)
df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD')) df
A | B | C | D | |
---|---|---|---|---|
2019-05-01 | 1.498666 | -0.143574 | -0.871839 | -0.106927 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 0.991350 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 0.826141 |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | -0.311225 |
2019-05-05 | 1.376085 | 0.310974 | 0.350129 | 0.221216 |
2019-05-06 | 0.821727 | 0.389651 | -1.202223 | -0.354074 |
s1 = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range('20190501', periods=6)) s1
2019-05-01 1
2019-05-02 2
2019-05-03 3
2019-05-04 4
2019-05-05 5
2019-05-06 6
Freq: D, dtype: int64
df['F'] = s1
df
A | B | C | D | F | |
---|---|---|---|---|---|
2019-05-01 | 1.498666 | -0.143574 | -0.871839 | -0.106927 | 1 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 0.991350 | 2 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 0.826141 | 3 |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | -0.311225 | 4 |
2019-05-05 | 1.376085 | 0.310974 | 0.350129 | 0.221216 | 5 |
2019-05-06 | 0.821727 | 0.389651 | -1.202223 | -0.354074 | 6 |
【解説】F列にSeriesデータを代入。結果としてF列が無いので新規に追加してSeriesのデータを各indexに代入された。
s2 = pd.Series([1, 2, 3], index=pd.date_range('20190501', periods=3)) df['G'] = s2 df
A | B | C | D | F | G | |
---|---|---|---|---|---|---|
2019-05-01 | 1.498666 | -0.143574 | -0.871839 | -0.106927 | 1 | 1.0 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 0.991350 | 2 | 2.0 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 0.826141 | 3 | 3.0 |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | -0.311225 | 4 | NaN |
2019-05-05 | 1.376085 | 0.310974 | 0.350129 | 0.221216 | 5 | NaN |
2019-05-06 | 0.821727 | 0.389651 | -1.202223 | -0.354074 | 6 | NaN |
【解説】インデックスによってデータが自動的に整列されます。の部分が分からなかったけど、追加するデータがindexに足して少ないとその要素はNaNとして扱われる。Gを適応させていくときに自動調整でNaNを追加していくという意味なんでしょう
len(df.index)
6
【解説】lenメソッドでindexの長さが分かるのでデータその分のデータがあるか確認できます
ラベルによる値の設定 (更新)
df.at[dates[0], 'A'] = 0 df
A | B | C | D | F | G | |
---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | -0.143574 | -0.871839 | -0.106927 | 1 | 1.0 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 0.991350 | 2 | 2.0 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 0.826141 | 3 | 3.0 |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | -0.311225 | 4 | NaN |
2019-05-05 | 1.376085 | 0.310974 | 0.350129 | 0.221216 | 5 | NaN |
2019-05-06 | 0.821727 | 0.389651 | -1.202223 | -0.354074 | 6 | NaN |
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')
【解説】A列,dates[0]='2019-05-01'行のデータが0.0に更新されました。A列のdtypeがfloat64なので0.0になる
位置による設定値: (更新)
df.iat[0, 1] = 0 df
A | B | C | D | F | G | |
---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | 0.000000 | -0.871839 | -0.106927 | 1 | 1.0 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 0.991350 | 2 | 2.0 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 0.826141 | 3 | 3.0 |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | -0.311225 | 4 | NaN |
2019-05-05 | 1.376085 | 0.310974 | 0.350129 | 0.221216 | 5 | NaN |
2019-05-06 | 0.821727 | 0.389651 | -1.202223 | -0.354074 | 6 | NaN |
【解説】こっちはかんたんですね。.iat[]なので数値指定できます。
NumPy配列で代入して設定する:
df.loc[:, 'D'] = np.array([5] * len(df)) df
A | B | C | D | F | G | |
---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | 0.000000 | -0.871839 | 5 | 1 | 1.0 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 5 | 2 | 2.0 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 5 | 3 | 3.0 |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | 5 | 4 | NaN |
2019-05-05 | 1.376085 | 0.310974 | 0.350129 | 5 | 5 | NaN |
2019-05-06 | 0.821727 | 0.389651 | -1.202223 | 5 | 6 | NaN |
len(df)
6
【解説】ラベル(.loc)で追加、Numpyの1次元配列をindex分作って代入しています。
【実況】あれ、そうですかdfにlen()を適応するとindexの行数が戻ってくるんですね。Numpyの場合、要素数になるのでそうかともってました
where設定ありの操作。
df2 = df.copy()
df2[df2 > 0] = -df2
df2
A | B | C | D | F | G | |
---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | 0.000000 | -0.871839 | -5 | -1 | -1.0 |
2019-05-02 | -0.092376 | -0.036220 | -0.146521 | -5 | -2 | -2.0 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | -5 | -3 | -3.0 |
2019-05-04 | -0.397105 | -1.731472 | -1.033554 | -5 | -4 | NaN |
2019-05-05 | -1.376085 | -0.310974 | -0.350129 | -5 | -5 | NaN |
2019-05-06 | -0.821727 | -0.389651 | -1.202223 | -5 | -6 | NaN |
【解説】フィルタの全体適応ですね。df2[df2 > 0] = -df2
は、自分に上書きしているのでその点は気を付けましょう。df2>0で0以上を調べTrue/FalseのMaskを作って該当す要素の書き換えをしています。結果としてすべてマイナス要素に代わってます。(0とNaN以外だね)
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形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。
【解説】公式「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が基本だからそのオブジェクトの作り方いろいろが学べたのは非常に良かったかもしれない。
そうなんだと持ったら上記のリンクを参照するかこっちのリンクを 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などを設定できる
Pandas のSeriesとDataFrame
Pandas(データ構造)
Pandasのデータ構造はどうなっているのか? * Series(ラベル付きデータ) * DataFrame(ラベル付きデータが複数)
です。
Series (シリーズ)
直列、連続など、繋がりのある何かの事ですね。Pandasは、スプレッドシート(表計算)と思っていたが思想が違うみたいだ。Seriesが基本でそれの拡張がDataFrameであるのかな。
インデックス(index)、索引、目次だったり、見出しだったりいろいろ意味があるがここではシリーズとして代入する値のラベル(label)
データ(data)、整数、文字列、浮動小数点数、Pythonオブジェクトなど値のこと
シリーズ(Series)、1次元のラベル付き配列で軸ラベルはまとめてインデックスという。
s = pd.Series(data, index=index)
例として、インデックスのラベルとして駅名、値として東京からの営業キロ。もちろん関係性(紐づいている)があります。を使ってシリーズを作ります
index=["東京","品川","新横浜","名古屋","京都","新大阪","新神戸","姫路","岡山","福山","広島","徳山","新山口","小倉","博多"]
data=[0.0,6.8,28.8,366.0,513.6,552.6,589.5,644.3,732.9,791.2,894.2,982.7,1027,1107.7,1174.9]
s = pd.Series(data,index=index) s
東京 0.0
品川 6.8
新横浜 28.8
名古屋 366.0
京都 513.6
新大阪 552.6
新神戸 589.5
姫路 644.3
岡山 732.9
福山 791.2
広島 894.2
徳山 982.7
新山口 1027.0
小倉 1107.7
博多 1174.9
dtype: float64
s.index
Index(['東京', '品川', '新横浜', '名古屋', '京都', '新大阪', '新神戸', '姫路', '岡山', '福山', '広島',
'徳山', '新山口', '小倉', '博多'],
dtype='object')
pd.Series(data)
0 0.0
1 6.8
2 28.8
3 366.0
4 513.6
5 552.6
6 589.5
7 644.3
8 732.9
9 791.2
10 894.2
11 982.7
12 1027.0
13 1107.7
14 1174.9
dtype: float64
indexがあるほうが分かりやすいですね。インデックスは、Str文字列なのでオブジェクト型になるみたいです。indexがなくても数値の羅列だけでもシリーズは作れます。測定(measure)データや記録(record)データなどを代入できるということですね。
Seriesのdata
をdataとして扱える Seriesにはindexが必要、Seriesを作るときindexを指定しないと0から始まる数値の番号が付く。 ゆえに、辞書の場合、keyとvalueがindex,dataの関係で1対1。1次元配列の場合は、指定するindexと配列数が同じサイズ出ないとErrorになる。スカラー値の場合同じ値の連続という(ブロードキャスト)意味もあるのでindexが指定されていればそれだけの分のデータをスカラー値の同じ値で埋めるのでErrorにはならない。
配列をもとに作る
import numpy as np s = pd.Series(np.arange(0,3*10,3)) s
0 0
1 3
2 6
3 9
4 12
5 15
6 18
7 21
8 24
9 27
dtype: int32
s = pd.Series(np.arange(0,3*10,3),index=['A','B','C'])
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-15-763913bcc66a> in <module>
----> 1 s = pd.Series(np.arange(0,3*10,3),index=['A','B','C'])
h:\Anaconda3\lib\site-packages\pandas\core\series.py in __init__(self, data, index, dtype, name, copy, fastpath)
247 'Length of passed values is {val}, '
248 'index implies {ind}'
--> 249 .format(val=len(data), ind=len(index)))
250 except TypeError:
251 pass
ValueError: Length of passed values is 10, index implies 3
indexとdata数がミスマッチすると「ValueError」になる
s = pd.Series(np.arange(0,3*10,3),index=['A','B','C','D','E','F','G','H','I','J']) s
A 0
B 3
C 6
D 9
E 12
F 15
G 18
H 21
I 24
J 27
dtype: int32
s = pd.Series([0,3,6,9,12,15,18,21,24,27],index=['A','B','C','D','E','F','G','H','I','J'])
s = pd.Series([0,3,6,9,12,15,18,21,24,27])
s = pd.Series([0,3,6,9,12,15,18,21,24,27],index=['A','A','A','A','A','A','A','A','A','A']) s
A 0
A 3
A 6
A 9
A 12
A 15
A 18
A 21
A 24
A 27
dtype: int64
index値が一意(ほかの値とダブル、被る)でなくてもErrorにはならないがサポートされてないメソッドもある。
辞書をもとに作る
x3 = {} x3['3x1'] = 3 x3['3x2'] = 6 x3['3x3'] = 9 x3['3x4'] = 12 x3['3x5'] = 15 pd.Series(x3)
3x1 3
3x2 6
3x3 9
3x4 12
3x5 15
dtype: int64
pd.Series({"壱":1,"弐":2,"参":3})
壱 1
弐 2
参 3
dtype: int64
データが辞書形式の場合、PythonとPandasのVersionによって、Seriesに登録される順番が変わる。知っての通り辞書型はindex(呼び出しオブジェクト)が自由で処理のスピードの観点からindex形式が数値だけだとしてもソートした順番で内部処理しているわけではない。'list(dict)','sorted(dict)'のどちらをとるかの違い。前者が代入順、後者がkeyをソートした順になっている。バージョンによってSeriesに代入される順が変わるので注意が必要。
辞書形式を使うときにもindex指定ができる。index指定をすると辞書型変数から必要なものだけをSeries化できる
di = {"壱":1,"弐":2,"参":3,'一':1,'二':2,'三':3,'四':4,'五':5,'六':6,'七':7,'八':8,'九':9}
pd.Series(di,["壱","弐","参","九","十","壱","壱"])
壱 1.0
弐 2.0
参 3.0
九 9.0
十 NaN
壱 1.0
壱 1.0
dtype: float64
diの辞書型からindexに登録したいものだけをリスト化してindexに入れることができる。ただし辞書に登録されてないものをdata化しようとするとErrorではなくNaN(数値ではない)として登録される。本来の意味ではなく該当値がないという意味である。
di2 = dict([(di[x],x) for x in list(di)]) di2
{1: '一', 2: '二', 3: '三', 4: '四', 5: '五', 6: '六', 7: '七', 8: '八', 9: '九'}
pd.Series(di2,[7,8,9,10])
7 七
8 八
9 九
10 NaN
dtype: object
たしかにオブジェクトは、'Not a Number'非数であるけどさ
スカラー値をもとに作る
スカラー値をもとに作るとindexの長さに一致するように値を入力する。
pd.Series(1)
0 1
dtype: int64
pd.Series(1,index=list("ABCDEF"))
A 1
B 1
C 1
D 1
E 1
F 1
dtype: int64
空のシリーズ
pd.Series()
Series([], dtype: float64)
Numpyな動作
NumpyのndarrayとSeriesは、似た動作をする。
s = pd.Series([0,3,6,9,12,15,18,21,24,27],index=['A','B','C','D','E','F','G','H','I','J'])
s[0]
0
s[:5]
A 0
B 3
C 6
D 9
E 12
dtype: int64
s[s> s.median()]
F 15
G 18
H 21
I 24
J 27
dtype: int64
s[[4,3,1]]
E 12
D 9
B 3
dtype: int64
np.pi*s**2
A 0.000000
B 28.274334
C 113.097336
D 254.469005
E 452.389342
F 706.858347
G 1017.876020
H 1385.442360
I 1809.557368
J 2290.221044
dtype: float64
Numpyのように pandas Series は、dtype(データタイプ)を持っている
s.dtype
dtype('int64')
このdtypeは、Numpyと同じだが一部拡張されておりExtensionDtypeとなっている。
s.array
<PandasArray>
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
Length: 10, dtype: int64
type(s)
pandas.core.series.Series
type(s.array)
pandas.core.arrays.numpy_.PandasArray
Seriesを配列として扱いたい場合はSeries.arrayを使う。使えるNumpy メソッド等が増える。この配列も一部拡張されたExtensionArrayとなっている。Seriesはndarrayに似ているが、実際のndarrayが必要な場合はSeries.to_numpy()を使用する。
s_array = s.to_numpy() s_array
array([ 0, 3, 6, 9, 12, 15, 18, 21, 24, 27], dtype=int64)
type(s_array)
numpy.ndarray
辞書形式な動作
Seriesは、インデックス(ラベル)で値を取得および設定できるので固定サイズの辞書ともいえる。
Python 辞書形式と同じようにdataの追加、変更、削除、indexの有り無し確認などができる
s = pd.Series([0,3,6,9,12,15,18,21,24,27],index=['A','B','C','D','E','F','G','H','I','J'])
s['A']
0
s['I']
24
s
A 0
B 3
C 6
D 9
E 12
F 15
G 18
H 21
I 24
J 27
dtype: int64
s['A'] = -1 s['A']
-1
s['K'] = 33
'D' in s
True
del s['J']
'J' in s
False
s['J']
KeyError: 'J'
s
A -1
B 3
C 6
D 9
E 12
F 15
G 18
H 21
I 24
K 33
dtype: int64
getメソッドを使うとラベルが見つからない処理が便利。getメソッドは、indexlabelがないときNoneまたは指定されたデフォルトが返される。
s.get('J')
type(s.get('J'))
NoneType
s.get('J', np.nan)
nan
s.get('J', 0)
0
s.get('J', 'データがありません')
'データがありません'
Seriesとndarrayの主な違いは、Series間の操作ではラベルに基づいてデータが自動的に整列されることです。したがって、関係するシリーズが同じラベルを持つかどうかを考慮せずに計算を書くことができます。
s[1:] + s[:-1]
A NaN
B 6.0
C 12.0
D 18.0
E 24.0
F 30.0
G 36.0
H 42.0
I 48.0
K NaN
dtype: float64
位置合わせされていないシリーズ間の演算の結果は、関連するインデックスの和集合になります。あるシリーズまたは別のシリーズでラベルが見つからない場合、結果は欠落としてマークされNaNます。明示的なデータアライメントを行わずにコードを記述できるようになると、対話型のデータ分析と研究に非常に大きな自由と柔軟性が与えられます。パンダデータ構造の統合されたデータ整列機能は、ラベル付きデータを扱うための関連ツールの大部分とは別にパンダを設定します。
同じインデックスがない演算は、演算できないためNaN(欠陥データ)になる。dropna関数を使って、データが欠けているラベルを削除することもできる。
s1 = s[1:] + s[:-1] s1.dropna()
B 6.0
C 12.0
D 18.0
E 24.0
F 30.0
G 36.0
H 42.0
I 48.0
dtype: float64
名前属性
indexのほかにもSeriesに名前を付けることができる。変数名のほかにオブジェクト内部で名前が保持される。
s = pd.Series(np.random.randn(5), name='something')
s
0 -0.284847
1 -0.368176
2 0.295479
3 0.432335
4 -0.894993
Name: something, dtype: float64
Series nameは、多くの場合自動的に割り当てられます。特に、以下に示すように、DataFrameから1Dスライスしたときなど
s1 = s.rename("different")
s1.name
'different'
s1
0 -0.284847
1 -0.368176
2 0.295479
3 0.432335
4 -0.894993
Name: different, dtype: float64
s
0 -0.284847
1 -0.368176
2 0.295479
3 0.432335
4 -0.894993
Name: something, dtype: float64
s1.name = 'original'
s1
0 -0.284847
1 -0.368176
2 0.295479
3 0.432335
4 -0.894993
Name: original, dtype: float64
DataFrame(データフレーム)
DataFrameは、潜在的に異なる型の列を持つ2次元のラベル付きデータ構造。スプレッドシートやSQLテーブル、あるいはSeriesオブジェクトの辞書のように考えることができる。Pandasでは、DataFrameが良く使われる。Seriesと同様に、DataFrameはさまざまな種類の入力を受け入れる。
- 1次元のndarrays、リスト、辞書、またはシリーズのDict
- 2D numpy.ndarray
- 構造化またはレコード ndarray
- A Series
- もう一つ DataFrame
データとともに、オプションでindex(行ラベル)および columns(列ラベル)引数を渡すことができる。インデックスやカラムスを渡すと、結果として得られるDataFrameのインデックスやカラムスを保証することになります。したがって、Seriesと特定のインデックスの辞書は、渡されたインデックスに一致しないすべてのデータを破棄する。
軸ラベルが渡されない場合、それらは常識的な規則に基づいて入力データから構築される。
辞書からDataFrameを作る
結果のインデックスは、さまざまなシリーズのインデックスの和集合になります。入れ子になった辞書があれば、それらは最初にSeriesに変換されます。列が渡されない場合、列は辞書キーの順序付きリストになります。
d = {'one': pd.Series([1., 2., 3.], index=['a', 'b', 'c']), 'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d) df
one | two | |
---|---|---|
a | 1.0 | 1.0 |
b | 2.0 | 2.0 |
c | 3.0 | 3.0 |
d | NaN | 4.0 |
pd.DataFrame(d, index=['d', 'b', 'a'])
one | two | |
---|---|---|
d | NaN | 4.0 |
b | 2.0 | 2.0 |
a | 1.0 | 1.0 |
pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
two | three | |
---|---|---|
d | 4.0 | NaN |
b | 2.0 | NaN |
a | 1.0 | NaN |
行ラベルと列ラベルはそれぞれindex属性とcolumns属性にアクセスすることでアクセスでき ます。
注意 特定の列のセットがデータの辞書と共に渡されると、渡された列は辞書のキーをオーバーライドします。
df.index
Index(['a', 'b', 'c', 'd'], dtype='object')
df.columns
Index(['one', 'two'], dtype='object')
ndarraysはすべて同じ長さでなければなりません。インデックスが渡される場合、インデックスも明らかに配列と同じ長さでなければなりません。インデックスが渡されなかった場合、結果は次のようrange(n)になります。ここnで、は配列の長さです。
d = {'one': [1., 2., 3., 4.], 'two': [4., 3., 2., 1.]}
pd.DataFrame(d)
one | two | |
---|---|---|
0 | 1.0 | 4.0 |
1 | 2.0 | 3.0 |
2 | 3.0 | 2.0 |
3 | 4.0 | 1.0 |
pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
one | two | |
---|---|---|
a | 1.0 | 4.0 |
b | 2.0 | 3.0 |
c | 3.0 | 2.0 |
d | 4.0 | 1.0 |
構造化配列またはレコード配列から
この場合は、一連の配列と同じように処理されます。
data = np.zeros((2, ), dtype=[('A', 'i4'), ('B', 'f4'), ('C', 'a10')])
data[:] = [(1, 2., 'Hello'), (2, 3., "World")]
data
array([(1, 2., b'Hello'), (2, 3., b'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
df = pd.DataFrame(data) df
A | B | C | |
---|---|---|---|
0 | 1 | 2.0 | b'Hello' |
1 | 2 | 3.0 | b'World' |
df.index
RangeIndex(start=0, stop=2, step=1)
df.index.dtype
dtype('int64')
df.columns
Index(['A', 'B', 'C'], dtype='object')
df.columns.dtype
dtype('O')
pd.DataFrame(data, columns=['C', 'A', 'B'])
C | A | B | |
---|---|---|---|
0 | b'Hello' | 1 | 2.0 |
1 | b'World' | 2 | 3.0 |
pd.DataFrame(data, index=['first', 'second'])
A | B | C | |
---|---|---|---|
first | 1 | 2.0 | b'Hello' |
second | 2 | 3.0 | b'World' |
注意 DataFrameは、2次元のNumPy ndarrayのように機能することを目的としていません。 DataFrameは、純粋なNumPy ndarrayをラベル付きで扱うのではなく、各カラムにいろいろなSeriesが入っていると考えたほうが良い
辞書のリストから
data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}] pd.DataFrame(data2)
a | b | c | |
---|---|---|---|
0 | 1 | 2 | NaN |
1 | 5 | 10 | 20.0 |
pd.DataFrame(data2, index=['first', 'second'])
a | b | c | |
---|---|---|---|
first | 1 | 2 | NaN |
second | 5 | 10 | 20.0 |
pd.DataFrame(data2, columns=['a', 'b'])
a | b | |
---|---|---|
0 | 1 | 2 |
1 | 5 | 10 |
タプルの辞書から作る
タプル辞書を渡すことで、MultiIndexedフレームを自動的に作成できます。
tuple_dict = {('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2}, ('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4}, ('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6}, ('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8}, ('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}} tuple_dict
{('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}}
tuple_dict[('a', 'b')]
{('A', 'B'): 1, ('A', 'C'): 2}
pd.DataFrame(tuple_dict)
a | b | |||||
---|---|---|---|---|---|---|
b | a | c | a | b | ||
A | B | 1.0 | 4.0 | 5.0 | 8.0 | 10.0 |
C | 2.0 | 3.0 | 6.0 | 7.0 | NaN | |
D | NaN | NaN | NaN | NaN | 9.0 |
なんだこれは?
tuple_dict2={} shinyoko={} shinyoko[('(着)東京','運賃')]=500 shinyoko[('(着)東京','指定席')]=2460 shinyoko[('(着)東京','自由席')]=860 shinyoko[('(着)品川','運賃')]=170 shinyoko[('(着)品川','指定席')]=2460 shinyoko[('(着)品川','自由席')]=860 shinyoko2={} shinyoko2[('(着)東京','運賃')]=500*2 shinyoko2[('(着)東京','指定席')]=2460*2 shinyoko2[('(着)東京','自由席')]=860*2 shinyoko2[('(着)品川','運賃')]=170*2 shinyoko2[('(着)品川','指定席')]=2460*2 shinyoko2[('(着)品川','自由席')]=860*2 shinagawa={} shinagawa[('(着)東京','運賃')]=170 shinagawa[('(着)東京','指定席')]=2460 shinagawa[('(着)東京','自由席')]=860 shinagawa[('(着)新横浜','運賃')]= 410 shinagawa[('(着)新横浜','指定席')]=2460 shinagawa[('(着)新横浜','自由席')]=860 shinagawa2={} shinagawa2[('(着)東京','運賃')]=170*2 shinagawa2[('(着)東京','指定席')]=2460*2 shinagawa2[('(着)東京','自由席')]=860*2 shinagawa2[('(着)新横浜','運賃')]= 410*2 shinagawa2[('(着)新横浜','指定席')]=2460*2 shinagawa2[('(着)新横浜','自由席')]=860*2 tokyo={} tokyo[('(着)品川','運賃')]= 170 tokyo[('(着)品川','指定席')]= 2460 tokyo[('(着)品川','自由席')]= 860 tokyo[('(着)新横浜','運賃')]= 500 tokyo[('(着)新横浜','指定席')]= 2460 tokyo[('(着)新横浜','自由席')]= 860 tokyo2={} tokyo2[('(着)品川','運賃')]= 170*2 tokyo2[('(着)品川','指定席')]= 2460*2 tokyo2[('(着)品川','自由席')]= 860*2 tokyo2[('(着)新横浜','運賃')]= 500*2 tokyo2[('(着)新横浜','指定席')]= 2460*2 tokyo2[('(着)新横浜','自由席')]= 860*2 tuple_dict2[('(発)東京','片道')] = tokyo tuple_dict2[('(発)東京','往復')] = tokyo2 tuple_dict2[('(発)品川','片道')] = shinagawa tuple_dict2[('(発)品川','往復')] = shinagawa2 tuple_dict2[('(発)新横浜','片道')] = shinyoko tuple_dict2[('(発)新横浜','往復')] = shinyoko2 pd.DataFrame(tuple_dict2)
(発)東京 | (発)品川 | (発)新横浜 | |||||
---|---|---|---|---|---|---|---|
片道 | 往復 | 片道 | 往復 | 片道 | 往復 | ||
(着)品川 | 指定席 | 2460.0 | 4920.0 | NaN | NaN | 2460.0 | 4920.0 |
自由席 | 860.0 | 1720.0 | NaN | NaN | 860.0 | 1720.0 | |
運賃 | 170.0 | 340.0 | NaN | NaN | 170.0 | 340.0 | |
(着)新横浜 | 指定席 | 2460.0 | 4920.0 | 2460.0 | 4920.0 | NaN | NaN |
自由席 | 860.0 | 1720.0 | 860.0 | 1720.0 | NaN | NaN | |
運賃 | 500.0 | 1000.0 | 410.0 | 820.0 | NaN | NaN | |
(着)東京 | 指定席 | NaN | NaN | 2460.0 | 4920.0 | 2460.0 | 4920.0 |
自由席 | NaN | NaN | 860.0 | 1720.0 | 860.0 | 1720.0 | |
運賃 | NaN | NaN | 170.0 | 340.0 | 500.0 | 1000.0 |
こういう、ことか?MultiIndexedとは、indexやcolumnsをグループ化した状態みたいな
Seriesから作る
結果は、入力Seriesと同じインデックスを持ち、その名前がSeriesの元の名前である1つの列を持つDataFrameになります(他の列名が指定されていない場合のみ)。 欠損データを含むDataFrameを構築するには、欠損値を表すためにnp.nanを使用します。 あるいは、numpy.MaskedArrayをDataFrameコンストラクターへのデータ引数として渡すと、マスクされたエントリは存在しないと見なされます。
s = pd.Series([0,3,6,9,12,15],index=['A','B','C','D','E','F'],name ='example')
s['D']=np.nan
s
A 0.0
B 3.0
C 6.0
D NaN
E 12.0
F 15.0
Name: example, dtype: float64
pd.DataFrame(s)
example | |
---|---|
A | 0.0 |
B | 3.0 |
C | 6.0 |
D | NaN |
E | 12.0 |
F | 15.0 |
s = pd.Series([0,3,6,9,12,15],index=['A','B','C','D','E','F'],name ='example') mask_s =[False] * len(s) mask_s[3] = True mask_s
[False, False, False, True, False, False]
masked_s=np.ma.masked_array(s , mask=mask_s) masked_s
masked_array(data=[0, 3, 6, --, 12, 15],
mask=[False, False, False, True, False, False],
fill_value=999999,
dtype=int64)
type(masked_s)
numpy.ma.core.MaskedArray
pd.DataFrame(masked_s)
0 | |
---|---|
0 | 0.0 |
1 | 3.0 |
2 | 6.0 |
3 | NaN |
4 | 12.0 |
5 | 15.0 |
代替コンストラクタで作る
DataFrame.from_dict 辞書の辞書または配列のような文字列の辞書を取り、DataFrameを返します。これは、デフォルトではあるがdictキーを行ラベルとして使用するために設定できるパラメータをDataFrame除いて、コンストラクタのように動作します。 オプションorientについて'columns''index'
dic = dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]) dic
{'A': [1, 2, 3], 'B': [4, 5, 6]}
pd.DataFrame.from_dict(dic)
A | B | |
---|---|---|
0 | 1 | 4 |
1 | 2 | 5 |
2 | 3 | 6 |
pd.DataFrame.from_dict(dic,orient='columns')
A | B | |
---|---|---|
0 | 1 | 4 |
1 | 2 | 5 |
2 | 3 | 6 |
pd.DataFrame.from_dict(dic,orient='index')
0 | 1 | 2 | |
---|---|---|---|
A | 1 | 2 | 3 |
B | 4 | 5 | 6 |
pd.DataFrame.from_dict(dic,orient='index',columns=['one', 'two', 'three'])
one | two | three | |
---|---|---|---|
A | 1 | 2 | 3 |
B | 4 | 5 | 6 |
辞書をもとにしたときに、keyとvalueがindex,column列になるがorientを使って逆にすることも可能
DataFrame.from_records
DataFrame.from_recordsタプルのリストまたは構造化dtypeを持つndarrayを取ります。DataFrame結果のDataFrameインデックスが構造化dtypeの特定のフィールドになる場合があることを除けば、通常のコンストラクタと同様に機能します。
data = np.zeros((2, ), dtype=[('A', 'i4'), ('B', 'f4'), ('C', 'U10')]) data[:] = [(1, 2., 'Hello'), (2, 3., "World")] data
array([(1, 2., 'Hello'), (2, 3., 'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', '<U10')])
df = pd.DataFrame.from_records(data, index='C')
df
A | B | |
---|---|---|
C | ||
Hello | 1 | 2.0 |
World | 2 | 3.0 |
df.index
Index(['Hello', 'World'], dtype='object', name='C')
df.index.dtype
dtype('O')
列選択、追加、削除
DataFrameを意味的に同じインデックスのSeriesオブジェクトの辞書のように扱うことができます。列の取得、設定、削除は、類似の辞書操作と同じ構文で動作します。
df = pd.DataFrame([[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12]], columns=['A', 'B', 'C'],index=['one','two','three','four']) df
A | B | C | |
---|---|---|---|
one | 1 | 2 | 3 |
two | 4 | 5 | 6 |
three | 7 | 8 | 9 |
four | 10 | 11 | 12 |
df['A']
one 1
two 4
three 7
four 10
Name: A, dtype: int64
df['C'] = df['A'] * df['B'] df
A | B | C | |
---|---|---|---|
one | 1 | 2 | 2 |
two | 4 | 5 | 20 |
three | 7 | 8 | 56 |
four | 10 | 11 | 110 |
df['flag'] = df['A'] > 2 df
A | B | C | flag | |
---|---|---|---|---|
one | 1 | 2 | 2 | False |
two | 4 | 5 | 20 | True |
three | 7 | 8 | 56 | True |
four | 10 | 11 | 110 | True |
列は辞書を使って削除またはポップすることができます。
del df['B'] c = df.pop('C') c
one 2
two 20
three 56
four 110
Name: C, dtype: int64
type(c)
pandas.core.series.Series
df
A | flag | |
---|---|---|
one | 1 | False |
two | 4 | True |
three | 7 | True |
four | 10 | True |
スカラー値を挿入すると、当然、列を埋めるために伝搬されます。
df['foo'] = 'bar' df
A | flag | foo | |
---|---|---|---|
one | 1 | False | bar |
two | 4 | True | bar |
three | 7 | True | bar |
four | 10 | True | bar |
DataFrameと同じインデックスを持たないSeriesを挿入すると、DataFrameのインデックスに準拠します。
df['A_trunc'] = df['A'][:2] df
A | flag | foo | A_trunc | |
---|---|---|---|---|
one | 1 | False | bar | 1.0 |
two | 4 | True | bar | 4.0 |
three | 7 | True | bar | NaN |
four | 10 | True | bar | NaN |
生のndarrayを挿入できますが、それらの長さはDataFrameのインデックスの長さと一致しなければなりません。 デフォルトでは、列は最後に挿入されます。このinsert関数は列の特定の位置に挿入するのに利用できます。
A列の内容をA列の後ろに'bar'列として挿入
df.insert(1, 'bar', df['A']) df
A | bar | flag | foo | A_trunc | |
---|---|---|---|---|---|
one | 1 | 1 | False | bar | 1.0 |
two | 4 | 4 | True | bar | 4.0 |
three | 7 | 7 | True | bar | NaN |
four | 10 | 10 | True | bar | NaN |
del df['bar'] df
A | flag | foo | A_trunc | |
---|---|---|---|---|
one | 1 | False | bar | 1.0 |
two | 4 | True | bar | 4.0 |
three | 7 | True | bar | NaN |
four | 10 | True | bar | NaN |
df.columns
Index(['A', 'flag', 'foo', 'A_trunc'], dtype='object')
list(df.columns).index('A')
0
list(df.columns).index('flag')
1
list(df.columns).index('foo')
2
df.insert(list(df.columns).index('foo'), '前','左') df.insert(list(df.columns).index('foo')+1, '後','右') df
A | flag | 前 | foo | 後 | A_trunc | |
---|---|---|---|---|---|---|
one | 1 | False | 左 | bar | 右 | 1.0 |
two | 4 | True | 左 | bar | 右 | 4.0 |
three | 7 | True | 左 | bar | 右 | NaN |
four | 10 | True | 左 | bar | 右 | NaN |
2019/05/16
いろんなデータ取り込みを考えてみる。
2019/05/15
pythonのbit演算について、調べてみる
Matplotlib 3Dプロット、meshgrid とは?
3Dプロット(mplot3d) 、 meshgrid とは?
始め何やっているのかわからなかったので、どうしてそうなっているのか羅列していきます
こんな感じのプロットはどのように描かれているのか?
次の式に従って描かれたのが上の図です
では、上から眺めたらどうなるでしょうか?
Z軸が少し傾いていますね
XYプロットで、描かせるとこんな感じです。どういうことでしょうか?
グラフを手で書くときには、ある点のに対して座標に沿ってポイントを打っていき、その間を補完する意味で繋げます。
ドットだけ打った図はこんな感じになります。これを上から見てY軸に沿ってドットをつないでいき。次にX軸に沿ってドットをつないでいきます。
これを横から見るとZX座標、ZY座標
こんな感じになります。式に対してYの値を振った図とXの値を振った図になります。つまり、パラメータが3つあっても、1つを固定すれば点と点をつなぐことができることを意味します。
mashgridについて
次に、実際に3Dプロットするときに何が必要になるか。すぐ、頭に浮かぶのはZの値がわかればいい。単純にそうでしょうか? 少し単純化して5x5の範囲で線を引く場合を考えていきます
Z=X+Y
の式からZの値は決まりますが、これをplot(X,Y,Z)とやってもプロットしてくれません(いまのところ)。なぜでしょうか?どういう風に点と点をつないでいくかがわからないからです。逆に言うと、線のつなぎ方はいろいろとあるのです。これから行う線の引き方は、z(x1,y1)→z(x2,y2)にせんを引く3Dプロットです。これに対して、numpyライブラリは便利な関数を持っています。meshgridです。
x = [0 1 2 3 4]
y = [0 1 2 3 4]
5x5の範囲なのでx,yを用意します
X, Y = np.meshgrid(x, y)
実行すると
このような配列を作ってくれます。はじめなんじゃこれは?と思っていたのですが、こういうことです
X,Y,Zすべての要素が決まったことになり、これに対してZXグラフとZYグラフを書く準備ができたということです。
1~5までは、Y軸を固定にしてX軸に沿って線を引いていきます。次に、6~10は、X軸を固定してY軸に沿って線を引いていきます。
この要領でプロットするのがplot_wireframeになります。
実際のソース
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import axes3d # 配列の要素数 n = 5 x = np.linspace(0, 4, n) y = np.linspace(0, 4, n) print("x =",x) print("y =",y,"\n") # 5×5の格子点を作成 X, Y = np.meshgrid(x, y) R = np.sqrt(X**2 + Y**2) #原点からの長さ Z = 4*np.pi/(np.pi+R)*np.cos(np.pi*R/2) np.set_printoptions(precision=3, suppress=True) print("Xサイズ ",X.shape) print("X =",X,"\n") print("Yサイズ ",Y.shape) print("Y =",Y,"\n") print("Zサイズ ",Y.shape) print("Z =",Z,"\n") print("X=3") print("X[:,3] =",X[:,3]) print("Y[:,3] =",Y[:,3]) print("Z[:,3] =",Z[:,3]) print("\n") print("Y=3") print("X[3,:] =",X[3,:]) print("Y[3,:] =",Y[3,:]) print("Z[3,:] =",Z[3,:])
x = [0. 1. 2. 3. 4.]
y = [0. 1. 2. 3. 4.]
Xサイズ (5, 5)
X = [[0. 1. 2. 3. 4.]
[0. 1. 2. 3. 4.]
[0. 1. 2. 3. 4.]
[0. 1. 2. 3. 4.]
[0. 1. 2. 3. 4.]]
Yサイズ (5, 5)
Y = [[0. 0. 0. 0. 0.]
[1. 1. 1. 1. 1.]
[2. 2. 2. 2. 2.]
[3. 3. 3. 3. 3.]
[4. 4. 4. 4. 4.]]
Zサイズ (5, 5)
Z = [[ 4. 0. -2.444 -0. 1.76 ]
[ 0. -1.671 -2.178 0.503 1.698]
[-2.444 -2.178 -0.56 1.516 1.217]
[-0. 0.503 1.516 1.58 0. ]
[ 1.76 1.698 1.217 0. -1.226]]
X=3
X[:,3] = [3. 3. 3. 3. 3.]
Y[:,3] = [0. 1. 2. 3. 4.]
Z[:,3] = [-0. 0.503 1.516 1.58 0. ]
Y=3
X[3,:] = [0. 1. 2. 3. 4.]
Y[3,:] = [3. 3. 3. 3. 3.]
Z[3,:] = [-0. 0.503 1.516 1.58 0. ]
各要素毎に、5x5の配列で用意できてます。またNumpyですので、Z=f(X,Y)的な演算もそのままできて便利です。X=3の時は、X,Y,Zを使ってこの順で線を引きます。Y=3の時は、X,Y,Zを使ってこの順で線を引きます。
実際のプロット
from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from matplotlib.ticker import LinearLocator, FormatStrFormatter import numpy as np # 配列の要素数 n = 5 x = np.linspace(0, 4, n) y = np.linspace(0, 4, n) print("x =",x) print("y =",y,"\n") # 5×5の格子点を作成 X, Y = np.meshgrid(x, y) R = np.sqrt(X**2 + Y**2) #原点からの長さ Z = 4*np.pi/(np.pi+R)*np.cos(np.pi*R/2) fig = plt.figure() ax = fig.gca(projection='3d') wire = ax.plot_wireframe(X, Y, Z) ax.set_xlabel("X", size = 15) ax.set_ylabel("Y", size = 15) ax.set_zlabel("Z", size = 15) ax.set_zlim(-5, 5) ax.zaxis.set_major_locator(LinearLocator(6)) plt.show()
格子が5x5なので少しガタガタしてますね。
from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from matplotlib.ticker import LinearLocator, FormatStrFormatter import numpy as np fig = plt.figure() ax = fig.gca(projection='3d') X = np.arange(-5, 5, 0.25) Y = np.arange(-5, 5, 0.25) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) #原点からの長さ Z = 4*np.pi/(np.pi+R)*np.cos(np.pi*R/2) wire = ax.plot_wireframe(X, Y, Z) ax.set_xlabel("X", size = 15) ax.set_ylabel("Y", size = 15) ax.set_zlabel("Z", size = 15) ax.set_zlim(-5, 5) ax.zaxis.set_major_locator(LinearLocator(6)) plt.show()
というわけで、3Dプロットはなにをやっているのか?mashgridは、何を作っているのか。また、その役割について話しました。