はじめての Python プログラミング

第2版  update: 2021年10月
葛西 真寿 弘前大学大学院理工学研究科

この Notebook は,

のプログラミング部分に相当する内容を,JavaScript のかわりに Python を使い,Jupyter Notebook で書いてみたものです。

エンゲル係数を求める例

エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。

消費支出 110,000円,飲食費 37,000円の人のエンゲル係数を求める例。Python では以下のような電卓的な使い方ですぐ答えが出ます。

本稿のように Jupyter Notebook 環境では,以下の1行を書いたあとに,Shift キーを押しながら Enter キー(または Return キー)を押して実行します。ウインドウ上部のショートカットから「▶︎ Run」をクリックしてもいいです。

In [1]:
37000/110000 * 100
Out[1]:
33.63636363636363

ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。

以下の例では,

  1. # で始まる行はコメントです。コメント行はそれを読む人間のための注釈であり,プログラムの実行時には無視されます。
  2. 3行目で変数 spending に消費支出である 110000(円)という数値を代入し,
  3. 4行目で変数 food に飲食費である 37000(円)という数値を代入し,
  4. 5行目でエンゲル係数を計算して,変数 eng に代入し,
  5. 6行目でエンゲル係数の値 eng を表示させています。
In [2]:
# エンゲル係数を求める
# 消費支出 spending, 飲食費 food, エンゲル係数 eng
spending = 110000
food = 37000
eng = food / spending * 100
print(eng)
33.63636363636363

エラーとデバグ

意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。

掛け算を示す演算子は *,割り算を示す演算子は / であるところを誤って,×÷ と書くと,これらの記号は Python では演算子として定義されていないのでエラーとなります。このようなエラーは文法エラー(SyntaxError) と呼ばれ,Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。

In [3]:
spending = 110000
food = 37000
eng = food ÷ spending × 100
print(x1)
  File "<ipython-input-3-5a58aab8cdc5>", line 3
    eng = food ÷ spending × 100
               ^
SyntaxError: invalid character in identifier

別の種類の誤りもあります。たとえば,パーセントにするためにかける「100」を誤って「1000」と入力した場合...

In [4]:
spending = 110000
food = 37000
eng = food / spending * 1000
print(eng)
336.3636363636364

上記の例では SyntaxError (文法エラー)の警告が出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)

このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。 このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。

プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Shift + Enter)します。この作業をデバグといいます。

データの型と変数

上記の例の 1行目では,spending という名前の変数に 110000 という 数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されます。

データ型には数値(整数型・浮動小数点数型)と文字列などがあります。

変数 moji に文字列を代入する場合は,以下のように " で囲みます。(' で囲んでもよい。)

In [5]:
moji = "エンゲル係数"
print(moji)
エンゲル係数

式と演算

主な演算子

足し算は +, 引き算は -,掛け算は *,割り算は /。です。

優先的に計算したい箇所は丸括弧 ( )で グループ化します。

In [6]:
2 +  50 - 5 * 6 /2
Out[6]:
37.0
In [7]:
2 + (50 - 5 * 6)/2
Out[7]:
12.0

+ は文字列の連結演算にも使います。以下の例を参照。

In [8]:
mo = "エンゲル"
ji = "係数"
print(mo + ji)
エンゲル係数

文字列と数値を直接連結することはできません。

そんなときは,str() 関数で数値を文字列に変換して連結します。

In [9]:
spending = 110000
food = 37000
eng = food / spending * 100
output = "エンゲル係数は " + str(eng)
print(output)
エンゲル係数は 33.63636363636363

print() のところは,文字列にして連結しなくても,以下のようにも書くことができます。 print() 文は,カンマ (,) で区切ることによって複数の項目(変数)を表示することができます。

In [10]:
spending = 110000
food = 37000
eng = food / spending * 100
print("エンゲル係数は", eng, "です。")
エンゲル係数は 33.63636363636363 です。

除算 / は常に浮動小数点数型の実数を返します。

In [11]:
17 / 3
Out[11]:
5.666666666666667

// 演算子は 整数除算を行い、(小数部を切り捨てた) 整数値を返します。剰余は、% で求めます。

In [12]:
17 // 3
Out[12]:
5
In [13]:
17 % 3
Out[13]:
2

冪乗は ** です。 以下の例では $2^4 = 2 \times 2 \times 2 \times 2$ と $2^{1/2} = \sqrt{2}$ の値を表示します。

In [14]:
print(2**4)
print(2**(1/2))
16
1.4142135623730951

(練習)

1 天文単位(1 au)の距離を光速 c で進むのに要する時間はいくらか。ただし,

  • 1 天文単位は 12 桁の定義定数(誤差無し)で 149597870700 m
  • 光速 c は 9 桁の定義定数(誤差無し)で 299792458 m/s である。

参考:

対話型プログラム

上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみます。

input() 関数は入力された「文字列」を返すので,数値として計算するために float() で数値(浮動小数点数)に変換します。

In [15]:
spending = input("消費支出? ")
food = input("飲食費? ")
eng = float(food) * 100 / float(spending)
print("エンゲル係数は", eng)
消費支出? 110000
飲食費? 37000
エンゲル係数は 33.63636363636363

関数の定義と呼び出し

(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。

以下の例では,1行目から4行目でエンゲル係数を計算する関数 calc() を定義し,8行目でその関数を呼び出しています。

In [16]:
def calc(spending, food):
    """ 消費支出 spending 円と
        飲食費 food 円からエンゲル係数を計算する。"""
    # 念のため,float() で数値に変換して計算
    return float(food) * 100 / float(spending)

spending = input("消費支出? ")
food = input("飲食費? ")
eng = calc(spending, food)
print("エンゲル係数は", eng)
消費支出? 110000
飲食費? 37000
エンゲル係数は 33.63636363636363

なお,""" で囲まれた文字列は「ドキュメンテーション文字列(docstring)」と呼ばれる関数の説明文です。

以下のように help() で参照できます。

In [17]:
help(calc)
Help on function calc in module __main__:

calc(spending, food)
    消費支出 spending 円と
    飲食費 food 円からエンゲル係数を計算する。

いったん定義された関数は,以下のように何回でも利用できます。

In [18]:
calc(120000, 53000)
Out[18]:
44.166666666666664

(練習)

消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。

数学関数を使う

$\sin x, \cos x, \tan x$などの数学関数を使う場合は,以下のように使う前に math モジュールを import します。(最初に1回だけ import すれば,それ以降は一々 import する必要はありません。)数学定数の円周率 math.pi なども利用できます。

In [19]:
import math

pi = math.pi;
pi, math.sin(pi/2), math.cos(pi/3), math.tan(pi/4)
Out[19]:
(3.141592653589793, 1.0, 0.5000000000000001, 0.9999999999999999)

以下の例では,x 以下の最大の整数を与える関数 math.floor() を使って,小数点以下を切り捨てたエンゲル係数の値を表示させています。(ちなみに,小数点以下を切り上げる関数は math.ceil() です。

また,エンゲル係数を計算する部分はすでに関数 calc() として定義していますが,プログラム全体を関数 engel() として定義してしまいます。

In [20]:
def engel():
    spending = input("消費支出?")
    food = input("飲食費?")
    eng = calc(spending, food)
    print("エンゲル係数は", math.floor(eng), 
          "です。(小数点以下切り捨て)")
In [21]:
engel()
消費支出?110000
飲食費?37000
エンゲル係数は 33 です。(小数点以下切り捨て)

math で使える数学関数

import math で使える数学関数や数学定数については,以下のマニュアルのページを参照してください。

(練習)

小数点以下を切り上げたエンゲル係数の値を表示させるように上記のプログラムを変更しなさい。

ヒント: math --- 数学関数math.ceil を参照。

参考:数値の丸めと四捨五入

Python の組み込み関数に round() があります。数値を丸める関数ですが,いわゆる四捨五入とはちょっと違うようです。

以下の例からわかるように,小数点以下が 0.5 未満の場合と 0.5 を超える場合は四捨五入と同様の丸め方ですが,ちょうど 0.5 のときは,偶数に丸められていることがわかります。

これを偶数への丸めといい,端数が 0.5 より小さいなら切り捨て,端数が0.5 より大きいなら切り上げ,端数がちょうど 0.5 なら切り捨てと切り上げのうち結果が偶数となる方へ丸めます。

In [22]:
round(12.49), round(12.50), round(12.51)
Out[22]:
(12, 12, 13)
In [23]:
round(13.49), round(13.50), round(13.51)
Out[23]:
(13, 14, 14)

条件分岐

条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。

関数 engel() を以下のように変更します。以下のプログラムでは,エンゲル係数の値が 40 以上だと,「高い値です。」と表示します。

In [24]:
def engel():
    spending = input("消費支出? ")
    food = input("飲食費? ")
    eng = calc(spending, food)
    if eng >= 40:
        print("エンゲル係数は", math.floor(eng), 
              "です。高い値です。")
    else:
        print("エンゲル係数は", math.floor(eng), 
              "です。")       
In [25]:
engel()
消費支出? 110000
飲食費? 45000
エンゲル係数は 40 です。高い値です。
In [26]:
engel()
消費支出? 110000
飲食費? 37000
エンゲル係数は 33 です。

条件分岐の if 文の書式は以下のようになっています。

if 条件式1:
    条件式1 が満たされた場合に実行する文
elif 条件式2: 
    条件式2 が満たされた場合に実行する文
else:
    それ以外の場合に実行する文

比較演算

上の例では,eng >= 40 つまり eng40 以上のとき,という比較演算を行いました。他の例は以下のとおりです。

a == b  ab が等しい
a != b  ab が等しくない
a < b  ab より小さい
a <= b  ab 以下
a > b  ab より大きい
a >= b  ab 以上

(練習)

上記の条件分岐を,エンゲル係数が

  1. 40 以上の場合には「高い値です。」
  2. 20 未満の場合には「低い値です。」

と表示させるように変更しなさい。

繰り返し処理

以下の例では,1行目で代入された消費支出に対してエンゲル係数を計算します。

1回だけ計算して表示するのではなく,2行目の飲食費の初期値(ここでは 40000 円)から,5行目の条件式(ここでは 80000 円以下)が成り立つ場合に,8行目にあるように 5000 円ずつ増やしながらエンゲル係数を表示します。

In [27]:
spending = 110000
food = 30000
print("消費支出",spending,"円に対するエンゲル係数の値")

while food <= 50000:
    eng = calc(spending, food)
    print("   飲食費", food, "円の場合: ", round(eng))
    food = food + 5000
消費支出 110000 円に対するエンゲル係数の値
   飲食費 30000 円の場合:  27
   飲食費 35000 円の場合:  32
   飲食費 40000 円の場合:  36
   飲食費 45000 円の場合:  41
   飲食費 50000 円の場合:  45

while 文の書式は以下の通りです。

while 条件式:
    条件式が成り立つ場合に実行する式

同様の繰り返し処理を for 文を使って行うこともできます。

Python では,以下の例の4行目のように for 文と range() 関数を使って書きます。他のプログラム言語になれていると少しとまどうかも知れませんが,50000 の場合は表示されません。

In [28]:
spending = 110000
print("消費支出",spending,"円に対するエンゲル係数の値")

for food in range(30000, 50000, 5000):
    eng = calc(spending, food)
    print("   飲食費", food, "円の場合: ", round(eng))  
消費支出 110000 円に対するエンゲル係数の値
   飲食費 30000 円の場合:  27
   飲食費 35000 円の場合:  32
   飲食費 40000 円の場合:  36
   飲食費 45000 円の場合:  41

補足:range() 関数と for

range() 関数を使った for 文の書式は以下の通りです。開始値を省略すると 0 とみなし,増分を省略すると 1 とみなします。

for i in range(開始値, 終端値, 増分):
    i が開始値以上終端値未満の場合に実行される文

以下の range(5) とした例を参照してください。

In [29]:
for i in range(5):
    print(i)
0
1
2
3
4

(練習)

上記の for 文を使った繰り返しのプログラムで,飲食費 50000 円のエンゲル係数まで表示させるように変更してください。

ヒント:55000 までする必要はありませんよ。

(練習)

以下の値を求めるプログラムを作成せよ。

$$\sum_{n = 1}^{10} n^2$$

リスト・配列とファイルの読み書き

リスト

要素が5個のリストの各成分に数値を入れたり表示したりする例です。

In [30]:
# リスト a の要素の値を設定
a = [5, 4, 3, 2, 1]
print("全成分を一挙に表示")
print(a)
全成分を一挙に表示
[5, 4, 3, 2, 1]

このようにして作られるのがリストであり,type() 関数で確認できます。

In [31]:
type(a)
Out[31]:
list

少し表示を工夫してみます。

In [32]:
print("全成分を横に表示")
[print(i, end='  ') for i in a];
print("")
print("全成分を縦に表示")
[print(i) for i in a];
全成分を横に表示
5  4  3  2  1  
全成分を縦に表示
5
4
3
2
1

5個の成分を全てゼロにしてリストを初期化する例です。

In [33]:
# リスト b の5つの要素を全て 0 に
b = [0] * 5
print(b)
# 3番目の要素の値を設定
b[2] = 3
print(b)
[0, 0, 0, 0, 0]
[0, 0, 3, 0, 0]

ファイルからリストへの読み込み

以下のようなテキストファイル mydata.txt を用意します。

In [34]:
! cat mydata.txt
1
2
3
4
5
6

ファイル mydata.txt の数値をリスト a に読み込みます。まずは readlines() を使って読み込んでみます。

In [35]:
f = open("mydata.txt")
a = f.readlines()
print(a)
['1\n', '2\n', '3\n', '4\n', '5\n', '6']
In [36]:
type(a)
Out[36]:
list

配列 numpy.ndarray

np.loadtxt() で配列へ読み込み

上記の出力結果を見ると,リスト a の各要素は改行コード \n を含む文字列となっています。これをいちいち数値(整数)に変換するのも面倒なので,ファイルから数値データを読み込むことを前提にして,以下のように NumPy を import し,loadtxt() を使うことにします。

In [37]:
import numpy as np
a = np.loadtxt('mydata.txt')
print(a)
[1. 2. 3. 4. 5. 6.]

import numpy as np としてインポートした後,np.loadtxt() 関数で読み込むと,anumpy.ndarray という配列になります。

In [38]:
type(a)
Out[38]:
numpy.ndarray

np.loadtxt() はデフォルトでは実数として読み込みます。整数として読み込む場合は,以下のように dtype='int' オプションを設定します。

In [39]:
a = np.loadtxt('mydata.txt', dtype='int')
print(a)
[1 2 3 4 5 6]
In [40]:
# 要素の総和,平均,最大値,最小値を出力します。
print("要素の総和は", sum(a))
print("要素の平均は", sum(a)/len(a))
print("最大値は    ", max(a))
print("最小値は    ", min(a))
要素の総和は 21
要素の平均は 3.5
最大値は     6
最小値は     1

次は,以下のような「6行2列」のデータが入ったテキストファイル mydata2.txt を読み込む例です。

In [41]:
! cat mydata2.txt
1  1
2  4
3  9
4 16
5 25
6 36
In [42]:
b = np.loadtxt('mydata2.txt', dtype='int')
print(b)
[[ 1  1]
 [ 2  4]
 [ 3  9]
 [ 4 16]
 [ 5 25]
 [ 6 36]]

np.savetxt() による書き込み

ファイルへの書き込み例です。この例では,$x$ と $\sin x$ の値を $x = 0.1$ から $x = 1.0$ まで書き込んでいます。

In [43]:
import numpy as np  # 1度 import していればいいですが,念のため
# まず,書き込む配列を作成します。
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)
In [44]:
# myoutput.txt の内容を表示します。
! cat myoutput.txt
1.000000000000000056e-01 9.983341664682815475e-02
2.000000000000000111e-01 1.986693307950612164e-01
3.000000000000000444e-01 2.955202066613396017e-01
4.000000000000000222e-01 3.894183423086505225e-01
5.000000000000000000e-01 4.794255386042030054e-01
6.000000000000000888e-01 5.646424733950354824e-01
7.000000000000000666e-01 6.442176872376911279e-01
8.000000000000000444e-01 7.173560908995227914e-01
9.000000000000000222e-01 7.833269096274834142e-01
1.000000000000000000e+00 8.414709848078965049e-01

リスト・配列の相互変換

np.loadtxt() で読み込むと,numpy.ndarray という配列になります。

In [45]:
type(b)
Out[45]:
numpy.ndarray

配列の成分の参照は以下のとおりです。例えば,3行2列目の値を参照したいときは,Python はゼロ始まりですから...

In [46]:
b[2,1]
Out[46]:
9

配列からリストへ tolist()

配列 numpy.ndarray から Python 標準のリストに変換する場合は以下のように tolist() を使います。

In [47]:
blist = b.tolist()
print(blist)
[[1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36]]

リストの場合の成分の参照例:

In [48]:
blist[2][1]
Out[48]:
9

リストから配列へ numpy.array()

Python 標準のリスト list から配列 numpy.ndarray へ変換する場合は,以下のようにnp.array() 関数を使います。

上記の方法で作成した x は Python 標準のリストです。

In [49]:
type(x)
Out[49]:
list
In [50]:
xarray = np.array(x)
type(xarray)
Out[50]:
numpy.ndarray

ndarray のほうが,print() したときに整って表示されます。

In [51]:
print("成分の参照の仕方:")
print("x[9][1] =     ", x[9][1])
print("xarray[9,1] = ", xarray[9,1])
print()
print("list")
print(x)
print()
print("ndarray")
print(xarray)
成分の参照の仕方:
x[9][1] =      0.8414709848078965
xarray[9,1] =  0.8414709848078965

list
[[0.1, 0.09983341664682815], [0.2, 0.19866933079506122], [0.30000000000000004, 0.2955202066613396], [0.4, 0.3894183423086505], [0.5, 0.479425538604203], [0.6000000000000001, 0.5646424733950355], [0.7000000000000001, 0.6442176872376911], [0.8, 0.7173560908995228], [0.9, 0.7833269096274834], [1.0, 0.8414709848078965]]

ndarray
[[0.1        0.09983342]
 [0.2        0.19866933]
 [0.3        0.29552021]
 [0.4        0.38941834]
 [0.5        0.47942554]
 [0.6        0.56464247]
 [0.7        0.64421769]
 [0.8        0.71735609]
 [0.9        0.78332691]
 [1.         0.84147098]]

書式指定

浮動小数点数の書式指定

実数を表示する際,全体の文字数や小数点以下の桁数を指定して表示させる例です。

例えば '%f8.5' は全体で 8 桁の表示範囲をとり,小数点以下は 5 桁表示します。

In [52]:
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)
12345678901234567890 桁数
1.4142135623730951
 1.41421
 1.414213562373095
2の平方根は  1.414213562373095 です。

小数点以下のみの桁数を指定することもできます。例えば,'%.5f' のように小数点以下5桁のみを指定します。

In [53]:
print(ten * 2, "桁数")
print('%.5f' % sqrtwo)
print('%.15f' % sqrtwo)
12345678901234567890 桁数
1.41421
1.414213562373095

(練習)

以下の出力を,エンゲル係数の値を(四捨五入して)小数点以下1桁まで表示するように変更しなさい。

In [54]:
spending = 110000
food = 37000
eng = food / spending * 100
print("エンゲル係数は", eng, "です。")
エンゲル係数は 33.63636363636363 です。

指数表式の指定

非常に大きい数や小さい数を表示するときには, $1.9891\times 10^{30}$ や $6.67430\times 10^{-11}$ などのような指数表示をします。

例えば '%e9.2' では,全体で 9 桁の表示分を確保し,小数点以下は 2 桁表示します。

In [55]:
print(ten * 2, "桁数")
c = 299792458
print('%9.2e' % c)
print('%15.8e' % c)
print('光速は %15.8e m/s です。' % c)
12345678901234567890 桁数
 3.00e+08
 2.99792458e+08
光速は  2.99792458e+08 m/s です。

小数点以下のみの桁数を指定することもできます。例えば,'%.2e' のように小数点以下2桁のみを指定します。

In [56]:
print(ten * 2, "桁数")
print('%.2e' % c)
print('%.8e' % c)
print('光速は %.8e m/s です。' % c)
12345678901234567890 桁数
3.00e+08
2.99792458e+08
光速は 2.99792458e+08 m/s です。

文字型の書式指定

文字列の書式指定は '%s' です。

また,以下の例では,書式指定ではなく ten[:n] のように文字列 ten の最初から n 番目までの部分を表示させています。

In [57]:
ten = '1234567890'
print('%s  桁数' %(ten*2))
print(ten[:5]*2)
print(ten[:6]+ten[:4])
12345678901234567890  桁数
1234512345
1234561234

整数の書式指定

整数の書式指定は '%d' です。'%5d' のように桁数を指定することもできます。

In [58]:
v = [1, 23, 456, 7890]
for row in v:
    print(row)
1
23
456
7890
In [59]:
# 書式指定で表示桁数を5桁とり,右寄せで整数を表示
for row in v:
    print('%5d' % row)
    1
   23
  456
 7890
In [60]:
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() # 改行
1 0 0 0 
0 23 0 0 
0 0 456 0 
0 0 0 7890 
In [61]:
# 書式指定で表示桁数を5桁とり,右寄せで整数を表示
for row in m:
    for col in row:
        print('%5d' % col, end=" ") 
    print()
    1     0     0     0 
    0    23     0     0 
    0     0   456     0 
    0     0     0  7890 

さらに Python を活用するために

この Notebook では,プログラミング言語として共通の部分に焦点を絞った Python の使い方について解説しました。

さらに発展的な Python の活用例については,以下のページを参考にしてください。

また,本家サイトのドキュメントは以下から。