該当のコード
data=[(155,48),(154,43),(157,46),(165,58),(161,49),(160,51),(158,44),(175,60),(163,47),(159,43),(159,44),(163,45),(155,44),(149,46),(160,48),(168,49),(163,55),(153,47),(162,51),(160,50),(159,47),(148,42),(165,56),(158,48),(170,54),(161,44)]
bmi = lambda x:round(x[1] / ((x[0]/100) ** 2))
map_obj=map(bmi,data)
print("最小BMI:",min(map_obj),
"最大BMI:",max(map_obj))
これを実行するとエラーになります。
Traceback (most recent call last):
File "t.py", line 6, in <module>
"最大BMI:",max(map_obj))
ValueError: max() arg is an empty sequence
なぜか
5行目のところですでにmapオブジェクト(イテレータ)が消費されているため、メモリからなくなっている。
なので6行目ではmax関数に渡せるものがなくなりエラーになる。
どうすべきか
listを使う
data=[(155,48),(154,43),(157,46),(165,58),(161,49),(160,51),(158,44),(175,60),(163,47),(159,43),(159,44),(163,45),(155,44),(149,46),(160,48),(168,49),(163,55),(153,47),(162,51),(160,50),(159,47),(148,42),(165,56),(158,48),(170,54),(161,44)]
bmi = lambda x:round(x[1] / ((x[0]/100) ** 2))
bmi_list=list(map(bmi,data))
print("最小BMI:",min(bmi_list),
"最大BMI:",max(bmi_list))
Q:何でlist返さないの?
A:イテレータを使うほうがメモリに優しいから
listを返すとなると元のリストから加工したリストを全部メモリに保持しなくてはならない。
イテレータなら都度計算するのでメモリに優しい
比較してみる
for xxx in yyy のように普通のループとして使われるとメモリにどのような差があるのかを調べる。
999999回計算されるアイドルのBMI
リストを使う
from memory_profiler import profile
import time
data = [(155, 48)] * 999999
@profile
def main():
start_time = time.perf_counter()
def bmi(x): round(x[1] / ((x[0]/100) ** 2))
list_obj = list(map(bmi, data))
for result in list_obj:
pass
end_time = time.perf_counter()
elapsed_time = end_time - start_time
print("processed time:", elapsed_time)
main()
listを使わない
from memory_profiler import profile
import time
data = [(155, 48)] * 999999
@profile
def main():
start_time = time.perf_counter()
def bmi(x): round(x[1] / ((x[0]/100) ** 2))
map_obj = map(bmi, data)
for result in map_obj:
pass
end_time = time.perf_counter()
elapsed_time = end_time - start_time
print("processed time:", elapsed_time)
main()
結果
listなし
processed time: 239.2219612
Filename: test.py
Line # Mem usage Increment Occurrences Line Contents
=============================================================
7 26.8 MiB 26.8 MiB 1 @profile
8 def main():
9 26.8 MiB 0.0 MiB 1 start_time = time.perf_counter()
10
11 26.8 MiB -75115.5 MiB 1999999 def bmi(x): round(x[1] / ((x[0]/100) ** 2))
12 26.8 MiB 0.0 MiB 1 map_obj = map(bmi, data)
13 26.8 MiB -37557.8 MiB 1000000 for result in map_obj:
14 26.8 MiB -37557.8 MiB 999999 pass
15 26.8 MiB -0.1 MiB 1 end_time = time.perf_counter()
16 26.8 MiB 0.0 MiB 1 elapsed_time = end_time - start_time
17 26.8 MiB 0.0 MiB 1 print("processed time:", elapsed_time)
listあり
processed time: 238.8371256
Filename: test.py
Line # Mem usage Increment Occurrences Line Contents
=============================================================
7 26.8 MiB 26.8 MiB 1 @profile
8 def main():
9 26.8 MiB 0.0 MiB 1 start_time = time.perf_counter()
10
11 35.2 MiB -92447.4 MiB 1999999 def bmi(x): round(x[1] / ((x[0]/100) ** 2))
12 35.2 MiB 0.0 MiB 1 list_obj = list(map(bmi, data))
13 35.2 MiB 0.0 MiB 1000000 for result in list_obj:
14 35.2 MiB 0.0 MiB 999999 pass
15 35.2 MiB 0.0 MiB 1 end_time = time.perf_counter()
16 35.2 MiB 0.0 MiB 1 elapsed_time = end_time - start_time
17 35.2 MiB 0.0 MiB 1 print("processed time:", elapsed_time)
感想
listを使った場合のほうがMem usageが増えています。
メモリをより動かすのはlistを使ったほうなのでしょうか。
ただ速度面で差はでていないのであまり考えるだけ無駄かも。