[Work/Class/Python3の基礎とデータ処理/python_basic]

numpy配列(array)とmatplotlib

numpy

numpyは数字(int, float)等の配列をlistとは別の数字専用の配列で扱えるようにしたライブラリである.listとは違いC言語で書かれているため,NumPyのデータ型(数字の配列)numpy.ndarrayとnumpyに用意されている関数群を使えば,listで処理をするよりもはるかに早い計算ができる.

numpyは配列データ型ndarrayと,それを操作する静的クラス関数で構成されている.ndarrayのインスタンス関数はほぼない(インスタンス変数のshapeを使うぐらい)ので,C言語的な手続き型プログラミングのスタイルになる.

numpyの配列numpy.ndarray

numpyでは数字専用の配列numpy.ndarray(n次元配列)クラスが提供されており,そのインスタンスを用いることが前提のNumPyの関数群が用意されている.

ndarrayは一般的にコンストラクタ呼び出しで生成するのではなく,初期化用の静的クラス関数numpy.array(), numpy.empty(), numpy.zeros(), numpy.ones(), numpy.identity()などを使ってインスタンスを生成する.

numpyの配列はC言語と同じくサイズが固定されている(サイズを動的に弄れない).したがって,インスタンスを生成してくれるる静的関数.array()を呼び出す時に要素を全て入れるか,.empty()関数で形状(n x mの行列である,など)を指定する必要がある.また.empty()でデータタイプ(int, floatなど)も生成時に指定しておくと,実行時に予期せぬエラーが起きることが少なくなる.他に単位行列を作る.identity()関数もある.

import numpy
# 要素を入れてarray関数を呼び出す
my_numpy_array = numpy.array([1, 3, 5])
print(my_numpy_array)

# float型の10要素の空配列(1次元)を生成
my_numpy_array = numpy.empty(10, dtype=float)
print(my_numpy_array) 
# emptyで生成すると,適当な値が入っていることがわかる

# float型0.0の要素で埋められた3x4の2次元配列(行列)を生成
# (3, 4)と中括弧で示すのはtupleの意味
my_numpy_array = numpy.zeros((3, 4), dtype=float)
print(my_numpy_array)

# int型の0の要素で埋められた2x6の行列を生成
my_numpy_array = numpy.zeros((2, 6), dtype=int)
print(my_numpy_array)

# int型の1の要素で埋められた3x4の行列を生成
my_numpy_array = numpy.ones((3, 4), dtype=int)
print(my_numpy_array)

# 4x4のfloatの単位行列を生成
# 単位行列は2次元配列であることが自明なので,
# tupleで(4,4)を与えずに1スカラ値変数だけでいける
my_numpy_array = numpy.identity(4, dtype=float)
print(my_numpy_array)

配列や行列の各要素へのアクセス

numpy_array[何番目か]という形で大かっこ[]を使ってアクセスする.

値を読みだすだけでなく,代入もできる.

my_numpy_array = numpy.array([1, 3, 5])

a = my_numpy_array[0]  # 0番目,つまり最初の要素を取り出し,aに代入する
print(a)

my_numpy_array[2] = 7 # 2番目に7を代入する
print(my_numpy_array)

行列の場合は,numpy_array[行の番号][列の番号]という形で,大かっこを二つつなげてアクセスする.

my_numpy_array = numpy.identity(4, dtype=int) # 4*4の整数の単位行列の生成
print(my_numpy_array)


a = my_numpy_array[0][3]  # 0行目3列目の要素を取り出し,aに代入する.
print(a)

my_numpy_array[2][2] = 6 # 2行目2列目に整数の6を代入する
print(my_numpy_array)

ndarrayのサイズを取得する

numpyの静的関数ndim()にndarrayのインスタンスを突っ込むと,n次元配列のnがわかる.

my_numpy_array = numpy.identity(4, dtype=float)
print(numpy.ndim(my_numpy_array)) # =>4x4の行列は2次元配列なので,出力は「2」

ndarrayのインスタンスが持つ.shape変数(tuple型)により,そのインスタンスの各次元のサイズを取得することができる.2次元配列の場合2要素のtupleなので,それぞれ[0][1]でアクセスする.

# 3要素の1次元配列を宣言
my_numpy_array = numpy.ones((3), dtype=int) 
print(my_numpy_array.shape) # => (3, )
print(my_numpy_array.shape()[0]) # => 3
print(my_numpy_array.shape()[1]) #1次元配列なので,これはエラーになる

# 5x4要素の行列(2次元配列)を宣言
my_numpy_array = numpy.zeros((5, 4), dtype=int)
print(my_numpy_array.shape) # => (5, 4)
print(my_numpy_array.shape[0]) # => 5
print(my_numpy_array.shape[1]) # => 4

3次元配列なら3要素のtupleになるので,[0], [1], [2]でそれぞれアクセスする.

ndarrayに要素を追加する

ndarrayの静的関数としてappend()関数が用意されていて,複数の1次元配列を新たな1次元配列として連結することができる.

前述の通りndarrayのサイズは変更不可なので,append()関数で連結しても,第1引数に入れたインスタンスそのものが拡張されているわけではなく,新しいndarrayのインスタンスを返すだけなので,別の変数に代入する必要がある.

my_numpy_array = numpy.array([1, 3, 5]) # 要素1, 3, 5を持つ1次元配列のインスタンスを作成
new_my_numpy_array = numpy.append(my_numpy_array, numpy.array([7, 9, 11]))
print(new_my_numpy_array) # 連結された新しいndarrayのインスタンス
print(my_numpy_array) # もとのインスタンスは変更されていない

arange関数とndenumerate関数

標準listでいう所のrange関数に当たるのが,numpyの静的関数arange()である.使い方は同じ.

my_numpy_array = numpy.array(numpy.arange(10, 15, 0.1))
print(my_numpy_array)

またnumpy配列は,普通にfor文でも回せる.indexを取り出すときはnumpy.ndenumerate関数を使う.例えば,

numpy_array1 = numpy.arange(0, 10, 1) #0.0から10未満まで,1刻みで整数の配列を作る
numpy_array2 = numpy.zeros(10, dtype=int) # 0が10個入った1次元のnumpy配列

for index, val in numpy.ndenumerate(numpy_array1):
  numpy_array2[index] = val + 10

print(numpy_array2) 
# array1に10が足された[10 11 12 13 14 15 16 17 18 19]を出力する

という感じでfor文を書く.

numpy.ndenumerateは1次元のベクトルだけではなく,「行列にも対応できる」という点が非常に優れている.

2次元以上の場合にnumpy.ndenumerateを適用すると,indexはtuple,つまり数字の組み合わせで出てくる.大かっこでtupleを0番目(行),1番目(列)に分解する必要がある.

numpy_array1 = numpy.identity(4, dtype=int)
numpy_array2 = numpy.zeros((4, 4), dtype=int)

for index, val in numpy.ndenumerate(numpy_array1):
  print(index) # tupleで出てきていることがわかる

  row_index = index[0] # tupleを分解して,行のindexと列のindexの値にする
  column_index = index[1]

  numpy_array2[row_index][column_index] = val * 10

print(numpy_array2)

vstackによる1次元配列の積み重ねによる2次元配列の生成

例えば要素数10の配列が二つあるとして,これをもとに2x10の2次元配列(2x10の行列)を作りたい時,

my_numpy_array1 = numpy.array(numpy.arange(10, 15, 0.1))
my_numpy_array2 = numpy.array(numpy.arange(5, 10, 0.1))
print(numpy.vstack((my_numpy_array1, my_numpy_array2)))

とすると,作り出せる.vstack()関数は小括弧が二重であることに注意.(tupleを意味するので)

これまでと同じく,元の配列が拡張されるわけではなく,新たな行列のインスタンスが生成されて返される.

vstack()関数で,要素数が同じ配列なら,どんどん足していく(行列の行が増えていく)ことが可能である.例えば,

my_numpy_array = numpy.array(numpy.arange(1, 10, 0.1))
my_numpy_matrix = numpy.vstack((my_numpy_array, numpy.array(numpy.arange(2, 11, 0.1))))
my_numpy_matrix = numpy.vstack((my_numpy_matrix, numpy.array(numpy.arange(3, 12, 0.1))))
print(my_numpy_matrix) # => 3x90の行列ができている.

という感じである.

配列とスカラ値,もしくは配列同士の四則演算

配列に対してスカラ値(値が一つだけ入っている)変数を四則演算すると,各要素に対してそれが適用される

my_array = numpy.array(numpy.arange(1, 10, 2))
my_array_plus_2 = my_array + 3 # => [4 6 8 10 12]
my_array_minus_2 = my_array - 2 # => [-1 1 3 5 7]

同様に,要素数が同じ配列同士を四則演算すると,要素同士の四則演算となる.(行列演算ではないことに注意)

行列の演算

上記の計算は,配列の各要素に対して適用されるが,行列として掛け算をしたい(行列の積を求めたい)場合に静的関数の.dot()関数を使う.やはり他の変数で受けとらなければならない.

my_matrix1 = numpy.ones((4, 4), dtype=float)
my_matrix2 = numpy.identity(4, dtype=float)
my_matrix3 = numpy.dot(my_matrix1, my_matrix2)

同様にdot()関数を使うことで,ベクトル(つまり1次元配列)と行列(つまり2次元配列)の積も求めることができる.(これは2次元CGや3次元CGの座標変換などでよく使われる)

my_vector = numpy.array([1, 3.2, 5.6])
my_matrix = numpy.array([[4.2, 3.6, 2.5], [3.4, 8.9, 0.4], [0.2, 1.1, 0.5]])
print(numpy.dot(my_matrix, my_vector))

他の行列演算は,numpyのさらに中に定義されているクラスlinalgの静的関数を使う.こちらも静的関数である.

my_matrix = numpy.ones(4, dtype=float)
my_transposed_matrix = numpy.linalg.transpose(my_matrix) # 転置行列
my_inversed_matrix = numpy.linalg.inv(my_matrix) # 逆行列
my_eigenvalue = numpy.linalg.eig(my_matrix) # 行列の固有値

matplotlib

簡単に言えばグラフを書くためのライブラリである.バックエンドのGUIライブラリに何を使っているかは環境によって違うが,AnacondaやMSYS2のmingw64でインストールされるPython3用のmatplotlibでは,C++用のマルチプラットフォームGUIライブラリQtをPythonから使うための「PyQt」Ver.5が用いられている.

線のグラフ

2次元の線のグラフ(例えば折れ線グラフ,直線グラフ)を書くための手順を簡単に言えば,「X軸とY軸のnumpy配列をそれぞれ1次元のnumpy配列で作り,plot()関数で値の配列をセットし,show()関数で表示する.」だけである.

以下の例では3次関数 f(x) = -2x^3 + 8x^2 + 5を描画している.

まず線を引く際に基準となるX軸の範囲を,0から0.99の範囲まで0.01刻みで作成する.これはrange関数のnumpy版であるnumpy.arange関数を用いる.

x_asis_array = numpy.array(numpy.arange(0, 10.0, 0.01))

次に,この3次関数のyすなわちf(x)を計算する.

まず,x軸の要素数と同じy軸のnumpy配列を作る.numpy.zeros_like()関数は,与えたnumpy配列と同じ要素数で中身が全て0の配列を作るので,x_axis_arrayを与えて,numpy配列を生成する.

# x座標用と同じ要素数のゼロ配列を作る
y_axis_array = numpy.zeros_like(x_axis_array)

for文で,先ほどnumpy.arange関数を利用して作った,X軸用の値を格納しているx_asis_arrayから1つずつ値を取り出して,この計算式に入れればよいのだが,ここでnumpyの最大の特徴である「numpy配列の要素同士をまとめて計算できる」特徴を有効活用できる.

y_axis_array = -2 * numpy.power(x_axis_array, 3) + 8 * numpy.power(x_axis_array, 2) + 5

numpy.power(x, y)関数は,numpy配列の要素同士を一気に計算してくれる.1行の中(Pythonでは「式」と呼称する)に書くのであれば,上記のように複数の項があっても同時に計算してくれる.

Y軸の計算とnumpy配列の生成を1行で行う事もできる.この方法の方が要素数を間違えるなどのミスが少ないので,積極的に使うと良い.

y_axis_array = numpy.array(-2 * numpy.power(x_axis_array, 3) + 8 * numpy.power(x_axis_array, 2) + 5)

これは中身としては,for文を回すのと同じ事になる.例えば,

for x_value in x_axis_array:
  y_value = -2 * x_value **3 + 8 * x_value **2  + 5

の計算をしているのと同じことになる.

numpyにはndenumerateというenumerateのnumpy版も存在し,以下のような書き方もできるようになる.(for文を回すとなると,for文を回す前にy_axis_arrayの初期化が必要になり,このようにインデックスを使わなければ代入できないので,実際は以下のコードでないと処理が書けない)

y_axis_array = numpy.zeros_like(x_axis_array)  # Y軸の配列の0初期化

# listのenumerateのnumpy版.インデックスも同時に出してくれる.
for index, x_value in numpy.ndenumerate(x_axis_array):
  y_axis_array[index] = -2 * x_value ** 3 + 8 * x_value ** 2 + 5

このX軸の値のnumpy配列とy軸の値のnumpy配列を,matplotlib.pyplot.plot()関数に入れて,matplotlib.pyplot.show()関数を実行すると,グラフが描かれる.

matplotlib.pyplot.plot(x_axis_array, y_axis_array, color='blue')
#'blue'は描画の線の色.省略可能.
matplotlib.pyplot.show()

ここまでのコードをまとめると以下のようになる.

# -*- coding: utf-8 -*-
import numpy
import matplotlib.pyplot

if __name__ == '__main__':
    # x座標用の配列を作る
    # 0から9.99までの1000要素
    x_axis_array = numpy.array(numpy.arange(0, 10.0, 0.01))

    # Y軸の配列をnumpyでまとめて計算して,Y軸の配列を作る
    y_axis_array = numpy.array(-2 * numpy.power(x_axis_array, 3) + 8 * numpy.power(x_axis_array, 2) + 5)

    # X軸とY軸をのデータをセットして描画する
    # 2つの同じ要素数分の配列を与えると,基本的に折れ線グラフになる.
    # 色指定もできる.
    matplotlib.pyplot.plot(x_axis_array, y_axis_array, color='blue')
    # 描画したウィンドウを見せる
    matplotlib.pyplot.show()

折れ線グラフに関しては,特に意識することなくこの方法で描くことができる.試しにx軸の値の間隔を狭くして,折れ線グラフにしてみよう.

散布図

import as

ここまで書いてくると,numpyだけならともかく,numpy.linalgクラスやmatplotlib.pyplotクラス内の静的関数を書くのが(文字数的に)面倒臭くなってくる.そこで,

import numpy as np
from numpy import linalg as la # numpyの中のlinalgクラスをimport……という意味
from matplotlib import pyplot as plt

asキーワードを使ってimportすると,その名前(この場合はnpla, plt)でそのクラスにアクセスできるようになる.

一般的にnumpyは「np」という名前で,linalglaという名前で,matplotlib.pyplotpltという名前でimportされることが多いので,慣習に従うべきである.


[Work/Class/Python3の基礎とデータ処理/python_basic]

Pythonの関数とクラスの定義

関数

プログラミング言語の関数とは,「ひとまとまりの処理を記述し,入力と出力を変数で整えることで,その処理を容易に再利用可能にしたもの」である.

数学でいう所の関数とほぼ同義である.

実はこれまで使用してきたprint(表示したい変数や文字列)というのも関数である.Pythonに標準で用意されている関数であることから,組み込み関数・ビルトイン関数などと呼ぶ.listの長さを返してくれるlen()も組み込み関数である.

関数の定義と呼び出し

自分独自の関数を定義したい場合,

def 関数名(引数をカンマで区切って定義):
    # 関数の処理内容...
    # 戻り値がある場合
    return 戻り値

というように定義する.

「引数」とは関数の入力,「戻り値」とは関数の出力である.例えば,

def increment(x):    # 関数名と引数名を定義する
  return x + 1    引数で取った値に+1をして返す

という関数が定義されている時,

a = 1
b = increment(a)  # 関数に引数aを与えて「呼び出す」
print(b)

というコードを実行すると,increment関数を引数整数1を与えて呼び出したことになり,既に定義されたincrement関数が呼び出され,1を足して返してくれる.それを変数bに代入し,printする.

従って,出力は整数の2になる.

この時,関数の定義の時に使った引数の名前(上記の例ではx)と,関数を呼び出している側の変数の名前(上記の例ではa)の名前は違ってもよい.関数を呼び出した時に,自動的に値が関数側の変数にコピーされるからである.

逆に言えば,関数と呼びだす側と関数内部の変数は全く別のものであり,同じものとして捉えるのは間違っている.関数呼び出し時に,変数内部に入っている値だけがコピーされるのである.これは戻り値についても同様である.

Pythonは関数型言語の側面も持っているので,クラスのインスタンスメンバ関数でない関数の場合,必ずreturnで何らかの値を返すようにしておく方が良い.(クラスのインスタンスメンバ関数の場合で返すものがない場合は,後述のselfを返すのが一番わかりやすい)

のように定義し呼び出す.ここでは,戻り値を受け取った変数aの中には6*2つまり12が,変数bの中には6/2つまり3が入ることになる.

引数が単純な「値」や文字列の場合,値は関数に入る時にコピーされる.しかしlistなど,単純な値以外のものを取る場合,実体(インスタンスと呼ぶ)がそのまま関数の中に引き継がれるという違いがある.

def append_apple(l):
  # listを取る関数.listの場合は,値がコピーされずに,listの実体そのままが飛んでいく
  l.append('apple')
  return l


my_list = ['grape', 'orange']
my_new_list = append_apple(my_list)
print(my_new_list)

また,関数の中でも,for文やif文をそのまま使うことができる.

def generate_list(x):
  return_list = list([])
  for item in range(x, x+10, 1): # 関数の中のfor文
    return_list.append(item)
  return return_list

a = 3
my_new_list = generate_list(a)
print(my_new_list)

Pythonでは複数の戻り値を指定することができる.例えば,

def multi_div_return(x, y):
    return x*y, x/y
    # 関数定義はここまで

# インデントを元に戻したので関数の外側
a, b = multi_div_return(6, 2)

グローバル変数

「クラスを定義してそれを駆動する」以前のPythonコードで,関数外部で宣言初期化された変数はグローバル変数となる.

変数は,その関数内部でしか通用しない.例えば違う関数の中で同じ名前の変数が宣言されていても,別の変数の扱いとなる.これをローカル変数と呼ぶ.(Pythonの変数のスコープについては前項を参照されたい)

それに対しグローバル変数は,どの関数からでもアクセスできる変数である.

関数内部でグローバル変数にアクセスするためには,アクセスする前にglobal グローバル変数名という式を実行しておく必要がある.

ただし,グローバル変数の多用は,名前がかぶってしまったり,実行する順番が明確になっていないと意図しない動作を引き起こしてしまう事が多い.グローバル変数はなるべく使用せずに,引数と戻り値で制御する方が良い.

# 関数の外側で変数を定義.グローバル変数になる.
var1 = 123

# 関数を定義する
def add_gv(x):
  global var1     # この関数内でvariable1というグローバル変数を使う事を宣言

  return var1 + x # グローバル変数を使って演算をした戻り値を返す
  # 関数定義ここまで

a = 789
b = add_gv(a)
print(b)          # 123 + 789で912がbの中には入っている

既にある関数やクラスの読み込み

自分で定義した関数やクラスではなくとも,関数やクラスをまとめたモジュールが既に存在し,それを読み込み,利用することができる.

例えば,数学的な関数や定数が定義されているmathモジュールを読み込むときには,

import math

という風にimportキーワードを使って,読み込みを行う.データ分析の大体の部分は既存のモジュールのみで可能である.

読み込んだ後は,モジュール名.モジュールで定義されている定数や,モジュール名.モジュールで定義されている関数などのように,「.」を付けて個別の定数や関数を呼び出すことができる.

print(math.pi) # mathモジュール内で定義済みの円周率の定数

a = 4
b = math.sqrt(4) # math.sqrtは,平方根を返してくれる関数.mathモジュール内で定義済み.
print(b)

Google Colaboratoryのような環境(逐次実行できる環境 - REPL環境と呼ぶ)では,一度importすると,他のセルでも読み込んだモジュールは有効である.ただし接続制限時間というものがあり,ウィンドウを閉じてしばらく経ったり,何も操作せずにしばらく経ったり,または長時間計算をすると,Googleが提供しているサーバとの接続が切れて再起動される.その時は再度importする必要がある.

Notebookの最初のセルにたとえば「このセルを最初に必ず実行!」という風に書いて,importをしておくと良い.

クラス

クラスとは,関数や定数,保持するべき内部パラメータなど必要な機能をひとまとめにしたものである.

関数との大きな違いは,クラスはオブジェクトインスタンス化(単にインスタンス化とも)することで,保持すべき内部パラメータ……データすなわち「状態」をまとめて保持できることにある.インスタンス関数がクラスにまとめられるのはむしろ,この「状態」にアクセスするためにである.この「保持されている状態」はインスタンス変数と呼ばれる.

またクラスは,「保持されている状態」には関係しない,似たような機能を持つ定数や関数をまとめる機能を持ち,それぞれクラス変数,静的関数・クラス関数と呼ばれる.

Pythonをデータ分析に用いる場合,殆どのデータ分析用のクラスは既にあるので新たに自分で定義する必要は殆どない.

クラスの定義

class クラス名(親クラス):
    # クラス定義の内容...

と定義する.親クラスがない時(正確にはobjectクラスが親の時)は,小括弧の中はobjectにする,空にする,小括弧をつけない,のどれかを選択する.

selfキーワード

Javaでthisに相当する「このクラスの」もしくは「このインスタンスの」を表すキーワードはselfである.

コンストラクタの定義

def __init__(self, コンストラクタが取る変数...):
    # コンストラクタ関数内の処理...

と書くことで,クラスのコンストラクタを定義する.

コンストラクタは「何も返さない」ことが決められているので,return Noneを最後に実行する.Noneキーワードに注意.

内部で親クラスのコンストラクタを実行したい時には,

super().__init__(親クラスのコンストラクタの引数からselfを除いた引数)

という形で親クラスのコンストラクタにアクセスする.(Python2では書法が異なる)

インスタンスメンバ変数の定義

コンストラクタや他の関数内部でself.変数名と宣言すると,インスタンスメンバ変数となる.

クラスのインスタンスメンバ変数は,基本的にprivateであるべきである.privateな変数の定義は,変数名がアンダースコア2つ連続で始まる規則になっており,__variable_nameと書かれるので,インスタンスメンバ変数はself.__variable_nameと定義する.

インスタンスメンバ関数の定義

インスタンスメンバ関数の定義では,第一引数が必ずselfになる.ただし呼び出しの時は第一引数を書かずに第二引数から書き始める.

クラス変数と静的関数・クラス関数

クラス変数

静的変数・定数つまりインスタンスを生成しなくても使えるクラス内の定数(Javaでいう所のpublic static定数)は,selfをつけずに,コンストラクタや関数外で宣言すればよい.

class MyClass:
  クラス変数の名前 = 値

という形で宣言する.クラスの外からは

クラス名.クラス変数の名前

でアクセスする.

クラス「変数」という名前ではあるが,実際は定数として運用する.そのクラスの中の静的関数を利用する際に有用となる定数が定義されている事が多い.前述の数学的な関数や定数が定義されているmathモジュールでは,円周率が定義されており,事前にimportしておけば,

print(math.pi)

という形で呼び出すことができる.

静的関数

静的関数は,データを実行対象として直接紐づけずに,実行時にデータを与える関数であり,与えたデータ例えばlistを破壊的に操作することはなく,たいていは新しいオブジェクトインスタンスを作って,そこに元のオブジェクトインスタンスからコピーを行い,操作を加えて,戻り値として返してくれる.

Pythonでの静的関数は,一般的にC言語のみで書かれた高速ライブラリで採用されている.通常のオブジェクト指向プログラミング言語ではオブジェクトインスタンスを作り操作を行うような場合でも,Pythonのライブラリでは静的関数として実装されていることも多い.numpyが代表的である.C言語の機能を直接実走したものに,このパターンが多い.

前述の数学の関数や定数が定義されているmathモジュールでは,三角関数が用意されており,事前にimportしておけば,

b = math.sin(a)
d = math.cos(c)

の要に呼び出すことができる.

自分でクラスを定義する場合の静的関数は以下のように宣言する.

@staticmethod
def my_function(引数):
    # 処理内容....

静的関数はselfを引数に取らないので,書かない.つまり静的関数は(当たり前だが)インスタンスメンバ変数にはアクセスできない.

クラス関数

上記の静的関数他に,Pythonには「クラス関数」(クラスメソッド)というものもある.これは第一引数にselfを取る静的関数のことで,静的変数にも内部的にアクセスできる.が,ややこしくなるので書かない方がよい.クラスの静的定数には「クラス名.定数名」でアクセスする方が明確である.

ソースコードが関数定義やクラス定義から始まる時のmain関数

Pythonのソースコードは,上から順次実行されるが,関数定義やクラス定義がある場合,まず初めに関数定義やクラス定義を書いて,実行開始main関数を次に書く.main関数の定義は以下のように行う.

# ...
# ここまでクラス定義

if __name__ == '__main__':
   # ソースコードの実行開始時に読まれる関数
   # この中でクラスのインスタンスを作り,処理する.
   # ...

自分でクラスを定義する時のコード例

以下は,PyQtというGUIライブラリを使ったグラフィック表示の例である.データ分析ではクラスを自分で定義することはほとんどないので,参考程度に眺めておけばよい.

コード中では,

  1. 必要なクラスライブラリの読み込み
  2. 自分で定義するクラスの定義
  3. main関数

の順で進む.

# -*- coding: utf-8 -*-
# BounceAndColoredBall.py

# 必要なクラスライブラリの読み込み
import sys # 今回はアプリケーション起動時の引数を取ってくるために使う
import math # 数学関係
import random # ランダム用ライブラリ

# GUIに使うQt5のライブラリ
# 本筋とは関係ないので,「そんなもんか」と捉えておけば良い
from PyQt5.QtCore import * 
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


# 自分のクラスの定義
class BounceBall():
    # コンストラクタ
    # 引数の頭が必ずselfになることに注意
    def __init__(self, windowWidth, windowHeight):
        self.__windowWidth = windowWidth #privateメンバ変数は「self.__変数名」
        self.__windowHeight = windowHeight
        self.__x_pos = math.floor(random.random() * self.__windowWidth)
        self.__y_pos = math.floor(random.random() * self.__windowHeight)
        if random.random() > 0.5 :
            self.__x_direction = 1
        else:
            self.__x_direction = -1
        if random.random() > 0.5 :
            self.__y_direction = 1
        else:
            self.__y_direction = -1

        return None # コンストラクタはNoneを返すと決められている

    def updateParameters(self):
        self.__x_pos += 10 * self.__x_direction
        if self.__x_pos > self.__windowWidth :
            self.__x_direction = -1
        elif self.__x_pos < 0 :
            self.__x_direction = 1

        self.__y_pos += 10 * self.__y_direction
        if self.__y_pos > self.__windowHeight :
            self.__y_direction = -1
        elif self.__y_pos < 0 :
            self.__y_direction = 1

        return self

    def get_x(self):
        return self.__x_pos

    def get_y(self):
        return self.__y_pos


class ColoredBall(BounceBall): # BounceBallを継承して,色情報を持たせたクラス
    # コンストラクタのオーバーライド
    def __init__(self, windowWidth, windowHeight):
        # 最初に親クラス(BounceBall)のコンストラクタを呼び出す
        super().__init__(windowWidth, windowHeight)

        # 子クラス独自のコンストラクタの内容を書いていく
        self.__r_color = math.floor(random.random() * 155) + 100
        self.__g_color = math.floor(random.random() * 155) + 100
        self.__b_color = math.floor(random.random() * 155) + 100
	
	return None

    # 子クラス独自の関数を定義
    def get_r(self):
        return self.__r_color
    def get_g(self):
        return self.__g_color
    def get_b(self):
        return self.__b_color

# 実際に描画するウィンドウを呼び出して描画するクラス(JavaではJFrameに相当する)
# 本筋には関係ないので「こんなもんか」と眺めるだけで良い
class MyWidget(QWidget):
    # コンストラクタのオーバーライド
    def __init__(self, parent=None):
        super().__init__(parent)

        self.__ball_list = list([])

        for i in range(10):
            a_ball = BounceBall(640, 480)
            self.__ball_list.append(a_ball)

        self.__colored_ball_list = list([])
        for i in range(10):
            a_ball = ColoredBall(640, 480)
            self.__colored_ball_list.append(a_ball)

        self.resize(640, 480)
        self.show()

	return None


    # 描画イベント関数(Processingでいうdraw関数)のオーバーライド
    def paintEvent(self, event):
        painter = QPainter(self) # QPainterのインスタンスを作成
        painter.setPen(Qt.black) # Stroke(枠線)に相当
        # 画面のRefresh
        painter.setBrush(Qt.white) # 中の色に相当
        painter.drawRect(0, 0, 640, 480);

        for a_ball in self.__ball_list:
            a_ball.updateParameters()
            painter.drawEllipse(a_ball.get_x(), a_ball.get_y(), 20, 20)

        for a_ball in self.__colored_ball_list:
            a_ball.updateParameters()
            painter.setBrush(QColor(a_ball.get_r(), a_ball.get_g(), a_ball.get_b()))
            painter.drawEllipse(a_ball.get_x(), a_ball.get_y(), 20, 

	# 最後にupdate関数を呼び出すと描画される
        self.update()

# main関数        
if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = MyWidget() # 自分で作ったクラスのインスタンスを作る
        w.raise_()
        app.exec_() #paintEventが回るようにする

[Work/Class/Python3の基礎とデータ処理]

Python3の基礎とデータ処理

この授業の目的

ビッグデータを使いたい場合を想定して「既にCSVとしてダウンロードできるデータを使う」ための,プログラム言語Pythonを使ったデータ処理の方法と統計の基礎についてを学ぶ.

実際の分析では,「データが少量の時に,表計算ソフトウェアで対話的にデータを整える」場合と,「大量のデータが存在する場合にデータを整える処理の部分もプログラムで自動化する」場合の2種類のテキストを用意した.

「プログラムなんか書かなくても,全部Excelでポチポチ対話的にやればいいじゃない.後ろの方はいらない」と思うかもしれないが,ビッグデータを使いたい場合,そもそもデータが大量である.CSVの行や列が1万もあった場合に,いちいちExcelで開いてちょこちょこ操作してというのを,さらに1万シート分も繰り返すのはとてもできない.そのような場合には「データを整える部分」プログラムで自動化する必要がある.

また,音楽の場合は,一つの音符ごとに計算をしなければならなかったりなど,本当に大量のデータを処理する必要がある.

「定型処理をまとめて繰り返す」のはプログラミングの得意技である.そこでプログラミングも一緒に学ぶ.

概要

近年PythonがそれまでのMATLABやR言語に代わって,データ処理に使われることが多くなった.また海外では教育用言語として90年代末から普及が始まり,現在様々なフレームワークが開発されているWebサーバ側をはじめとしたサーバ用途と,通常のデスクトップのスクリプト処理用途,両用のLL(Lightweight Language)として使われている.

Pythonの特徴として「Python本体はスクリプト言語だが,ライブラリをC言語で実装しPythonにAPIを用意することで,高速に実行可能」という点がある.近年ではデータ解析や機械学習などで頻繁にPythonが使われているが,これはこの「Cで書かれた高速ライブラリをPythonで実行」というスタイルに依るものである.

現在のPythonは大きく分けてVer.2.x系とVer.3系の2つが使われているが,本授業ではPython3を対象とする.(OSX標準のPythonは2.7なので,授業の下準備としてAnacondaをインストールしておくこと)

コンテンツ

Pythonの基礎

  1. Pythonの基本と配列(list)の取り扱い
  2. 関数とクラスの定義
  3. numpy配列(array)とmatplotlib
  4. CSVの取得とPlot,pandas.DataFrameの取り扱い
  5. 文字列の取り扱い
  6. lambda式,高階関数,内包表記
  7. HTMLParserクラスによるWebページの取得と分析

表計算ソフトウェアを使って対話的にデータを整える場合

  1. CSVデータを加工して相関係数を算出
  2. CSVデータを加工して単純線形回帰分析
  3. CSVデータを加工してt検定
  4. CSVデータを加工して分散分析と多重比較
  5. CSVデータを加工して重回帰分析と因果関係
  6. CSVデータを加工して主成分分析
  7. CSVデータを加工して線形二項分類
  8. CSVデータを加工して因子分析

大量のデータがある時にプログラムを書いてデータを整える処理を自動化する場合

  1. CSVデータからの相関係数の算出
  2. CSVデータからの単純線形回帰分析
  3. CSVデータからのt検定
  4. CSVデータからの分散分析と多重比較
  5. CSVデータからの重回帰分析と因果関係
  6. CSVデータからの主成分分析
  7. CSVデータからの線形二項分類
  8. CSVデータからの因子分析

授業の下準備

Google Colaboratoryを試用する場合(インストール不要,OS不問)

このページの内容は,Googleが提供している,Webブラウザから実行できるPythonの環境「Google Colaboratory」で全て実行可能である.従ってこのページの内容を学習するために自分のコンピュータにPythonの環境を整える必要はない.

C言語のツールや,他の環境(例えばアート系であればCycling'74のJavaScript環境)との通信・連動を行いたい場合は,自分のコンピュータ内に実行環境を整える必要があるため,後述のAnacondaをインストールするのがよい.

Windows

Anacondaを使う場合

Anacondaは現在標準となっているPythonのデータアナリシスディストリビューションである.こちらを使うのが一番楽.

Anacondaダウンロードページから,「64-Bit Graphical Installer」をダウンロードする.

インストール画面1

「Next」

インストール画面2

「I Agree」

インストール画面3

「All Users」の方が実は楽.管理者パスワードがわからないときには「Just Me」

インストール画面4

インストール場所に半角スペースなどが含まれているとエラーになるので,そのまま.もしくはCドライブ直下にするなど.

インストール画面5

上の方(システムパスに追加)は,システム的に混乱する可能性があるので外して置いた方がいい.

逆に下はVisual Studio等のWindowsコンパイラのみに設定を与えるもので,GPUの利用などを考えているなら,チェックを入れておく.(基本的にチェックを入れて置いて大丈夫)

インストール画面6

インストールが進む.

インストール画面7

終わったら「Next」

インストール画面8

Visual Studio Codeというエディタを追加インストールするか訊かれる.Visual Studio Communityが既に入っていたり,VS Codeを使わないなら「Skip」

インストール画面9

「Finish」

WindowsでMSYSを使う場合

CやC++のツールと連携させたい場合のみ(VSのコンパイラが必要なCUDA系は除く),Cコンパイラとセットになるこちらを使う.MSYSを使ってインストールしたPythonは文字コードがWindowsのもの縛りになるので注意.データ分析ならAnacondaの方が使い勝手は良い.

まっさらな状態からのMSYS2+GCCでWindows上にC言語とC++の環境を整えるの通りにコンパイラ環境を整える.

その後「MSYS2 MSYS」ショートカットで起動する端末エミュレータから,

$ pacman -S mingw64/mingw-w64-x86_64-python3
$ pacman -S mingw64/mingw-w64-x86_64-python3-numpy
$ pacman -S mingw64/mingw-w64-x86_64-python3-scipy
$ pacman -S mingw64/mingw-w64-x86_64-python3-matplotlib
$ pacman -S mingw64/mingw-w64-x86_64-python3-pandas

の五つのパッケージをインストールしておく.依存関係にある必要なライブラリ(主にQt関係)も全て一緒に入ってくれる.(64bit Windowsの場合)

Pythonの実行時には,MSYS2 MSYSから,

 $ /mingw64/bin/python3 MyPythonCode.py

と実行する.

OSX, macOS

OSX,macOSはPython本体とnumpy, scipy, matplotlibは標準で入っているのだが,バージョンが2.7である.HomebrewなどでPython3を入れるという方法もあるが,Homebrewでインストールを行うとシステム全体に影響を及ぼし,元々入っていたPython2.7とごちゃ混ぜになりシステムにダメージを与える可能性が高い.

そこでデータ処理に特化したPythonのパッケージであるAnacondaを「HDDのアプリケーション」以下にインストールして使う.Anacondaは/Applications/anacondaフォルダ内で独立したファイルツリーを持つ.

Python3を使いたい場合はAnacondaのPythonを明示的に実行してやる形にすれば,通常のシステムの処理に必要なPythonは元々システムに入っていた2.7がそのまま使われることになる.この方法で,システムに必要なPython2.7と,プログラミング言語として使いたいPython3系を,安全に同居させることができる.

Anacondaのダウンロードページの「Download for macOS」から「Python 3.6 version」の「Graphical Installer」をダウンロードする.

ダウンロードされた.pkgを実行して,ライセンスに同意し,「インストール先を変更」して「特定のディスク」の「アプリケーション」フォルダにインストールする.そうするとマシンの「/Applications」に「anaconda」フォルダ内にanacondaがインストールされ,Terminal.appから実行するPythonはこの中のPythonを使うように設定される.

Anaconda3のインストールパッケージ一覧の画面 Anaconda3のライセンス表示 Anaconda3のライセンス同意 Anaconda3「インストール先を変更」 Anaconda3「特定のディスクにインストール」を選択 Anaconda3メインのシステムHDDが選択されていることを確認し「フォルダを選択」 Anaconda3 フォルダを選択.ハードディスク直下の「アプリケーション」 Anaconda3 選択終了「続ける」

「このユーザがTerminal.appを開いた時はAnacondaのPythonを使う」という設定が記述されている~/.bash_profileからexport PATH=/Applications/anaconda...という行を削除しておき,Python3を実行する時は「Terminal.app」から,

 $ /Applications/anaconda/bin/python MyPythonCode.py

と「Anacondaのpython」を明示的に実行するようにしておくと,システム的に混乱が起きない.

OSXのPython2.7には,この授業の目的である基本的なデータ解析で使うnumpy, matplotlib, scipyも標準で含まれているのだが,matplotlibが使うGUI部品(システム標準はおそらくTk, Anacondaや上記WindowsのMSYS2環境ではQt5)や,リストを使ったイテレータの言語仕様などが違うため,Python2.7と3で互換性がないプログラムもある.

Anacondaの環境を作って実行環境のターミナルを開く

Pythonはライブラリがたくさんあり,衝突するものもある.Anacondaは複数の「環境environment」を作り,「環境」ごとにライブラリをインストールすることができるため,ライブラリの衝突を防ぐことができる.

ここでは授業用の環境「for_class」を作ってみる.

環境を作る

「Environment」タブにして,下の「Create」ボタン.

環境の名前をつける

環境の名前(ここでは"for_class")をつけて,「Create」.

環境が作られている

環境が作られている.

再生ボタンからOpen Terminal

環境の作成が終わったら,作成した環境の再生ボタンから「Open Terminal」.

Terminalが開いて実行できる

Terminalが開いて実行できるようになるので,プログラムが置いてあるフォルダにcdコマンドで移動して,

python 実行したいpythonプログラムファイル.py

で実行できるようになる.

Anaconda-Navigatorでの追加ライブラリのインストール

AnacondaはGUIでライブラリが追加インストールできる.ここでは例としてTwitterのAPIを使うために必要なOauth認証に必要なrequests_oauthlibをインストールする.

conda-forgeというレポジトリ(AnacondaではChannelと呼称する)がよくメンテされているようなので,それを追加して,Anacondaから使えるようにする.

まず管理者権限をもつユーザでAnaconda-Navigatorを起動する.

管理者権限を持つユーザでAnaconda-Navigatorを起動する

「Environment」(環境)を選択→「当該プロジェクトで使っているAnacondaの環境(ここではrootだが,基本的には前述の通りプロジェクトごとに環境を作る)」が選択されていることを確認→「Channels」ボタンを押す.

環境を選択しChannelを追加する

ミニウィンドウが開くので「Add」

ミニウィンドウが開く

新たなフィールドに「conda-forge」チャンネルを入力.

新たなフィールドに「conda-forge」チャンネルを入力

追加したら「Update channels」

「Update channels」

もとの画面に戻るので「Update Index」すると,下のバーでIndexをアップデートしている進行状況が表示される.

「Update Index」

一番左のプルダウンメニューから「Not Installed」(まだインストールされていないライブラリの検索)→一番右のボックスに「ライブラリの名前」(ここでは「requests-oauthlib」)を入力すると,下のフィールドに検索結果が出てくる.

「Not Installed」→「ライブラリの名前入力」→検索結果がでる

ライブラリにチェックを入れて,「Apply」

ライブラリにチェックを入れて「Apply」

進行状況のミニウィンドウが出る.

進行状況のミニウィンドウ

入れたいライブラリに必要なライブラリ(依存関係と呼称する)が全て並んで出てくるので「Apply」.

依存関係の表示

プルダウンメニューを「Installed」にして,インストールされていることを確認する.

「Installed」で確認.

以上である.Environment→rootの再生マークからターミナルを呼び出しても使えるし,root環境に入れておけば,通常のターミナルから/Applications/anaconda/bin/python3でPythonインタプリタを呼び出した時もライブラリが使える.

Anacondaで環境をたくさん作り,使用するライブラリを切り替えることもできる.


[Work/Class]

授業資料

コンテンツ

Java With Processing

Processing IDEを用いて,Javaの基礎からオブジェクト指向プログラミングまでを学ぶ.

JavaScript with P5.js

ProcessingのJavaScript版であるP5.jsを用いて,ES2015準拠でオブジェクト指向プログラミングを学ぶ.

Python3の基礎とデータ処理

Pythonの基礎と,それを用いたオープンデータの取得や処理を行う.

Rの基礎とデータ処理

R言語の基礎と,それを用いたオープンデータの取得や処理を行う.

IPythonでアルゴリズム&AI作曲

Google Colaboratoryの対話型Python実行環境で,アルゴリズム作曲とAI作曲を実践する.

C++11の場合

Javaのオブジェクト指向プログラムをC++に移植するための,C++のオブジェクト指向の基礎.

ES2015で動的Webアプリ

WebSocket通信を用いて,ウェブブラウザ上で動作する動的Webアプリケーションを製作する.JavaScriptの最新版であるECMAScript2015を使用する.

OpenGL - WebGL

OpenGL1系と2系以降,そのブラウザ動作版であるWebGL.

サウンドトラック制作基礎

映像で使用するオリジナル劇伴をコンピュータで作る.

Web基礎

Webサイト制作.コンテンツの作り方.

Google Maps APIの基礎

GoogleMapsを独自にカスタマイズし,自分のWebサイトに取り込む方法.


[Work/Class/Python3の基礎とデータ処理/python_basic]

Pythonの基本と配列(list)の取り扱い

ソースコードの文字コード,拡張子

基本的にはutf-8を使用する.ソースコードファイルの一番上の行に,

# -*- coding: utf-8 -*-

と書いて文字コードを指定する.

ソースコードファイルの拡張子は一般的に「.py」である.

インデント

Pythonはインデント(字下げ)の量で,ブロック(C言語やJavaでいうところの中括弧{}で囲まれた部分)を決める.

if a == 1:
    print(a)
else:
    print('それ以外の時')

のようにする.

インデントの挿入は、「スペースを複数個入れる」操作ではなく「TABキーを1回押す」操作によってなされるべきである。

この「ブロック」という概念は,後述する制御構造の箇所で用いる.制御構造の分岐の時にはコロン「:」を付けて,その後にインデントを入れた一まとまりのプログラムを書く.

変数

変数の概念

変数とは,ある値を格納するスペースである.コンピュータでは値を扱う時にメモリ上にスペースを用意しその中に値を入れておく.そのスペースの事をプログラム上では変数と呼ぶ.

変数宣言や変数の型宣言は要らない

Pythonの変数を宣言する時に,整数型や文字列型等の型宣言は要らない.また変数であることを宣言するキーワードもない.

a = 1

だけでaという名前の整数型の変数が宣言され,1が代入される.

b = 'bの中に入れる文字列'

だとbという名前の文字列型の変数が宣言され,代入される.

print(a)
print(b)

というコードを実行すると,それぞれaという名前の変数の中身(ここでは1という整数)と,bという名前の変数の中身(ここでは「bの中に入れる文字列」という文字列)が表示される.

print(表示したい変数や文字列)で,内容を表示させる事ができる.

変数の型

「変数の型」とは,その変数が格納することができる値の種類,例えば整数(Integer),実数(プログラミングでは基本的に浮動小数点Floatとして扱う),文字列(String)などの事である.

コンピュータのメモリ上では,整数と実数(浮動小数点)では,値を格納するのに必要とするサイズが異なるため,「変数の型」という概念がある.

Pythonでは,ただの変数では変数の型(その変数が整数なのか実数なのか文字列なのか)を意識する必要はないが,今後出てくる配列や行列を扱うための事実標準となっているnumpyというライブラリでは明確に意識する必要がある.

変数を使った簡単な四則演算とその表示

a = 1
b = 2
c = a + b
print(c)

というコードは,

  1. aという変数の中に1という整数を代入
  2. bという変数の中に2という変数を代入
  3. cという変数の中に,変数aの中身と変数bの中身を足し算した結果を代入
  4. cの変数の中身を表示

という流れの処理をプログラムで書いたものである.

このように,プログラムは行単位で上から下,右から左に向けて実行される.

この + の事を演算子と呼ぶ.

四則演算の演算子は,それぞれ + が足し算, - が引き算, * が掛け算, / が割り算を表す.

四則演算以外で標準の演算子として実装されている中で,データ処理でよく使うのが,べき乗である.

b = a**2    # aの2乗

というように,演算子**を用いて記述する.(一般的にWebの表記でx^2のように「^」を用いることがあるが,これはプログラムでは使わない)

演算時の自分自身への代入

a = 1
b = 2
a = a + b

プログラムは「右から左」つまり「行末から行頭に向かって」実行される事に注意する.つまり上記のコードでは,aにはa + bの値だが移入され,3行目の実行が終わった段階では,aの中身は整数の3になっている.

Boolean

Booleanとは,論理的な「真」と「偽」を表現するための値・変数型の事を指す.後述するif文などで用いられる.

Boolean型の値はTrue, Falseである.頭が大文字になることに注意.

ifブロックやforブロックの中の変数(スコープ)

C言語やJavaとは違い,ifやforの中で新たに変数を宣言しても,その外側に変数が作られたのと同じ意味になる.つまりifやforの中の変数はそのまま保持され,外側からそのまま参照,変更することが可能である.

この変数(変数の名前)が影響を及ぼす範囲を「スコープ」と呼び,C言語やJava, PythonやRubyでは異なることに注意.

つまりPythonの変数の影響の範囲スコープは関数内が最小である.

Pythonの影響範囲スコープは,グローバルで定義した変数(プログラム全体で有効)→クラス内変数の影響(オブジェクトインスタンス内で有効)→関数内変数(関数の中で有効),の3段階である.

C言語やJavaはさらにfor文if文などのブロック内で有効,というスコープがある.

標準配列list

Pythonの標準配列はlistというクラスのオブジェクトインスタンスである.

Pythonでは宣言をしてメモリ上に確保された状態の,プリミティブな整数や浮動小数点実数以外の変数の事を「オブジェクトインスタンス」と呼ぶ.もしくは単に「オブジェクト」「インスタンス」とも.(他言語でもどちらを使うかは人によってまちまちで,同様の呼び方をする)

宣言

my_list1 = [] #listの宣言と初期化(空)
my_list2 = [1, 3, 8, 20] #listの宣言と同時に配列の中身を入れて初期化

listクラスのオブジェクトインスタンスなので,

my_list = list([1, 3, 5, 7])

のようにコンストラクタ呼び出しで生成することもできる.

というか,コンストラクタ呼び出しで生成する方がオブジェクト志向プログラミング的なので,なるべくこちらを使うように癖をつけておくこと.特にPython3ではこちらの記法を必ず使うこと.(後述のrange関数などで使われる)

listの連結

my_list = list([1, 3, 5])
new_list = list(my_list + list([7, 9, 11]))

で連結される.(結果が[1, 3, 5, 7, 9, 11]のリストになる)

list動詞の + 演算子による連結は,非破壊的,つまり足す方には影響を与えない.従って上記の場合のmy_listを更新しようと思った場合,

my_list = list([1, 3, 5])
my_list = list(my_list + list([7, 9, 11]))

のように,自分自身に代入する必要がある.

listへの追加 append関数

append関数を使うと末尾に要素が追加される.

my_list = list([1, 3, 5])
my_list.append(7)
my_list.append(9)
my_list.append(11)
print(my_list)

の結果は[1, 3, 5, 7, 9, 11]になる.

appendを使った場合,my_listそのものが更新される事に注意する.このようなlist型のオブジェクトインスタンスが持っている機能(関数)の事をインスタンス関数と呼び,再代入ではなくそのオブジェクトインスタンス自身が更新される.

listへの挿入 insert関数

my_list = list([1, 3, 5])
my_list.insert(2, 7) #insert(挿入したい場所のインデックス, 挿入したい要素)

の結果は[1, 3, 7, 5]になる.

つまりに先頭に要素を追加したい場合は

my_list.insert(0, 挿入したい要素)

となる.

listの長さを取得

len(my_list)

となる.lenはlistクラスオブジェクトの関数ではないことに注意.

range()関数で範囲指定で単調増加する整数listを生成

例えば[0, 1, 2, 3, 4]というlistを生成したい時,

my_list = list(range(5))

とすると,my_listの中には[0, 1, 2, 3, 4]というlistが入る.

my_list = list(range(3, 8))

の結果は,[3, 4, 5, 6, 7]となる.つまり「3から8未満の間に1ずつ増加させながら追加していく」のような意味となる.

2ずつ増加する整数リストを作る場合には,

my_list = list(range(3, 10, 2))

とする.結果は,[3, 5, 7, 9]である.(「10未満の場合は2ずつ増加させて追加していく」なので)

ちなみにrange()関数そのものは,listのコンストラクタに突っ込んで回すと(勝手に回ってくれる)整数の集合を生成してlistオブジェクトを作ることができる,「イテレータオブジェクト」(もしくはジェネレータオブジェクト)を返す,という仕様になっている.後述するmap()関数やfilter()関数と同じである.(Python2系はrange()関数はlistを返すので,2系と3系で一番互換性が問題となる違いである)

タプルtuple

タプル tupleという「宣言時に初期化したら変更できない」リストも存在する.

tupleの宣言には,

my_tuple = (123, 456, 789)

という形でカンマで区切った小括弧を用いる.

もしくはコンストラクタ呼び出しで,

my_tuple = tuple((123, 456, 789))

と宣言する.小括弧が二重になっていることに注意する.

my_tuple = (123, 456, 789)
my_list = list(my_tuple)
my_tuple = tuple(my_list)

という形で相互に変換することも可能である.

listと同じく,[]で囲んだ添え字で,tupleの個々の要素にアクセスすることが可能である.

my_tuple = (123, 456, 789)
print(my_tuple[2])

というコードでは,0,1,2の2番目(先頭から3つ目)の値の内容,つまり789という整数がprintされる.

ハッシュテーブル 辞書 dict

「キーと値をセットにして,キーで値を取り出せる」いわゆるハッシュテーブルはdictというクラスを使う.

my_dict = {'apple': 4, 'pine': 8, 'pen': 6}

というように,文字列と値を「:」でペアにし,間まで区切り,中括弧で囲んで宣言する.

同じくコンストラクタ呼び出しで,

my_dict = dict({'apple': 4, 'pine': 8, 'pen': 6})

とも宣言できる.

キーを与えて値を取り出すには,

value_from_dict = my_dict['apple']

と大括弧でキーを囲む.(取り出し時は中括弧ではなく大括弧であることに注意)

その他にitems()関数でキーと値のペアをイテレータオブジェクトとして取り出す,keys()関数で全てのキーをイテレータオブジェクトとして取り出す,values()関数で全ての値をイテレータオブジェクトとして取り出す,等の関数がある.

コメント

コメントとは,プログラムコードの中に記述されているが,実際には実行されない部分を指す.

Pythonの場合, # を書いた行は,#以降の部分は実行されなくなる.行頭に#を置くと,その行全体が実行されなくなる.

a = 1
b = 2
# a = a + b コメントになっているので,この行は実行されない
print(a) # 従って,ここのprintの出力は1になる

制御構造

プログラミング言語における制御構造とは,条件による分岐や繰り返しを記述するための機能である.通常「変数の中身による条件を書いて,その条件によりその後に続くブロックに分岐したり,ブロックを繰り返したりする」.

if文

if文は「もし与えられた条件が真だったら,次に続くブロックを実行する」という制御構造である.

if a == 1: # もしaが1だったら.
  # Trueの時の処理内容
  print(a)
# インデントを元に戻してブロックを終わらせて,次のプログラムへ続く

のように記述する.ブロックを始めるためには,コロン「:」が必要であることに注意.コロンが書かれた次の行からインデントを追加してブロックを始める.

条件式,比較演算子,論理演算子

上記のa == 1というのが条件式である.この条件分は先述したBoolean型を返す.つまりTrueかFalseを返す.Trueは条件が真の時に返され,Falseは条件が偽の時に返される.

条件式に使われる==などの記号を比較演算子と呼び,以下のようなものがある

a == b   # aがbと等しい場合
a > b    # aがbより大きい場合
a < b    # aがbより小さい場合
a >= b   # aがb以上の場合
a <= b   # aがb以下の場合
a != b   # aとbが等しくない場合

また複数の条件式を論理演算子と呼ばれるもので組み合わせることで,論理和(AまたはBが真の時),論理積(AかつBが真の時)を表現することができる.論理和はor(もしくは||),論理積はand(もしくは&&)で表現される.

a == b and c == d   # aがbと等しく,かつ,cがdと等しい場合
a != b or C == d   # aとbが等しくない,または,cとdが等しい場合

さらにPythonではnotという否定詞をつけることができる.

not a == b    # a == bが成立していない場合

これは厳密には,「続く条件式のTureとFalseを逆転させる」ものである.従って,not Ture都だけ書いた場合,Falseが返る.

if - else文

if文を拡張した形であり,条件が真である時には次に続くブロックを,偽である時にはelse:以降のブロックを実行する.

Pythonには中括弧によるブロック指定がないので,空のブロックを作ることができない.処理内容がないけど分岐させたい(空のブロックを作りたい)場合にはpassキーワードを必ず入れること.

if a == 1: # もしaが1だったら.
  # Trueの時の処理内容
  pass
  # passと書くとTrueの時は何もしない.
else:
  # aが1ではない時の処理内容

for文

for文は,とある条件を満たすとき,続くブロックを繰り返す制御構造である.

Pythonに通常のfor文はない.他言語でいう所の拡張for文(for_each文)のみ存在する.つまりPythonのfor文は,繰り返しを指定する「イテレータオブジェクト」の方で条件を決定した繰り返しを行い,for文側では条件判定等を行うことはない.

初学者向けに分かりやすく言えば,Pythonのfor文は「与えられたリストの要素を一つずつ取り出して,取り出した要素に対してブロック内で記述した処理を適用する」というものである.

my_list1 = list([1, 3, 5, 7, 9])
my_list2 = list([]) # 別の空のlistを作っておく
for item in my_list1:
  # my_list1から一つずつの要素(奇数の整数)を取り出し,itemという変数に格納して,続くブロックに送る.
  my_list2.append(item + 1) # ブロックの中ではitemの中の整数に1を足してmy_list2に追加する.

の結果,my_list2の中身は,[2, 4, 6, 8, 10]となる.

(他言語の学習者向け)普通にfor文を実行したい場合,つまり事前にlistを生成しない場合,前述のlist(range(0, 10, 1))でlistのオブジェクトを生成して,それをfor文に食わせることになる.

my_list = list([])
for item in list(range(0, 10, 1)):
  my_list.append(item)

の結果のmy_listの中身は[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]となる.

ちなみに食わせるのはlistだけではなく,listにする前のイテレータ(もしくはジェネレータ)オブジェクトでも構わないので

for item in range(0, 10, 1)

だけでも動作する.

ついでに,range関数の引数が一つだけの場合,前述のように「0からその数字未満まで1ずつ増加させた整数の集合」を出力してくれるので,C言語系の言語で書く所の

for(int i=0; i<10; i++){}

は,

for i in range(10):

と省略して書くことができる.

for文をindex付で取り出すenumerate

例えば,['apple', 'orange', 'grape']というlistが合って,これでfor文を回したい場合を考える.この時に「何番目か」の情報が処理の内容で欲しい時には,enumerateという機能を使う.

fluit_list = ['apple', 'orange', 'grape']
for index, item in enumerate(fluit_list):
  print(index, item)    # printの中で,をつけて変数を区切ると,区切って出力される

と書くと,何番目かを出してくれる.このlistの中の何番目かを表現する言葉として「添え字」「インデックス」という用語が用いられることが多い.

多くのプログラミング言語では,何番目かを表現するインデックスは0から始まる.