あちらの「はじめての Python プログラミング」に若干の補足説明を追加したもの。
エンゲル係数を求める例
エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。
消費支出 110,000円,飲食費 37,000円の人のエンゲル係数を求める例。Python では以下のような電卓的な使い方ですぐ答えが出ます。
本稿のように Jupyter Notebook 環境では,以下の1行を書いたあとに,Shift キーを押しながら Enter キー(または Return キー)を押して実行します。ウインドウ上部のショートカットから「▶︎ Run」をクリックしてもいいです。
37000/110000 * 100
ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。
以下の例では,
- # で始まる行はコメントです。コメント行はそれを読む人間のための注釈であり,プログラムの実行時には無視されます。
- 3行目で変数
spending
に消費支出である110000
(円)という数値を代入し, - 4行目で変数
food
に飲食費である37000
(円)という数値を代入し, - 5行目でエンゲル係数を計算して,変数
eng
に代入し, - 6行目でエンゲル係数の値
eng
を表示させています。
# エンゲル係数を求める
# 消費支出 spending, 飲食費 food, エンゲル係数 eng
spending = 110000
food = 37000
eng = food / spending * 100
print(eng)
エラーとデバグ
意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。
掛け算を示す演算子は *
,割り算を示す演算子は /
であるところを誤って,×
や ÷
と書くと,これらの記号は Python では演算子として定義されていないのでエラーとなります。このようなエラーは文法エラー(SyntaxError) と呼ばれ,Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。
spending = 110000
food = 37000
eng = food ÷ spending × 100
print(x1)
別の種類の誤りもあります。たとえば,パーセントにするためにかける「100」を誤って「1000」と入力した場合…
spending = 110000
food = 37000
eng = food / spending * 1000
print(eng)
上記の例では SyntaxError
(文法エラー)の警告が出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)
このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。
このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。
プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Shift + Enter)します。この作業をデバグといいます。
データの型と変数
上記の例の 1行目では,spending
という名前の変数に 110000
という
数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されます。
データ型には数値(整数型・浮動小数点数型)と文字列などがあります。
変数 moji
に文字列を代入する場合は,以下のように "
で囲みます。('
で囲んでもよい。)
moji = "エンゲル係数"
print(moji)
式と演算
主な演算子
足し算は +
,
引き算は -
,掛け算は *
,割り算は /
。です。
優先的に計算したい箇所は丸括弧 ( )
で グループ化します。
2 + 50 - 5 * 6 /2
2 + (50 - 5 * 6)/2
+
は文字列の連結演算にも使います。以下の例を参照。
mo = "エンゲル"
ji = "係数"
print(mo + ji)
文字列と数値を直接連結することはできません。
そんなときは,str()
関数で数値を文字列に変換して連結します。
spending = 110000
food = 37000
eng = food / spending * 100
output = "エンゲル係数は " + str(eng)
print(output)
print()
のところは,文字列にして連結しなくても,以下のようにも書くことができます。
print()
文は,カンマ (,
) で区切ることによって複数の項目(変数)を表示することができます。
print("エンゲル係数は", eng, "です。")
割り算の商と余り
割り算 /
は常に浮動小数点数型の実数を返します。
17/3
//
演算子は 整数除算を行い、(小数部を切り捨てた) 整数値を返します。剰余は、%
で求めます。
17 // 3
17 % 3
冪乗は **
です。
以下の例では $2^4 = 2 \times 2 \times 2 \times 2$ と $2^{1/2} = \sqrt{2}$ の値を表示します。
print(2**4)
print(2**(1/2))
練習 1
1 天文単位(1 au)の距離を光速 c で進むのに要する時間は何時間何分何秒か。ただし,
- 1 天文単位は 12 桁の定義定数(誤差無し)で
149597870700
m - 光速 c は 9 桁の定義定数(誤差無し)で
299792458
m/s である。
参考:
対話型プログラム
上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみます。
input()
関数は入力された「文字列」を返すので,数値として計算するために float()
で数値(浮動小数点数)に変換します。
spending = input("消費支出? ")
food = input("飲食費? ")
eng = float(food) * 100 / float(spending)
print("エンゲル係数は", eng)
関数の定義と呼び出し
(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。
以下の例では,はじめにエンゲル係数を計算する関数 calc()
を定義し,あとのセルでその関数を呼び出しています。
def calc(spending, food):
""" 消費支出 spending 円と
飲食費 food 円からエンゲル係数を計算する。"""
# 念のため,float() で数値に変換して計算
return float(food) * 100 / float(spending)
spending = input("消費支出? ")
food = input("飲食費? ")
# 上で定義された関数 calc() を呼び出す
eng = calc(spending, food)
print("エンゲル係数は", eng)
なお,"""
で囲まれた文字列は「ドキュメンテーション文字列(docstring)」と呼ばれる関数の説明文です。
以下のように help()
で参照できます。
help(calc)
いったん定義された関数は,以下のように何回でも利用できます。
calc(120000, 53000)
練習 2
消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。
数学関数を使う
$\sin x, \cos x, \tan x$などの数学関数を使う場合は,以下のように使う前に math
モジュールを import
します。(最初に1回だけ import
すれば,それ以降は一々 import
する必要はありません。)数学定数の円周率 math.pi
なども利用できます。
import math
pi = math.pi;
pi, math.sin(pi/2), math.cos(pi/3), math.tan(pi/4)
参考:math
以外の数学関数
三角関数などの数学関数は,標準ライブラリである math
以外にも,numpy
や sympy
などのライブラリでも定義されています。詳細は,それぞれが必要になったときにでも…
import math
import numpy as np
from sympy import *
from sympy.abc import *
# math.sin()
print(math.sin(math.radians(30)))
# numpy.sin() は以下のようにリストでも可
angle = [30, 45, 60]
print(np.sin(np.radians(angle)))
#sympy.sin() は微分もできる
diff(sin(x), x)
以下の例では,x
以下の最大の整数を与える関数 math.floor()
を使って,小数点以下を切り捨てたエンゲル係数の値を表示させています。(ちなみに,小数点以下を切り上げる関数は math.ceil()
です。
また,エンゲル係数を計算する部分はすでに関数 calc()
として定義していますが,プログラム全体を関数 engel()
として定義してしまいます。
def engel():
spending = input("消費支出?")
food = input("飲食費?")
# 上で定義された関数 calc() を呼び出す
eng = calc(spending, food)
print("エンゲル係数は", math.floor(eng),
"です。(小数点以下切り捨て)")
engel()
参考:数値の丸めと四捨五入
Python の組み込み関数に round()
があります。数値を丸める関数ですが,いわゆる四捨五入とはちょっと違うようです。
以下の例からわかるように,小数点以下が 0.5
未満の場合と 0.5
を超える場合は四捨五入と同様の丸め方ですが,ちょうど 0.5
のときは,偶数に丸められていることがわかります。
これを偶数への丸めといい,端数が 0.5
より小さいなら切り捨て,端数が0.5
より大きいなら切り上げ,端数がちょうど 0.5
なら切り捨てと切り上げのうち結果が偶数となる方へ丸めます。
round(12.49), round(12.50), round(12.51)
round(13.49), round(13.50), round(13.51)
条件分岐
条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。
関数 engel()
を以下のように変更します。以下のプログラムでは,エンゲル係数の値が 40 以上だと,「高い値です。」と表示します。
def engel():
spending = input("消費支出? ")
food = input("飲食費? ")
# 上で定義された calc() を呼び出す
eng = calc(spending, food)
if eng >= 40:
print("エンゲル係数は", math.floor(eng),
"です。高い値です。")
else:
print("エンゲル係数は", math.floor(eng),
"です。")
engel()
engel()
条件分岐の if
文の書式は以下のようになっています。
if 条件式1:
条件式1 が満たされた場合に実行する文
elif 条件式2:
条件式2 が満たされた場合に実行する文
else:
それ以外の場合に実行する文
比較演算
上の例では,eng >= 40
つまり eng
が 40
以上のとき,という比較演算を行いました。他の例は以下のとおりです。
a == b
a
と b
が等しい
a != b
a
と b
が等しくない
a < b
a
が b
より小さい
a <= b
a
が b
以下
a > b
a
が b
より大きい
a >= b
a
が b
以上
練習 4
上記の条件分岐を,エンゲル係数が
- 40 以上の場合には「高い値です。」
- 20 未満の場合には「低い値です。」
と表示させるように変更しなさい。
繰り返し処理
while
以下の例では,1行目で代入された消費支出に対してエンゲル係数を計算します。
1回だけ計算して表示するのではなく,2行目の飲食費の初期値(ここでは 40000
円)から,5行目の条件式(ここでは 80000
円以下)が成り立つ場合に,8行目にあるように 5000
円ずつ増やしながらエンゲル係数を表示します。
spending = 110000
food = 30000
print("消費支出",spending,"円に対するエンゲル係数の値")
while food <= 50000:
# 上で定義された calc() を呼び出す
eng = calc(spending, food)
print(" 飲食費", food, "円の場合: ", round(eng))
food = food + 5000
while
文の書式は以下の通りです。
while 条件式:
条件式が成り立つ場合に実行する式
for
同様の繰り返し処理を for
文を使って行うこともできます。
Python では,以下の例の4行目のように for
文と range()
関数を使って書きます。他のプログラム言語になれていると少しとまどうかも知れませんが,50000
の場合は表示されません。
spending = 110000
print("消費支出",spending,"円に対するエンゲル係数の値")
for food in range(30000, 50000, 5000):
# 上で定義された calc() を呼び出す
eng = calc(spending, food)
print(" 飲食費", food, "円の場合: ", round(eng))
補足:range()
関数と for
文
range()
関数を使った for
文の書式は以下の通りです。開始値を省略すると 0
とみなし,増分を省略すると 1
とみなします。
for i in range(開始値, 終端値, 増分):
i が開始値以上,終端値未満の場合に実行される文
以下の range(5)
とした例を参照してください。
for i in range(5):
print(i)
参考:5000「以下」まで表示させるには
上記の for
文を使った繰り返しのプログラムで,飲食費 50000
円のエンゲル係数まで表示させるように変更してください。
ヒント:55000
までする必要はありませんよ。答えは… 以下の通り!
spending = 110000
print("消費支出",spending,"円に対するエンゲル係数の値")
for food in range(30000, 50001, 5000):
# 上で定義された関数 calc() を呼び出す
eng = calc(spending, food)
print(" 飲食費", food, "円の場合: ", round(eng))
例:数列の和
$\displaystyle \sum_{n = 1}^{10} n$ を繰り返し処理の練習として求めてみます。
# 繰り返し処理 while の練習として
answer = 0
i = 1
while i <= 10:
answer = answer + i
i = i + 1
answer
# 繰り返し処理 for の練習として
answer = 0
for i in range(1, 11):
answer = answer + i
answer
参考:再帰的定義関数と繰り返し処理
参考までに,再帰的定義関数を使った方法について。
$i = 1$ から $i = n$ までの $i$ の和 $\displaystyle \sum_{i=1}^n i$ を求める関数 $\mbox{mysum}(n)$ を以下のように定義する。
\begin{eqnarray}
\mbox{mysum}(1) &=& 1 \\
\mbox{mysum}(2) &=& 1 + 2 = \mbox{mysum}(1) + 2 \\
\mbox{mysum}(3) &=& 1 + 2 + 3 = \mbox{mysum}(2) + 3 \\
&\vdots& \\
\mbox{mysum}(n) &=& \mbox{mysum}(n-1) + n
\end{eqnarray}
# 自分自身の一つ前の値を使って自分自身を定義する
# このように定義された関数を再帰的定義関数という
def mysum(n):
if n == 1:
return 1
else:
return mysum(n-1) + n
mysum(10)
練習 5
以下の値を求める関数 mysum2(n)
を複数の方法で作成し,$n=10$ および $n=100$ の場合を求めよ。
$$\sum_{k = 1}^{n} k^2$$
# 繰り返し処理 while を使った作成例
def mysum2(n):
answer = 0
i = 0
while i <= 10:
answer = answer + i**2
i = i + 1
return answer
mysum2(10)
# みなさんはこれ以外の方法で作成するのですよ。
リスト・配列とファイルの読み書き
リスト
要素が5個のリストの各成分に数値を入れたり表示したりする例です。
# リスト a の要素の値を設定
a = [5, 4, 3, 2, 1]
print("全成分を一挙に表示")
print(a)
このようにして作られるのがリストであり,type()
関数で確認できます。
type(a)
少し表示を工夫してみます。
print("全成分を横に表示")
[print(i, end=' ') for i in a];
print("")
print("全成分を縦に表示")
[print(i) for i in a];
5個の成分を全てゼロにしてリストを初期化する例です。
# リスト b の5つの要素を全て 0 に
b = [0] * 5
print(b)
# 3番目の要素の値を設定
b[2] = 3
print(b)
ファイルからリストへの読み込み
以下のようなテキストファイル mydata.txt
を用意します。
! cat mydata.txt
ファイル mydata.txt
の数値をリスト a
に読み込みます。まずは readlines()
を使って読み込んでみます。
f = open("mydata.txt")
a = f.readlines()
print(a)
np.loadtxt()
で配列へ読み込み
上記の出力結果を見ると,リスト a
の各要素は改行コード \n
を含む文字列となっています。これをいちいち数値(整数)に変換するのも面倒なので,ファイルから数値データを読み込むことを前提にして,以下のように numpy
を import
し,loadtxt()
を使うことにします。
import numpy as np
a = np.loadtxt('mydata.txt')
print(a)
np.loadtxt()
はデフォルトでは実数として読み込みます。整数として読み込む場合は,以下のように dtype='int'
オプションを設定します。
a = np.loadtxt('mydata.txt', dtype='int')
print(a)
# 要素の総和,平均,最大値,最小値を出力します。
print("要素の総和は", sum(a))
print("要素の平均は", sum(a)/len(a))
print("最大値は ", max(a))
print("最小値は ", min(a))
次は,以下のような「6行2列」のデータが入ったテキストファイル mydata2.txt
を読み込む例です。
! cat mydata2.txt
b = np.loadtxt('mydata2.txt', dtype='int')
print(b)
np.savetxt()
による書き込み
ファイルへの書き込み例です。この例では,$x$ と $\sin x$ の値を $x = 0.1$ から $x = 1.0$ まで書き込んでいます。
# まず,書き込む配列を作成します。
x = []
for i in range(1, 11):
xi = 0.1 * i
x.append([xi, np.sin(xi)])
# 配列 x をファイル myoutput.txt に書き込みます。
np.savetxt('myoutput.txt', x)
# myoutput.txt の内容を表示します。
! cat myoutput.txt
リスト・配列の相互変換
np.loadtxt()
で読み込むと,numpy.ndarray
という配列になります。
type(b)
配列の成分の参照は以下のとおりです。例えば,3行2列目の値を参照したいときは,Python はゼロ始まりですから…
b[2,1]
配列からリストへ tolist()
配列 numpy.ndarray
から Python 標準のリストに変換する場合は以下のように tolist()
を使います。
blist = b.tolist()
print(blist)
リストの場合の成分の参照例:
blist[2][1]
リストから配列へ numpy.array()
Python 標準のリスト list
から配列 numpy.ndarray
へ変換する場合は,以下のようにnp.array()
関数を使います。
上記の方法で作成した x
は Python 標準のリストです。
type(x)
xarray = np.array(x)
type(xarray)
ndarray
のほうが,print()
したときに整って表示されます。
print("成分の参照の仕方:")
print("x[9][1] = ", x[9][1])
print("xarray[9,1] = ", xarray[9,1])
print()
print(type(x))
print(x)
print()
print(type(xarray))
print(xarray)
書式指定
浮動小数点数の書式指定 '%f'
実数を表示する際,全体の文字数や小数点以下の桁数を指定して表示させる例です。
例えば '%8.5f'
は全体で 8
桁の表示範囲をとり,小数点以下は 5
桁表示します。
import numpy as np # 最初に1回やればいいが,念のため
ten = '1234567890'
print(ten * 2, "桁数")
sqrtwo = np.sqrt(2)
print(sqrtwo) # NumPy の sqrt は全18桁
print('%8.5f' % sqrtwo)
print('%18.15f' % sqrtwo)
print('2の平方根は %18.15f です。' % sqrtwo)
小数点以下のみの桁数を指定することもできます。例えば,'%.5f'
のように小数点以下5桁のみを指定します。
print('%.5f' % sqrtwo)
print('%.15f' % sqrtwo)
練習 6
以下の出力を,エンゲル係数の値を(四捨五入して)小数点以下1桁まで表示するように変更しなさい。
spending = 110000
food = 37000
eng = food / spending * 100
print("エンゲル係数は", eng, "です。")
指数表式の指定 '%e'
非常に大きい数や小さい数を表示するときには, $1.9891\times 10^{30}$ や $6.67430\times 10^{-11}$ などのような指数表示をします。
例えば '%9.2e'
では,全体で 9
桁の表示分を確保し,小数点以下は 2
桁表示します。
c = 299792458
print('%9.2e' % c)
print('%15.8e' % c)
print('光速は %15.8e m/s です。' % c)
小数点以下のみの桁数を指定することもできます。例えば,'%.2e'
のように小数点以下2桁のみを指定します。
print('%.2e' % c)
print('%.8e' % c)
print('光速は %.8e m/s です。' % c)
'%f'
または '%e'
となる '%g'
'%g'
は数値の大きさによって,'%f'
または '%e'
(ちょっと違うけどだいたい似たような形)になります。
print('%.2g' % sqrtwo)
print('%.2f' % sqrtwo)
print('%.5g' % c)
print('%.5e' % c)
文字型の書式指定 '%s'
文字列の書式指定は '%s'
です。
また,以下の例では,書式指定ではなく ten[:n]
のように文字列 ten
の最初から n
番目までの部分を表示させています。
ten = '1234567890'
print('%s 桁数' %(ten*2))
print(ten[:5], ten[:5])
print(ten[:6], ten[:4])
整数の書式指定 '%d'
整数の書式指定は '%d'
です。'%5d'
のように桁数を指定することもできます。
v = [1, 23, 456, 7890]
for row in v:
print(row)
# 書式指定で表示桁数を5桁とり,右寄せで整数を表示
for row in v:
print('%5d' % row)
参考:左から 0 詰めで整数を表示する例 %05d
整数値を左から 0 詰めで表示する例。
# 表示桁数を5桁とり,左から0詰めして右寄せで整数を表示
for row in v:
print('%05d' % row)
m = [[1, 0, 0, 0],
[0, 23, 0, 0],
[0, 0, 456, 0],
[0, 0, 0, 7890]]
for row in m:
for col in row:
print(col, end=" ") # 改行せずにスペース1個あけて出力
print() # 改行
# 書式指定で表示桁数を5桁とり,右寄せで整数を表示
for row in m:
for col in row:
print('%5d' % col, end=" ")
print()
参考:0詰めの連番ファイル名の作成例 .zfill()
連番ファイル名の作成例。整数値の0詰めは '%05d'
のように書式設定しますが,(ファイル名等に使う)文字列の0詰めは,以下のように整数 i
を一旦 str(i)
で文字列にして .zfill()
で0詰めにできます。
for i in range(4):
filename = 'fig'+str(i).zfill(3)+'.png'
print(filename)