download notebook

Pythonの基本#

(Colab only)

このノートブックの内容は自由に編集,事項していただいて構いません 本ノートブックは統計学を学上で最低限の知識を著者の偏見でまとめたものです. もしpythonに公式ドキュメントを参照したい場合は日本語翻訳がありますので

を参照することができます.公式のpythonチュートリアルもありますが,内容はかなり膨大です.

§ 1 数の基本演算#

pythonにおける数の四則演算は加法演算子:+,減法演算子:-,乗法演算子:*除法演算子(÷):/で与えれます.まずは電卓としてpythonを使ってみよう

In:
1 + 2
3
In:
2 - 3
-1
In:
4 * 5
20
In:
1 / 3
0.3333333333333333

pythonで虚数単位\(i=\sqrt{-1}\)1j\((\neq j)\)で表され,複素数\(1 + 2\mathrm{i}\)は次のように表記します.

In:
1 + 2j
(1+2j)

複数の計算を一つのコードセルにまとめ記載することも可能です.

In:
4 + 4
5 + 5
10

注意

複数のコードを1つのコードセルに記載した場合,コードセルの実行で出力されるものは最後の行の結果のみであるということに注意してください.最終行以外も出力したい場合は明示的に以下のようにする必要があります.

In:
print(4 + 4)
print(5 + 5)
8
10

これらの四則演算を組み合わせて複雑な式を評価することも可能です.括弧がない式において,乗法と除法は加法と減法より先に計算されます.

In:
5 + 5 * 5
30

明示的に括弧をつけると演算の順番を変更することができます

In:
(5 + 5) * 5
50

数学では不要な括弧は省略されますが,プログラミングにおいて括弧は重要で省略してはならない場合が多いです.例えば\(\frac{3 + 3}{1 + 2} = 2\)をプログラミンで評価したい場合は次のように表記します

In:
(3 + 3) / (1 + 2)
2.0

もし,括弧を省略すると

In:
3 + 3 / 1 + 2
8.0

\(3 + \frac{3}{1} + 2 = 8\)と解釈され

In:
3 + 3 /(1 + 2)
4.0

\(3 + \frac{3}{1+2} = 4\)と解釈されてしまう.

ちなみに除算(÷):/を含む式を実行すると結果は小数点表記(浮動小数点)で表示されていることに気がつくであろうか. 整数(Integer)同士について加法,減法,乗法の演算結果は整数であるが,除法は一般に整数ではなく有理数になる.コンピューターでは一般に有理数(と無理数)を含めた数を浮動小数(Float)で扱い,整数はIntegerとして明確に区別する.整数はint型(type),浮動小数はfloat型として扱う.コード中で1とした場合はint型の1を意味し,1.0とした場合はFloatの型の1.0を意味します.型の調べ方は type 関数を利用することで可能です.

In:
type(1), type(1.0)
(int, float)

整数除算(例えば\(\frac{5}{2} = 2\) 余り\(1\))の計算を行いたい場合は 整数除算演算子://,余りは剰余演算子:%で求めることができます

In:
5 // 2, 5 % 2
(2, 1)

冪乗の計算は指数演算子**を用います.例えば\(2^4=16\)

In:
2 ** 4
16

指数が複数の式で構成される場合も必ず括弧を付けてください.例えば,\(2^{2 + 1}\)の計算の場合

In:
2 ** (2 + 1)
8

ですが,括弧を付けないと

In:
2 ** 2 + 1
5

これは\(2^2 + 1\)を意味します.\(10^{~3}\)10**3ともかけますが1e3と書くこともできます.この表記は\(1\times\mathrm{e}3\)を意味し,eは底が10の指数(exponent)の頭文字で指数が3であることを意味しています.3e2と書いた時は\(3\times 10^{2} = 300\)を意味します.4e-3と書いた時は\(1\times 10^{-3} = 1/1000 = 0.001\)を表します.

In:
print(1e3)
print(3e2)
print(1e-3)
1000.0
300.0
0.001

§ 2 変数と代入#

一つ前のセクションではpythonを電卓として利用したが,コードを書く上で変数を使えた方が便利である.変数\(x\)に1を代入する操作は代入演算子:=で実現し

In:
x = 1
x
1

上のコードで1行目は 変数x に 1を代入する.1行目の出力は何もないため,2行目を追記してでxに代入された値を表示させている.2行目のコードを明示的にprint(x)としても良い.

注意

プログラミングにおける“\(=\)”は右辺の評価を左辺に代入する操作を意味し,数学の等号“\(=\)”(等しい)とは異なる. 例えば\(x\)を数として,数学では式\(x=x+1\)\(0=1\)となり矛盾するため式\(x=x+1\)は偽の命題である. 一方プログラミングにおいて\(x=x+1\)は右辺の変数\(x\)の値に1を加え,その結果を左辺の変数\(x\)に再代入(上書き)する操作を意味する.

In:
x = x + 1
x
2

他方\(x + 1 = 2\)のような表記は左辺が“\(x+1\)”を一括りとした変数として扱えないのでプログラミングではSyntaxError(構文エラー)となる.

In:
(x + 1) = 2
  File "/tmp/ipython-input-21-2315787992.py", line 1
    (x + 1) = 2
     ^
SyntaxError: cannot assign to expression here. Maybe you meant '==' instead of '='?

変数は1文字以外でもよく,下記は”\(abc\)” (\(\neq a\times b\times c\))という3文字の変数名に3を代入する操作である.

In:
abc = 3

一般に変数に何らかの意味がある場合は,その意味がわかるような意味がある場合は,意味がわかるような変数名をつけるべきだが,変数名にスペースを含めることはできない.そのため複数の単語を繋ぐ場合は_などでつなげたり,単語の1文字目を大文字にしたり,思い切ってtemporal -> tmp; argument -> argのように省略したりする工夫が必要である.なお-は引き算を意味するため利用できない.

変数の略記(Abbriviation)はAbbreviations in codeなどが参考になるであろうが,絶対的な基準がある訳ではないので固執する必要性はない.

In:
this_is_a_extreamly_marvelous_important_number = 1
ThisIsAExtreamlyMarvelousImportantNumber = 1

意味を持たない(使い捨てるような)変数で値が実数ならば\(x\)\(y\)\(z\)などを用い,値が整数ならば\(i\)\(j\)\(k\)\(l\)などを利用する場合が多い.記号jは単独で使うと変数を意味するが2jのように数字がjの前についていると複素数を意味することに注意が必要である.

In:
j = 3
j
3
In:
2j, type(2j)
(2j, complex)

§ 3 比較演算子#

プログラミングにおいて=は右辺の評価を左辺の変数に代入する代入演算子であった.

では,左辺と右辺が等しいかどうか比較したい場合は,比較演算子:==を用る.比較演算子は比較した結果が「真」であればTrueを返し,「偽」であればFalseを返す.

In:
3 + 2 == 5
True
In:
3 + 2 == 4
False

代表的な比較演算子は

  • ==:等しい

  • !=:等しくない

  • < :右辺の方が大きい

  • > :左辺の方が大きい

  • <= :右辺の方が大きいか等しい(\(\leqq\))

  • >= :左辺の方が大きいかか等しい(\(\geqq\))

In:
a = 3
b = 4
a <= b
True
In:
b > a
True

上の例題わかるように,式の比較はTrue/Falseのいずれかを返す論理命題になっています.論理命題A,Bいずれも真であればTrue,A,Bのいずれかが偽であればFalseを返す論理式は

A & B (AかつB)

で与えられ AまたはBが真であればTrue,A,Bいずれも偽であればFalseを返す論理式は

A | B (AまたはB)

で与えることができます.&| をで式を比較する場合は必ず括弧()で論理式をくくる必要があります.

In:
(3 < 5) & (5 < 7)
True
In:
(3 < 5) & (8 < 7)
False
In:
(3 < 5) | (8 < 7)
True
In:
(6 < 5) | (8 < 7)
False

§ 4 関数#

§ 4.1 定義済み関数#

$:raw-latex:sin `x, :raw-latex:cos`, e^x (= :raw-latex:`\exp `x), :raw-latex:`log x $など基本的な数学関数は`numpyと呼ばれるパッケージで関数が定義されています.pythonでパッケージを利用するには import <PackageName> as <NickName>のようにしてパッケージをインポートする必要があります.ニックネームはつけなくても構いませんが,numpynpと省略するのが一般的です.

In:
import numpy as np

numpyで定義された関数を呼び出すにはnp.sin(0)のようにします.

In:
print( np.sin(0) )
print( np.cos(0) )
0.0
1.0

平方根(square root)はnp.sqrtもしくは**(1/2)で評価することができます

In:
print( np.sqrt(2) )
print( 2 ** (1/2) )
1.4142135623730951
1.4142135623730951

ネピア数(オイラー数)\(e\)は底が\(e\)の指数関数\(e^x = \exp x\)\(x=1\)を代入した値なので以下のようにすれば良い.

In:
e = np.exp(1)
print(e)
2.718281828459045

numpylog関数は底が\(e\)の自然対数,log10は底が\(10\)の常用対数を意味します.

In:
print( np.log(e) )
print( np.log10(10**3) )
1.0
3.0

print()も関数で,これは括弧の中に代入した値(関数の引数)を文字列として出力する関数です.print関数の引数は複数代入することができ

In:
print( "sin(0) = ", np.sin(0) )
sin(0) =  0.0

のように記載することもできます.上記の例でprint関数の1番目の引数(第1引数)“sin(0) =” は"で括られた内容が単なる文字列であることを意味しています.2番目の引数は\(\sin(0)\)の値をnumpyパッケージのsin関数で評価した結果を代入しています.

文字列も変数に代入することが可能で以下のように示すこともできます.

In:
fujisan1 = "あたまを雲の上に出し"
fujisan2 = "四方の山を見下ろして"
print(fujisan1, fujisan2, "...")
あたまを雲の上に出し 四方の山を見下ろして ...

また複数の変数の値をprintで出力する場合,コンマ,が増えて読みづらくなってしまう場合があります.そのような場合は次のようにすると視認性が高まります.

In:
a = 1
b = 2
c = 3
print( f"a = {a}, b = {b} のとき,a + b = {c} で与えられる" ) #最初の"の前にfをつけると{}で括った変数の値をそのまま出力できる
a = 1, b = 2 のとき,a + b = 3 で与えられる

numpyでの三角関数の引数はラジアンを用いて評価されます.そのため\(\theta=\pi/2\)での\(\sin \theta\), :raw-latex:`\cos `:raw-latex:`theta`$の値は

In:
print("π≈", np.pi) # ≈は近似値を意味する.
print( np.sin(np.pi/2) )
print( np.cos(np.pi/2) )
π≈ 3.141592653589793
1.0
6.123233995736766e-17

ここでnp.pinumpyで定義された\(\pi\)の値です.\(\pi\)は無理数なので小数点で表示する場合は無限に長い桁が必要です.しかし,コンピューター上で数は有限桁しか扱うことができず一般には少数部第16位の桁(\(10^{-16}\))以降は打ち切られています(無理数を有理数で近似している).np.cos(np.pi/2)を評価すると6.123233995736766e-17と表示されていますが,この値は\(6.123233995736766\times 10^{~-17}\)を意味します.\(\cos(\frac{\pi}{2})\)は厳密には\(0\)ですがコンピューター上では\(10^{-16}\)以下の実数は原則扱えない為,6.123233995736766e-17と表示されてしまいます.この係数6.123233995736766の値に意味はなく(ランダムに近い値で)重要なのはe-17 = \(10^{-17}\)です.これは\(10^{-16}\)より小さいので,コンピューター的にはほとんど0に近い値ということを示しています.

したがってnp.cos(np.pi/2)0はコンピューター上で厳密に一致はしません.

In:
np.cos(np.pi/2) == 0
np.False_

しかしコンピューター上の計算誤差を考慮して0に近い(close)かnumpyのisclose関数で調べることができ

In:
np.isclose(np.cos(np.pi/2), 0)
np.True_

となりTrueが返ってきているので,np.cos(np.pi/2)の値はコンピューター上では0に見做せることを意味しています.別の例を与えます.\(\sqrt{2}\)は2乗すると2になる無理数ですが,計算機では16桁程度の有限桁しか扱えない為\((\sqrt{2})^2\)の計算を行うと誤差が生じます.

In:
two = np.sqrt(2) * np.sqrt(2)
print(two)
2.0000000000000004

この値を真の値からとの誤差を\(|\mathrm{two} -2|\)で評価すると,計算機で発生している誤差の大きさを見積もることができます.数の絶対値(absolute value)はnp.absで定義されています.

In:
np.abs(two - 2)
np.float64(4.440892098500626e-16)

なお,4.440892098500626の1桁目以降の値は\(10^{-16}\)より小さいため信頼できない(ほとんどランダム)な値が入っています.

§ 4.2ユーザー定義関数#

基本的な関数はnumpyを使うことで利用することができますが,自分で定義した(ユーザー定義関数)を使うことが多々ある.関数の定義は

def 関数名(引数):
  <手続き>
  return 返り値

です.defの次の行以降を<tab>キーでインデントを下げて(def行の:をつけ忘れなければ自動的にインドデントが下がるので明示的に<tabキーを入力する必要性はない)具体的な関数の定義式を定義する.例えば\(f(x) = 3x\)と定義したい場合は

In:
def f(x):
  return 3 * x
In:
print( f(1) )
print( f(2) )
3
6

return行のコードが(ユーザー定義)関数\(f\)の返り値である.返り値とは\(y=f(x)\)と表記した際の\(y\)(出力)に相当する量です.関数\(f\)の入力\(x\)\(f\)の引数と呼ばれます.関数の引数を含めdefコードの中で使用された変数はdefの中でのみ有効な局所変数です.

In:
x = 100
print( f(x=1) ) # 明示的にx=1をfに代入しているがこの代入は関数の中でのみ有効
print( x )
3
100
In:
def f(x):
  z = 1
  return x + z
In:
print( f(3) )
z
4
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

/tmp/ipython-input-51-2391348818.py in <cell line: 0>()
      1 print( f(3) )
----> 2 z


NameError: name 'z' is not defined

注意

同じ関数名で関数を定義すると,古い関数の定義は削除され新しい関数の定義で上書きされます.

関数の引数は省略することも可能ですが,関数を呼び出す際には,関数名の後に必ず括弧()をつける必要があります.括弧()を付けないと関数自身のオブジェクトを意味します.

In:
def euler():
  return np.exp(1)

print(euler())
print(euler)
2.718281828459045
<function euler at 0x788851521ee0>

関数の引数は複数設定することが可能です.例えば与えらえた2つの数を足した 結果を変える関数,myAddは次のように定義できます.

In:
def myAdd(a, b):
  return a + b
In:
print( myAdd(1, 2) )
print( myAdd(2.0, 3.0) )
print( myAdd(np.sqrt(2), 1) )
3
5.0
2.414213562373095

関数の定義において入力(引数)および,出力(返り値)は数は何個でも構わない.例えば,整数除算と剰余(余り)の2つの要素を返り値に指定したければ,","で区切って返り値を複数指定すれば良い.

In:
def myDivMod(a, b):
  return a // b, a % b
In:
myDivMod(5, 3)
(1, 2)

§ 5 リスト(list),配列(array),データフレーム(dataframe)#

§ 5.1 リスト(list)と1次元配列(array)#

リスト(list)や配列(array)を使って複数のもの(数以外も可能)を一つにまとめることができます.複数のものが数以外のものが含まれる場合は,リストを用いて次のようにまとめることができます.

In:
lst = [1, 1, 5, "this", "is", "a", "list", np.sin]
print(lst)
[1, 1, 5, 'this', 'is', 'a', 'list', <ufunc 'sin'>]

リストを構成する要素はほとんどなんでも良く非常に柔軟である一方, 複数のものがのみで構成される場合はnumpyarray(配列)を用いた方が効率的で,数に特化した機能(総和,平均などの計算など)を利用することができます. (正確にはarraylistのように数以外の要素を扱うことも可能)

In:
import numpy as np
arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(arr)
print( arr.sum() ) # 総和の計算
print( arr.mean() ) # 平均の計算
print( arr.var() ) # 分散の計算
print( arr.std() ) # 標準偏差の計算
print( arr.min() ) # 最小値
print( arr.max() ) # 最大値
[ 1  2  3  4  5  6  7  8  9 10]
55
5.5
8.25
2.8722813232690143
1
10

ここでarr.sum()numpyarrayで定義されているsum関数を引数なしで呼び出しているように見えますが.正確にはsum関数にはの引数にはarrが代入されており,以下の操作とarr.sum()は完全に等しい操作になっています.

In:
print( np.sum(arr) ) # 総和の計算
print( np.mean(arr) ) # 平均の計算
print( np.var(arr) ) # 分散の計算
print( np.std(arr) ) # 標準偏差の計算
print( np.min(arr) ) # 最小値
print( np.max(arr) ) # 最大値
55
5.5
8.25
2.8722813232690143
1
10

listやarrayの要素の数はlen(length)関数を用いて調べることができます.

In:
print( len(lst) )
print( len(arr) )
8
10

listやarrayの\(i\)番目の要素を取り出す場合はlst[i-1], ary[i-1]で取り出すことができます. 長さ\(n\)のlistおよびarrayの最初の要素番号(index)は\(0\)番目から始まり\(n-1\)で終わります.

In:
print( arr[0] )
print( arr[len(arr) - 1] )
1
10

listやarrayの要素を後ろから\(i\)番目のように指定する場合は負のindexで指定することもできます\(i=-1\)の場合は一番最後の要素,\(i=-2\)の場合は後ろから2番目の要素を意味します.

In:
print( lst[-1] )
print( arr[-2] )
<ufunc 'sin'>
9

連続した数の列(数列)を作る関数は良く利用されます.

In:
np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In:
np.arange(0, 10, 2) # 初項2,公差2で (10-1)を超えない数列
array([0, 2, 4, 6, 8])

全く同じ操作をpythonの標準関数rangeでも実現可能ですが,rangeはiteratorと呼ばれるオブジェクトを返し,実際に数列の要素が必要になるまで評価しないため(遅延評価),rangeで生成したlistの要素全てをprintで示すことはできません.

In:
print( range(10) )
print( range(0, 10, 2) )
rng = range(10)
rng[-1]
range(0, 10)
range(0, 10, 2)
9

\(s\)から\(e\)まで\(n\)等分した数列はnumpylinspace (linearly spaced value) を用いてnp.linspace(s, e, n)得ることができます.

In:
np.linspace(0, 1, 10) # 0から1まで10等分した数列
array([0.        , 0.11111111, 0.22222222, 0.33333333, 0.44444444,
       0.55555556, 0.66666667, 0.77777778, 0.88888889, 1.        ])

終点を含めずに\(n\)等分した数列を生成するにはendpoint=Falseのオプションをつければよく,

In:
np.linspace(0, 1, 10, endpoint=False) # 0から1まで,終点1を含めずに10等分した数列
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

なお上の数列と同じ数列はnp.arangeでも生成可能で

In:
np.arange(0, 1, 0.1) # 0から0.1刻みで1を超えない数列
array([0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])

§ 5.2 多次元配列 (n dimensional array)#

複数配列を一つにまとめた方が効率的であることも多い.2つの配列を1つの配列にまとめたものを2次元配列,\(n\)個の配列を一つの配列にまとめたものを多次元配列という. numpyarrayは多次元配列(ndarray)を扱うように設計されています.

In:
x = np.arange(0, 10, 1)
y = x ** 2
arr = np.array([x, y])
arr
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [ 0,  1,  4,  9, 16, 25, 36, 49, 64, 81]])

多次元配列の行と列の大きさ(配列の型)はshapeと呼ばれる変数(属性)に代入されています.shapeはarrayの関数ではなくarrayの属性値(attribution, 変数)なので括弧は不要です.

In:
arr.shape
(2, 10)

これはarrが2行10列の多次元配列(2次元配列であって各配列は10個の要素を持つ)です.\(i\)番目の配列を取り出すにはarr[i, :]のようにします.

In:
print( arr[0,:] ) # 0番目(最初)の配列
print( arr[1,:] ) # 1番目(最初)の配列
[0 1 2 3 4 5 6 7 8 9]
[ 0  1  4  9 16 25 36 49 64 81]

上は2次元配列の例ですが,numpyarrayは多数の配列を要素にとることができます.

In:
x = np.arange(0, 10, 1)
ndarr = np.array([x, x**2, x**3, x**4])
print(ndarr)
print(ndarr.shape)
[[   0    1    2    3    4    5    6    7    8    9]
 [   0    1    4    9   16   25   36   49   64   81]
 [   0    1    8   27   64  125  216  343  512  729]
 [   0    1   16   81  256  625 1296 2401 4096 6561]]
(4, 10)

ただし長さの異なる配列を要素にすることはできません.

In:
x1 = np.linspace(0, 1, 10)
x2 = np.linspace(0, 1, 9)
np.array([x1, x2])
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

/tmp/ipython-input-73-3792097717.py in <cell line: 0>()
      1 x1 = np.linspace(0, 1, 10)
      2 x2 = np.linspace(0, 1, 9)
----> 3 np.array([x1, x2])


ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.

§ 5.3 pandas DataFrame#

numpyarrayは主にを扱う場合は非常に強力ですが,統計学では数以外のデータを扱うことが非常に多いです.python標準のlistを用いて,数以外のデータを扱うことも可能ですが,統計学で必要とする機能が定義されていません.そのため,統計学で用いるようなデータは`pandas <https://pandas.pydata.org/>`__パッケージで定義された`DataFrame <https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html>`__を使うと効率的です.pandasは内部的にnumpyを継承しているため,numpyを使う代わりpandasだけで完結することも可能です.加えてcsvやexcelのデータをpandasから呼び出すこともできます.

pandasパッケージからヒストグラムや箱ひげ図を作図できますが,日本語が文字化けしてしまうので,その対策として下記を実行してjapanize-matplotlibをインストールします(ランタイムが再起動した場合は再度インストールが必要です.).

さっそくpandasDataFrameを使ってみましょう.pandasパッケージはimport pandas as pdとしてインポートすることが一般的です.

In:
!pip install japanize-matplotlib #japanize-matplotlibをinstall
import japanize_matplotlib # 作画の日本語文字化けを防ぐために利用
import pandas as pd

sex = ['M', 'F', 'F', 'M', 'F', 'M', 'M', 'M'] # 性別(M = male, F = female)
weight = [60.3, 50.4, 55.3, 70.2, None, 52.2, 57.5, 55.5] # 体重 Noneは欠損値を意味する
height = [166.2, 160.3, 165.3, 174.2, 171.2, 157.6, 165.5, 163.4] # 身長
df = pd.DataFrame({'性別': sex, '体重': weight, '身長': height})
df
Collecting japanize-matplotlib
  Downloading japanize-matplotlib-1.1.3.tar.gz (4.1 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.1/4.1 MB 34.6 MB/s eta 0:00:00
[?25h  Preparing metadata (setup.py) ... [?25l[?25hdone
Requirement already satisfied: matplotlib in /usr/local/lib/python3.11/dist-packages (from japanize-matplotlib) (3.10.0)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (1.3.2)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (4.58.4)
Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (1.4.8)
Requirement already satisfied: numpy>=1.23 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (2.0.2)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (24.2)
Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (11.2.1)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (3.2.3)
Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.11/dist-packages (from matplotlib->japanize-matplotlib) (2.9.0.post0)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil>=2.7->matplotlib->japanize-matplotlib) (1.17.0)
Building wheels for collected packages: japanize-matplotlib
  Building wheel for japanize-matplotlib (setup.py) ... [?25l[?25hdone
  Created wheel for japanize-matplotlib: filename=japanize_matplotlib-1.1.3-py3-none-any.whl size=4120257 sha256=eba16dcfbabeec23d8151d5a3a07e975a59bf27e992332e312e4f5acdd6a9aca
  Stored in directory: /root/.cache/pip/wheels/da/a1/71/b8faeb93276fed10edffcca20746f1ef6f8d9e071eee8425fc
Successfully built japanize-matplotlib
Installing collected packages: japanize-matplotlib
Successfully installed japanize-matplotlib-1.1.3
性別 体重 身長
0 M 60.3 166.2
1 F 50.4 160.3
2 F 55.3 165.3
3 M 70.2 174.2
4 F NaN 171.2
5 M 52.2 157.6
6 M 57.5 165.5
7 M 55.5 163.4

最後に表示されているものがdf,すなわちpandasのDataFrameに格納したsexweightheightのデータセットです.上の定義で体重データにデータの欠損を表す,NaNが含まれていますいます.以下の各種統計量の計算においてNaNは基本的に無視されます.

In:
# 欠損値 NaN (Not a Number)は基本的に無視して計算される
print( df['体重'].count() )  #体重データの個数(欠損値が1つ含まれる)
print( df['身長'].count() )  #身長データの個数

print( df['体重'].mean() )
print( df['体重'].var() )
print( df['体重'].std() )
print( df['体重'].min() )
print( df['体重'].max() )
7
8
57.34285714285714
42.716190476190484
6.535762425011368
50.4
70.2

上で計算されている統計量は個別に計算する必要ななく,describe()関数を使えばDataFrameの要約することができます.ただし,平均値や分散の計算ができないデータ(上の例では性別)は表示されません.

In:
df.describe()
体重 身長
count 7.000000 8.000000
mean 57.342857 165.462500
std 6.535762 5.379309
min 50.400000 157.600000
25% 53.750000 162.625000
50% 55.500000 165.400000
75% 58.900000 167.450000
max 70.200000 174.200000

DataFrameに含まれるデータのヒストグラムを描画します.pandasでの描画は`matplotlib <https://matplotlib.org/>`__と呼ばれる描画パッケージを継承しています.なお,デフォルトではmatplotlibは日本語を表示できないので,japanize_matplotlibimportできていないと日本語が□(tofu)になります.

In:
df.hist()
array([[<Axes: title={'center': '体重'}>, <Axes: title={'center': '身長'}>]],
      dtype=object)
notebooks/python%E3%81%AE%E5%9F%BA%E6%9C%AC_files/python%E3%81%AE%E5%9F%BA%E6%9C%AC_148_1.png

多数のデータからなるDataFrameの場合,DataFrame全体を表示させる前にどのようなデータ(列の名前)があるか知りたい場合があります.列名(column name)を表示させるにはdf.columnsに格納されています.columnsDataFrameの属性値(変数)であって関数ではないので括弧()は不要です.

In:
df.columns
Index(['性別', '体重', '身長'], dtype='object')

DataFrameから特定の列のデータを取り出す場合は,列の名前(column name)を指定します.

In:
df['体重']
体重
0 60.3
1 50.4
2 55.3
3 70.2
4 NaN
5 52.2
6 57.5
7 55.5

ここから性別が”F”のデータのみを抽出してみましょう. 以下のコードを入力すると’性別’のデータが’F’に一致した場合にTrue, そうでない場合にFalseが返ってきます.

In:
df['性別'] == 'M'
性別
0 True
1 False
2 False
3 True
4 False
5 True
6 True
7 True

True/Falseのデータをデータフレームdfのインデックスに指定するとTrueの結果のみ抽出されます.

In:
df[ df['性別'] == 'M' ]
性別 体重 身長
0 M 60.3 166.2
3 M 70.2 174.2
5 M 52.2 157.6
6 M 57.5 165.5
7 M 55.5 163.4

これにdescribe()関数をを加えれば’性別’が’M’(Male)のデーターに制限した際の,データの要約がえれれます.

In:
df[ df['性別'] == 'M' ].describe()
体重 身長
count 5.000000 5.000000
mean 59.140000 165.380000
std 6.851496 5.977625
min 52.200000 157.600000
25% 55.500000 163.400000
50% 57.500000 165.500000
75% 60.300000 166.200000
max 70.200000 174.200000

注意 DataFrameを作成する際にdf = pd.DataFrame({'性別': sex, '体重': weight, '身長': height})のようにDataFrame()の関数に代入しているものは辞書呼ばれる名前付き集合です.pythonでは集合(set)は{}で定義されます.

In:
A = {5, 1, 3, 1}
A
{1, 3, 5}

集合の要素はlistやarrayと異なり一意的で,重複する要素は削除されます.また要素の順番に無関係です,

In:
{1, 2, 3} == {3, 2, 1, 1, 1}
True

集合は要素の順番に無関係なのでlistやarrayのように\(i\)番目の要素といった指定ができません

In:
A[1]
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

/tmp/ipython-input-85-2665442193.py in <cell line: 0>()
----> 1 A[1]


TypeError: 'set' object is not subscriptable

集合{}の要素に任意の’記号(key)’を割り当てたものが辞書(dictinary)です. 例えば’x’という記号に5,’y’という記号に1,’z’という記号に3,’w’という記号に1を割り当てた名前付き集合(辞書)は以下のようにして表現されます.

In:
A = { 'x':5, 'y':1, 'z':3, 'w':1 }
A
{'x': 5, 'y': 1, 'z': 3, 'w': 1}

集合同様に辞書の要素の順序は無関係です,集合と同様に要素を数字で指定することはできません.

In:
{ 'x':5, 'y':1 , 'z':3 ,'w':1 } == { 'w':1, 'z':3 , 'y':1, 'x':5 }
True
In:
A[1]
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

/tmp/ipython-input-88-2665442193.py in <cell line: 0>()
----> 1 A[1]


KeyError: 1

しかし,割り当てた記号を指定して辞書の要素を取り出すことができます.

In:
A['x']
5

§ 6 for文 (繰り返し)#

同じ操作を繰り返す場合にはfor文を用います.

In:
print("for文を始めます")

for i in [1, 3, 5]:
  print(i)

print("for文が終わりました")
for文を始めます
1
3
5
for文が終わりました

for文の基本は

for i in <iterator>:
  処理内容

です.iteratorはlistやarrayに代表されるオブジェクトで,len(・)を評価して0以上の値になるもであればiteratorとしての資格があります. for i in <list>:の場合,はリストの要素を0番目から順に取り出し\(i\)に代入し,インデントが下げられた行の処理を逐次的に行なっています.例えば次で定義されるsumはrange(10) = [0, 1, 2, 3, …,9]の総和をfor文を使って計算しています. インデントの高さをfor文の1行目と同じ場所に戻す(Shift+Tab)ことでfor文中の処理の終わりを知らせます.

In:
sum = 0
for i in range(10):
  print(i)
  sum = sum + i

print(f"sum = {sum}")
0
1
2
3
4
5
6
7
8
9
sum = 45

多次元配列もiteratorで,多次元配列をfor文で回すと

In:
x = np.arange(10)
data = np.array([x, x**2, x**3])
for d in data:
  print(d)
[0 1 2 3 4 5 6 7 8 9]
[ 0  1  4  9 16 25 36 49 64 81]
[  0   1   8  27  64 125 216 343 512 729]

となります.何番目の要素を宿する場合はenumerate関数を使って以下のようにします.

In:
for i, d in enumerate(data):
  print( f"{i}番目のデータ = {d}" )
0番目のデータ = [0 1 2 3 4 5 6 7 8 9]
1番目のデータ = [ 0  1  4  9 16 25 36 49 64 81]
2番目のデータ = [  0   1   8  27  64 125 216 343 512 729]

以下のようにfor文の中にfor文を重ねることもてきます.

In:
for i in [1, 2]:
  for j in [3, 4]:
    print(f"(i, j) = ({i}, {j})")
(i, j) = (1, 3)
(i, j) = (1, 4)
(i, j) = (2, 3)
(i, j) = (2, 4)

§ 7 if文(条件分岐)#

真(True)もしくは偽(False)の論理式を用いて処理を条件分岐させることができ,if文と呼ばれます.

if文の基本は次のようになります.

if <論理式>:
  論理式が真(True)の場合の処理
else:
  論理式が偽(False)の場合の処理

例えば\(n\)が偶数の場合,\(y=n/2\)を評価し, \(n\)が奇数の場合\(y=n/2 + 1\)を出力するコードは次のように与えられます.

In:
n = 10
#n = 11 # <-#を削除するとn=11が代入される
if n % 2 == 0: # (nを2で割ったあまりが0 <=> nは偶数)
  y = n//2
  print(f"{n}/2 = {y}")
else: # nを2で割ったあまりが0でない <=> nは奇数)
  y = n//2
  print(f"{n}/2 = {y} + 1/2")
10/2 = 5

上の例では論理式がTrueもしくはFalse二律背反となる状況であったが,複数の条件をif文で設定する子も可能です.

if <論理式1>:
  論理式1が真(True)の場合の処理
elif <論理式2>:
  論理式1が**偽(False)**かつ論理式2が**真**の場合の処理
elif <論理式3>
  論理式1が**偽**かつ論理式2が**偽**かつ論理式3が**真**の場合の処理
else:
  それ以外<=>論理式1,論理式2,論理式3のいずれも**偽**

elif は else ifの省略形.

In:
n = 7
#n = 6
if n % 2 == 0:
  print(f"{n}は2の倍数です")
elif n % 3 == 0:
  print(f"{n}は3の倍数です")
elif n % 5 == 0:
  print(f"{n}は5の倍数です")
else:
  print(f"{n}は2, 3, 5の倍数でもありません")
7は2, 3, 5の倍数でもありません

上の条件分岐で\(n=6\)とすると\(6\)は2と3の倍数であるが最初のif n % 2 == 0:の条件にマッチすると,それ以下の条件分岐を無視するため,6は2の倍数ですのみ表示されることに注意せよ.

for文とif文を組み合わせると『3の倍数と3が付く数字の時だけアホになる』世界のナベアツ(の元ネタはFizz Buzz)っぽいものができる.

In:
for n in range(1, 41):
  if n % 3 == 0: # 3の倍数のとき
    print("%d ━━━━(゚∀゚)━━━━!!" % n)
  elif ('3' in f'{n}'): # nを文字列に変換して文字列に'3'がが含まれている
    print("%d ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!" % n)
  else:
    print(f"{n}...")
1...
2...
3 ━━━━(゚∀゚)━━━━!!
4...
5...
6 ━━━━(゚∀゚)━━━━!!
7...
8...
9 ━━━━(゚∀゚)━━━━!!
10...
11...
12 ━━━━(゚∀゚)━━━━!!
13 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
14...
15 ━━━━(゚∀゚)━━━━!!
16...
17...
18 ━━━━(゚∀゚)━━━━!!
19...
20...
21 ━━━━(゚∀゚)━━━━!!
22...
23 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
24 ━━━━(゚∀゚)━━━━!!
25...
26...
27 ━━━━(゚∀゚)━━━━!!
28...
29...
30 ━━━━(゚∀゚)━━━━!!
31 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
32 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
33 ━━━━(゚∀゚)━━━━!!
34 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
35 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
36 ━━━━(゚∀゚)━━━━!!
37 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
38 ━━━(゚∀゚≡(゚∀゚≡゚∀゚)≡゚∀゚)━━━━!!
39 ━━━━(゚∀゚)━━━━!!
40...