応用プログラミング及び実習 2019年度 第3回 Notebook

プログラミング言語 Python (後編)

続 Python とは

Pythonでは,プログラミング言語としての標準機能だけでも複雑な処理を簡潔に書けるようになっているが,拡張機能を組み込むことで(モジュールをインポートするという),様々な応用に向けた高度な処理を簡単にプログラミングできる. そのため,科学技術計算,人工知能・機械学習,統計・データ分析,Webアプリ開発等,様々な分野で活用されている(特に人工知能・機械学習やデータサイエンスの分野で人気である.また,オンラインストレージサービスで有名な Dropbox のコードは全部 Python で書かれたと言われている).

ちょこっと体験してみよう.

In [ ]:
# 科学技術計算のモジュール NumPy の利用例
import numpy as np  # NumPy ライブラリを np という名前で呼び出せるようにインポート

A = np.array( [ [ 2, -1 ], [ 1, 1 ] ] ) # 2 x 2 行列
print("# 行列 A:")
print(A)

B = A @ A.T  # @ はベクトル間の内積,行列間の積. A.T は A の転置行列
print("# 行列 B:")
print(B)

# 固有値と固有ベクトルを求める
lam, P = np.linalg.eig(B)  # lam は固有値のならんだ1次元配列,P は固有ベクトルをならべた2次元配列(行列)
print("# B の固有値:" , lam)
print("# B の固有ベクトルをならべた行列")
print(P)
In [ ]:
# 対角化の逆をやってみる. 結果は B に等しくなるはず
P @ np.diag(lam) @ P.T
In [ ]:
# 乱数で 1000 x 1000 の行列 X をつくる
X = np.random.rand(1000, 1000)
# 行列 Y をつくる.Yは対称行列になる.
Y = X @ X.T
# 固有値と固有ベクトルを求める
lam, P = np.linalg.eig(Y)
print(lam)
In [ ]:
# 画像処理のデモの準備
import os
from IPython.display import Image, display_png
fn = "blackuni3.png"
if not os.path.exists(fn):
    # takataka のWebサーバから画像ファイルをダウンロード
    ! wget https://www-tlab.math.ryukoku.ac.jp/~takataka/course/AProg/blackuni3.png
    # wget のかわりに curl が使える環境では以下の # をはずして実行すればよい
    #! curl -O https://www-tlab.math.ryukoku.ac.jp/~takataka/course/AProg/blackuni3.png
display_png(Image(fn))
In [ ]:
# コンピュータビジョン・画像処理ライブラリ OpenCV の利用例
import cv2 # OpenCV のモジュールをインポート

img = cv2.imread('blackuni3.png')  # 画像を読み込み
if img is None:
    print('File Not Found')
print(img.shape)  # 正しく読み込めたら, img は NumPy の3次元配列になる

img2 = 255 - img                                       # 画素値の反転
img2 = cv2.flip(img2, 1)                          # 画像の左右反転.第2引数の値を 0 や -1 に変えると....
img2 = cv2.resize(img2, (400, 100))   # 幅 400, 高さ 100 にする

cv2.imwrite('hoge.png', img2)  # ファイルに書き出す.拡張子が png だと自動的に PNG フォーマットで保存
In [ ]:
# 作成した hoge.png を表示してみる.
display_png(Image("hoge.png"))
In [ ]:
# 数式処理のモジュール SymPy の利用例
import sympy as sp
from sympy import *
init_printing()   # これを呼んでおくと出力を LaTeX 表記のきれいな数式にしてくれる(読み込みにだいぶ時間かかります)

$e^{x}$ のマクローリン展開

In [ ]:
x = Symbol('x')
series(exp(x), x)
$$\int_{-\infty}^{\infty} e^{-x^2/2} dx$$
In [ ]:
integrate(exp(-x**2/2), (x, -oo, oo))

Python プログラミングの初歩 (3)

この資料中に説明なくページ番号が出てくる場合,教科書のページ番号を表しています.

前回の復習+α

In [ ]:
L = [ 0, "位置", "荷", "酸"]
L += [ 4.649, "碁" ]

以下の実行結果がどうなるか予想してから,実行してみなさい

In [ ]:
L[5]
In [ ]:
L[6]
In [ ]:
L[-1]
In [ ]:
L[1:4]
In [ ]:
L[:2]
In [ ]:
L[3:]
In [ ]:
L[1:4] + L[3:]   # リスト + リスト
In [ ]:
L[1:4] + L[3]   # リスト + 文字列

以下の実行結果がどうなるか予想してから,実行してみなさい

In [ ]:
for item in L:
    print(item)
In [ ]:
for item in L[2:]:
    print(item)
In [ ]:
for i in range(len(L)):
    print(i, L[i])
In [ ]:
for i in range(2, 5):  # range に引数を2つ指定すると... (p.197)
    print(i, L[i])

最後の例では,第2引数の数は含まないことに注意. スライス を使ってリストなどの一部を指定するとき(例: L[2:5])と同様.

以下の実行結果がどうなるか予想してから,実行してみなさい.

In [ ]:
if "hoge" in "gehogohogeho":
    print("あったで")
if "gehogohogeho" in "hoge":
    print("ありまっせ")    

以下は,変数 y に格納された整数値が「18未満」,「18以上20未満」,「20以上」のいずれであるか判定して表示しようとしたコードである. 正しく動作するかどうか考えてから,何度か実行してみなさい.

注意: 以下のセルを実行すると,最下行に入力待ちの箱が表示されるはず.入力すれば結果が出力されます. 実行が途中で止まってどうしようもない([*]が出たままになる)ときは,↑の ■ ボタンを押していったん実行を停止させて実行をやり直すとよいでしょう.

In [ ]:
y = int(input("【入力してね】 お歳は?"))

print("### (1) ###")
if y < 18:
    print("18歳未満")
if y < 20:
    print("18歳以上20歳未満")
if 20 <= y:    
    print("20歳以上")
    
print("### (2) ###")
if y < 18:
    print("18歳未満")
else:
    if y < 20:
        print("18歳以上20歳未満")
    else:
        print("20歳以上")
        
print("### (3) ###")
if y < 18:
    print("18歳未満")
elif y < 20:
    print("18歳以上20歳未満")
else:
    print("20歳以上")

(★) このセルに,上記(1),(2),(3)が正しい動作をするかを書き記しなさい(このセルをダブルクリックして編集する)

  • (1) 正しい/正しくない
  • (2) 正しい/正しくない
  • (3) 正しい/正しくない

以下はリストの要素の最小値を求めようとしたコードである.実行結果がどうなるか予想してから,実行してみなさい.

In [ ]:
L = [ 47, 99, 53, 26, 35, 16, 77 ]

x = 0
for i in range(len(L)):
    if x > L[i]:
        x = L[i]

print(x)

(★)上記を修正して,正しく最小値を求められるようにしなさい.

関数を使う (pp.93-106)

関数とは,引数,戻り値 (pp.93-97)

In [ ]:
# 組み込み関数 abs を,引数として 10 を渡して呼び出す
abs(10)
In [ ]:
abs(-200)

以下の文は上の2つ目の例の説明である.「★(1)★」などに当てはまるものを答えなさい.

「★(1)★」という名前の関数に「★(2)★」として -200 を渡して呼び出すと, 200 が返ってきた.関数が返す値(この例では 200)は「★(3)★」値とも呼ばれる.

正解は,以下に白字で書いてあります.マウスでドラッグして反転させると見えるはず.

  • ★(1)★:  abs 
  • ★(2)★:  引数(ひきすう) 
  • ★(3)★:  戻り 

組み込み関数 int() は,引数として渡されたものを整数に変換して返す関数である.

In [ ]:
int("10")

上記の例では引数は1つだが,実は2つ目の引数も指定することができる.

In [ ]:
int("10", 2)
In [ ]:
int("10", 16)

第二の引数に自然数 p を指定すると,第一の引数の内容を p進法で表現された数と解釈して変換した数を10進数で返す.

In [ ]:
int("ff", 16)

Pythonの関数は複数の引数をとることができる. それだけならC言語の関数と同じだが,上記の例に示すように,渡すことのできる引数のうち一部だけを指定しても動作するように関数を作ることもできる点は,C言語の場合とは異なっている.ただし,そのような関数の作り方は次回以降に学ぶ.

関数を定義する (pp.97-102)

In [ ]:
# 運命の戦車を教えてくれる関数
def destiny_tank():
    tanks = ["IV号戦車D型", "III号戦車J型", "チャーチル Mk.VII",
             "M4シャーマン", "P40重戦車", "T-34/76"]  
    num = input("好きな数字を入力してください:")
    idx = int(num) % len(tanks)     # 入力値をリストの番号に変換. % のおかげで大きな数が入力されても大丈夫
    print("あなたの運命の戦車は")
    print(tanks[idx])
In [ ]:
# 関数 destiny_tank() を呼び出す
destiny_tank()  
In [ ]:
# 運命の戦車を教えてくれる関数 ver.2
def destiny_tank2(num):
    tanks = ["IV号戦車D型", "III号戦車J型", "チャーチル Mk.VII",
             "M4シャーマン", "P40重戦車", "T-34/76"]
    idx = num % len(tanks)
    print("あなたの運命の戦車は")
    print(tanks[idx])
In [ ]:
x = input("なんか数入力して")  # input() の戻り値は文字列

#  x の値を int() 使って整数に変換したものを引数として関数 destiny_tank2() を呼ぶ
destiny_tank2(int(x))
In [ ]:
from random import randint   # random モジュールの randint() 関数を使えるようにするおまじない
x = randint(0, 100)                 # randint() の使い方が気になるひとは p.386
print(x)
destiny_tank2(x)

上記のセルは実行するたびに異なる値が x に代入される.何度か実行してみよう.

In [ ]:
# 運命の戦車を教えてくれる関数 ver.3
def destiny_tank3(num):
    tanks = ["IV号戦車D型", "III号戦車J型", "チャーチル Mk.VII",
             "M4シャーマン", "P40重戦車", "T-34/76"]
    idx = num % len(tanks)
    return tanks[idx]             # 結果を戻り値として返す => 戻り値は文字列
In [ ]:
from random import randint   # 上で import してるはずだけどここだけ実行したときのために...
x = randint(0, 100)
tank = destiny_tank3(x)
print("今日あなたが乗るべき幸運の戦車は", tank, "です")
print("明日あなたが乗るべき幸運の戦車は", destiny_tank3(randint(0, 100)), "です")

関数の名前を間違えたら(存在しない関数を呼び出した)どうなるか実験してみよう.

In [ ]:
destiny_tank4(1818)

というエラーになった. 「destiny_tank4 なんちう名前は定義されてまへん」ってさ.

関数 destiny_tank() は引数なしの関数やけど,引数渡して実行したらどうなるかな?

In [ ]:
destiny_tank(1818)

destiny_tank() は引数 0 個しか受け取らへんのに 1 つ渡されたで」って言っている. argument が引数. positional argumentってのには意味があるのだけど,その話はいずれまた.

Jupyter Notebook では,セルを実行した順序が結果に影響する. ある関数を定義したセルを実行せずにその関数を呼び出すセルを実行したら,当然エラーになる. その時出るエラーは,上記の

name hoge is not defined

と同様.例えばこの notebook を後で復習しようとしたときとか,そういう目に遭ったら思い出そう.

ローカル変数 (pp.103,104)

In [ ]:
def test_func(arg1):
    # 数値の引数に100を足して表示する関数
    inner_var = 100
    print(arg1+inner_var)
In [ ]:
test_func(10)       # 関数を呼び出す - 110と表示される
print(inner_var)    # 関数内で定義した変数を表示(エラーになる)

inner_varなんてのは定義されてへん」ってエラーになった. 関数定義のブロックの中で定義された変数は,その中だけの存在なので,その関数の外では使えない. これは,C言語の場合と同様である.

じゃあ,同じ名前の変数があったらどうなるだろう?

In [ ]:
def test_hoge(x):
    hogehoge = 1314      #  変数 hogehoge に数を代入
    return x + hogehoge
In [ ]:
hogehoge = 1818     #  変数 hogehoge に数を代入
rv = test_hoge(4649)   # ここで hogehoge の値は変わるのか?
print("関数 test_hoge() の戻り値は", rv, "でっせ")
print("変数 hogehoge の値は", hogehoge, "でっせ")  # hogehoge はいくつ?

test_hoge() の中で hogehoge1314 を代入しているが,この変数 hogehoge は外の世界の変数 hogehoge とは別の存在なので,外の世界の変数 hogehoge には test_hoge() を呼び出す前の値が入ったままになっていることがわかる.

それでは,次の例はどうなるだろう?

In [ ]:
def test_hoge2(x):
    return x + hogehoge     # この関数の中では hogehoge 定義してないよ?
In [ ]:
hogehoge = 1818
rv = test_hoge2(4649)   
print("関数 test_hoge() の戻り値は", rv, "でっせ")
print("変数 hogehoge の値は", hogehoge, "でっせ")

test_hoge2() の中には hogehoge という変数の定義はない. しかし,外の世界には hogehoge という名前の変数が存在しており,1818 という値だったので,その値を使った計算が実行された.

見つけにくいバグの原因になるので,変数名にはなるべくユニークなもの(面白いという意味ではなく,一意なもの,他と異なるものという意味)を付けるようにした方がよい.

さらにもう一例.

In [ ]:
def test_hoge3(x):
    return x + hogefugahenapiyo     # この関数の中でも外でもこんな変数は使ってない
In [ ]:
rv = test_hoge3(4649)   
print("関数 test_hoge() の戻り値は", rv, "でっせ")

上の例のように,外でも定義されてない変数使ったら当然エラーになる.

次は,pp.105,106の例.

In [ ]:
# リストを受け取ってその要素の数のぶんさんを返す関数
def calc_variance(a_list):

    total = sum(a_list)    # リストの合計
    length = len(a_list)   # リストの要素数(長さ)
    mean = total/length    # 算術平均を求める
    variance = 0           # 分散を計算するための変数

    for height in a_list:
        variance = variance+(height-mean)**2 # 身長から平均を引いて二乗
    variance = variance/len(monk_fish_team)  # 足した数値を要素数で割って分散を求める

    return variance     # 求めた分散を戻り値として返す
In [ ]:
# リストを定義する
monk_fish_team = [158, 157, 163, 157, 145]
volleyball_team = [143, 167, 170, 165]
pravda_team = [127, 172, 140, 160, 174]

# 分散を計算する
monk_team_variance = calc_variance(monk_fish_team)
volley_team_variance = calc_variance(volleyball_team)
pravda_team_variance = calc_variance(pravda_team)

# 標準偏差を計算する
print(monk_team_variance**0.5)
print(volley_team_variance**0.5)
print(pravda_team_variance**0.5)

「あんこうチーム(monk_fish_team)」,「アヒルさんチーム(volleyball_team)」,「プラウダ高校の5人(pravda_team)」の中では,プラウダ高校のひとたちの身長の分散が大きいようです.

モジュールを使う (pp.107-112)

モジュールを import する (pp.107-109)

In [ ]:
import random                 # randomモジュールを読み込む

print(random.random())        # 0 < x < 1 の乱数を得る
print(random.randint(0, 6))   # 0 < x <= 6 の乱数を得る
a_list = [0, 1, 2, 3, 4, 5]
random.shuffle(a_list)        # リストをランダムに入れ替える
print(a_list)
print(random.choice(a_list))  # リストの要素をひとつランダムに選ぶ

上記で利用している random というモジュールは Python の標準ライブラリに含まれているモジュールの一つである.

import random

を実行すると,この random モジュールに含まれている hoge() という関数を random.hoge() という形で呼び出すことができるようになる.

次の例は,matplotlib というライブラリを使ってグラフを描画するコードである.

In [ ]:
# 次の行は,notebook 内にグラフを描くためのおまじない
%matplotlib inline
import matplotlib.pyplot    # matplotlib.pyplot モジュールを読み込む

X = [ -3, -2, -1, 0, 1, 2, 3]
Y = [ (x-2)*x*(x+2) for x in X ]   # (よだんだよん) 「リスト内包表記」という書き方.forループしてリストを作る処理を簡潔に書ける(p.236)

matplotlib.pyplot.plot(X, Y, color = "green", marker = "o", linestyle = "dashed")   #  plot 関数を呼び出す(名前が長い...)

上記では, matplotlib というグラフ描画ライブラリの中の pyplot というモジュールを普通に読み込もうとして

import  matplotlib.pyplot

と書いている.そのため,plot() 関数を呼び出す際には

matplotlib.pyplot.plot(...)

と書かないといけない.長くて面倒くさいと思ったら,次のように as を使って別名を付ければすればよい.

In [ ]:
# 次の行は,notebook 内にグラフを描くためのおまじない
%matplotlib inline
import matplotlib.pyplot as plt     # matplotlib.pyplot モジュールを plt という名前で使えるように読み込む

X = [ -3, -2, -1, 0, 1, 2, 3]
Y = [ (x-2)*x*(x+2) for x in X ]   # (よだんだよん) 「リスト内包表記」という書き方.forループしてリストを作る処理を簡潔に書ける(p.236)

plt.plot(X, Y, color = "green", marker = "o", linestyle = "dashed")  #  plot 関数を呼び出す(名前 plt.plot で済む)

import する際に asplt という別名をつけて,

plt.plot(...)

で済むようにしている.

from を使ったインポート/モジュールいろいろ (pp.110-112)

In [ ]:
monk_fish_team = [158, 157, 163, 157, 145]
volleyball_team = [143, 167, 170, 165]
In [ ]:
# statisticsモジュールを普通に import した場合
import statistics 

print(statistics.median(monk_fish_team)) 
print(statistics.median(volleyball_team))
In [ ]:
# 「from ほげ import へな」の場合
from statistics import median 

print(median(monk_fish_team))
print(median(volleyball_team))

前者の例では,statistics モジュールの median 関数を呼び出すには statistics.median() と書く必要があるが,後者の例では, median() と略記できることがわかる.ただし,この例では statistics モジュールの他の関数まで省略形で書けるわけではない.

 from statistics import *

と書くことで, statistics モジュール内の全ての関数などを statistics 付けず省略形で書けるようになるが,元から存在する関数など(標準の組み込み関数や自分で作った関数など)と同じ名前のものがあったりすると混乱するので,ご利用は計画的に.

ところで,ここまでの例で登場したモジュールについては,ウェブ上にリファレンスが存在している.

これらは Python の標準ライブラリに含まれているモジュールである.以下にアクセスすると,Python の標準ライブラリの詳細等の様々な文書を閲覧することができる. https://docs.python.jp/3/

matplotlib については,以下へどうぞ.ウェブで検索すれば日本語の情報も見つかるでしょう.

次の例は,以下を参考に作ったコードである(p.356以降も参照). https://docs.python.jp/3/library/datetime.html

In [ ]:
import datetime     # 標準ライブラリの datetime モジュールをインポート
today = datetime.date.today()                   # 今日の日付
birthday = datetime.date(2004, 10, 26)  # 誰かの誕生日
delta = today - birthday     # 時間差を表す timedelta オブジェクト
print("生まれてから", delta.days, "日経ったみたいほげ")

日付を変えて実行し直してみよう.

次の例は,以下を参考に作ったコードである(p.377以降も参照). https://docs.python.jp/3/library/os.html

In [ ]:
import os     # 標準ライブラリの os モジュールをインポート
uname = os.uname()                   #  OSを識別する情報を取得
cwd = os.getcwd()                      #  この jupyter notebook が動作しているディレクトリ (current working directory)を取得
envUSER = os.environ['USER']   # 環境変数 USER (ユーザ名)の値を取得
print("やあこんちは.")
print()
print("ふむ,僕は", uname.nodename, "ちう名前のコンピュータ上で")
print(uname.sysname, "ちう OS の環境下で動いてるほげね.")
print("で,僕が今いる場所は", cwd, "ほげね.")
print()
print("でもって君のIDは", envUSER, "ほげね.")
print("よろしくほげ.")

チェック

TAさんに課題Aのチェックを受ける際は,以下のセルを実行して「OK!」が出ているようにしてください.

In [ ]:
# 以下のコードは,この notebook を(それなりに)ちゃんと実行したかどうか確認するためのものです.
# 中身の理解は不要です.

fnpy = '/roes/sample/takataka/aprog/aprogCheck.py'
import os
if not os.path.isfile(fnpy):
    print('この環境では確認できないようです')
else:
    import sys
    sys.path.append('/roes/sample/takataka/aprog/')
    import aprogCheck
    aprogCheck.check('03note', In)