大量のデータを処理する場合、データを一括で読み込むと以下のようなエラーメッセージが出ます。
killed
これは、メモリが上限を超えてしまったことを意味します。どうすれば実行できるようになるでしょうか。
メモリとは
コンピュータにおいて、プログラムやデータを記憶する装置のことです。
特に、RAMやROMなどの半導体記憶装置のことを指しますが、ここでは本題からずれますので割愛します。
したがって、メモリが不足するとプログラムのデータを記憶しておくことが困難になります。
データ作成
本記事では、分かりやすくするため、単純なデータ(csvファイル)を準備します。
list= []
for i in range(100):
list.append(str(i) + ',csv,file')
with open('sample.csv', 'w') as f:
f.write('\n'.join(list))
データを分割して読み込む
メモリの上限を超えてしまうような大量のデータは、基本的に分割して読み込みます。
yield
yieldを用いることで、ファイルの中身を一行ずつ返すことで、ファイル全体をメモリ上に読み込む必要がありません。
def file_generator(file):
with open(file, encoding="utf-8") as f:
for line in f:
yield line
file_path = 'sample.csv'
gen = file_generator(file_path)
Pandasのchunksize
Pandasのchunksizeを用いることで、csvファイルから数行ずつ読み込むことができます。
import pandas as pd
reader = pd.read_csv('sample.csv', encoding='utf-8', chunksize=10, header=None)
for i in reader:
print(i)
実行結果
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
5 5 csv file
6 6 csv file
7 7 csv file
8 8 csv file
9 9 csv file
0 1 2
10 10 csv file
11 11 csv file
・・・・・・・・・・・
88 88 csv file
89 89 csv file
0 1 2
90 90 csv file
91 91 csv file
92 92 csv file
93 93 csv file
94 94 csv file
95 95 csv file
96 96 csv file
97 97 csv file
98 98 csv file
99 99 csv file
データから特定して読み込む
行の指定
Pandasのskiprows
csvファイルで特定の行を除外して読み込む場合、read_csvの引数にskiprowsを用います。
ファイルの先頭をその行数分スキップして読み込みます。
import pandas as pd
df=pd.read_csv('sample.csv',header=None)
print(df)
df=pd.read_csv('sample.csv',header=None,skiprows=10)
print(df)
実行結果
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
.. .. ... ...
95 95 csv file
96 96 csv file
97 97 csv file
98 98 csv file
99 99 csv file
[100 rows x 3 columns]
0 1 2
0 10 csv file
1 11 csv file
2 12 csv file
3 13 csv file
4 14 csv file
.. .. ... ...
85 95 csv file
86 96 csv file
87 97 csv file
88 98 csv file
89 99 csv file
[90 rows x 3 columns]
100×3から90×3に少なくなりました。
スキップする行番号をリストで指定することもできます。
import pandas as pd
df=pd.read_csv('sample.csv',header=None)
print(df)
df=pd.read_csv('sample.csv',header=None,skiprows=[0,2,4,95,97,99])
print(df)
実行結果
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
.. .. ... ...
95 95 csv file
96 96 csv file
97 97 csv file
98 98 csv file
99 99 csv file
[100 rows x 3 columns]
0 1 2
0 1 csv file
1 3 csv file
2 5 csv file
3 6 csv file
4 7 csv file
.. .. ... ...
89 92 csv file
90 93 csv file
91 94 csv file
92 96 csv file
93 98 csv file
[94 rows x 3 columns]
0,2,4,95,97,99行のデータをスキップして読み込んでいます。
Pandasのskipfooter
csvファイルの末尾をスキップする場合、read_csvの引数にskipfooterを用います。スキップする行数を整数で指定します。engine=”python”は、警告文を表示させないためです。
import pandas as pd
df=pd.read_csv('sample.csv',header=None)
print(df)
df=pd.read_csv('sample.csv',header=None,skipfooter=10,engine='python')
print(df)
実行結果
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
.. .. ... ...
95 95 csv file
96 96 csv file
97 97 csv file
98 98 csv file
99 99 csv file
[100 rows x 3 columns]
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
.. .. ... ...
85 85 csv file
86 86 csv file
87 87 csv file
88 88 csv file
89 89 csv file
[90 rows x 3 columns]
Pandasのnrows
csvファイルの最初の数行だけを読み込みたいときは、read_csvの引数にnrowsを用います。
import pandas as pd
df=pd.read_csv('sample.csv',header=None)
print(df)
df=pd.read_csv('sample.csv',header=None,nrows=10,engine='python')
print(df)
実行結果
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
.. .. ... ...
95 95 csv file
96 96 csv file
97 97 csv file
98 98 csv file
99 99 csv file
[100 rows x 3 columns]
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
5 5 csv file
6 6 csv file
7 7 csv file
8 8 csv file
9 9 csv file
列の指定
Pandasのusecols
特定の列だけを読み込む場合、read_csvの引数にusecolsを用います。
読み込む列番号をリストで指定します。
import pandas as pd
df=pd.read_csv('sample.csv',header=None)
print(df)
df=pd.read_csv('sample.csv',header=None, usecols=[0,2])
print(df)
実行結果
0 1 2
0 0 csv file
1 1 csv file
2 2 csv file
3 3 csv file
4 4 csv file
.. .. ... ...
95 95 csv file
96 96 csv file
97 97 csv file
98 98 csv file
99 99 csv file
[100 rows x 3 columns]
0 2
0 0 file
1 1 file
2 2 file
3 3 file
4 4 file
.. .. ...
95 95 file
96 96 file
97 97 file
98 98 file
99 99 file
[100 rows x 2 columns]
変数、配列のサイズを定量化
上記を施すことでどのぐらいサイズ(メモリを使う量)が変化するのかをみたいとき、以下のような関数を指定します。
def print_varsize():
import types
print("{}{: >15}{}{: >10}{}".format('|','Variable Name','|',' Size','|'))
print(" -------------------------- ")
for k, v in globals().items():
if hasattr(v, 'size') and not k.startswith('_') and not isinstance(v,types.ModuleType):
print("{}{: >15}{}{: >10}{}".format('|',k,'|',str(v.size),'|'))
elif hasattr(v, '__len__') and not k.startswith('_') and not isinstance(v,types.ModuleType):
print("{}{: >15}{}{: >10}{}".format('|',k,'|',str(len(v)),'|'))
こちらの関数でデータサイズを測ってみましょう。
import pandas as pd
df01=pd.read_csv('sample.csv',header=None)
df02=pd.read_csv('sample.csv',header=None, usecols=[0,2])
df03=pd.read_csv('sample.csv',header=None,skiprows=10)
def file_generator(file):
with open(file, encoding="utf-8") as f:
for line in f:
yield line
file_path = 'sample.csv'
gen = file_generator(file_path)
def print_varsize():
import types
print("{}{: >15}{}{: >10}{}".format('|','Variable Name','|',' Size','|'))
print(" -------------------------- ")
for k, v in globals().items():
if hasattr(v, 'size') and not k.startswith('_') and not isinstance(v,types.ModuleType):
print("{}{: >15}{}{: >10}{}".format('|',k,'|',str(v.size),'|'))
elif hasattr(v, '__len__') and not k.startswith('_') and not isinstance(v,types.ModuleType):
print("{}{: >15}{}{: >10}{}".format('|',k,'|',str(len(v)),'|'))
print_varsize()
実行結果
| Variable Name| Size|
--------------------------
| df01| 300|
| df02| 200|
| df03| 270|
| file_path| 10|
| line| 11|
まとめ
大量のデータを開くためには、分割して開く、または特定して開く2つの方法があります。
本記事では、データを分割する方法として2つ、特定して開く方法として3つ紹介しました。
Pythonはたくさんのモジュール、機能があり、この他にもメモリ上限を超えないようにファイルを開く方法があるかと思います。
また、daskというライブラリは、大量のデータを扱うことに特化したライブラリとしてdaskというものもあるようです。勉強中です。