ループ処理
ループ処理について。
for | イテレータ(シーケンスなど)を添える |
---|---|
while | イテレータの代わりに条件式。while True, while 1という形をよく見る |
break | ブロックから抜ける |
continue | それ以降のループブロックを実行せず、最初に戻る |
else | forブロックやwhileブロックで使う時は、ループの実行後の処理。ただし、breakすると無視 |
x = 0 while x < 10: x +=1 print(x, end=" ") else: print("finished") # 1 2 3 4 5 6 7 8 9 10 finished x = 0 while x < 10: x +=1 print(x, end=" ") if x == 5: break else: print("finished") print("ended") # 1 2 3 4 5 ended x = 0 while x < 10: x +=1 if x % 2 == 0: continue print(x, end=" ") else: print("finished") # 1 3 5 7 9 finished
・反復中のリスト自身を改変したいときは、スライスをループ要素とする。
nlist = list(range(6)) for i in nlist[:]: if i % 2 == 0: nlist.append(i) print(nlist) # [0, 1, 2, 3, 4, 5, 0, 2, 4]
ネストしたループから一気に抜けるには
・フラグ管理
・エラーをraiseして、try/catch
・関数化など。
def break_test(escape): counter = 0 for i in range(10): for j in range(10): counter += 1 print(counter, end=" ") if i * j == escape: print("i={}, j={}".format(i,j)) return break_test(8) # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 i=1, j=8
enumerate()
ループカウンタとして使える。
回数や条件を判断してループを抜けられる。
zip()
2つのシーケンスを使ったループが作れる。
for i, j in zip(["A", "B", "C"], [1, 2, 3]): print(i, j) # A 1 # B 2 # C 3
3つのif
ifの使い方について。
「1000未満の3または5の倍数の和を求める」場合
○条件分岐
limit = 1000 result = 0 for i in range(limit): if i%3 == 0 or i%5 ==0: result += i print(result)
○内包表記
limit = 1000 result = sum([x for x in range(limit) if x%3 == 0 or x%5 ==0 ]) print(result)
○3項演算子
limit = 1000 result = 0 for i in range(limit): result += i if i%5 == 0 or i%3 ==0 else 0 print(result)
NumPyの使い方(19) 数え上げ
NumPyの数え上げ(要素のカウント)について。
0,1のみのデータから1や0の個数を数える方法
import numpy as np a1 = np.array([0, 1, 1, 0, 1, 0, 0, 0]) # 1の個数をカウントする例 print(np.count_nonzero(a1)) # 3 print(a1.sum()) # 3 print(len(np.where(a1 != 0)[0])) # 3 # 0の個数をカウントする例 print(np.count_nonzero(1 - a1)) # 5 print(len(a1) - a1.sum()) # 5
0と1を同時にカウントする例
# 例1 print(np.unique(a1, return_counts=True)) # (array([0, 1]), array([5, 3])) unique, count = np.unique(a1, return_counts=True) print(dict(zip(unique, count))) # {0: 5, 1: 3} # 例2 from collections import Counter c = Counter(a1) print(c) # Counter({0: 5, 1: 3}) # 例3 c = np.bincount(a1) print(c) # [5 3]
CounterについてはCollectionsライブラリを参照
0, 1以外のデータからのカウント
a2 = np.array([0, 1, 2, 4 , 2, 4, 4]) # np.uniqueは存在しない値をスルー u, c = np.unique(a2, return_counts=True) print(dict(zip(u, c))) # {0: 1, 1: 1, 2: 2, 4: 3} # np.bincountは存在しない値を0個とする print(np.bincount(a2)) # [1 1 2 0 3]
素数
def gen2(start=2, stop=1000000): pr = max(1, start - 1) while True: while pr < stop: pr += 1 if all(pr%x != 0 for x in range(2, int(pr**0.5) + 1)): break yield pr g = gen2() for i in range(10): print(next(g), end=" ") print() # 2 3 5 7 11 13 17 19 23 29
# 素因数分解 def factorial(n): if n in [0, 1]: return [n] result = [] stop = int(n**0.5) + 1 g = gen1() pr = next(g) while pr < stop: if n % pr == 0: result.append(pr) n //= pr continue pr = next(g) if n > 1: result.append(n) return result g = gen2() for i in range(10): print(next(g), end=" ") print() for i in range(0, 10): print(i, factorial(i)) # 0 [0] # 1 [1] # 2 [2] # 3 [3] # 4 [2, 2] # 5 [5] # 6 [2, 3] # 7 [7] # 8 [2, 2, 2] # 9 [3, 3] print(10**6, factorial(10**6)) # 1000000 [2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5] import random r = random.randint(1, 10**7) # 5197227 [3, 7, 379, 653]
ジェネレータ
ジェネレータについて。
通常の関数のreturnの部分をyieldとするとジェネレータを自作できる。
偶数を生成するジェネレータ
def gene(): even = 0 while True: yield even even += 2 e = gene() for i in range(5): print(next(e))
素数を生成するジェネレータ
def gen2(start=2, stop=1000000): pr = max(1, start - 1) while True: while pr < stop: pr += 1 if all(pr%x != 0 for x in range(2, int(pr**0.5) + 1)): break yield pr g = gen2() for i in range(10): print(next(g), end=" ") # 2 3 5 7 11 13 17 19 23 29
フィボナッチ数列を生成するジェネレータ
def fib_gene(): n = 1 while True: f = ((1 + 5**0.5) / 2)**n / 5**0.5 + 0.5 yield int(f) n += 1 f = fib_gene() for i in range(10): print(next(f), end=" ") # 1 1 2 3 5 8 13 21 34 55
・ジェネレータは無限ループを作れる。
というか、無限ループに陥ることもある。
・値をすべて取り出すとStopIterationのエラーが発生。
def gene(): week = ["月", "火", "水", "木", "金", "土", "日"] for day in week: yield day day = gene() for i in range(7): print(next(day), end=" ") # 月 火 水 木 金 土 日 day = gene() for i in range(15): print(next(day), end=" ") # StopIteration
StopIterationを回避するには取り出す個数を確認するか、whileループを使うなど。
def gene(): week = ["月", "火", "水", "木", "金", "土", "日"] while True: for day in week: yield day day = gene() for i in range(15): print(next(day), end="") # 月火水木金土日月火水木金土日月
・値の取り出し
・next()関数
・__next__()メソッド
・イテレートする
・max(), list()など
・値の代入
send()
PILの使い方(1)
PIL(Python Imaging Library)の使い方について。
画像の読み込み
img = Image.open("filepass")
のように、ファイルを読み込む。
この時点では参照されているだけで、必要になってからデータを読み込む。
下の例では作業ディレクトリの下のdataフォルダに該当ファイルがなければ
FileNotFoundError: [Errno 2] No such file or directory: 'data/img01.png'
というエラーが出る。
from PIL import Image # 画像を読み込む img = Image.open( "data/img01.png" ) print(type(img)) # <class 'PIL.PngImagePlugin.PngImageFile'> print(img.size) # (320, 240) print(img.mode) # RGBA img.show() # Winodwsで関連付けされているアプリで画像を表示
変換
# グレースケールに変換 gray_img = img.convert("L") gray_img.show()
保存
パス、ファイル名、形式を指定して保存
gray_img.save( "img/gray.jpg", quality=90 )
サムネイル化(縮小)
resize()は戻り値として新しい画像オブジェクトを返す。拡大もできる。
img.thumnail()で元の画像オブジェクトそのものを縮小。
元画像をまた使うならばcopy()してからサムネイル化。
img.thumbnail( (160, 120) ) img.show()
回転、反転
・transpose() 上下・左右反転、90度単位の回転
・rotate() 角度指定の回転、
resampleオプションで近傍法の種類を選べる。
左右反転 | transpose(Image.FLIP_LEFT_RIGHT) |
上下反転 | transpose(Image.FLIP_TOP_BOTTOM) |
90度回転 | transpose(Image.ROTATE_90) |
180度回転 | transpose(Image.ROTATE_180) |
270度回転 | transpose(Image.ROTATE_270) |
90度回転? | transpose(Image.TRANSPOSE) |
角度指定の回転 | img.rotate(10)など |
貼り付け
paste(img_obj, box, mask)
img_obj貼り付けるイメージオブジェクト
box:2次元タプルで左上の座標、または4次元タプルで左上、右下の座標
mask:マスクの指定
img = Image.open("data/img01.png") thum = img.copy() thum.thumbnail( (160, 120) ) img.paste(thum.transpose(Image.FLIP_TOP_BOTTOM), (0, 0)) img.paste(thum.convert("L"), (0, 120)) img.paste(thum, (160, 120, 320, 240)) img.show()
色の取り出し、変換
split()でR, G, BやA(不透明度)を取り出せる。
merge()で再構成
img = Image.open("data/img01.png") r, g, b, a= img.split() # RGB形式なら r, g, b= img.split() img = Image.merge("RGBA", (r, b, g, a)) img.save("data/img01_rgba.png") img.show()
ブドウ?紫イモ?
カラフルすぎるソフトクリームだと気持ち悪いので折り紙画像を準備して
img = Image.open("data/img03.png") thum = img.copy() thum.thumbnail( (160, 120) ) r, g, b= thum.split() # R, G, Bを適当に入れ替えてみる img.paste(Image.merge("RGB", (r, g, b)), (0, 0)) img.paste(Image.merge("RGB", (g, r, b)), (160, 0)) img.paste(Image.merge("RGB", (r, b, g)), (0, 120)) img.paste(Image.merge("RGB", (b, g, r)), (160, 120)) img.show()
ヒストグラムの表示
import numpy as np import matplotlib.pyplot as plt from PIL import Image img = Image.open("data/img01.png") r, g, b, a= img.split() rh = np.array(r).flatten() gh = np.array(g).flatten() bh = np.array(b).flatten() plt.hist((rh, gh, bh), 20, color=("r", "g", "b"), rwidth=0.9)
フィボナッチ数列
フィボナッチ数列について。
def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b return b print([fib(i) for i in range(10)]) # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
メモ化した場合
fib_memo = {} def fib(n): if n < 3: return 1 if n not in fib_memo: fib_memo[n] = fib(n-1) + fib(n-2) return fib_memo[n] print(fib(10)) # 55
このメモ化だと、fib_memoという辞書が使用後も残ってしまったり、
n>1000だとrecursive errorが出たりする。
@lru_cacheデコレータ(Least-recently-used cache decorator)を使う。
関数の引数と戻り値を記憶し、同じ引数で呼ばれたら関数を実行せずに値を返す。
同じ引数で何度も呼び出す関数だと劇的に速くなる。
counter = 0 def fib1(n): global counter counter += 1 if n in [0, 1]: return 1 return fib1(n - 1) + fib1(n - 2) print(fib1(24)) print(counter, "回の関数呼び出し") # 75025 # 150049 回の関数呼び出し from functools import lru_cache counter = 0 @lru_cache(maxsize=1024) def fib2(n): global counter counter += 1 if n in [0, 1]: return 1 return fib2(n - 1) + fib2(n - 2) print(fib2(24)) print(counter, "回の関数呼び出し") # 75025 # 25 回の関数呼び出し
lru_cacheなしだと関数を15万回呼び出していたが、lru_cacheありだと25回で済む。
一般項の公式
より、ループなしでフィボナッチ数列の項を求めることもできます。
def fib(n): f = ((1 + 5**0.5) / 2)**n / 5**0.5 + 0.5 return int(f) print(fib(10)) # 55
2項間の比を調べてみると
import numpy as np import matplotlib.pyplot as plt x = np.arange(20) y = np.array([fib(i + 1) / fib(i) for i in range(20)]) plt.plot(x, y, "ro-") plt.show()
1.62くらいに収束してます。