最終更新日:2021/8/19
dfを使った簡単な計算の方法を説明します。この先、もっと難しい計算を行うための基礎となります。
【説明すること】
1. 列を使った結果を新しい列として追加する
dfのカラム(列)を使って計算し、その結果を新しいカラム(列)として元のdfに追加する方法です。例えば、単位の変換などがあります。
1.1 1つの列を使った計算
例えば、ある列の値に1を加えて値を新しい列としたい場合です。これは、
df[‘new’] = df[‘clm’] + 1
と書きます。この場合、新しい列は最後に追加されます。
どんな時に使うのか?
例えば、株価のネットリターン(0.01=1%)をグロスリターン(元本込みの1.01)に変換する場合です。
或いは、現実的によく使うのが%表示のリターン(5 (%))を数値に変換する(0.05)にする場合です。Pythonでは、%表示のリターンはとても扱いにくいです。
この場合、
df[‘new’] = df[‘clm’] /100
と書きます。
もう少し複雑な計算は、ラムダ関数(lambda)を使って書きます。
ラムダ関数は、ここでは詳しく触れず、例だけを挙げておきます。
以下のcsvファイルを読み込みます。
1.2 複数の列同士の計算
例えば、clm0を100倍して、clm1を足す(100*clm0 + clm1)の場合は
df[‘new’] = 100 * df[‘clm0’] + df[‘clm1’]
と書きます。
1.3 Warningが出た!その理由は
たまに、次のWarningが出る事があります。
このエラーは、例えば、「カラム名を同じもので上書きしようとした場合」に出るはずです。
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
Warningの意味は、「もとのdfからコピーした。(=もとのdfは上書きしなかった)」というものです。
しかし、欲しい結果は得られているのではないでしょうか。
結果は正しいのですが、Pythonは「この結果は、あなたが望んだ結果ではないかもしれないので、注意して」と言っているのです。
【どんな時に起きるか】
これは、例えば、%表示の変換で、「1.00(%)」となっているものを数値「0.01」に同じカラム名で変換したくて、
df2[‘pct’] = df2[‘pct’] / 100
と書いた場合に起きると思います。それは、コードを辿ると、df2が他のDFを参照し
df2 = df1[[‘clm0’, ‘clm1’]]
のように書いているのではないでしょうか?
【原因】
これはchained indexingと呼ばれるものです。
dfを連鎖的にカラム名を指定して参照(複製)した場合に起きます。
例えば、df1が所与の時、新しいdf2に対して df2 = df1[['clm0', 'clm1']]
と書いて、更にdf2['clm0'] =1
のように、辿った先の、元のdf1を、df1のカラム名を使って上書きする(上書きするのと同じコードを書いた)ような場合に起きます。
df2 = df1
と書けば、df2['clm0']=1
(カラム’clm0’を全部1にする)と書いても問題は起きません。それは、df1の全てがdf2に複製され、df2をいじると、df1も同時に上書きされるからです。
ところが、カラム名を指定して、df2 = df1[['clm0', 'clm1']]
と書くと、df2には写しが渡されますが、ここでdf1に存在するカラム名’clm0’を指定してdf2の方からdf2['clm0']=1
と書くと、Pythonはdf1は上書きしない、という判断をします。
本来、df2 = df1
という書き方は、df2をいじれば、df1も上書きする、という仕様です。しかし、df2['clm0']=1
と書くと、この仕様とは異なるので、Warningを出すと思われます。
Warningの指示通り、df.loc[]を使っても(例えば、df.loc[:,’pct’])、同じWarningが出るはずです。
【解決方法】
1. 同じ名前のカラム名で、結果を入れた新しいカラムを作るのを「避ければ」、このWarningは回避できます。
または、
2. 同じカラム名にしたければ、以下のように、df2 = df1['clm0'].copy()
と書いて、コピーである事を明確にすれば、Warningは出ません。
勿論、Warningの内容を分かっていて使っていれば、多くの場合、問題ありません。
以下はpandasの該当部分のドキュメント(英語)です。
https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html
2. フィルターをかけて計算する(条件に合うものを抽出して計算)
列にフィルターをかけて(条件に合うものだけを抽出)して、計算する場合です。
2.1 カラム名(列名)で指定する方法
カラム名(列名)でカラムを指定する場合は、dfの条件を最初に書き、 df[dfの条件]['clm1'].sum()
のように、計算の対象とするカラム名(ここでは’clm1’が対象)と、計算の方法を書きます。
この条件で、新しいdfを作りたい場合は df_new = df[dfの条件]['clm1'].sum()
と書けばいいです。
よく使う計算は関数が用意されています。
例えば、
.sum() = 和
.mean() = 平均
.var() = 分散
.std() = 標準偏差
などです。
.var()と.std()はddof=k
というオプションで、不偏分散(不偏標準偏差)を指定することができます。
不偏分散は、確率・統計で扱う話なので、扱わなければ気にしなくて構いません。
しかし、実際に分散や標準偏差を計算する場合には、何を計算しているのか、を正確に理解することがとても重要です。
【ちょっとマニアックな分散、標準偏差の話】
普通に知られている分散、標準偏差はddof=0の場合のはずです。(nで割る。n-1ではない)
ddof=1とすれば、不偏分散になります。(n-1で割る。nではない)
pandasのデフォルトはddof=1です。
Excelで言うと、ddof=0がSTDEV.P()で、ddof=1がSTDEV.S()に相当します。
以下のcsvファイルを読み込んで例を挙げます。
2.2 列番号で指定する方法
列番号で指定する場合は、iloc[]
を使います。 .iloc[]
はfor文を用いた、連続的な計算をする場合に有効です。
例えば、
df[df.iloc[:,1] > 0]['clm2'].sum()
です。これは
(1)1番目の列(0始まりのなので見た目は2列目)が0より大きいものを抽出し
(2)カラム名が’clm2’の和
ということです。
このケースでは、(1)と(2)は対象が異なります。(同じでも構いません)
この条件で新しいdfを作りたい場合は、
df_new = df[df.iloc[:,1] > 0]['clm2'].sum()
と書けばいいです。