Pandas 集中講座 その6 時系列、カテゴリ、プロット、データ入出力

Pandasライブラリを覚えたい!公式「10 Minutes to pandas」をモチーフに使って実際に動作させ学習する。第6回 時系列、カテゴリ、プロット、データ入出力

Pandas 集中講座(6)

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」を実際にやってみる (6/6)

 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」を実際にやってみる

9.時系列

Pandasは、周波数変換中にリサンプリング操作を実行するための単純で強力で効率的な機能を持っています(例えば、2番目のデータを5分ごとのデータに変換する)。これは金融アプリケーションでは非常に一般的ですが、これに限定されません。時系列のセクションを参照してください。

rng = pd.date_range('2019/1/1', periods=1000, freq='s')
rng[:10]
DatetimeIndex(['2019-01-01 00:00:00', '2019-01-01 00:00:01',
               '2019-01-01 00:00:02', '2019-01-01 00:00:03',
               '2019-01-01 00:00:04', '2019-01-01 00:00:05',
               '2019-01-01 00:00:06', '2019-01-01 00:00:07',
               '2019-01-01 00:00:08', '2019-01-01 00:00:09'],
              dtype='datetime64[ns]', freq='S')

【解説】pd.date_rangeは、'2019/1/1'から 100回繰り返し, 周期は秒でということでしょうか?freq='s'は、小文字のsでもいけるみたいです。内部処理では'S'を使っております。なんでしょうね。これ、中二病でしょうか?もう、21世紀結構たつのにSI単位系での授業でやらないのでしょうか?やっぱり、(mS)と書いたほうが格好いい!という世代がいるのでしょうか。困ったものです。

ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
ts.head()
2019-01-01 00:00:00    355
2019-01-01 00:00:01    100
2019-01-01 00:00:02    103
2019-01-01 00:00:03    258
2019-01-01 00:00:04    193
Freq: S, dtype: int32
ts.tail()
2019-01-01 00:16:35    447
2019-01-01 00:16:36    410
2019-01-01 00:16:37    362
2019-01-01 00:16:38    248
2019-01-01 00:16:39    279
Freq: S, dtype: int32
ts.resample('3Min')
DatetimeIndexResampler [freq=<3 * Minutes>, axis=0, closed=left, label=left, convention=start, base=0]
ts.resample('3Min').sum()
2019-01-01 00:00:00    45914
2019-01-01 00:03:00    43718
2019-01-01 00:06:00    45345
2019-01-01 00:09:00    47489
2019-01-01 00:12:00    44633
2019-01-01 00:15:00    24273
Freq: 3T, dtype: int32

【解説】Freq: 3Tは、3分のことです。周期的に集計ができるみたいです。

タイムゾーン表示:

rng = pd.date_range('3/6/2017 00:00', periods=5, freq='D')
ts = pd.Series(np.random.randn(len(rng)), rng)
ts
2017-03-06    1.022949
2017-03-07   -0.556746
2017-03-08    1.040104
2017-03-09    0.901689
2017-03-10   -0.511703
Freq: D, dtype: float64
type(rng)
pandas.core.indexes.datetimes.DatetimeIndex
ts_utc = ts.tz_localize('UTC')
ts_utc
2017-03-06 00:00:00+00:00    1.022949
2017-03-07 00:00:00+00:00   -0.556746
2017-03-08 00:00:00+00:00    1.040104
2017-03-09 00:00:00+00:00    0.901689
2017-03-10 00:00:00+00:00   -0.511703
Freq: D, dtype: float64
ts_jst = ts.tz_localize('Asia/Tokyo')
ts_jst
2017-03-06 00:00:00+09:00    1.022949
2017-03-07 00:00:00+09:00   -0.556746
2017-03-08 00:00:00+09:00    1.040104
2017-03-09 00:00:00+09:00    0.901689
2017-03-10 00:00:00+09:00   -0.511703
Freq: D, dtype: float64

【解説】DatetimeIndexのindexを使っているので単なる文字列ではなく、時系列として内部処理されています。よって、ローカル時間に変換みたいなことも可能になります。上記の例ではcoordinated universal timeなのになぜかUTCの略字の協定世界時の事です。日本の場合ts.tz_localize('Asia/Tokyo')とかですかね。+0900にはなります。

別のタイムゾーンに変換する:

ts_utc.tz_convert('US/Eastern')
2017-03-05 19:00:00-05:00    1.022949
2017-03-06 19:00:00-05:00   -0.556746
2017-03-07 19:00:00-05:00    1.040104
2017-03-08 19:00:00-05:00    0.901689
2017-03-09 19:00:00-05:00   -0.511703
Freq: D, dtype: float64
ts_utc.tz_convert('Asia/Tokyo')
2017-03-06 09:00:00+09:00    1.022949
2017-03-07 09:00:00+09:00   -0.556746
2017-03-08 09:00:00+09:00    1.040104
2017-03-09 09:00:00+09:00    0.901689
2017-03-10 09:00:00+09:00   -0.511703
Freq: D, dtype: float64

【解説】00:00:00+00:00:00から9時間足した09:00:00にして+09:00のoffsetを付ければUTCで00:00:00+00:00時刻で日本では09:00:00+09:00と同じことになります。

タイムスパン表現間の変換:

rng = pd.date_range('1/1/2018', periods=5, freq='M')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts
2018-01-31    0.649878
2018-02-28   -0.316178
2018-03-31    0.601950
2018-04-30   -2.371119
2018-05-31    2.307606
Freq: M, dtype: float64
type(ts.index)
pandas.core.indexes.datetimes.DatetimeIndex
ps = ts.to_period()
ps
2018-01    0.649878
2018-02   -0.316178
2018-03    0.601950
2018-04   -2.371119
2018-05    2.307606
Freq: M, dtype: float64
type(ps.index)
pandas.core.indexes.period.PeriodIndex
ps.to_timestamp()
2018-01-01    0.649878
2018-02-01   -0.316178
2018-03-01    0.601950
2018-04-01   -2.371119
2018-05-01    2.307606
Freq: MS, dtype: float64
ps.to_timestamp(how='end')
2018-01-31 23:59:59.999999999    0.649878
2018-02-28 23:59:59.999999999   -0.316178
2018-03-31 23:59:59.999999999    0.601950
2018-04-30 23:59:59.999999999   -2.371119
2018-05-31 23:59:59.999999999    2.307606
Freq: M, dtype: float64

【解説】変換DatetimeIndexからPeriodIndexに変換。月末集計のデータと何月のデータという感じかな?

ピリオドとタイムスタンプを変換すると、便利な算術関数を使用できます。次の例では、11月に終了する年の四半期頻度を、四半期終了後の月末の午前9時に変換します。

prng = pd.period_range('1990Q1', '2000Q4', freq='Q-NOV')
ts = pd.Series(np.random.randn(len(prng)), prng)
ts.index = (prng.asfreq('M', 'e') + 1).asfreq('H', 's') + 9
ts.head()
1990-03-01 09:00   -0.929966
1990-06-01 09:00   -0.153660
1990-09-01 09:00    1.111212
1990-12-01 09:00   -1.835358
1991-03-01 09:00   -0.440545
Freq: H, dtype: float64

10.カテゴリ

PandasはDataFrameにカテゴリカルデータを含めることができます。完全なドキュメントについては、カテゴリ別紹介とAPIドキュメントを参照してください。

df = pd.DataFrame({"id": [1, 2, 3, 4, 5, 6],"raw_grade": ['a', 'b', 'b', 'a', 'a', 'e']})
df
id raw_grade
0 1 a
1 2 b
2 3 b
3 4 a
4 5 a
5 6 e

生の成績をカテゴリカルデータ型に変換します。

df["grade"] = df["raw_grade"].astype("category")
df["grade"]
0    a
1    b
2    b
3    a
4    a
5    e
Name: grade, dtype: category
Categories (3, object): [a, b, e]

【解説】dtypeをカテゴリに変更

カテゴリの名前をより意味のある名前に変更します(に代入するの Series.cat.categoriesは適切です)。

df["grade"].cat.categories = ["very good", "good", "very bad"]
df
id raw_grade grade
0 1 a very good
1 2 b good
2 3 b good
3 4 a very good
4 5 a very good
5 6 e very bad

カテゴリを並べ替えると同時に不足しているカテゴリを追加します(メソッドはデフォルトで新しいを返します)。Series .catSeries

df["grade"] = df["grade"].cat.set_categories(["very bad",
        "bad", "medium", "good", "very good"])
df["grade"]
0    very good
1         good
2         good
3    very good
4    very good
5     very bad
Name: grade, dtype: category
Categories (5, object): [very bad, bad, medium, good, very good]

ソートは字句順ではなく、カテゴリ内の順番ごとに行われます。

df.sort_values(by="grade")
id raw_grade grade
5 6 e very bad
1 2 b good
2 3 b good
0 1 a very good
3 4 a very good
4 5 a very good

カテゴリ別の列でグループ化すると、空のカテゴリも表示されます。

df.groupby("grade").size()
grade
very bad     1
bad          0
medium       0
good         2
very good    3
dtype: int64

11.プロット

プロットドキュメントを参照してください。

ts = pd.Series(np.random.randn(1000),
index=pd.date_range('1/1/2000', periods=1000))
ts = ts.cumsum()
ts.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x18bbf22e780>

f:id:chiyoh:20190609100940p:plain

df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index,columns=['A', 'B', 'C', 'D'])
df = df.cumsum()

DataFrameでは、このplot()メソッドはすべての列をラベル付きでプロットするのに便利です。

df.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x18bbf4ae630>

f:id:chiyoh:20190609100911p:plain

12.データを入出力する

CSV

csvファイルへの書き込み

df.to_csv('foo.csv')

csvファイルからの読み取り

pd.read_csv('foo.csv')[:10]
Unnamed: 0 A B C D
0 2000-01-01 0.277849 -0.854028 0.997447 0.683385
1 2000-01-02 2.380521 -1.792953 0.961907 -0.503520
2 2000-01-03 2.159351 -1.587149 0.124225 -0.216280
3 2000-01-04 1.919500 0.916933 0.943439 1.193825
4 2000-01-05 2.702664 0.660521 0.066178 2.300615
5 2000-01-06 3.730054 2.200855 -0.658531 3.261455
6 2000-01-07 4.698684 2.556608 -0.882894 4.743601
7 2000-01-08 4.371757 1.637595 0.574554 4.217433
8 2000-01-09 4.557831 0.725914 0.493527 4.998890
9 2000-01-10 5.512622 0.575462 0.350790 4.389341

HDF5

HDFStoreへの読み書き。

HDF5ストアへの書き込み

df.to_hdf('foo.h5', 'df')

HDF5ストアからの読み取り

pd.read_hdf('foo.h5', 'df')[:10]
A B C D
2000-01-01 0.277849 -0.854028 0.997447 0.683385
2000-01-02 2.380521 -1.792953 0.961907 -0.503520
2000-01-03 2.159351 -1.587149 0.124225 -0.216280
2000-01-04 1.919500 0.916933 0.943439 1.193825
2000-01-05 2.702664 0.660521 0.066178 2.300615
2000-01-06 3.730054 2.200855 -0.658531 3.261455
2000-01-07 4.698684 2.556608 -0.882894 4.743601
2000-01-08 4.371757 1.637595 0.574554 4.217433
2000-01-09 4.557831 0.725914 0.493527 4.998890
2000-01-10 5.512622 0.575462 0.350790 4.389341

エクセル

読み取りと書き込みにMSエクセル。

Excelファイルへの書き込み

df.to_excel('foo.xlsx', sheet_name='Sheet1')

Excelファイルからの読み取り

pd.read_excel('foo.xlsx', 'Sheet1', index_col=None, na_values=['NA'])[:10]
Unnamed: 0 A B C D
0 2000-01-01 0.277849 -0.854028 0.997447 0.683385
1 2000-01-02 2.380521 -1.792953 0.961907 -0.503520
2 2000-01-03 2.159351 -1.587149 0.124225 -0.216280
3 2000-01-04 1.919500 0.916933 0.943439 1.193825
4 2000-01-05 2.702664 0.660521 0.066178 2.300615
5 2000-01-06 3.730054 2.200855 -0.658531 3.261455
6 2000-01-07 4.698684 2.556608 -0.882894 4.743601
7 2000-01-08 4.371757 1.637595 0.574554 4.217433
8 2000-01-09 4.557831 0.725914 0.493527 4.998890
9 2000-01-10 5.512622 0.575462 0.350790 4.389341

終わった!

って全然終わってねー。毎日コツコツやっても全然終わらない!ほんとに10分の内容なのか?10日じゃないのか?

chiyoh.hatenablog.com
chiyoh.hatenablog.com
chiyoh.hatenablog.com
chiyoh.hatenablog.com
chiyoh.hatenablog.com
chiyoh.hatenablog.com