最終更新日: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ファイルを読み込みます。

a004_007a.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ファイルを読み込んで例を挙げます。

a004_007b.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()

と書けばいいです。