matplotlib の hist
データをプロットして、ヒストを作って、確認する
いつものように、乱数でデータを作って、プロットとヒストとその結果を表示してみる
import matplotlib.pyplot as plt import numpy as np def print_bins(n,bins): tmp = 'hist data:\n' if len(n) != len(bins)-1: return "Error not equal n,(bins-1).\n" elif len(bins) < 1: return "Error bins\n" elif len(bins) == 2: return tmp + '[{},{}] {}\n'.format(bins[0],bins[1],n[0]) for st,ed,ct in zip(bins[0:-2],bins[1:],n): tmp += '[{:>8.3f},{:>8.3f}) {:>6.0f}\n'.format(st,ed,ct) tmp += '[{:>8.3f},{:>8.3f}] {:>6.0f}\n'.format(bins[-2],bins[-1],n[-1]) return tmp def print_info(dat): tmp = 'info:\n' tmp += 'count = {}\n'.format(len(dat)) tmp += 'min = {}\n'.format(np.amin(dat)) tmp += 'max = {}\n'.format(np.amax(dat)) tmp += 'mean = {}\n'.format(np.mean(dat)) tmp += 'std = {}\n'.format(np.std(dat)) tmp += 'var = {}\n'.format(np.var(dat)) return tmp
今回は、事前にやりたいことを確認していたので
cnt = 100 dat = np.random.normal(1, 2, cnt) fig = plt.figure() fig.dpi = 200 # 200dpi #fig, ax_lst = plt.subplots(2, 2) ax1=fig.add_subplot(2,2,1) # row=1,col=2,index=1 ax2=fig.add_subplot(2,2,2) # row=1,col=2,index=2 ax3=fig.add_subplot(2,2,3) # row=1,col=2,index=3 ax4=fig.add_subplot(2,2,4) # row=1,col=2,index=4 x = np.arange(0, cnt) ax1.set_title("randn({})".format(cnt)) ax1.plot(x,dat) ax2.set_title("hist") pHist = ax2.hist(dat,bins=10,orientation='horizontal') dat_info = print_info(dat) ax3.tick_params(labelbottom=False,labeltop=False,labelleft=False,labelright=False, bottom=False, top=False, left=False, right=False) ax3.text(0.1,1-0.1,dat_info,size=6,family='monospace',horizontalalignment='left', verticalalignment='top',transform=ax3.transAxes) hist_text = print_bins(pHist[0],pHist[1]) ax4.tick_params(labelbottom=False,labeltop=False,labelleft=False,labelright=False, bottom=False, top=False, left=False, right=False) ax4.text(0.1,1-0.1,hist_text,size=6,family='monospace',horizontalalignment='left', verticalalignment='top',transform=ax4.transAxes) plt.show()
【解説】左上データをプロット、右上データをヒストプロット、右下ヒストのデータ、左下データの平均値とかいろいろ
xlsxwriter.utility 詳細
xlsxwriter.utility
Python ライブラリ xlsxwriter の xlsxwriter.utility は、xlsxwriter内部で使われるヘルパーメソッドですが便利なのでユーザーにも開放されています。
- xlsxwriter.utility
- 前書き
- 準備(xlswritewを使ってみる)
- xlsxwriter.utility
- xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False)
- xl_rowcol_to_cell_fast(row, col)
- xl_col_to_name(col, col_abs=False)
- xl_cell_to_rowcol(cell_str)
- xl_cell_to_rowcol_abs(cell_str)
- xl_range(first_row, first_col, last_row, last_col)
- xl_range_abs(first_row, first_col, last_row, last_col)
- xl_range_formula(sheetname, first_row, first_col, last_row, last_col)
- quote_sheetname(sheetname)
- xl_color(color)
- get_rgb_color(color)
- get_sparkline_style(style_id)
- supported_datetime(dt)
- remove_datetime_timezone(dt_obj, remove_timezone)
- datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone)
前書き
いつものように、調べた結果を羅列していきます。
準備(xlswritewを使ってみる)
準備としてxlsxwriterでインスタンスを作ります。
import xlsxwriter import xlsxwriter.utility
# VBAみたいですね。はじめにWorkbookのインスタンスを作ります workbook = xlsxwriter.Workbook('example01.xlsx') # workbookにworksheetを追加します worksheet = workbook.add_worksheet()
# セルデータを配列とtupleで作ります data = ( ['経路', '運賃','特急料金'], ['大阪', 8960, 0 ], ['新大阪', 0, 5700], ['東京', 0,0], ['舞浜/リゾートゲートウェイ', 260,0], ['東京ディズニーシー', 0,0], ['小計', '=SUM(B2:B6)' , '=SUM(C2:C6)'], ['合計', '' ,'=B7+C7'], )
# 配列を使うのでindexはゼロから始めます row = 0 col = 0
# ワークシートにwriteメソッドでrow,colを指定して書き込んでいるだけです。 for item, cost1,cost2 in (data): worksheet.write(row, col, item) worksheet.write(row, col + 1, cost1) worksheet.write(row, col + 2, cost2) row += 1
# 作ったワークブックを閉じると保存されるみたいです。
workbook.close()
うーん。簡単でしょ?!
xlsxwriter.utility
xlsxwriter.utilityで定義されているメソッド一覧です
xl_rowcol_to_cell(row, col, row_abs=False, col_abs=False)
row(行)とcolumn(列)のセル参照をA1表示方式の文字列に変換します。rowとcolは0スタートの整数。row_absとcol_abs は絶対値表記で'$'が付きます。
引数
- row: int セル行(first rowは、0)
- col: int セル列(first columnは、0)
- row_abs: boolean セル行を絶対値にする。ディフォルトはFalse
- True セル行を絶対値にする
- False セル行を相対値にする
- col_abs: boolean セル列を絶対値にする。ディフォルトはFalse
- True セル列を絶対値にする
- False セル列を相対値にする
戻り値
- str A1表示方式の文字列
xlsxwriter.utility.xl_rowcol_to_cell(0,0)
'A1'
xlsxwriter.utility.xl_rowcol_to_cell(1,20,0,1)
'$U2'
【解説】A1表示方式は、表を見ながら操作するのには便利であるがプログラムする側からするとカラムを5つ奥に移動するとcolumn名は何になる?forでループはできない?などと不便である。VBAではoffset等を使ってしのいでいるが。。。なのでセルに数式で埋め込むとき等に便利そうなメソッドである。
xl_rowcol_to_cell_fast(row, col)
xl_rowcol_to_cell関数の最適化バージョン 内部でのみ使用されています。
引数
- row: int セル行(first rowは、0)
- col: int セル列(first colは、0)
戻り値
- str A1表示方式の文字列
【解説】絶対値表記等は、EXCEL上の操作の際に意味があることでバックグランドのプログラムではrow,col共に配列番号なので必要ないので余計なところを端折って高速化したメソッドである。
xl_col_to_name(col, col_abs=False)
列番号を列セル参照を文字列に変換します。col_abs は絶対値表記で'$'が付きます。
引数
- col: int セル列(first columnは、0)
- col_abs: boolean セル列を絶対値にする。ディフォルトはFalse
- True セル列を絶対値にする
- False セル列を相対値にする
戻り値
- str A1表記方式の列文字列
xlsxwriter.utility.xl_col_to_name(0,1)
'$A'
xlsxwriter.utility.xl_col_to_name(30)
'AE'
【解説】早い話列番号を列アルファベット文字列に変換。行に関してはゼロスタートなのでA1表示方式にするには+1すれば終わる。
xl_cell_to_rowcol(cell_str)
A1表記方式文字列を、row,col数値行列に変換する。
引数
- cell_str str A1表記方式文字列(
$A$1
等の絶対値表記も可)
戻り値
- row: int セル行(first rowは、0)
- col: int セル列(first columnは、0)
xlsxwriter.utility.xl_cell_to_rowcol('ABC$1000')
(999, 730)
【解説】xl_rowcol_to_cell_fastの逆変換
xl_cell_to_rowcol_abs(cell_str)
A1表記方式文字列を、row,col数値行列に変換する。絶対値表記も処理する
引数
- cell_str str A1表記方式文字列(
$A$1
等の絶対値表記も可)
戻り値
- row: int セル行(first rowは、0)
- col: int セル列(first columnは、0)
- row_abs: boolean セル行を絶対値にする。ディフォルトはFalse
- True セル行を絶対値にする
- False セル行を相対値にする
- col_abs: boolean セル列を絶対値にする。ディフォルトはFalse
- True セル列を絶対値にする
- False セル列を相対値にする
xlsxwriter.utility.xl_cell_to_rowcol_abs('ABC$1000')
(999, 730, True, False)
【解説】xl_rowcol_to_cellの逆変換
xl_range(first_row, first_col, last_row, last_col)
行と列のセル参照をA1:B2形式範囲文字列に変換します。
引数
- first_row: int 最初のセル行(first rowは、0)
- first_col: int 最初のセル列(first columnは、0)
- last_row: int 最後のセル行(first rowは、0)
- last_col: int 最後のセル列(first rowは、0)
戻り値
- str A1:B2形式範囲文字列
xlsxwriter.utility.xl_range(10,20,30,40)
'U11:AO31'
【解説】:
でxl_rowcol_to_cell_fastを使って挟んだ感じ
xl_range_abs(first_row, first_col, last_row, last_col)
行と列のセル参照を$A$1:$B$2
形式絶対範囲文字列に変換します。
引数
- first_row: int 最初のセル行(first rowは、0)
- first_col: int 最初のセル列(first rowは、0)
- last_row: int 最後のセル行(first rowは、0)
- last_col: int 最後のセル列(first rowは、0)
戻り値
- str $A$1:$B$2形式範囲文字列
xlsxwriter.utility.xl_range_abs(40,30,20,10)
'$AE$41:$K$21'
【解説】xl_rangeの絶対値付きの範囲文字列変換であるが、相対値と絶対値の混合ができない
xl_range_formula(sheetname, first_row, first_col, last_row, last_col)
ワークシート名と行列番号をSheet1!A1:B2
範囲表示方式文字列に変換
引数
- sheetname: str ワークシート名
- first_row: int 最初のセル行(first rowは、0)
- first_col: int 最初のセル列(first columnは、0)
- last_row: int 最後のセル行(first rowは、0)
- last_col: int 最後のセル列(first columnは、0)
戻り値
- str
Sheet1!A1:B2
範囲表示方式文字列に変換
xlsxwriter.utility.xl_range_formula('sheet1',4,3,7,101)
'sheet1!$D$5:$CX$8'
【解説】quote_sheetnameとxl_range_absを足した感じ
quote_sheetname(sheetname)
ワークシート名にスペースまたは特殊文字が含まれている場合は、 その名前を引用符付きの名前に変換します。
引数
- sheetname: str ワークシートの名前
戻り値
- str 引用符付きのワークシート
【解説】引数が数値文字列以外は、文字列を引用符付きにする。例外処理もやる
# シート名は引用符を付ける st_name = xlsxwriter.utility.quote_sheetname('結果') st_name
'結果'
# シート名にスペースが入っていようとなかろうと関係ない print(xlsxwriter.utility.quote_sheetname('結果 1'))
'結果 1'
# 2重にかけても無視される print(xlsxwriter.utility.quote_sheetname(st_name))
結果
# VBAとかでもシート名を数値(バーバリアン型)で表すので数値で戻す print(xlsxwriter.utility.quote_sheetname("5"))
5
xl_color(color)
XlsxWriterのcolorメソッドで一緒に使用して変換してます。RGB形式の文字列に変換します。これらの色は以前のバージョンのExcelとの下位互換性。
引数
- color: str 色名 または、 RGB形式文字列
戻り値
- str ARGB形式
注意
- 'AARRGGBB'フォーマットで返される
- 対応色名
- 'black','blue','brown','cyan','gray','green','lime','magenta',
- 'navy','orange','pink','purple','red','silver','white','yellow'
#色名 xlsxwriter.utility.xl_color('silver')
'FFC0C0C0'
#色コード xlsxwriter.utility.xl_color('#00FF00')
'FF00FF00'
#アルファ付き色コード xlsxwriter.utility.xl_color('#80FFFF00')
'FF80FFFF00'
get_rgb_color(color)
色名をRGB形式の文字列に変換します。
引数
- color: str 色名
戻り値
- str RGB形式
注意
- 'RRGGBB'フォーマットで返される
#色名 xlsxwriter.utility.get_rgb_color('silver')
'C0C0C0'
【解説】xl_color(color)のアルファ無版
get_sparkline_style(style_id)
スパークラインのスタイルを収得します(add_sparklineで使用している)
引数
- style_id: int スタイル番号
戻り値
- dict スタイル情報
注意
- サポートされているのは0~36
xlsxwriter.utility.get_sparkline_style(0)
{'series': {'theme': '4', 'tint': '-0.499984740745262'},
'negative': {'theme': '5'},
'markers': {'theme': '4', 'tint': '-0.499984740745262'},
'first': {'theme': '4', 'tint': '0.39997558519241921'},
'last': {'theme': '4', 'tint': '0.39997558519241921'},
'high': {'theme': '4'},
'low': {'theme': '4'}}
xlsxwriter.utility.get_sparkline_style(36)
{'series': {'theme': '1'},
'negative': {'theme': '9'},
'markers': {'theme': '8'},
'first': {'theme': '4'},
'last': {'theme': '5'},
'high': {'theme': '6'},
'low': {'theme': '7'}}
supported_datetime(dt)
xlsxwrite(excel)のChartやWorksheetでサポートされている日時オブジェクトかどうかを判別します。
引数
- dt: 検査するオブジェクト
戻り値
- boolean
- True サポートする
- False サポートしない
注意
- サポートされているのはdatetimeの
datetime
,date,time
,timedelta
です
from datetime import datetime xlsxwriter.utility.supported_datetime(datetime.today())
True
print(datetime.today())
2019-06-15 21:18:11.820430
xlsxwriter.utility.supported_datetime("2019-06-15 10:16:46.687568")
False
d = datetime(2019, 2, 3) xlsxwriter.utility.supported_datetime(d)
True
remove_datetime_timezone(dt_obj, remove_timezone)
Excelでは日時のタイムゾーンはサポートされていないため削除する関数。引数で指定した日時オブジェクトからtzinfoを削除します。
引数
- dt_obj: datetimeクラスのオブジェクト
- remove_timezone: 削除するタイムゾーン
戻り値
- datetimeクラスのオブジェクト
from datetime import datetime, timedelta, timezone JST = timezone(timedelta(hours=+9), 'JST')
jtime = datetime.now(JST) jtime
datetime.datetime(2019, 6, 15, 21, 18, 12, 452426, tzinfo=datetime.timezone(datetime.timedelta(seconds=32400), 'JST'))
xlsxwriter.utility.supported_datetime(jtime)
True
t = xlsxwriter.utility.remove_datetime_timezone(jtime,'JST')
t
datetime.datetime(2019, 6, 15, 21, 18, 12, 452426)
xlsxwriter.utility.supported_datetime(t)
True
datetime_to_excel_datetime(dt_obj, date_1904, remove_timezone)
日時オブジェクトをExcelのシリアルの日時に変換します。 数値の整数部にはエポック以降の日数が格納され、小数部にはその日の割合が格納されます。
引数
- dt_obj: datetimeクラスのオブジェクト
- date_1904: boolean エポックを'1904-01-01'にするか'1899-12-31'にするか選択。
- True '1904-01-01'
- False '1899-12-31'
- remove_timezone: boolean remove_datetime_timezoneを使ってタイムゾーンを削除するかどうか
- True 削除する
- False 削除しない
戻り値
xlsxwriter.utility.datetime_to_excel_datetime(jtime,True,True)
42169.8876441253
xlsxwriter.utility.datetime_to_excel_datetime(jtime,False,True)
43631.8876441253
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形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。
【解説】公式「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>
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>
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
Pandas 集中講座 その5 グループ、再構成
Pandasライブラリを覚えたい!公式「10 Minutes to pandas」をモチーフに使って実際に動作させ学習する。第5回 グループ、再構成
Pandas 集中講座(5)
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」を実際にやってみる (5/6)
Pandasを覚える!さて、グーグル先生に教えてもらえば大抵のことは分かるのだが、それが今の最適解なのか?と考えるとそうではない。グーグル先生のサーチ機能は、カンニングペーパーであり、単語帳である。欲しい答えを最小ステップでたどり着くための方法。例えるなら、望遠鏡、又は顕微鏡で拡大されたエリアで見つけた希望(答え)。なぜそうなるのか?なぜそう書けば動くのか?など、答えに対する成り立ちがすっぽり抜けてしまうのである。最近のPython環境は、Jupyter Notebookで敷地も下がって来ている。入門書や学習本を眺めて図を見てなんとなくへーとか言って分かった気になって何も残らないパターンになりかねない。今回選んだ方法は、グーグル先生の支援のもと公式のチュートリアル以前のPandas概要説明『10 Minutes to pandas』を上から順にJupyter Notebookを使い実行していってみようと思う。これにより、学習講座のWorkshop形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。
【解説】公式「10 Minutes to pandas」をJupyter Notebookを使って動作させながら確認し、突っ込みを入れる!(この文章を見る価値としては、原書が何をやっているのかが分かる、、、分かるはずである)
公式「10 Minutes to pandas」を実際にやってみる
7.グループ化
「グループ化」とは、以下のステップのうちの1つ以上を含むプロセスを指します。
- いくつかの基準に基づいてデータをグループに分割する
- 各グループに独立して機能を適用する
- 組み合わせデータ構造に結果を
グループ化の節を参照してください。
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar','foo', 'bar', 'foo', 'foo'], 'B': ['one', 'one', 'two', 'three','two', 'two', 'one', 'three'], 'C': np.random.randn(8), 'D': np.random.randn(8)}) df
A | B | C | D | |
---|---|---|---|---|
0 | foo | one | 0.447141 | 0.013312 |
1 | bar | one | 0.546690 | 0.039544 |
2 | foo | two | -0.520826 | 1.888179 |
3 | bar | three | 0.788315 | -0.085696 |
4 | foo | two | 0.542692 | -1.728545 |
5 | bar | two | -1.449532 | -0.242919 |
6 | foo | one | 1.238537 | -0.221444 |
7 | foo | three | 0.633757 | 0.419697 |
グループ化してsum()から、結果のグループに関数を適用します。
df.groupby('A').sum()
C | D | |
---|---|---|
A | ||
bar | -0.114527 | -0.289071 |
foo | 2.341301 | 0.371199 |
【コメント】B列が消えてしまうんですね。
複数の列でグループ化することで階層的なインデックスが形成され、やはりsum関数を適用できます。
df.groupby(['A', 'B']).sum()
C | D | ||
---|---|---|---|
A | B | ||
bar | one | 0.546690 | 0.039544 |
three | 0.788315 | -0.085696 | |
two | -1.449532 | -0.242919 | |
foo | one | 1.685677 | -0.208132 |
three | 0.633757 | 0.419697 | |
two | 0.021867 | 0.159634 |
8.再整形
階層化された索引付けと 再整形に関するセクションを見てください。
スタック
tuples = list(zip(*[['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']])) tuples
[('bar', 'one'),
('bar', 'two'),
('baz', 'one'),
('baz', 'two'),
('foo', 'one'),
('foo', 'two'),
('qux', 'one'),
('qux', 'two')]
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second']) index
MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']],
codes=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=['first', 'second'])
【解説】マルチインデックスのindexを作っております。タプルから組み合わせを作りindex名を付けて用意する
df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B']) df
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 | |
foo | one | -0.054618 | 0.587165 |
two | 0.411690 | 0.241520 | |
qux | one | 0.683476 | 1.856185 |
two | -0.435505 | 0.604112 |
【解説】index8つに対してデータ割り当てDataFrameを作ります。マルチインデックスの役割は他で調べてください。ほかのページでも説明してます。
df2 = df[:4]
df2
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 |
【解説】マルチインデックスでも、上から4つをスライスしたデータを取り出せます。
このstack()メソッドは、DataFrameの列のレベルを「圧縮」します。
stacked = df2.stack() stacked
first second
bar one A 0.681572
B 0.922410
two A -0.273510
B -1.867662
baz one A -0.072902
B 0.386001
two A -1.570309
B -0.933379
dtype: float64
【解説】stack()
メソッドでfirst,secondが同じindex同士のA,Bを3th indexに変換している。
「スタック」DATAFRAMEまたはシリーズ(持つMultiIndexように index)、逆の操作のstack()IS unstack()、どのunstacksデフォルトで最後のレベル:
stacked.unstack()
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 |
stacked.unstack(1)
second | one | two | |
---|---|---|---|
first | |||
bar | A | 0.681572 | -0.273510 |
B | 0.922410 | -1.867662 | |
baz | A | -0.072902 | -1.570309 |
B | 0.386001 | -0.933379 |
stacked.unstack(0)
first | bar | baz | |
---|---|---|---|
second | |||
one | A | 0.681572 | -0.072902 |
B | 0.922410 | 0.386001 | |
two | A | -0.273510 | -1.570309 |
B | -1.867662 | -0.933379 |
stacked.unstack(2)
A | B | ||
---|---|---|---|
first | second | ||
bar | one | 0.681572 | 0.922410 |
two | -0.273510 | -1.867662 | |
baz | one | -0.072902 | 0.386001 |
two | -1.570309 | -0.933379 |
【解説】unstack()を使ってマルチインデックスからcolumnに展開できる
ピボットテーブル
ピボットテーブルのセクションを参照してください。
df = pd.DataFrame({'A': ['one', 'one', 'two', 'three'] * 3, 'B': ['A', 'B', 'C'] * 4, 'C': ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2, 'D': np.random.randn(12), 'E': np.random.randn(12)}) df
A | B | C | D | E | |
---|---|---|---|---|---|
0 | one | A | foo | -0.264204 | 0.644464 |
1 | one | B | foo | 0.725091 | 1.411540 |
2 | two | C | foo | 0.685939 | 0.292828 |
3 | three | A | bar | 0.902787 | 1.326445 |
4 | one | B | bar | 0.213242 | 0.233129 |
5 | one | C | bar | -1.297688 | 0.455064 |
6 | two | A | foo | 1.245784 | 0.281448 |
7 | three | B | foo | 0.453100 | 0.087195 |
8 | one | C | foo | 0.275489 | -0.662055 |
9 | one | A | bar | -1.138988 | -0.573590 |
10 | two | B | bar | -0.674417 | -0.639242 |
11 | three | C | bar | 0.099451 | 0.757837 |
このデータからピボットテーブルを非常に簡単に作成できます。
pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'])
C | bar | foo | |
---|---|---|---|
A | B | ||
one | A | -1.138988 | -0.264204 |
B | 0.213242 | 0.725091 | |
C | -1.297688 | 0.275489 | |
three | A | 0.902787 | NaN |
B | NaN | 0.453100 | |
C | 0.099451 | NaN | |
two | A | NaN | 1.245784 |
B | -0.674417 | NaN | |
C | NaN | 0.685939 |
【解説】ピボットテーブルが簡単はいいけど説明がないとわからん! * 集計する列 values='D' * インデックス index=['A', 'B'] * 列 columns=['C']
D列が使われている。A,B列がマルチインデックスになる。C列が列として扱うそれで? * A,B列でマルチインデックスになっておりソートされている。 * C列のデータをもとにして種類があるだけ列データとして展開する。 * D列の内容を当てはめるという感じか
df = pd.DataFrame({'氏名': ['Aさん', 'Aさん', 'Bさん', 'Cさん'] * 3, '年度': ['2017', '2018', '2019'] * 4, '半期': ['上期', '上期', '上期', '下期', '下期', '下期'] * 2, '出張数': np.random.randint(0, 20, size=12), 'レシオ': np.random.randn(12)}) df
氏名 | 年度 | 半期 | 出張数 | レシオ | |
---|---|---|---|---|---|
0 | Aさん | 2017 | 上期 | 17 | -1.333364 |
1 | Aさん | 2018 | 上期 | 19 | 0.556026 |
2 | Bさん | 2019 | 上期 | 19 | -0.196643 |
3 | Cさん | 2017 | 下期 | 9 | 1.896971 |
4 | Aさん | 2018 | 下期 | 14 | -1.753202 |
5 | Aさん | 2019 | 下期 | 10 | -0.348465 |
6 | Bさん | 2017 | 上期 | 4 | -0.884795 |
7 | Cさん | 2018 | 上期 | 18 | 0.002419 |
8 | Aさん | 2019 | 上期 | 1 | 0.292434 |
9 | Aさん | 2017 | 下期 | 13 | -0.913100 |
10 | Bさん | 2018 | 下期 | 2 | -0.892237 |
11 | Cさん | 2019 | 下期 | 2 | -0.023665 |
pd.pivot_table(df, values='出張数', index=['氏名', '年度'], columns=['半期'],fill_value=0)
半期 | 上期 | 下期 | |
---|---|---|---|
氏名 | 年度 | ||
Aさん | 2017 | 17 | 13 |
2018 | 19 | 14 | |
2019 | 1 | 10 | |
Bさん | 2017 | 4 | 0 |
2018 | 0 | 2 | |
2019 | 19 | 0 | |
Cさん | 2017 | 0 | 9 |
2018 | 18 | 0 | |
2019 | 0 | 2 |
【解説】やっぱり、one、two、さん、しーとかじゃ!わからんて。適当に当てはめてみました。どうでしょうか。出張がなかったのでindexには上がってこないためNaNになっているのは、理由がはっきりしているのでfill_value=0で置き換えております。
Pandas 集中講座 その4 マージ(結合)
Pandasライブラリを覚えたい!公式「10 Minutes to pandas」をモチーフに使って実際に動作させ学習する。第4回 マージ(結合)
Pandas 集中講座(4)
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」を実際にやってみる (4/6)
Pandasを覚える!さて、グーグル先生に教えてもらえば大抵のことは分かるのだが、それが今の最適解なのか?と考えるとそうではない。グーグル先生のサーチ機能は、カンニングペーパーであり、単語帳である。欲しい答えを最小ステップでたどり着くための方法。例えるなら、望遠鏡、又は顕微鏡で拡大されたエリアで見つけた希望(答え)。なぜそうなるのか?なぜそう書けば動くのか?など、答えに対する成り立ちがすっぽり抜けてしまうのである。最近のPython環境は、Jupyter Notebookで敷地も下がって来ている。入門書や学習本を眺めて図を見てなんとなくへーとか言って分かった気になって何も残らないパターンになりかねない。今回選んだ方法は、グーグル先生の支援のもと公式のチュートリアル以前のPandas概要説明『10 Minutes to pandas』を上から順にJupyter Notebookを使い実行していってみようと思う。これにより、学習講座のWorkshop形式で講師が腹痛で欠席したので自己学習してなさい版くらいの知識に残ることを期待。実行した結果をここに残し、その時のメモ書きとエッセンスと晒してみる。
【解説】公式「10 Minutes to pandas」をJupyter Notebookを使って動作させながら確認し、突っ込みを入れる!(この文章を見る価値としては、原書が何をやっているのかが分かる、、、分かるはずである)
公式「10 Minutes to pandas」を実際にやってみる
6.マージ
CONCAT
Pandasは、結合/マージ型操作の場合に、Series、DataFrame、およびPanelオブジェクトを、インデックスおよびリレーショナル代数機能のためのさまざまな種類の設定ロジックと簡単に組み合わせるためのさまざまな機能を提供します。
マージセクションを参照してください。以下と一緒にパンダオブジェクトを連結するconcat():
df = pd.DataFrame(np.random.randn(10, 4)) df
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0.604099 | 0.990079 | -0.521005 | 0.794407 |
1 | -1.028698 | -0.426349 | -1.379724 | -0.316860 |
2 | 1.980853 | -1.300201 | 1.245424 | 1.271737 |
3 | 1.547143 | 0.859297 | 0.453607 | 0.972436 |
4 | 0.976275 | 0.075214 | 0.576674 | -1.065869 |
5 | 0.143601 | -1.579016 | -0.335929 | -0.560030 |
6 | 1.771954 | -0.239358 | -0.652136 | -0.414211 |
7 | 0.107051 | 0.061522 | 0.685471 | 1.188601 |
8 | 1.958523 | -0.195262 | -0.214413 | 0.463809 |
9 | -0.913350 | -0.467059 | 1.669981 | -0.923343 |
pieces = [df[:3], 0*df[7:], df[3:7]] pieces
[ 0 1 2 3
0 0.604099 0.990079 -0.521005 0.794407
1 -1.028698 -0.426349 -1.379724 -0.316860
2 1.980853 -1.300201 1.245424 1.271737, 0 1 2 3
7 0.0 0.0 0.0 0.0
8 0.0 -0.0 -0.0 0.0
9 -0.0 -0.0 0.0 -0.0, 0 1 2 3
3 1.547143 0.859297 0.453607 0.972436
4 0.976275 0.075214 0.576674 -1.065869
5 0.143601 -1.579016 -0.335929 -0.560030
6 1.771954 -0.239358 -0.652136 -0.414211]
【解説】indexをスライスしてDataFrameを3つに分ける。0~2,3~6,7~9、アクセントとして7~9は、0を書けてゼロのスカラーにしてみた。それをリスト化した。
p_df = pd.concat(pieces) p_df
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0.604099 | 0.990079 | -0.521005 | 0.794407 |
1 | -1.028698 | -0.426349 | -1.379724 | -0.316860 |
2 | 1.980853 | -1.300201 | 1.245424 | 1.271737 |
7 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
8 | 0.000000 | -0.000000 | -0.000000 | 0.000000 |
9 | -0.000000 | -0.000000 | 0.000000 | -0.000000 |
3 | 1.547143 | 0.859297 | 0.453607 | 0.972436 |
4 | 0.976275 | 0.075214 | 0.576674 | -1.065869 |
5 | 0.143601 | -1.579016 | -0.335929 | -0.560030 |
6 | 1.771954 | -0.239358 | -0.652136 | -0.414211 |
【解説】ちょっとアレンジを加えた。pd.concat
を使うことで、DataFrameの3つのリストが1つのDataFrameになった。
リストの頭から、順にindexに入っていることがわかる。元々付けられているIndexラベルがそのままになっている。
t = (p_df[0][8], p_df[1][8]) t
(0.0, -0.0)
t[0] == t[1]
True
【コメント】index=8ラベルで0掛けた結果-0と0と分離してしまった。 [https://ja.wikipedia.org/wiki/IEEE_754%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E8%B2%A0%E3%81%AE%E3%82%BC%E3%83%AD]
参加する
SQLスタイルのマージ データベーススタイルの結合の節を参照してください。
left = pd.DataFrame({'key': ['foo', 'foo'], 'lval': [1, 2]}) right = pd.DataFrame({'key': ['foo', 'foo'], 'rval': [4, 5]})
left
key | lval | |
---|---|---|
0 | foo | 1 |
1 | foo | 2 |
right
key | rval | |
---|---|---|
0 | foo | 4 |
1 | foo | 5 |
pd.merge(left, right, on='key')
key | lval | rval | |
---|---|---|---|
0 | foo | 1 | 4 |
1 | foo | 1 | 5 |
2 | foo | 2 | 4 |
3 | foo | 2 | 5 |
【解説】pd.merge
でマージである。引数でleft、次にrightそして、on='key'となっている。つまり、ベースはleft、追加はright、マージキーは'key'となり、indexを上から見ていく。マージなので2つのleft、right共に同じキーの時出力され同じ出でなければ破棄される。
l = 0 for lkey in left['key']: r =0 for rkey in right['key']: if lkey == rkey: print(lkey,left['lval'][l],right['rval'][r]) r+=1 l+=1
foo 1 4
foo 1 5
foo 2 4
foo 2 5
【解説】な感じに、leftとrightでの2重ループで同じキーかどうか確認して、同じであれば、マージして出力する。なので、keyの値が同じだと総当たりの組み合わせが出力されることになる。
pd.merge(left, right)
key | lval | rval | |
---|---|---|---|
0 | foo | 1 | 4 |
1 | foo | 1 | 5 |
2 | foo | 2 | 4 |
3 | foo | 2 | 5 |
与えることができるもう一つの例は次のとおりです。
left = pd.DataFrame({'key': ['foo', 'bar'], 'lval': [1, 2]}) right = pd.DataFrame({'key': ['foo', 'bar'], 'rval': [4, 5]})
left
key | lval | |
---|---|---|
0 | foo | 1 |
1 | bar | 2 |
right
key | rval | |
---|---|---|
0 | foo | 4 |
1 | bar | 5 |
pd.merge(left, right, on='key')
key | lval | rval | |
---|---|---|---|
0 | foo | 1 | 4 |
1 | bar | 2 | 5 |
【解説】今度のマージデータは、キーが一意なのでfooとbarが1ずつしかないので横付けマージと同じ結果になる。
追加
データフレームに行を追加します。付録を参照してください 。
df = pd.DataFrame(np.random.randn(8, 4), columns=['A', 'B', 'C', 'D']) df
A | B | C | D | |
---|---|---|---|---|
0 | 1.667774 | -0.436216 | -1.308609 | -0.271015 |
1 | 0.801015 | 0.398531 | 0.215097 | -1.087617 |
2 | 1.765974 | -0.364113 | -0.033961 | 1.117321 |
3 | 1.205805 | 0.155813 | -0.550467 | 1.129920 |
4 | -0.713234 | -0.595935 | -0.266757 | 1.690120 |
5 | -1.172786 | -0.069766 | 0.653920 | -0.537090 |
6 | -0.619824 | -0.507945 | -0.369917 | 0.600396 |
7 | -1.825166 | 2.128321 | 1.500123 | -1.485171 |
s = df.iloc[3]
s
A 1.205805
B 0.155813
C -0.550467
D 1.129920
Name: 3, dtype: float64
df.append(s, ignore_index=True)
A | B | C | D | |
---|---|---|---|---|
0 | 1.667774 | -0.436216 | -1.308609 | -0.271015 |
1 | 0.801015 | 0.398531 | 0.215097 | -1.087617 |
2 | 1.765974 | -0.364113 | -0.033961 | 1.117321 |
3 | 1.205805 | 0.155813 | -0.550467 | 1.129920 |
4 | -0.713234 | -0.595935 | -0.266757 | 1.690120 |
5 | -1.172786 | -0.069766 | 0.653920 | -0.537090 |
6 | -0.619824 | -0.507945 | -0.369917 | 0.600396 |
7 | -1.825166 | 2.128321 | 1.500123 | -1.485171 |
8 | 1.205805 | 0.155813 | -0.550467 | 1.129920 |
【解説】df.append
を使ってindexの最後に行を追加する。追加するのは、スライスされたindex=3ラベルのDataFrame。もちろんindexラベル3が付いているが、これを無視して最後に追加自動附番で8が割り当てられている。
df.append(s)
A | B | C | D | |
---|---|---|---|---|
0 | 1.667774 | -0.436216 | -1.308609 | -0.271015 |
1 | 0.801015 | 0.398531 | 0.215097 | -1.087617 |
2 | 1.765974 | -0.364113 | -0.033961 | 1.117321 |
3 | 1.205805 | 0.155813 | -0.550467 | 1.129920 |
4 | -0.713234 | -0.595935 | -0.266757 | 1.690120 |
5 | -1.172786 | -0.069766 | 0.653920 | -0.537090 |
6 | -0.619824 | -0.507945 | -0.369917 | 0.600396 |
7 | -1.825166 | 2.128321 | 1.500123 | -1.485171 |
3 | 1.205805 | 0.155813 | -0.550467 | 1.129920 |
【解説】ignore_index=Trueを追加しないと元からついているindexが代入される。
Pandas 集中講座 その3 欠陥データと操作
Pandasライブラリを覚えたい!公式「10 Minutes to pandas」をモチーフに使って実際に動作させ学習する。第3回 欠陥データと操作
Pandas 集中講座(3)
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」を実際にやってみる (3/6)
4.欠落データ
Pandasは主に
np.nan
欠損値を表すために値を使用します。デフォルトでは計算に含まれていません。欠損データセクションを参照してください。インデックスを再作成すると、指定した軸のインデックスを変更/追加/削除できます。これはデータのコピーを返します。
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 |
reindexを使って作り直す
df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ['E']) df1
A | B | C | D | F | G | E | |
---|---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | 0.000000 | -0.871839 | 5 | 1 | 1.0 | NaN |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 5 | 2 | 2.0 | NaN |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 5 | 3 | 3.0 | NaN |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | 5 | 4 | NaN | NaN |
【解説】reindex
を使ってindexとcolumnをリビルドしている。E列は、dataが指定されてないのでいわゆる未定義とかNULL
の状態
df1.loc[dates[0]:dates[1], 'E'] = 1 df1
A | B | C | D | F | G | E | |
---|---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | 0.000000 | -0.871839 | 5 | 1 | 1.0 | 1.0 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 5 | 2 | 2.0 | 1.0 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 5 | 3 | 3.0 | NaN |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | 5 | 4 | NaN | NaN |
【解説】ラベル(.loc)を使って行選択し'E'列に1を代入
データが欠落している行を削除します。
df1.dropna(how='any')
A | B | C | D | F | G | E | |
---|---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | 0.00000 | -0.871839 | 5 | 1 | 1.0 | 1.0 |
2019-05-02 | -0.092376 | 0.03622 | -0.146521 | 5 | 2 | 2.0 | 1.0 |
【解説】dropnaを使ってN/Aを落としている。df1全体からNaNのデータを以外を選択
【余談】判断結果としてのNaN以外に、取り込んだデータがNaNである理由は、測定ミス、測定忘れ、記録忘れ、データが飛び値になっていて明らかに記録読み取りミスなどいろいろ考えられるがこういう値に対して適当に自分に都合がよい値を入れてしまうことを改竄といいます。学校の先生方は、よくテスト問題でこれを実行してテスト問題を作ってます。あれは、都合の良い答えを作ってそれをもとに問題を作成していく手法なのかもしれませんが(いやいやあれは、合わせこみっていうものだよ!)。話がそれました
欠落データを埋める。
df1.fillna(value=5)
A | B | C | D | F | G | E | |
---|---|---|---|---|---|---|---|
2019-05-01 | 0.000000 | 0.000000 | -0.871839 | 5 | 1 | 1.0 | 1.0 |
2019-05-02 | -0.092376 | 0.036220 | -0.146521 | 5 | 2 | 2.0 | 1.0 |
2019-05-03 | -0.307696 | -0.051588 | -0.413379 | 5 | 3 | 3.0 | 5.0 |
2019-05-04 | 0.397105 | 1.731472 | -1.033554 | 5 | 4 | 5.0 | 5.0 |
【解説】df1全体からNaNを探しvalue値を代入する。
【余談】Seriesや、DataFrameにするデータのNaN
の扱いは注意しましょう。Pandasは、NaN
の扱いが上手みたいなのでいろいろできます。NaN
の値をどういう理由で変更するのかソースコードにコメントとして残していくのがベターと思われます。後で、データの改竄といわれないように
NaN値が格納されているブールマスクを取得します
pd.isna(df1)
A | B | C | D | F | G | E | |
---|---|---|---|---|---|---|---|
2019-05-01 | False | False | False | False | False | False | False |
2019-05-02 | False | False | False | False | False | False | False |
2019-05-03 | False | False | False | False | False | False | True |
2019-05-04 | False | False | False | False | False | True | True |
【解説】isna
を使い。Is N/A? ってことなんでしょう。"Not Applicable" "Not Available" え?"Not a Number"とちがうじゃん!と思うのですが適応範囲外の値ということでは同じなんでしょうね。True/False(真/偽)(Yes/No)(合否)、、、Trueの部分がNaN
が入っている箇所です
【余談】isna他でもう説明したような
pd.notna(df1)
A | B | C | D | F | G | E | |
---|---|---|---|---|---|---|---|
2019-05-01 | True | True | True | True | True | True | True |
2019-05-02 | True | True | True | True | True | True | True |
2019-05-03 | True | True | True | True | True | True | False |
2019-05-04 | True | True | True | True | True | False | False |
【解説】ちなみにnotna
は「NaNじゃない」です。
5.操作
バイナリ演算の基本セクションを参照してください。
統計
一般的な操作は、欠けているデータを除外します。
記述統計を実行します
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 |
df.mean()
A 0.365808
B 0.402788
C -0.552898
D 5.000000
F 3.500000
G 2.000000
dtype: float64
【解説】mean() 平均です。列毎に集計します。G列の平均が2になってます。(1+2+3)/6で1ではなく、(1+2+3)/3で2になってます。素晴らしい
他の軸でも同じ操作です。
df.mean(1)
2019-05-01 1.021360
2019-05-02 1.466221
2019-05-03 1.704556
2019-05-04 2.019005
2019-05-05 2.407438
2019-05-06 2.201831
Freq: D, dtype: float64
【解説】df.mean()では列毎でしたが、df.mean(1)では、行(index)毎になります。
df.mean(0)
A 0.365808
B 0.402788
C -0.552898
D 5.000000
F 3.500000
G 2.000000
dtype: float64
【解説】何が軸なの?に対してdf.mean(0)だと列毎になります。つまりNumpy風axis=0,1という意味ですね。
次元が異なり、位置合わせが必要なオブジェクトを操作します。さらに、Pandasは指定された次元に沿って自動的にブロードキャストします。
s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(2) s
2019-05-01 NaN
2019-05-02 NaN
2019-05-03 1.0
2019-05-04 3.0
2019-05-05 5.0
2019-05-06 NaN
Freq: D, dtype: float64
【解説】shift(2)で下側に2つシフトしています。上2つは定義無いのでNaNです
s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates) s
2019-05-01 1.0
2019-05-02 3.0
2019-05-03 5.0
2019-05-04 NaN
2019-05-05 6.0
2019-05-06 8.0
Freq: D, dtype: float64
s=s.shift(2)
s
2019-05-01 NaN
2019-05-02 NaN
2019-05-03 1.0
2019-05-04 3.0
2019-05-05 5.0
2019-05-06 NaN
Freq: D, dtype: float64
【解説】ですです。shiftしてもindex値は変更しないことに注意してください
s=s.shift(10)
s
2019-05-01 NaN
2019-05-02 NaN
2019-05-03 NaN
2019-05-04 NaN
2019-05-05 NaN
2019-05-06 NaN
Freq: D, dtype: float64
【余談】あうあう
s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(-2) s
2019-05-01 5.0
2019-05-02 NaN
2019-05-03 6.0
2019-05-04 8.0
2019-05-05 NaN
2019-05-06 NaN
Freq: D, dtype: float64
【解説】-2で上側に2つシフト
s.shift(1)
2019-05-01 NaN
2019-05-02 5.0
2019-05-03 NaN
2019-05-04 6.0
2019-05-05 8.0
2019-05-06 NaN
Freq: D, dtype: float64
【解説】シフトしたデータは、消えているので戻りません。
df.sub(s, axis='index')
A | B | C | D | F | G | |
---|---|---|---|---|---|---|
2019-05-01 | -5.000000 | -5.000000 | -5.871839 | 0.0 | -4.0 | -4.0 |
2019-05-02 | NaN | NaN | NaN | NaN | NaN | NaN |
2019-05-03 | -6.307696 | -6.051588 | -6.413379 | -1.0 | -3.0 | -3.0 |
2019-05-04 | -7.602895 | -6.268528 | -9.033554 | -3.0 | -4.0 | NaN |
2019-05-05 | NaN | NaN | NaN | NaN | NaN | NaN |
2019-05-06 | NaN | NaN | NaN | NaN | NaN | NaN |
【解説】df.sub
って、一瞬Main/Subとかで、範囲取り出しの処理かと思ったら。subtraction(引き算)のことじゃん!sのSeriesとdfのindexをマッチングさせて引き算している。ってsubtractがあるが・・・
df.subtract(s, axis='index')
A | B | C | D | F | G | |
---|---|---|---|---|---|---|
2019-05-01 | -5.000000 | -5.000000 | -5.871839 | 0.0 | -4.0 | -4.0 |
2019-05-02 | NaN | NaN | NaN | NaN | NaN | NaN |
2019-05-03 | -6.307696 | -6.051588 | -6.413379 | -1.0 | -3.0 | -3.0 |
2019-05-04 | -7.602895 | -6.268528 | -9.033554 | -3.0 | -4.0 | NaN |
2019-05-05 | NaN | NaN | NaN | NaN | NaN | NaN |
2019-05-06 | NaN | NaN | NaN | NaN | NaN | NaN |
適用する
import pandas as pd import numpy as np dates = pd.date_range('20130101', periods=6) df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))
df
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | -1.174480 | 0.131666 | 0.615321 | -0.674131 |
2013-01-02 | 0.360878 | 0.286958 | -0.901880 | -1.644234 |
2013-01-03 | 0.175645 | 0.967822 | -0.817930 | -0.160181 |
2013-01-04 | -2.190213 | -0.217229 | 0.158892 | 0.891357 |
2013-01-05 | 0.408453 | 0.685262 | 2.027146 | -0.426742 |
2013-01-06 | 0.909910 | -0.743968 | 0.980539 | -1.879472 |
データに関数を適用する:
df.apply(np.cumsum)
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | -1.174480 | 0.131666 | 0.615321 | -0.674131 |
2013-01-02 | -0.813602 | 0.418624 | -0.286558 | -2.318364 |
2013-01-03 | -0.637957 | 1.386446 | -1.104489 | -2.478546 |
2013-01-04 | -2.828170 | 1.169217 | -0.945597 | -1.587188 |
2013-01-05 | -2.419717 | 1.854479 | 1.081549 | -2.013930 |
2013-01-06 | -1.509807 | 1.110511 | 2.062088 | -3.893402 |
【解説】np.cumsumは、累積加算する関数。上から順に足していった値になる。
np.cumsum([1,2,3,4,5,6,7,8,9,10])
array([ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55], dtype=int32)
np.sum([1,2,3,4,5,6,7,8,9,10])
55
【解説】np.cumsumは、使ったことなかったので、リストを入れた結果を表示してみた。予想通り。つまり、列毎にcumsumが適応されて実行されたということになる。オブジェクト指向なのでcumsum
とcumsum()
は違うもの、名詞と動詞みたいな感じか。前者がメソッドの自身を示し、後者が実行する関数を示している感じ。cumsumを使って適応して!っていうのがdf.apply(np.cumsum)
の意味合い。
np.cumsum(df)
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | -1.174480 | 0.131666 | 0.615321 | -0.674131 |
2013-01-02 | -0.813602 | 0.418624 | -0.286558 | -2.318364 |
2013-01-03 | -0.637957 | 1.386446 | -1.104489 | -2.478546 |
2013-01-04 | -2.828170 | 1.169217 | -0.945597 | -1.587188 |
2013-01-05 | -2.419717 | 1.854479 | 1.081549 | -2.013930 |
2013-01-06 | -1.509807 | 1.110511 | 2.062088 | -3.893402 |
type(np.cumsum(df))
pandas.core.frame.DataFrame
【解説】であれば、本来の使い方np.cumsum(df)
としたら?あ、できた。dfをnp.cumsum()が扱える値にcastして実行しているはず。が、戻り結果はpandas.core.frame.DataFrameのまま?なんでしょうね。しかし、微妙に結果が変わっていて。演算はfloat扱いになってますね。
【コメント】お!出たなlambda
だ。謎な引数をもとに演算して結果を謎な出力に戻すということをしているやつですね。
df.applyをちょっと調べると。axis = 0が、デフォなので列毎にSeriesにを切り出して処理をしている。df.applyは、Series切り出しオブジェクトを入力として与え、結果を受け取るということか。lambdaを何回も回るのではなく1回適応で終わり。
df.apply(lambda x: x.max() - x.min())
A 3.100123
B 1.711790
C 2.929026
D 2.770829
dtype: float64
df['A'].max() - df['A'].min()
3.100122888374953
【解説】上記の結果をcolumn毎に実行した結果になったと
ヒストグラム
Histogramming and Discreizationsで詳細を参照してください。
s = pd.Series(np.random.randint(0, 7, size=10)) s
0 0
1 3
2 2
3 1
4 4
5 4
6 2
7 6
8 6
9 1
dtype: int32
s.value_counts()
6 2
4 2
2 2
1 2
3 1
0 1
dtype: int64
type(s.value_counts())
pandas.core.series.Series
【コメント】おい!終わりかよ。きっと苦手な分野なのかな?
s.value_counts().index
Int64Index([6, 4, 2, 1, 3, 0], dtype='int64')
【解説】ヒストグラム?頻度を数えている。上記のSeriesに3が3回出現し、6が2回、、、という結果を違うSeriesに戻してる。作られているindexはintになっている
s = pd.Series(np.random.randn(1000)) s.value_counts(bins=10,sort=False)
(-3.017, -2.416] 6
(-2.416, -1.822] 31
(-1.822, -1.228] 68
(-1.228, -0.634] 158
(-0.634, -0.0396] 237
(-0.0396, 0.555] 247
(0.555, 1.149] 154
(1.149, 1.743] 64
(1.743, 2.337] 28
(2.337, 2.931] 7
dtype: int64
【解説】Numpyで1000個乱数を作成し10区画に分けたヒストグラムを作成。瓶の範囲が明確に表示されているのがいい感じですね。また、s.value_counts
の目的がSeriesで同じ値になるものを多い順に並べなさいという関数ですね。indexがそのキーでdataが頻度になる。出てくる結果が多いbest5を調べるときとか便利そうですね
文字列メソッド
以下のコードスニペットのように、seriesはstr属性に一連の文字列処理メソッドを備えているため、配列の各要素に対する操作が簡単になります。strのパターンマッチングは一般にデフォルトで正規表現を使います(そして場合によっては常にそれらを使います)。
詳しくはベクトル化された文字列メソッドをご覧ください。
【解説】コードスニペット(code snippet)ソースコードの断片
s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat']) s
0 A
1 B
2 C
3 Aaba
4 Baca
5 NaN
6 CABA
7 dog
8 cat
dtype: object
s.str.lower()
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
【解説】Seriesのdata形式が文字列の場合いろいろと便利な関数が用意されているよ。ということですね。上記の例は、各データを小文字化するlower()という関数です。注意点としては、NumpyとPandasでは文字をstrとして認識せず、数値以外のものでしょうがなく扱っているオブジェクト型として認識しているのでその辺が注意です。dtypeがstrではなくobjectです
type('ABC')
str
s.str.len()
0 1.0
1 1.0
2 1.0
3 4.0
4 4.0
5 NaN
6 4.0
7 3.0
8 3.0
dtype: float64
【コメント】Excelでもそうですがセルに入っている文字列を処理することを多いですね。
詳しくはベクトル化された文字列メソッドをご覧ください。の部分時間がある時に調べるといろいろとできる幅が広がりそうです。区切り位置とかどうやるんだろ。columnを横断してしまうけどなどと思考をはしらせてしまいますね。
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以外だね)