{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Python 入門2\n",
"\n",
"この回では、プログラミングの基本的知識についてさらに学ぶ。\n",
"\n",
"前回学習した`while`ループのような制御構造である`for`ループや`if`文の使い方、\n",
"さらに、科学計算用ライブラリ`numpy`の基本的な使用方法について学ぶ。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 繰り返し構造\n",
"\n",
"### for ループ\n",
"\n",
"`for`ループは、`while`ループによく似た繰り返し処理を行うための枠組みである。\n",
"ただし、Pythonの`for`ループは、FortranやC言語のものとは少し異なる。"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# 文字列を複数格納したリスト\n",
"words = ['cat', 'window', 'defenestrate']"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"cat 3\n",
"window 6\n",
"defenestrate 12\n"
]
}
],
"source": [
"for w in words:\n",
" print(w, len(w))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"ここで`for w in words:`は、\n",
"\n",
"+ `words`の中から1つ要素を取り出してきて`w`に代入する。\n",
"+ `for`文以下ブロックの命令を実行する\n",
"+ それが終わると、`words`の次の要素を`w`に代入する。\n",
"+ すべての要素について計算が終われば`for`文の実行を終わる。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### range() 関数 を用いた繰り返し\n",
"\n",
"`for`文によく用いられるものが`range`関数である。"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0\n",
"1\n",
"2\n",
"3\n",
"4\n"
]
}
],
"source": [
"for i in range(5):\n",
" print(i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"このように、`for i in range(5):`とすることで、`i`に0〜4までの数字を順に入れて繰り返し計算をすることが可能である。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### for ループの応用 〜ベクトル・行列計算〜\n",
"\n",
"これまで学習したリストと `for` ループを使って、ベクトル・行列演算を実装してみよう。\n",
"\n",
"なお、ベクトル・行列演算は後述のnumpyという計算ライブラリに実装されている。\n",
"速度・確実性の観点から、本来は確立されたライブラリの機能を用いる方がよい。\n",
"\n",
"今回はプログラミング学習のため、あえてこれを実装する。"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"# 以下の2つのベクトルの内積を計算する。\n",
"x = [1.0, 2.0]\n",
"y = [1.0, 3.0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"len(x) がリストxの要素数を返すことを思い出そう。\n",
"\n",
"上記の`range()`関数と組み合わせることで\n",
"\n",
"`for i in range(len(x)):` \n",
"\n",
"`i` を0からxの要素数まで変化させることができる。"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"ExecuteTime": {
"end_time": "2016-07-28T19:46:09.098450",
"start_time": "2016-07-28T19:46:09.093247"
}
},
"outputs": [],
"source": [
"dot = 0.0\n",
"for i in range(len(x)):\n",
" dot += x[i] * y[i]"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"ExecuteTime": {
"end_time": "2016-07-28T19:46:12.352178",
"start_time": "2016-07-28T19:46:12.347940"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7.0\n"
]
}
],
"source": [
"print(dot)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 課題1\n",
"\n",
"同様に、以下のベクトルの内積を計算せよ\n",
"$$\n",
"\\mathbf{x} = \\left(\\begin{array}{ccc}\n",
"1.0 & 2.0 & 3.0\\end{array}\\right)^\\text{T}\n",
"$$\n",
"\n",
"$$\n",
"\\mathbf{y} = \\left(\\begin{array}{ccc}\n",
"2.0 & -1.0 & 2.0\\end{array}\\right)^\\text{T}\n",
"$$"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# 解答をここに記入する"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 行列とベクトルの内積"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"行列は、リストの中にリストを作ることで表すことができる。\n",
"\n",
"$$\n",
"A = \\left(\\begin{array}{cc}\n",
"1.0 & 0.0 \\\\ 2.0 & 3.0\n",
"\\end{array}\\right)\n",
"$$\n",
"\n",
"は、以下のように表現できる。"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"ExecuteTime": {
"end_time": "2017-02-17T23:37:52.065531",
"start_time": "2017-02-17T23:37:52.055510"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[[1.0, 0.0], [2.0, 3.0]]\n"
]
}
],
"source": [
"A = [[1.0, 0.0], [2.0, 3.0]]\n",
"print(A)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"ExecuteTime": {
"end_time": "2017-02-17T23:37:52.999589",
"start_time": "2017-02-17T23:37:52.995902"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1.0, 0.0]\n"
]
}
],
"source": [
"print(A[0])"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"ExecuteTime": {
"end_time": "2017-02-17T23:37:53.387487",
"start_time": "2017-02-17T23:37:53.384865"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.0\n"
]
}
],
"source": [
"print(A[0][1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 課題2\n",
"\n",
"上の例と同様に、行列とベクトルの積を計算せよ。\n",
"\n",
"なお、`len(A)`で行の数を、`len(A[0])`で列の数を得ることができる。\n",
"\n",
"また、リストの`.append()`関数を使うことでリストへの要素の追加が可能である。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\n",
"\\mathrm{A} = \\left(\\begin{array}{cc}\n",
"1.0 & 2.0 \\\\\n",
"2.0 & 3.0\n",
"\\end{array}\\right)\n",
"$$\n",
"\n",
"$$\n",
"\\mathbf{x} = \\left(\\begin{array}{cc}\n",
"2.0 & -1.0\n",
"\\end{array}\\right)^\\text{T}\n",
"$$\n",
"\n",
"としたときの $A$ と $x$ の内積を計算せよ。"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# 解答をここに記入する"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pythonの関数\n",
"\n",
"上記の内積・行列積・外積のように、何度も行う操作を、操作の数だけコードを書くことは非効率である。\n",
"このような同一の操作を汎用的に行うことができる仕組みを**関数**と呼ぶ。\n",
"\n",
"関数は、**引数**で与えられた入力に対して、何か計算した結果を**戻り値**として返すものである。\n",
"例えば、これまで出てきた`len()`も関数である。\n",
"\n",
"リスト`x`が引数に与えられたとき、その要素数を戻り値として返す。"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"ExecuteTime": {
"end_time": "2016-07-28T20:06:48.817423",
"start_time": "2016-07-28T20:06:48.796588"
}
},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(x) # リスト(など)を引数にとり、要素数を返す関数"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"関数は、以下のように定義する。ここでは、2つのリストを引数に持ち、その内積を計算する関数を作る。"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"ExecuteTime": {
"end_time": "2016-07-28T20:09:59.368213",
"start_time": "2016-07-28T20:09:59.352801"
}
},
"outputs": [],
"source": [
"# 関数の例 `def` + 関数の名前(この場合はlist_dot)+ カッコ として定義する。\n",
"# カッコの中に引数を入れる。\n",
"def list_dot(x1, x2):\n",
" \"\"\"\n",
" 2つのリストを受け取り、その内積を計算する関数。\n",
" なお関数に関する説明は、\n",
" このように3つの連続するダブルコーテーション内に記述することが推奨されている。\n",
" \"\"\"\n",
" dot = 0.0\n",
" for i in range(len(x1)):\n",
" dot += x1[i]*x2[i] # x += y は、x に x+y を代入することを表す。\n",
" return dot # return 文で計算した値を返す。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"この場合 ``list_dot(x, y)`` のように関数名(変数, 変数)とすることで、\n",
"この関数を実行することができる。\n",
"\n",
"この関数が実行されると、まず``dot``という変数が定義され、値 0.0 が代入される。\n",
"次の `for` ループにより内積の値が`dot`に代入される。\n",
"計算された`dot`の値が返される。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"このように定義した関数は、繰り返し用いることができる。"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"ExecuteTime": {
"end_time": "2016-07-28T20:10:32.616094",
"start_time": "2016-07-28T20:10:32.611717"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4.0\n"
]
}
],
"source": [
"x = [1.0, 2.0]\n",
"y = [2.0, 1.0]\n",
"print(list_dot(x, y))"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"ExecuteTime": {
"end_time": "2016-07-28T20:10:43.383877",
"start_time": "2016-07-28T20:10:43.371478"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.0\n"
]
}
],
"source": [
"x = [1.0, -2.0]\n",
"y = [2.0, 1.0]\n",
"print(list_dot(x, y))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 課題3\n",
"\n",
"2つの行列 $A, B$ の積を計算する関数を作成せよ。\n",
"1つの関数により、以下の組み合わせの行列に対して計算せよ。\n",
"\n",
"1. \n",
"$$\n",
"\\;\\; \n",
"A = \\left(\\begin{array}{cc}\n",
"1.0 & 2.0 \\\\\n",
"2.0 & 3.0\n",
"\\end{array}\\right)\n",
",\\;\\;\n",
"B = \\left(\\begin{array}{cc}\n",
"-1.0 & -3.0 \\\\\n",
"0.0 & 1.0\n",
"\\end{array}\\right)\n",
"$$\n",
"\n",
"2. \n",
"$$\n",
"\\;\\; \n",
"A = \\left(\\begin{array}{ccc}\n",
"1.0 & 0.1 & 1.5 \\\\\n",
"0.9 & 0.2 & -1.0\n",
"\\end{array}\\right)\n",
",\\;\\; \n",
"B = \\left(\\begin{array}{cc}\n",
"-2.0 & -0.5 \\\\\n",
"0.0 & 1.2 \\\\\n",
"0.8 & 1.0 \\\\\n",
"\\end{array}\\right)\n",
"$$\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"# 解答をここに記入する"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 数値計算ライブラリNumpy\n",
"\n",
"Pythonは科学計算に用いることのできるライブラリが十分に用意されていることが特徴である。\n",
"ここでは、最も基本的な数値計算ライブラリであるNumpyを使用する。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 基本的な使い方\n",
"\n",
"まず、PythonでNumpyを用いるための命令をする必要がある。\n",
"これは前回学んだ`matplotlib`を用いるときと同様の`import`文を用いる。"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"# まず最初に、グラフ描画ライブラリと、数値計算ライブラリをimportしておく。\n",
"import numpy as np\n",
"# 同様にmatplotlibもimportしておく。\n",
"%matplotlib inline\n",
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"なお、`as np` の部分は、`numpy`を省略して`np`として用いる、という意味である。\n",
"\n",
"原理的には各自の好きな略語にできるが、`numpy`の場合、`np`と略すのが一般的である。\n",
"逆にそれ以外の略称を用いることは、コードをわかりづらくさせるので避けるべきである。"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 数学関数の計算\n",
"\n",
"ここでは、`Numpy`の機能の1つである特殊関数の計算をしてみる。\n",
"\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.8414709848078965"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# sin 関数\n",
"np.sin(1)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"このように、numpyの機能を用いる場合は、`np.`の後に関数名を書いて実行する。\n",
"\n",
"他に使える数学関数の一覧は\n",
"\n",
"http://docs.scipy.org/doc/numpy/reference/routines.math.html\n",
"\n",
"にリストされている。\n",
"\n",
"なお、Colabでは関数にカーソルをホールドすることで、その関数の説明を見ることができる。\n",
"Jupyter-notebook では、関数の括弧内にカーソルがあるときに `Shift + Tab` を押すことで、同様の説明を見ることができる。\n",
"説明は英語であるが、わざわざマニュアルを検索しなくても簡単な情報は得られるようになっている。\n",
"\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"次に、この関数の形を視覚的に把握するためにグラフにプロットする。"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"# まず、計算する範囲、個数を指定する。今回は、-5~5, 100 点で計算する。\n",
"x = []\n",
"y = []\n",
"for i in range(100):\n",
" x.append(-5.0 + 10.0 / 100 * i)\n",
" y.append(np.sin(x[i]))"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[]"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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",
"text/plain": [
"