Return to コンピュータ演習

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

エンゲル係数を計算して表示する簡単な例から始めて,Python によるプログラミングの極々基本をおさらいします。

update: 2024.11.6

エンゲル係数を求める例

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

消費支出 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]:
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 天文単位(1 au)の距離を光速 c で進むのに要する時間は何時間何分何秒か。ただし,

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

割り算 / と整数除算 // の値を比べて,小数点以下を無視すれば整数除算の答えを使ってよいことを確認して…

参考:

In [15]:
149597870700 / 299792458
Out[15]:
499.00478383615643
In [16]:
149597870700 // 299792458
Out[16]:
499

小数点以下は切り捨ててもよいことがわかるから,答えは 499 秒として良いことがわかった。これを分に直すには?

In [ ]:

残りの秒は?

対話型プログラム

文字列の入力 input()

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

数字文字列から数値へ float()

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

In [17]:
spending = input("消費支出?(半角数字で入力後,Enter キーを押す) ")
food = input("飲食費?(半角数字で入力後,Enter キーを押す) ")
eng = float(food) * 100 / float(spending)
print("エンゲル係数は", eng)
消費支出?(半角数字で入力後,Enter キーを押す) 110000
飲食費?(半角数字で入力後,Enter キーを押す) 37000
エンゲル係数は 33.63636363636363

関数の定義と呼び出し

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

以下の例では,はじめにエンゲル係数を計算する関数 calc() を定義し,あとのセルでその関数を呼び出しています。

関数定義の書式は以下の通りです。

def 文の次の行からのインデント(字下げ)に注意。Python ではインデントに大事な意味があります。

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

# 上で定義された関数 calc() を呼び出す
eng = calc(spending, food)
print("エンゲル係数は", eng)
消費支出? 110000
飲食費? 37000
エンゲル係数は 33.63636363636363

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

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

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

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

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

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

○練習 2

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

ヒント:消費支出 spending が入力されれば,エンゲル係数が 20 となる食費 food20 は以下のように計算される。

food20 = spending * 0.2
In [ ]:

数学関数を使う

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

math の import

In [22]:
import math

円周率 math.pi

数学定数の円周率 math.pi なども利用できます。

In [23]:
print("π =", math.pi)
π = 3.141592653589793

三角関数 math.sin() math.cos() math.tan()

$\displaystyle \sin \frac{\pi}{2}, \ \cos \frac{\pi}{3}, \ \tan \frac{\pi}{4}$ の値を一気に表示させてみます。

In [24]:
math.sin(math.pi/2), math.cos(math.pi/3), math.tan(math.pi/4)
Out[24]:
(1.0, 0.5000000000000001, 0.9999999999999999)
度からラジアンへ math.radians()

角度の単位として度(°)を使いたいときは,math.radians() でラジアンになおしてから引数として関数に入れます。

$\displaystyle \sin 90^{\circ}, \ \cos 60^{\circ}, \ \tan 45^{\circ}$ の値を一気に表示させてみます。

In [25]:
r90 = math.radians(90)
r60 = math.radians(60)
r45 = math.radians(45)

math.sin(r90), math.cos(r60), math.tan(r45)
Out[25]:
(1.0, 0.5000000000000001, 0.9999999999999999)

参考:math 以外の数学関数

NumPy

三角関数などの数学関数は,標準ライブラリである math 以外にも,numpysympy などのライブラリでも定義されています。詳細は,それぞれが必要になったときにでも…

In [26]:
import numpy as np    # np. をつけて使う

$\displaystyle \sin \frac{\pi}{2}, \ \cos \frac{\pi}{3}, \ \tan \frac{\pi}{4}$ の値を一気に表示させてみます。

In [27]:
# numpy の円周率は np.pi

np.sin(np.pi/2), np.cos(np.pi/3), np.tan(np.pi/4)
Out[27]:
(1.0, 0.5000000000000001, 1.0)

numpy の数学関数は,リストを引数に入れることもできます。

$\displaystyle \sin 30^{\circ}, \ \sin 45^{\circ}, \ \sin 60^{\circ}$ の値を一気に表示させてみます。

In [28]:
# numpy の sin() は以下のようにリストでも可

angles = [30, 45, 60]
print(np.sin(np.radians(angles)))
[0.5        0.70710678 0.8660254 ]
SymPy
In [29]:
from sympy.abc import *
from sympy import *   # 何もつけずに使う
In [30]:
# sympy の円周率

pi
Out[30]:
$\displaystyle \pi$
In [31]:
# sympy の sin() は厳密な答えを返す

sin(pi/6), sin(pi/4), sin(pi/3)
Out[31]:
(1/2, sqrt(2)/2, sqrt(3)/2)
In [32]:
# sympy の sin() は微分もできる

diff(sin(x), x)
Out[32]:
$\displaystyle \cos{\left(x \right)}$

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

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

In [33]:
def engel():
    spending = input("消費支出?")
    food = input("飲食費?")
   
    # 上で定義された関数 calc() を呼び出す
    eng = calc(spending, food)
    print("エンゲル係数は", math.floor(eng), 
          "です。(小数点以下切り捨て)")
In [34]:
engel()
消費支出?110000
飲食費?37000
エンゲル係数は 33 です。(小数点以下切り捨て)

参考:math で使える数学関数

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

○練習 3

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

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

In [ ]:

参考:数値の丸め round()

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

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

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

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

○練習 4

1 天文単位(1 au)の距離を光速 c で進むのに要する時間は,小数点以下を丸めると何秒か?

まず,round() を使って秒をあらわせ。次にそれが何時間何分何秒となるか,計算して表示せよ。

条件分岐

if

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

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

In [37]:
def engel():
    spending = input("消費支出? ")
    food = input("飲食費? ")
    
    # 上で定義された calc() を呼び出す
    eng = calc(spending, food)
    if eng >= 40:
        print("エンゲル係数は", round(eng), 
              "です。高い値です。")
    else:
        print("エンゲル係数は", round(eng), 
              "です。")       
In [38]:
# spending = 10000, food = 4500 など,
# eng >= 40 となるような値を入力
engel()
消費支出? 10000
飲食費? 4500
エンゲル係数は 45 です。高い値です。
In [39]:
# spending = 10000, food = 1500 など,
# eng >= 40 とならないような値を入力
engel()
消費支出? 10000
飲食費? 1500
エンゲル係数は 15 です。

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

if 文の次の行からのインデント(字下げ)に注意。Python ではインデントに大事な意味があります。

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

比較演算

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

a == b  $a = b$(= は代入,区別して)
a != b  $a \neq b$
a < b  $a < b$
a <= b  $a \leq b$
a > b   $a > b$
a >= b  $a \geq b$

○練習 5

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

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

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

In [ ]:

繰り返し処理

while

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

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

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

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

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

while 文の次の行からのインデント(字下げ)に注意。Python ではインデントに大事な意味があります。

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

for

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

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

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

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

補足:range() 関数と for

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

for 文の次の行からのインデント(字下げ)に注意。Python ではインデントに大事な意味があります。

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

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

range(5)range(0, 5, 1) のことですから,0 から 5 まで表示されるかと思いきや…

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

0 以上 5 未満ですから,0, 1, …, 4 までの表示になります。

参考:range() の終端値

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

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

In [ ]:

例:数列の和

$\displaystyle \sum_{n = 1}^{10} n$ を繰り返し処理の練習として求めてみます。

In [43]:
# 繰り返し処理 while の練習として
answer = 0
i = 1
while i <= 10:
    answer = answer + i
    i = i + 1
print(answer)
55
In [44]:
# 繰り返し処理 for の練習として
answer = 0
for i in range(1, 11):
    answer = answer + i
print(answer)
55

参考:再帰的定義関数

参考までに,再帰的定義関数を使った方法について。

$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}

自分自身の一つ前の値を使って自分自身を定義する。このように定義された関数を再帰的定義関数という。

In [45]:
def mysum(n):
    if n == 1:
        return 1
    else:
        return mysum(n-1) + n

print(mysum(10))
55

○練習 6

以下の値を求める関数 mysum2(n) を複数の方法で作成し,$n=10$ および $n=100$ の場合を求めよ。

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

In [46]:
# 繰り返し処理 while を使った作成例

def mysum2(n):
    answer = 0
    i = 1
    while i <= n:
        answer = answer + i**2
        i = i + 1
    return answer

print("方法 0:while を使う例")
print(mysum2(10))
print(mysum2(100))

# みなさんはこれ以外の方法で作成するのですよ。
方法 0:while を使う例
385
338350

インクリメント += (デクリメント -=)

answer = answer + i**2i = i + 1 は「左辺」=「右辺」という意味の「
等式」としては全く成り立ちませんが,Python(を含む多くのプログラミング言語)では,
answer = answer + i**2 とは
その時点で変数 answer に代入されている値に i**2 を加えた値を,
あらためて answer に代入する,という意味になります。

Python では,同様の内容を以下のように書くこともできます。

answer = answer + i**2 $\Rightarrow$ answer += i**2

  • answer の現在の値に i**2 を加えた結果を answer 自身に代入する。

i = i + 1 $\Rightarrow$ i += 1

  • i の現在の値に 1 を加えた結果を i 自身に代入する。

+= を使って書き直した関数 mysum2(n) の例:

In [47]:
def mysum2(n):
    answer = 0
    i = 1
    while i <= n:
        answer += i**2
        i += 1
    return answer

print("方法 0:while を使う例")
print(mysum2(10))
print(mysum2(100))
方法 0:while を使う例
385
338350

もうちょっと表示を工夫してみます。

In [48]:
for n in [10, 100]:
    print('mysum2(',n,') = ', mysum2(n), sep='')
mysum2(10) = 385
mysum2(100) = 338350

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

リスト (list)

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

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

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

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

また,要素の個数が多くて,いちいち全てを書くのが面倒な場合は,以下のように

for i in range()

を使って書くこともできます。

In [51]:
[i for i in range(10, 0, -1)]
Out[51]:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

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

In [52]:
print("成分を横に表示")
for i in a:
    # 改行せずにスペースを空けて表示させる例
    print(i, end='  ')

# 最後は改行のみ
print()
成分を横に表示
5  4  3  2  1  
In [53]:
print("成分を縦に表示")
for i in a:
    print(i)
成分を縦に表示
5
4
3
2
1

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

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

readlines() で読み込み

以下のようなテキストファイル mydata.txt を用意します。授業では予めダウンロードして,このノートブックファイルと同じフォルダーに保存しておきます。

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

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

In [56]:
f = open("mydata.txt")
c = f.readlines()

readlines() で読み込んだ値を代入した変数 c はリストになっています。

In [57]:
type(c)
Out[57]:
list

数値として読み込んでくれるかな?と期待しましたが,リスト c の要素は改行コード \n を含んだ文字列になっています。これではちょっと使いにくいです。

In [58]:
print(c)
['1\n', '2\n', '3\n', '4\n', '5\n', '6']

NumPy 配列 (numpy.ndarray)

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

その際に,Numpy 配列numpy.ndarray(以下,配列)というデータ型が出てきます。

NumPy の import

In [59]:
import numpy as np # 必要なとき,最初に import する。

配列は以下のように np.array() を使って定義します。print() による配列の表示は,リストの場合と少し違っていて,カンマ区切りがありません。

In [60]:
A = np.array([5, 4, 3, 2, 1])
print("成分を一挙に表示")
print(A)
成分を一挙に表示
[5 4 3 2 1]

このように定義された A のデータ型を確認すると,list ではなく numpy.ndarray であることがわかります。

In [61]:
type(A)
Out[61]:
numpy.ndarray

np.loadtxt() で読み込み

ファイルから数値データを読み込むことを前提にして,以下のように numpyloadtxt() を使ってみます。

loadtxt() で読み込んだデータを代入した変数 C は配列(numpy.ndarray)となります。

In [62]:
C = np.loadtxt('mydata.txt')
type(C)
Out[62]:
numpy.ndarray

np.loadtxt() はデフォルトでは実数として読み込みます。

In [63]:
print(C)
[1. 2. 3. 4. 5. 6.]

整数として読み込む場合は,以下のように dtype='int' オプションを設定します。

In [64]:
C = np.loadtxt('mydata.txt', dtype='int')
print(C)
[1 2 3 4 5 6]
総和 sum(), 要素数 len(), 最大値 max(), 最小値 min()

リストや配列の要素の総和,要素数,平均値,最大値,最小値は以下のようにして計算できます。

In [65]:
# 要素の総和,平均,最大値,最小値を出力します。
print("要素の総和は", sum(C))
print("要素数は    ", len(C))
print("要素の平均は", sum(C)/len(C))
print("最大値は    ", max(C))
print("最小値は    ", min(C))
要素の総和は 21
要素数は     6
要素の平均は 3.5
最大値は     6
最小値は     1

次は,以下のような「6行2列」のデータが入ったテキストファイル mydata2.txt を読み込む例です。授業では予めダウンロードして,このノートブックファイルと同じフォルダーに保存しておきます。

In [66]:
! cat mydata2.txt
0  0
1  1
2  4
3  9
4 16
5 25
In [67]:
B = np.loadtxt('mydata2.txt', dtype='int')
print(B)
[[ 0  0]
 [ 1  1]
 [ 2  4]
 [ 3  9]
 [ 4 16]
 [ 5 25]]

np.savetxt() で書き込み

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

In [68]:
# まず,書き込む配列を作成します。
x = []
for i in range(6):
    xi = 0.1 * i
    x.append([xi, np.sin(xi)])

# 配列 x をファイル myoutput.txt に書き込みます。
np.savetxt('myoutput.txt', x)
In [69]:
# myoutput.txt の内容を表示します。
! cat myoutput.txt
0.000000000000000000e+00 0.000000000000000000e+00
1.000000000000000056e-01 9.983341664682816863e-02
2.000000000000000111e-01 1.986693307950612442e-01
3.000000000000000444e-01 2.955202066613396572e-01
4.000000000000000222e-01 3.894183423086505225e-01
5.000000000000000000e-01 4.794255386042029499e-01

リスト・配列の相互変換

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

まずは(入れ子になった)リスト m を作成します。

In [70]:
m = [[0, 0], [1, 1], [2, 4]]
print(m)
[[0, 0], [1, 1], [2, 4]]
In [71]:
type(m)
Out[71]:
list

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

In [72]:
M = np.array(m)
type(M)
Out[72]:
numpy.ndarray
In [73]:
print(M)
[[0 0]
 [1 1]
 [2 4]]
配列からリストへ tolist()

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

In [74]:
mm = M.tolist()
type(mm)
Out[74]:
list
In [75]:
print(mm)
[[0, 0], [1, 1], [2, 4]]

参考:リストと配列の違い

成分(要素)の参照

2次元的に入れ子になったリストの場合の成分の参照例:

In [76]:
print(m)
[[0, 0], [1, 1], [2, 4]]

3つ目の要素 [2, 4] の2つ目の要素 4 を表示させるには,Python はゼロ始まりだから…

In [77]:
# m の3つ目の要素だから [2]

m[2]
Out[77]:
[2, 4]
In [78]:
# m[2] の2つ目の要素だから [1]

m[2][1]
Out[78]:
4

一方,配列の場合は,

In [79]:
print(M)
[[0 0]
 [1 1]
 [2 4]]

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

In [80]:
# M の3行2列目だから [2, 1]
M[2, 1]
Out[80]:
4
「足し算」とか「掛け算」とか

リスト同士を足したり,定数倍したりすると…

In [81]:
a = [1, 2, 3]
b = [4, 5, 6]

# リストを「2倍」するとは
# a+a, b+b のこと
print('a*2 =', a*2)
print('a+a =', a + a)
print()
print('b*2 =', b*2)
print('b+b =', b + b)
print()

# リストの「足し算」とは
# 要素を追加すること
print('a+b =', a + b)
a*2 = [1, 2, 3, 1, 2, 3]
a+a = [1, 2, 3, 1, 2, 3]

b*2 = [4, 5, 6, 4, 5, 6]
b+b = [4, 5, 6, 4, 5, 6]

a+b = [1, 2, 3, 4, 5, 6]

一方,配列 numpy.ndarray 同士の足し算や定数倍は…

In [82]:
A = np.array([1, 2, 3])
B = np.array([4, 5, 6])

# 配列を「2倍」すると各成分が2倍に
print('A*2 = ', A*2)
print('B*2 = ', B*2)

# 配列の「足し算」は各成分同士の足し算に
print('A+B = ', A + B)
A*2 =  [2 4 6]
B*2 =  [ 8 10 12]
A+B =  [5 7 9]

なので,リストの場合は以下のように追加するだけだが…

In [83]:
[1, 2, 3] + [4, 5]
Out[83]:
[1, 2, 3, 4, 5]

配列の場合は,成分の個数が異なると足し算ができずエラーとなる。

In [84]:
np.array([1, 2, 3]) + np.array([4, 5])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-84-2f42f0c89925> in <module>
----> 1 np.array([1, 2, 3]) + np.array([4, 5])

ValueError: operands could not be broadcast together with shapes (3,) (2,)

書式指定

浮動小数点数の書式指定 '%f'

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

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

In [85]:
import numpy as np  # 最初に1回やればいいが,念のため

$\sqrt{2}$ の値をいくつか書式指定して表示します。

In [86]:
sqrtwo = np.sqrt(2)
ten = '1234567890'
print(ten * 2, "桁数")
print('-'*26)

sqrtwo = np.sqrt(2)
print(sqrtwo)      # NumPy の sqrt は全18桁
12345678901234567890 桁数
--------------------------
1.4142135623730951
In [87]:
print(ten * 2, "桁数")
print('-'*26)

print('%8.4f' % sqrtwo)
print('%8.5f' % sqrtwo)
print('%8.6f' % sqrtwo)
print('%17.15f' % sqrtwo)
print('2の平方根は %17.15f です。' % sqrtwo)
12345678901234567890 桁数
--------------------------
  1.4142
 1.41421
1.414214
1.414213562373095
2の平方根は 1.414213562373095 です。

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

In [88]:
print('%.5f' % sqrtwo)
print('%.15f' % sqrtwo)
1.41421
1.414213562373095

○練習 7

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

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

指数表式の指定 '%e'

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

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

In [90]:
c = 299792458
print('%9.2e' % c)
print('%15.8e' % c)
print('光速は %15.8e m/s です。' % c)
 3.00e+08
 2.99792458e+08
光速は  2.99792458e+08 m/s です。

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

In [91]:
print('%.2e' % c)
print('%.8e' % c)
print('光速は %.8e m/s です。' % c)
3.00e+08
2.99792458e+08
光速は 2.99792458e+08 m/s です。

'%f' または '%e' となる '%g'

'%g' は数値の大きさによって,'%f' または '%e' (ちょっと違うけどだいたい似たような形)になります。

In [92]:
print('%.2g' % sqrtwo)
print('%.2f' % sqrtwo)

print('%.5g' % c)
print('%.5e' % c)
1.4
1.41
2.9979e+08
2.99792e+08

文字型の書式指定 '%s'

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

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

In [93]:
ten = '1234567890'
print('%s  桁数' %(ten*2))
print(ten[:6], ten[:4])
print(ten[:6], ten[:4], sep='') # スペースで間を空けない
12345678901234567890  桁数
123456 1234
1234561234

整数の書式指定 '%d'

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

In [94]:
v = [1, 23, 456, 7890]
for i in v:
    print(i)
1
23
456
7890
In [95]:
# 書式指定で表示桁数を5桁とり,右寄せで整数を表示
for i in v:
    print('%5d' % i)
    1
   23
  456
 7890

整数値を左から 0 詰めで表示する例。

In [96]:
# 表示桁数を5桁とり,左から0詰めして右寄せで整数を表示
for i in v:
    print('%05d' % i)
00001
00023
00456
07890
In [97]:
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='  ') # 改行せずにスペース2個あけて出力
    print() # 改行
1  0  0  0  
0  23  0  0  
0  0  456  0  
0  0  0  7890  
In [98]:
# 書式指定で表示桁数を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 

以上のようなことが面倒臭いなら,np.array() で配列 numpy.ndarray に変換しておくと,表示が少し整います。

In [99]:
M = np.array(m)
print(M)
[[   1    0    0    0]
 [   0   23    0    0]
 [   0    0  456    0]
 [   0    0    0 7890]]

参考:0詰めの連番文字列の作成例 .zfill()

連番ファイル名の作成とかに。整数値の0詰めは '%05d' のように書式設定しますが,(ファイル名等に使う)文字列の0詰めは,以下のように整数 i を一旦 str(i) で文字列にして .zfill() で0詰めにできます。

In [100]:
for i in range(4):
    filename = 'fig'+str(i).zfill(3)+'.png'
    print(filename)
fig000.png
fig001.png
fig002.png
fig003.png

別解:

特に .zfill() を使わなくても,書式指定の print() の中身をそのまま文字列変数に代入すればよいようです。

In [101]:
for i in range(4):
    filename = 'fig%03d.png' % i
    print(filename)
fig000.png
fig001.png
fig002.png
fig003.png

複数のグラフに,それぞれの場合の数値(パラメータ等)を入れた凡例をつけて描く際にも,この方法が使えます。

In [102]:
for th in [30, 45, 60]:
    key = 'θ = %2d°, %5.3f ラジアン' % (th, np.radians(th))
    print(key)
θ = 30°, 0.524 ラジアン
θ = 45°, 0.785 ラジアン
θ = 60°, 1.047 ラジアン

参考:配列の表示の際の書式指定 np.set_printoptions()

以下のようなリストの場合,それぞれの要素の小数点以下をもう少し少なく表示させようとすると,ちょっと工夫がいる。

In [103]:
u = [0, np.sin(0.1), np.sin(0.2)]
print(u)
[0, 0.09983341664682817, 0.19866933079506124]

そんなときは,手っ取り早く配列に変換して表示させてみると…

In [104]:
U = np.array(u)
print(U)
[0.         0.09983342 0.19866933]

デフォルトでは以下のように設定されている。

In [105]:
print(np.get_printoptions())
{'edgeitems': 3, 'threshold': 1000, 'floatmode': 'maxprec', 'precision': 8, 'suppress': False, 'linewidth': 75, 'nanstr': 'nan', 'infstr': 'inf', 'sign': '-', 'formatter': None, 'legacy': False}

以下では,小数点以下の桁数を precision=5 として5桁とし,floatmode='fixed' で小数点以下の表示を5桁固定にしている。(0 の場合であっても 0 で埋めて小数点以下5桁を固定表示。)

In [106]:
np.set_printoptions(precision=5, floatmode='fixed')
print(U)
[0.00000 0.09983 0.19867]