データの平均・共分散の逐次計算

# はじめに

パソコンのメモリに載せないほどの容量が大きいデータセットがあるとしましょう。パソコンのメモリが16GBに対し、データセットの容量が200GBのことを言っています。この時には、データセットがメモリに乗らないため、データを逐次に取り出し、平均と分散（共分散）を計算・更新する方法をよくとります。今日はその方法をご説明します。

# 巨大データセットの生成
Featureが六つ、データの数が2000000の仮想のデータセットを作成し、csvファイルに保存します。csvファイルの容量は約2GBになります。
* 〇を二つ追記すれば、200GBのCSVファイル作成も可能です。パソコンの処理がかなり重くなります。
```python
import numpy as np
import pandas as pd

# create a pandas DataFrame with random data
num_data = 20000000
df = pd.DataFrame(np.random.rand(num_data, 6),
                  columns=['feature1', 'feature2', 'feature3', 'feature4', 'feature5', 'feature6'])

df.to_csv('./datasets/large_dataset.csv', index=False)
```

# 一般的な平均・共分散の計算
メモリにデータセットを乗せ、一気に平均・共分散を計算します。大体、この段階でパソコンのメモリ利用率が100%になり、動作が重くなります。（Freezeされ、計算が終わらないこともあります。）

```python
import numpy as np
import pandas as pd

df = pd.read_csv('./datasets/large_dataset.csv')


# calculate the average of each feature using numpy
averages_numpy = np.mean(df, axis=0)
print("Averages:\n", averages_numpy)


# calculate the covariance matrix using numpy
covariance_numpy = np.cov(df, rowvar=False)
print("Covariance:\n", covariance_numpy)
```

# 逐次方法の平均・共分散の計算

一気にデータを取り出すのではなく、chunk単位で取り出します。
その都度、データの統計量を計算し、新しいchunkが追加されるときにそれを更新する方法です。
考えてみれば、Deep Learningの学習ってこのようなやり方でやっていますね。
コードを書いてみます。　前章で作成したデータセットを利用します。


```python
import numpy as np
import pandas as pd

#data file name
fName = './datasets/large_dataset.csv'

#データセットの次元確認のため、
df_sample = pd.read_csv(fName, chunksize=5).get_chunk()
# http://blog.livedoor.jp/itengineerblog/archives/15060246.html
print(df_sample.head())
print(df_sample.shape)
num_feature = df_sample.shape[1]
print(df_sample.values) #data values

# define the chunk size
chunk_size = 10000


# initialize the counters
count = 0
sums = np.zeros(num_feature) #number of features
sums_sq = np.zeros((num_feature, num_feature)) #(n features, n features)

#extract chunk
for i, chunk in enumerate(pd.read_csv(fName,  chunksize=chunk_size)):

    data = chunk.values
    n = data.shape[0]
    count += n
    sums += np.sum(data, axis=0)
    sums_sq += np.dot(data.T, data)


# calculate the mean and covariance
mean = sums / count
cov = (sums_sq / count) - np.outer(mean, mean)

print("Mean:\n", mean)
print("Covariance:\n", cov)

```

コードを説明しますと、chunk単位を決めます。こちらでは、chunk_size = 10000になっていて、データを10000個単位で取り出します。
pd.read_csv(fName,  chunksize=chunk_size)　read_csv()のオプションにchunksizeがあります。これを使うと、繰り返しながらchunk_sizeでデータと取り出してくれます。

# 計算結果

```python
Mean:
 [0.50001305 0.49991214 0.5000087  0.50006749 0.49998438 0.49998971]
Covariance:
 [[ 8.33555032e-02  2.20143863e-05  3.08444789e-05 -1.33690942e-05
   3.14321419e-05  4.73431331e-06]
 [ 2.20143863e-05  8.33174013e-02 -1.83491930e-05  4.04559066e-05
   3.32762766e-05  1.42837743e-05]
 [ 3.08444789e-05 -1.83491930e-05  8.33674115e-02  4.37566769e-05
  -1.07664114e-05 -5.27177048e-07]
 [-1.33690942e-05  4.04559066e-05  4.37566769e-05  8.33394753e-02
  -1.39420447e-05  1.99202341e-05]
 [ 3.14321419e-05  3.32762766e-05 -1.07664114e-05 -1.39420447e-05
   8.33050283e-02 -1.85984257e-06]
 [ 4.73431331e-06  1.42837743e-05 -5.27177048e-07  1.99202341e-05
  -1.85984257e-06  8.33443293e-02]]

```

# まとめ
多次元データの平均・共分散の逐次計算について、ご説明いたしました。

# 参考資料

https://ehbtj.com/electronics/average-online-algorithm/

https://qiita.com/awakia/items/f58e90b3b2562d787aa5

https://myenigma.hatenablog.com/entry/20150117/1421484158