Remrinのpython攻略日記

python3に入門しました。python3についてあれこれとサンプルコードとか。

ニュートン法

ニュートン法平方根を求める。
 
・まずは√2 

def a(x):
    return (x**2 + 2) / (2 * x)

x = 2   # 初期値(0以外の値)
for i in range(6):
    x = a(x)
    print(x)
    
# 1.5
# 1.4166666666666667
# 1.4142156862745099
# 1.4142135623746899
# 1.414213562373095
# 1.414213562373095

 
ニュートン法は接線を利用することで解の近似を行っている。
f:id:rare_Remrin:20170612170636p:plain
 
続いて√3

def a(x):
    return (x**2 + 3) / (2 * x)

x = 2   # 初期値(0以外の値)
for i in range(8):
    x = a(x)
    print(x)
    
# 1.75
# 1.7321428571428572
# 1.7320508100147274
# 1.7320508075688772
# 1.7320508075688774
# 1.7320508075688772
# 1.7320508075688774
# 1.7320508075688772

 
誤差のせいか収束しないので、decimalを

from decimal import Decimal

def a(x):
    return Decimal(Decimal(x)**2 + s) / (2 * Decimal(x))

s = 3   # √s
x = 2   # 初期値(0以外の値)
for i in range(8):
    x = a(x)
    print(x)
    
# 1.75
# 1.732142857142857142857142857
# 1.732050810014727540500736377
# 1.732050807568877295254353946
# 1.732050807568877293527446342
# 1.732050807568877293527446342
# 1.732050807568877293527446342
# 1.732050807568877293527446342

収束おっけ
 
一般的に、x(new)=x-f(x)/f'(x)を繰り返すとf(x)=0の解の1つが得られる。

# y=f(x)
def f(x):
    return x**2 - 2

# 導関数
def diff(x):
    return (f(x + 0.001) - f(x)) / 0.001

# ニュートン法の漸化式
def a(x):
    return x - f(x) / diff(x)

x = 2  # 初期値
while f(x) > 10**-10:  # 誤差が10**-10になるまで繰り返す
    x = a(x)
    print(round(x, 10))

# 1.5001249688
# 1.4167014195
# 1.4142166238
# 1.4142135635
# 1.4142135624

 
解が複数あったり、あるいは解がなかったりのときは何か工夫しないと。 
 
matplotlibで描いたグラフのコード

def f(x):
    return x**2 - 2

# 導関数
def diff(x):
    return (f(x + 0.001) - f(x - 0.001)) / 0.002

# x=3 における接線の方程式
def tangent(x):
    return diff(3) * (x - 3) + f(3) 

x = np.linspace(-2, 4, 50)
y = f(x) 

plt.plot(x, y)
ax = plt.gca()
ax.spines['top'].set_position(("data",0))
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.xaxis.set_ticks_position("top") 

fn = "Times New Roman"

plt.plot([3, 3], [2, f(3)], "k:")
plt.plot([1, 4], [tangent(1), tangent(4)], color="r")
plt.text(2.9, -1, "$x_0$")
plt.text(1.8, -1, "$x_1$")
plt.title("$y=x^2-2$")

plt.xlim(-2, 4)
plt.show()

 
f:id:rare_Remrin:20170612170636p:plain

ビット操作

pythonのビット操作について
 

ビットand, or, xor, not

& :ビットand 
| :ビットor
^ :ビットxor
~ :ビットnot(反転)

 

print(bin(0b0101 & 0b1100))  # 0b100
print(5 & 12)                # 4
  
print(bin(0b0101 | 0b1100))  # 0b1101
print(5 | 12)                # 13

print(bin(0b0101 ^ 0b1100))  # 0b1001
print(5 ^ 12)                # 9

print(bin(~0b0101))          # -0b110
print(~5)                    # -6

 

ビットシフト

>> :右シフト
<< :左シフト

 

print(bin(0b111 << 2))  # 0b11100
print(7 << 2)           # 28

print(bin(0b111 >> 2))  # 0b1
print(7 >> 2)           # 1

右シフトはpow(2, n)での除算、左シフトはpow(2, n)の乗算にあたる。
 
以上はすべて、全ビット対象。
 

特定のビットの操作

下からn桁目のbitを取り出す A & pow(2, n)
下からn桁目のbitのみを1にする A | pow(2, n)
下からn桁目のbitのみを0にする A & ~pow(2, n)
下からn桁目のbitを反転する A & ~pow(2, n) | ~(A & pow(2, n))

反転は他にいい方法ありそう
 

print(bin(0b1111 & pow(2, 1)))  # 0b10

print(bin(0b0000 | pow(2, 3)))  # 0b1000

print(bin(0b1111 & ~pow(2, 1))) # 0b1011

print(bin(0b1111 & ~pow(2, 2) | 
        ~(0b1111) & pow(2, 2))) # 0b1011

 

演算子の優先順位

** べき乗
~, +, - 単項の+, -
*, /, //, % 乗除
+, - 二項間の+-
>>, << ビットシフト
& ビットand
^, | ビットxor, or
<, >, <=, >= 比較演算子
==, !=, <> 等価演算子
=, +=など 代入, 累算代入
is, not is 同一オブジェクト比較
in, not in 存在
and, not, or 論理演算子

 
べき乗の注意

print(-1**2)  # -1
print(2**-1)  # 0.5
print(-2**-1) # -0.5

 

ビット計算の注意

10進数と2進数の計算は10進数で値が戻る。

print(type(0b11 + 1))     # <class 'int'>
print(type(0b11 + True))  # <class 'int'>

 
こう見ると、python標準でのビット操作はめんどくさそう。
NumPyでdtype=boolでやってみると

import numpy as np

a1 = np.ones(8, dtype=bool)
a2 = np.array([1, 0, 1, 0, 1, 1, 0, 0], dtype=bool)
print(a1)       # [ True  True  True  True  True  True  True  True]
print(a2)       # [ True False  True False  True  True False False]
print(a2.sum()) # 4

# ビットand
print(a1 & a2)  # [ True False  True False  True  True False False]

# ビットor
print(a1 | a2)  # [ True False  True False  True  True False False]

# ビットxor
print(a1 ^ a2)  # [False  True False  True False False  True  True]

# ビットnot
print(~a2)      # [False  True False  True False False  True  True]

# 特定のビット反転(右からn桁目)
n = 2
a2[7-n] = not a2[7-n]
print(a2)       # [True False  True False  True False False False]

scikit-learn 最小二乗法

scikit-learnで最小二乗法 
 
y=sin(x)に誤差をランダムで付加した100個のデータを準備し、
学習用:テスト用=8:2に分けて実行。
 

import numpy as np
import matplotlib.pyplot as plt
#sklearn.cross_validationは非推奨、次のヴァージョンで廃止予定
#sklearn.model_selectionへ移動
from sklearn.model_selection import train_test_split as tts

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline

from sklearn.metrics import mean_squared_error as mse

# y=sin(x)に誤差を付加したデータを作る
data_size = 100    # サンプル数
x_orig = np.linspace(0, 1, data_size)

def f(x):
    return np.sin(2 * np.pi * x)

x = np.random.rand(data_size)[:, np.newaxis]
y = f(x) + np.random.rand(data_size)[:, np.newaxis] - 0.5
x_train, x_test, y_train, y_test = tts(x, y, test_size=0.8)

plt.plot(x_orig, f(x_orig), ":")
plt.scatter(x_train, y_train)
plt.xlim( (0, 1) )

f:id:rare_Remrin:20170528230031p:plain
 

# 最小二乗法の次数を変えたサブプロットを描画
row, column = 2, 3    # サブプロットの行数、列数
fig, axs = plt.subplots(row, column, figsize=(8, 6))
for deg, ax in enumerate(axs.ravel()):
    # パイプラインを作る
    e = make_pipeline(PolynomialFeatures(deg), LinearRegression())
    # 学習
    e.fit(x_train, y_train)
    # 予測
    px = e.predict(x_orig[:, np.newaxis])
    
    ax.scatter(x_train, y_train)
    ax.plot(x_orig, px, "m")
    ax.set(xlim=(0, 1), ylim=(-2, 2), yticks=([-1, 0, 1]),
           title="degree={}".format(deg))

plt.tight_layout()
plt.show()

f:id:rare_Remrin:20170528230045p:plain
 

train_error = np.empty(10)
test_error = np.empty(10)

# 最適な次数を探す
for deg in range(10):
    # 学習と予測
    e = make_pipeline(PolynomialFeatures(deg), LinearRegression())
    e.fit(x_train, y_train)
    # 誤差の評価
    train_error[deg] = mse(y_train, e.predict(x_train))
    test_error[deg] = mse(y_test, e.predict(x_test))    

plt.plot(np.arange(10), train_error, "--", label="train")
plt.plot(np.arange(10), test_error, label="test")
# 最適次数を表示
plt.plot(test_error.argmin(), test_error.min(), "ro")
plt.text(test_error.argmin()+0.5, test_error.min(),
         "best fit", color="r", backgroundcolor="w", 
         fontsize=14)

plt.ylim( (0, 1) )
plt.legend(fontsize=14)
plt.show()

f:id:rare_Remrin:20170528230106p:plain
 
3次関数での近似が良さそう。
選んだ関数がsinなのでなんとなくうなづける。
今回のデータサイズだと、3~6次は大差なさそう。
乱数の初期値(seed)がかわると、4次や5次が最適となることもあった。
ただ、7次以上は学習用データで過学習をしてしまい、
テスト時の誤差が非常に大きくなることも。

最後のグラフのブルーのtrainは、多項式近似のため右肩下がりのはず。
 
参考:
みんなのpython 第4版

遺伝的アルゴリズム

遺伝的アルゴリズムの例
 
①初期の遺伝子プール(個体群)を決める
②一定確率で遺伝子を交差させ、個体数を2倍にする
③一定確率で突然変異を起こす
④重みづけをして評価し、生き残る個体を選ぶ
⑤一定回数または目標値に達するまで②に戻る
 
16桁の2進数の数を正解として、遺伝的アルコリズムで正解をさがす例。

import matplotlib.pyplot as plt
import numpy as np
import random

def mating(pool):
    """ランダムに選ばれる両親から交差し、個体数を2倍にする。
       子のみのnewpoolを戻り値とする"""
    newpool = []
    for i in range(len(pool)):
        p1, p2 = random.choice(pool), random.choice(pool)
        # 遺伝子が交差する地点を一か所選ぶ
        crosspoint = int(random.random()*16)
        div = pow(2, crosspoint)
        # 遺伝子を交差させて入れ替える
        f1 = (p1 // div)*div + p2 % div 
        f2 = (p2 // div)*div + p1 % div
        newpool += [f1, f2]
    return newpool

def mutation(pool):
    "突然変異。20%の個体に、1部位の突然変異が起きる"
    for i in range(len(pool)//5):
        p_index = int(random.random()*len(pool))
        p1 = pool[p_index]
        mutate_point = int(random.random()*16)
        bit = pow(2, mutate_point)
        # bitを1つだけ反転させる
        p_bit = p1 & bit
#        p1 = p1 - p_bit + (bit - p_bit)
        p1 = p1 + bit - p_bit * 2
        pool[p_index] = p1
    return
    
def selection(pool) ->list:
    "2倍に増えた個体数をもとに自然淘汰する。上位から40%、下位から10%を残す"
    evaluate = []
    for i in pool:
        e = 16 - bin((i^d)).count("1")
        evaluate.append( (i, e) )
    evaluate.sort(key=lambda x:x[1], reverse=True)
    # 上位と下位に分ける
    l = len(evaluate)
    upper, lower = evaluate[:l//2], evaluate[l//2:]
    # random.shuffleはin placeで戻り値はNone
    random.shuffle(upper)
    random.shuffle(lower)
    survived = upper[:int(l*0.4)] + lower[:data_size - int(l*0.4)]
    return [i for i, e in survived]

def random_selection(pool):
    "自然淘汰をせずにランダムで個体を残す"
    random.shuffle(pool)
    return pool[:data_size] 

def print_evaluate(pool, generation, flag):
    """合致するビット数を評価 
       flat=True  のときは、各個体の評価を表示.
       flag=False のときは、世代数と平均のみ表示"""
    evaluate = []
    for i in pool:
        evaluate.append(16 - bin((i^d)).count("1"))
    if flag:
        for i, e in zip(pool, evaluate):
            print(bin(i)[2:].zfill(16),":", e)
    print("第{}世代".format(generation), end=" ")
    score = sum(evaluate)/len(evaluate)
    print("平均:", score, "\n"*flag)
    return score

def main(pool, select=True, print_flag=True):
    "世代を重ねるメインループ"
    generation = 0
    score = []
    score.append(print_evaluate(pool, generation, print_flag))
    for i in range(40):
        pool = mating(pool)
        mutation(pool)
        if select:
            pool = selection(pool)
        else:
            pool = random_selection(pool)
        generation += 1
        score.append(print_evaluate(pool, generation, print_flag))
    return score

if __name__ == "__main__":        
    d = 0b0101011000001111
    data_size = 20
    # initialize
    #pool = [int(random.random()*256) for i in range(data_size)]
    pool = np.random.randint(65536, size=data_size, dtype=int) 
    pool2 = pool.copy()
    
    scores = main(pool, select=True, print_flag=False)
    scores2 = main(pool2, select=False, print_flag=False)
    
    x = np.arange(len(scores))
    plt.plot(x, np.array(scores)/16, "-", label="selection")
    plt.plot(x, np.array(scores2)/16, ":", label= "without selection")
    plt.legend(fontsize=14)
    plt.ylim(0, 1)

 
print_flag=Trueとすると、各世代の個体を表示。

1001100010111001 : 6
1000000011110001 : 4
1001000111110001 : 4
1000100010001001 : 7
1000000011110001 : 4
1001100011110001 : 4
1001000111110001 : 4
1001000111110001 : 4
1000000010111010 : 6
1001000011010001 : 6
1001000111110001 : 4
1001100010111010 : 6
1001000010111011 : 8
1001000111110001 : 4
1001000110110001 : 5
1001100010111011 : 7
1000000010101001 : 7
1001000110110000 : 4
1001000011111010 : 6
0011000010110001 : 640世代 平均: 5.3 

 
print_flag=Falseとすると、各世代の平均のみ表示

0世代 平均: 8.41世代 平均: 9.252世代 平均: 10.853世代 平均: 10.954世代 平均: 11.75世代 平均: 12.2 
・・・
第37世代 平均: 15.938世代 平均: 15.9539世代 平均: 16.040世代 平均: 15.8 

 
f:id:rare_Remrin:20170528222725p:plain
 
今回の例だと20世代ほどでほぼ正解に辿りついている。
 
課題:
numpyの2進数の扱い方がよくわからず、あまり使えていない。
それ以前にpythonで2進数をどう扱うか勉強が必要。
特定のbitだけ演算をする方法を調べ中。
bit演算の優先順位なども頭に入れないと。
 
自然選択なしで、多数の試行を重ねて結果をヒストグラムなりにしても面白そう。
 

NumPyのboolで

無理にビット演算をするのをやめて、NumPyのdtype=boolで書き直してみた。

import matplotlib.pyplot as plt
import numpy as np
import random

def mating(pool):
    """ランダムに選ばれる両親から交差し、個体数を2倍にする。
       子のみのnewpoolを戻り値とする"""
    newpool = []
    for i in range(data_size):
        # ランダムで親を選ぶ
        p1, p2 = random.choice(pool), random.choice(pool)
        # 遺伝子が交差する地点を一か所選ぶ
        crosspoint = int(random.random()*16)
        f1 = np.hstack((p1[:crosspoint], p2[crosspoint:]))
        f2 = np.hstack((p2[:crosspoint], p1[crosspoint:]))
        # 遺伝子を交差させて入れ替える
        newpool += [f1, f2]
    return np.array(newpool)

def mutation(pool):
    "突然変異。20%の個体に、1部位の突然変異が起きる"
    for i in range(len(pool)//5):
        p_index = int(random.random()*len(pool))
        p1 = pool[p_index]
        mutate_point = int(random.random()*16)
        p1[mutate_point] = ~p1[mutate_point]
        pool[p_index] = p1
    return

def selection(pool):
    "2倍に増えた個体数をもとに自然淘汰する。上位から40%、下位から10%を残す"
    # 正解数で降順にソート
    pool = sorted(pool, key=lambda x:(x==d).sum(), reverse=True)
    # 上位と下位に分ける
    l = len(pool)
    upper, lower = pool[:l//2], pool[l//2:]
    # random.shuffleはin placeで戻り値はNone
    random.shuffle(upper)
    random.shuffle(lower)
    # 上位から80%,下位から20%選ぶ
    survived = np.vstack((upper[:int(l*0.4)], lower[:data_size - int(l*0.4)]))
    return survived

def random_selection(pool):
    "2倍に増えた個体数から自然淘汰をせずにランダムで個体を残す"
    random.shuffle(pool)
    return pool[:data_size] 

def print_evaluate(pool, generation, flag):
    """合致する数を評価 
       flat=True  のときは、各個体ごとの評価を表示.
       flag=False のときは、世代数と平均のみ表示"""
    # 正解数をカウント
    evaluate = (pool==d).sum(axis=1)
    # 表示フラグがTrueなら各個体を表示
    if flag:
        for i, e in zip(pool, evaluate):
            print(i, ":", e)
    # flagに関係なく、各世代の平均を表示
    print("第{}世代".format(generation), end=" ")
    score = sum(evaluate)/(data_size * 16)
    print("平均:", score, "\n"*flag)
    return score

def main(pool, select=True, print_flag=True):
    "世代を重ねるメインループ"
    generation = 0
    score = []
    score.append(print_evaluate(pool, generation, print_flag))
    for i in range(20):
        pool = mating(pool)
        mutation(pool)
        if select:   # 自然淘汰ありのとき
            pool = selection(pool)
        else:        # 自然淘汰なしのとき
            pool = random_selection(pool)
        generation += 1
        score.append(print_evaluate(pool, generation, print_flag))
    return score

if __name__ == "__main__":        
    # initialize
    d = np.array([0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1], dtype=bool)
    data_size = 20
    pool = np.random.randint(0, 2, (data_size, 16), dtype=bool) 
    pool2 = pool.copy()

    # 自然淘汰あり    
    scores = main(pool, select=True, print_flag=True)
    # 自然淘汰なし
    scores2 = main(pool2, select=False, print_flag=False)
    
    # グラフ表示    
    x = np.arange(len(scores))
    plt.plot(x, np.array(scores), "-", label="selection")
    plt.plot(x, np.array(scores2), ":", label= "without selection")
    plt.legend(fontsize=14)
    plt.ylim(0, 1)
    plt.title("Genetic Argorithm", fontsize=14)
    plt.show()

 

matplotlib グラフの設定

matplotlibのグラフの設定について
 

日本語の表示

日本語を表示する場合は、表示するごとにfontproperties = fpをセットする。

from matplotlib.font_manager import FontProperties
fp = FontProperties(fname='C:\WINDOWS\Fonts\msgothic.ttc', size=14)

plt.title("折れ線グラフ", fontproperties=fp)

 

グラフのタイトル

 plt.title(str, loc='center')
・表示位置

loc='center' 中央。デフォルト
loc='left' 左寄せ
loc='right' 右寄せ

 

表示範囲(変域)

・両軸の表示範囲の取得
 xmin, xmax, ymin, ymax = plt.axis()
 
・両軸の表示範囲の設定
 plt.axis(xmin, xmax, ymin, ymax)
 
・軸ごとに範囲を設定

plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)
plt.xlim(xmax=10)     # xminは変えずに、xmaxだけ変更
plt.xlim(xmin=0)      # xmaxは変えずに、ximnだけ変更

 

 ax = plt.gca() # get current axis
 ax.xaxis.set_ticks_position("bottom") # 目盛りは軸の下
 
・枠と目盛りを消去
 plt.axis("off")
 

目盛り

・現在の横軸の位置配列、ラベル配列の取得
 xlocs, xlabels = plt.xticks()
・現在の縦軸の位置配列、ラベル配列の取得
 ylocs, ylabels = plt.yticks()
 
・目盛りの配分

plt.xticks(-4, 4, 9)   # -4から4まで9個(8等分)
plt.sticks([-1, 0, 1]) # -1, 0, 1の3つ

 
・目盛りの配分とラベルを同時に設定
 plt.xticks(xlocs, xlabels, color, rotation) # xlocs:位置の配列 xlabels:ラベルの配列
 

グラフの枠線

・枠線を消す

ax = plt.gca() # get current axis
ax.spines["right"].set_color("none")  # 右消し
ax.spines["left"].set_color("none")   # 左消し
ax.spines["top"].set_color("none")    # 上消し
ax.spines["bottom"].set_color("none") # 下消し

 
・枠線の色を変える
 上記でset_color("blue")など

凡例

plt.plot(x, y, color=~, label="y=sin(x)") # 凡例の設定
plt.legend(loc="upper_left")               # 凡例の表示

 

グリッドを表示する

 plt.grid(color='gray')
 

水平線、垂直線、直線

水平線をまとめて引く
 plt.hlines(seq, xmin, xmax, colors="k", linestyle="solid", label="")
 
・垂直線をまとめて引く
 plt.vlines(seq, ymin, ymax, colors="k", linestyle="solid", label="")
 
・直線を1本引く
 plt.plot({x1, x2], [y1, y2])
 
・折れ線を引く
 plt.plot(seqx, seqy)

・グラフの大きさ
 inchで指定
 plt.figure(figsize=(width,height), dpi=80)
 
・サブプロットに分ける

# グラフエリアを縦2、横3に分割してその1番目
plt.subplot(2, 3, 1)
plt.plot(x, y1)        # そのサブエリアに描画

# グラフエリアを縦2、横3に分割してfig配列、ax配列に格納
fig, ax = plt.subplots(2, 3)
ax[0, 0].plot(x, y)    # その0行0列目のサブエリアにプロット

# サブエリアの大きさの比率を変える
from matplotlib import gridspec
gs = gridspec.GridSpec(2, 2, width_ratios=(6, 1), height_ratios=(4, 1))
plt.subplot(gs[0, 0]).plot(x, y) # 0行0列目にプロット

・グラフの塗りつぶし
 plt.fill(x, y, color)
 

キーワードいろいろ

・線種linestyle, ls

solid - 実線
dashed -- 破線
dashdot -. 破点線
dotted : 点線
(offset, on-off-dash-seq)    

・線の幅 linewidth, lw
 floatで単位はpoint
 

サンプル

サンプル1

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
fp = FontProperties(fname='C:\WINDOWS\Fonts\msgothic.ttc', size=14)

x = np.linspace(-np.pi, np.pi*1.1, 100)
plt.plot(x, np.cos(x), "r", label="y=cos(x)")
plt.plot(x, np.sin(x), "olive", label="y=sin(x)")
plt.title("三角関数", loc="center", fontproperties=fp)

plt.xlim(xmin=-np.pi)
plt.ylim(-1.1, 1.1)
xtick = np.array(["-π", "-1/2π", "0", "1/2π", "π"])
locs = np.linspace(-np.pi, np.pi, 5)
plt.xticks(locs, xtick, color="c", fontsize=14, rotation=30)
plt.yticks([-1, 0, 1])

ax = plt.gca()                        # get current axis
ax.spines["right"].set_color("none")  # 右枠消し
ax.spines["top"].set_color("none")    # 上枠消し
ax.spines["left"].set_color("m")      # 左枠をマゼンダに
ax.spines["bottom"].set_color("c")    # 下枠をシアンに
         
plt.vlines(np.linspace(-np.pi, np.pi, 5),
           -1.1, 1.1, "c",linestyle=":", lw=1)
plt.hlines([0], -np.pi, np.pi*1.1, "m", linestyle=":", lw=1)

plt.legend()    # 凡例の表示
plt.show()

f:id:rare_Remrin:20170527105105p:plain
 
サンプル2

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
fp = FontProperties(fname='C:\WINDOWS\Fonts\msgothic.ttc', size=14)
from matplotlib import gridspec

x = np.random.randn(1000)
y = np.random.randn(1000)

gs = gridspec.GridSpec(2, 2, width_ratios=(6, 1), height_ratios=(4, 1))
ax = [plt.subplot(gs[0, 0]), plt.subplot(gs[0, 1]), plt.subplot(gs[1, 0])]

# メインの散布図
ax[0].set_title("散布図", fontproperties=fp)
ax[0].scatter(x, y, c="c", alpha=0.5) # 不透明度0.5
ax[0].grid()                          # グリッド表示

# 右側のヒストグラム
ax[1].hist(y, 50, orientation="horizontal")
ax[1].invert_xaxis()       # 左右反転
ax[1].yaxis.tick_right()   # 縦軸の目盛りを右側に配置

# 下のヒストグラム
ax[2].hist(x, 100)
ax[2].tick_params(labelbottom="off")

plt.tight_layout()
plt.show()

f:id:rare_Remrin:20170527105118p:plain
 
サンプル3

import matplotlib.pyplot as plt
import numpy as np

# 3x3の9個のサブプロットに分割
fig, axs = plt.subplots(3, 3, figsize=(8, 6))

t = np.linspace(0, np.pi*2, 100)
for i in range(3):
    for j in range(3):
        axs[i, j].plot(np.cos(t*(i+1)), np.sin(t*(j+1)))

f:id:rare_Remrin:20170527114609p:plain
 
サンプル4

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-2, 2, 100)

# 2x3の6個のサブプロットに分割
for i in range(5):
    plt.subplot(2, 3, i+1)
    plt.plot(x, x**i)
    plt.title("y=x^{}".format(i))
plt.tight_layout()
plt.show()

f:id:rare_Remrin:20170527114600p:plain
 

例外処理

pythonの例外処理について。
 
例外が発生すると例外オブジェクトを作る。
すべての例外オブジェクトはExceptionクラスを継承している。
基本形は

try:
    処理
except:
    例外発生時の処理

 
例外処理の書式

except すべての例外を捕捉
except 例外クラス名 捕捉する例外を指定
except 例外クラス名 as ~ 捕捉する例外を指定し、変数に代入
else 例外が発生しなかった時の処理
finally 例外の有無にかかわらず行う処理

例外のクラス指定はサブクラスまで含めて判定
 
・エラーが捕捉されなかった場合は、トレースバックを表示。
 
・例外処理のメリット
 エラーの発生位置とエラー処理を分離できる。

・tryブロックはできるだけ短くする。
 長いコードを記述すると、意図せぬ部分からの例外をとらえてしまう。

・tryブロックから呼び出された関数の例外もとらえる。
 

raise

・自作のクラスなどでは、意図的に例外を発生させることができる。

# dictクラスを継承し、strだけキーにできるようにする
class StrDict(dict):                    
    def __setitem__(self, key, value):
        if not isinstance(key, str):
            raise ValueError("Key must be str.")
        super().__setitem__(key, value)
        
d1 = StrDict()
#d1[0] = 0  # ValueError: Key must be str.

既存の例外クラスを継承して、独自の例外クラスを作れる。 
 

tracebackモジュール

例外発生時のトレースバックを操作でき、例外が発生しても処理を続けられる。

import traceback

d1 = {1:"Jan", 2:"Feb", 5:"may"}
err = []
for i in range(6):
    try:
        print(d1[i])
    except:
        err.append(traceback.format_exc()) # 例外を文字列として保持
print(len(err))
#print(err)   # エラー内容を確認するとき表示

# Jan
# Feb
# may
# 3

traceback.print_exc()で例外を表示できる。

モジュール

1つのモジュールは~.pyという1つのスクリプトファイルに相当する。

__name__

スクリプトファイルは単独のプログラムとしても動作するし、他のプログラムやモジュールから呼び出して使うこともできる。
・__name__属性
 単独のプログラムとして起動したときは"__main__"が代入される。
 モジュールとして呼ばれたときはファイル名の.pyの前が代入される。
 
 if __name__ == "__main__":
 とすると、単独プログラムで実行された時の動作を記述できる。
 

importの挙動

import math

a = 1

def func():
    return 100

class Test(int):
    pass    

print("__name__は", __name__)  # __name__は __main__

if __name__ == "__main__":
    print("testmodule.pyを起動しました")
else:
    print("testmodule.pyをimportしました")

 
これをtestmodule.pyとして保存し、他のプログラムからimportしてみると

import testmodule as tm
# __name__は testmodule
# testmodule.pyをimportしました

a = 2

# モジュール変数,関数,クラスはモジュール名をつける
print(tm.a, a)             # 1 2
print(tm.func())           # 100

c = tm.Test("3")

#importしたモジュールでimportしたモジュールが使える
d = tm.math.sqrt(400)

print(c, d)                # 3 20.0

 
複数モジュールのimportはもちろん、多重importもできるが、
関数名、クラス名が同一のものがある場合は、あとからimportしたモジュールで上書きされる。
from module import * のようにすべての関数をimportするときは注意。
 

importの書き方

import module シンプルな書き方
import module as mo moduleをimportし、moという名で扱う
import m1, m2, ... カンマ区切りでmoduleを列挙できる
from module import f1, f2, ... カンマ区切りで関数,クラスを列挙できる
from module import * すべての関数をimportし、module名なしで使う
from module import func func関数だけをimportし、module名なしで使う

 

パッケージ

 スクリプトファイルをフォルダでまとめると、複数のモジュールをひとまとまりのパッケージとして扱える。
 パッケージとして使うフォルダには__init__.pyという名前のファイルを設置する。
 __init__.pyファイルは空ファイルでもいいし、初期化用のコードを書いてもよい。
 階層は何層にも重ねることができる。
 
 package1フォルダのmodule1ファイルをimportするときは、
 import package1.module1 などとする。
 

モジュールの検索順

1 起動したスクリプトファイルのあるフォルダ
2 環境変数PYTHONPATHに設定されているフォルダ
3 標準ライブラリのフォルダ
4 追加モジュールのフォルダ(site-packagesフォルダ)
 
PyPI(Python Package Index)で多数のモジュールが登録されている。