スプレッドシートの計算をPandasで再現してみた
スプレッドシートでやる計算を、軽量化のためにpythonのPandasで実装しようとしたら、思いの他詰まったのでまとめたいと思います。
取り込んだ統計データのcsv等を使ってシミュレーションを組みたいときに活用できるかと思います。
なおサンプルとして、口座から引落と預入をして残高を算定するスプレッドシートを挙げています。
引落し額の上限を3,000円と設定しており、入力した引落し額が3,000円を超えている場合は、自動的に3,000円を引落し額として設定しています。
pandas化のテクニックとして取り上げているのは、以下の2つになります。
2つのセルを比較して小さい(大きい)数字をピックアップする
pandasの列(又は)行の中で一番小きい(大きい)値をピックアップする方法はたくさん紹介されているのですが、Aという列の要素とBという列の要素を比較して、小さい(大きい)数値を入力する列を作成する手法を見つけるのに苦労しました。
1つ上の行の計算結果を参照する (前月末の残高を当月頭の残高にするイメージ)
なお、コードをダウンロードする場合はこちら。
import pandas as pd # 使用する日付、引落入力、預入のDataFrameを作成。 # 実務ではこちらのデータはcsv等で取り込むイメージです df1 = pd.DataFrame({ "date":["4/1", "4/8", "4/15", "4/22", "4/29", "5/6", "5/13", "5/20", "5/27", "6/3"], "withd_inp":[2000, 0, 4000, 0, 1500,3500,0,6000,0,4000], "deposit":[0,3000,0,0,2000,0,0,0,8000,0] })
色々調べたのですが、for文を使う以外に変数とpandasの要素を比較して最小値、最大値を抽出する方法が見つからなかったので、引出制限 3,000円をpandasの要素として設定しています。
見た目を気にする場合は、最後に削除するとOK。
# 引落入力と比較するための"引出制限 3,000円" をlimitカラムに入力 # 次にdf の要素に min関数を適用するためにカラムに入力している df1["limit"] = 3000 # 要素同士を比較して小さい方を実際の引落に入力 df1["withd_act"] = df1[["withd_inp", "limit"]].min(axis=1) # for文を適用するために 今週頭の残高と、今週終の残高のカラムを作成する # None を入力する必然性はないが、数字と区別するために None を入力しておく df1["accnt_head"] = None df1["accnt_tail"] = None
出力結果はこうなります。
# 今週頭の残高と、今週終の残高の作成 for i in range(len(df1)): if i == 0: df1["accnt_head"][0] = 3000 else: df1["accnt_head"][i] = df1["accnt_tail"][i-1] df1["accnt_tail"][i] = df1["accnt_head"][i] - df1["withd_act"][i] + df1["deposit"][i]
以下のようにエラーが出てしまいますが、一応計算されているようです。
C:\path\lib\site-packages\ipykernel_launcher.py:4: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy after removing the cwd from sys.path.
SettingWithCopyWarningの参考サイト note.nkmk.me
スプレッドシートと並びが違いますので、気になる方は以下の処理をご参考ください。
# 順番が気になるので並び替え df2 = pd.DataFrame(df1["date"]) df2 = pd.concat([df2, df1.iloc[:,-2]], axis=1) df2 = pd.concat([df2, df1.iloc[:,1]], axis=1) df2 = pd.concat([df2, df1.iloc[:,4]], axis=1) df2 = pd.concat([df2, df1.iloc[:,2]], axis=1) df2 = pd.concat([df2, df1.iloc[:,-1]], axis=1)
また、スプレッドシートでは使用していないのでgithub中のコードでは省略しましたが、pandasの要素に自作の関数等を使うテクニックもご紹介します。
# pandasの要素に、自作の関数を適用したい場合 def 自作の関数: 設定したい処理 df['X'] = df['A'].map(自作の関数) # pandasの要素に、無名関数 lambda を適用したい場合 df["X"] = df["A"].map(lambda x : 設定したい処理 ) # 単純にmax()、min()関数を設定するだけなら関数を作成する必要はありませんが、要素を計算した後の数値に対してmax()、min()を設定したい場合には、以下のような方法も使えます。 def 関数名(row): #rowはDataFrame型にあたります return max((row['A'] - row['B'])*0.9, 0) df['X'] = df.apply(関数名, axis=1)
以上になります、最後までお読みいただきありがとうございました。
参考サイト