5. Python Essentials

Python_Essentials

5.1. Data Type

Data Type では,Pythonでよく使われるデータの種類について学びます.

5.1.1. Primitive Data Types

5.1.2. Bool Type

最も基本的なデータタイプは, Bool Typeです.

これは,変数自体に TrueFalse が入ります.

>>> x = True

という感じです.

また,命題についても,True or False に変換されて変数に入ります.

>>>  y = (100 < 10)
>>>  y
False

5.1.3. Data typeの確認方法

プログラムを書いていて,ある変数がどのようなデータタイプなのか, わからなくなることがあります.これ以降にも,様々なデータタイプが出てきますが,先にデータタイプの確認方法を学びましょう.

ある変数 x がどのようなデータタイプかを調べるためには組込み関数 type() を使います.

>>> type(x)
>>> bool

5.1.4. bool Typeの性質

bool Typeの変数には True もしくは False が割り当てられますが, TrueFalse にはそれぞれ, 10 という数字も割り当てられています

>>> x, y = True, False
>>> x + y
1

5.1.5. ListとBool

この,Bool TypeはListの要素にもなります.

5.1.6. Listとは

Listは様々なオブジェクトを格納できる列のことです.

Listには,値や文字なども入れることができます.例えば,

>>> bools = [True, True, False, True]

という感じです.先ほどの, TrueFalse10 が割り当てられることを考えると,

>>> sum(bools)
3

となります.この sum() のような命令のことを関数と呼びます.

特に,この sum() は組み込み関数とよばれ,pythonにもともと入っています.

ほかの組み込み関数については,公式の組み込み関数_ を参照してください.

5.1.7. そのほかのデータータイプ

PythonにはBool Type以外にのデータタイプも存在します.

例えば数字の, intfloat の2つの種類のデータタイプがあります.

       >>> a, b = 1, 2
       >>> type(a)
       int

``int`` がinterger Typeであり,
>>> c, d = 2.5, 10.0
>>> type(c)
float

となります.この intfloat については後に詳しく説明します.

5.1.8. 注意

この,integerに関連する問題を一つ見てみましょう.

Python 2x では,2つのinteger(整数)同士の割り算では,integerの部分だけを返します.

n:

>>> 1/2
0

ただし, integerfloat や, floatfloat 同士の割り算では,少数以下も返されます.

>>> 1.0/2.0
0.5

ですし,

>>> 1.0/2
0.5

となります.

このような問題はPython 3xでは発生しません.しかし,この教科書はPyhton 2xを用いるので,読者はこのような問題に留意する必要があるでしょう.

5.1.9. complex Type

複素数も,PythonにおけるPrimitiveなデータタイプの一つです.

Pythonでは,Complex Type と呼ばれます.

Pythonで複素数を表現するには,組み込み関数の complex() を使います. complex(実部,虚部) のように指定します.また,Pythonでは複素数は j で表現されます.

>>> x = complex(1, 2)
>>> y = complex(2, 1)

とすれば,

>>> x*y
5j

となります.

5.1.10. Containers

Pythonには様々なコンテナが存在します. コンテナは,データーを集めておくために使われます.

例えば,先に説明した, list は組み込みコンテナといって,Pythonにもともと備わっています.

listと同じような,組み込みコンテナとして tuple (トゥープル,タプル)があります.

この, tuplelist の大きな違いの一つに, tupuleimmutable であることが挙げられます.

tupleimmutable とは, tuple の値が変更できないことを意味します.

一方で, listmutable なので,値を変更することができます.

以下に例を示します,まず listmutable すなわち持っている変数の数が増えたり,減ったり変わったりします.

>>> x = [1, 2]

という list を考え,この1行目の,1を変化させてみましょう.

ところで,この行番号ですが,pythonでは,0から数えます.
x[0] というようにすると, list であるxの0行目を指定できます.これを変更するには,
>>> x[0]=10

というようにします.確認すると,

>>> x
[10, 2]

というように,変更されていることがわかります.

このように, listmutabl です.しかし,一方で, tupuleimmutable です.

>>> X = (1, 2)
X[0] = 10

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-531149b57146> in <module>()
----> 1 X[0] = 10

TypeError: 'tuple' object does not support item assignment

となってしまいます

5.1.11. もう少し,mutable とimmutableの話をしよう

mutablelist にも, immutable なリストにも, unpacke という操作を施すことができます.

unpacked では,それぞれの行を指定した変数に当てはめることができます.

integers = (10, 20, 30)
x, y, z = integers

とすると

>>> x
10

>>> y
20

>>> z
30

というように割り当てられます.

また,slice notetion という操作も, mutable , immutable のどちらにも施すことができます.

例えば,

>>> a = [2, 4 , 6, 8]

という,listの1行目から,最後の行までを抜き出したいときは,

>>> a[1:]
[4, 6, 8]

と指定します.

また,ある行から,ある行までを抜き出したいとき,例えば,1-2行目を抜き出したいとき,

>>> a[1:3]
[4, 6]

というような指定の仕方をします.

list[抜き出しを開始する行番号:抜き出しを終わる行番号+1]

という感じです.

また,

>>> a[-2:]
[6, 8]

のように,最後の2行を抜き出すことができます.

以上の一連の操作は,文字列に対しても行えて,

>>> s = 'kobe univ.'
>>> s[-5:]
'univ.'

と抜き出せることができます.

このような,最後の数行を抜き出すという操作は,全体を確認するには長すぎるデータの内容を確認するときに,有効な場合があります.

5.1.12. Sets と Dictionaries

先に, listtupule という二種類の container を紹介しました.

次に, setdictionary という2つの container について説明します.

まず, dictionary は, list と似ていますが,要素がkeyと言われる変数とヒモ付されている点が異なります.:

d = {'name': 'Frodo', 'age' : 33}

ここでは, 'name''age' がkeyになっています.

こうすることで,作った dictionary に対して,keyを指定することで,ヒモ付けされた情報を抜き出すことができます.

>>> d['age']
33

次に, set というコンテナについて説明します.

set はその名の通り, 集合の container です.

>>> s1 = {'a', 'b'}

当然, type(s1)

>>> type(s1)
set

となります.

別の, s2 という set を考えてみましょう.

>>> s2 = {'b',  'c'}

set に対して,行える演算の一つに, issubset() があります.

s1. issubset(s2) としたとき, s1s2 の部分集合の場合,Trueを返し,そうでないとき,Falseと返します.

>>> s1. issubset(s2)
False

他にも, issubset()set 同士の共通部分を返します.

::
>>> s1. intersection(s2)
{'b'}

同じような,演算として,2つの集合の間の異なる要素を返す, difference() があります.

::
>>> s1. difference(s2)
{'a'}

また, set は重複する要素を持ちません.

>>> s3  = {'b',  'c', 'c', 'c'}

としても,

>>> s3
{'b', 'c'}

となります.

5.2. import

Pythonはその基本に,

- small core language

- extra functionality in separate libraries or modules

を持ちます.

例えば,平方根を計算する関数は,Pythonにはありません.(表現がアヤシイ)

この場合,moduleから関数を import します.例えば, mathimport してみましょう.

>>> import math
>>> math.sqrt(4)
2.0

となります.

他にも, numpy (ナンパイ)にも同じような関数が入っていますが, nampylist に対しても同じ計算を行える点が異なります.

>>> numpy.sqrt([1,4,16,64])
array([ 1.,  2.,  4.,  8.])

試しに, math で同じ計算をしてみると,

::
>>> math.sqrt([1,4,16,64])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-50876051fb1b> in <module>()
----> 1 math.sqrt([1,4,16,64])

TypeError: a float is required

5.3. Input and Output

Pythonで分析を行う上で,テキストファイルを読み込んだり,作成したりする必要性が出てきます.

まずは, newfile.txt というファイルを読み込んでみましょう.

(ここは飛ばしてる・・・・)

5.4. Iterating

computingにおける,最も重要なtaskの一つに,繰り返しを用いた処理があります.

Pythonの強みの一つは,そのシンプルさと柔軟性にあります.

例えば,繰り返しの処理は forin を使って,表現します.

その一例として, us_cities.txt という都市とその人口のデータの処理をしてみましょう.

new york: 8244910
los angeles: 3819702
chicago: 2707120
houston: 2145146
philadelphia: 1536471
phoenix: 1469471
san antonio: 1359758
san diego: 1326179
dallas: 1223229

都市とその人口の間には, : があるので,それを基準にして,都市名と人口を切り離します.

人口には,1000ごとに , で区切りましょう.

まずは,データを読み込みます. us_cities.txt を,同じディレクトリに置いて,

>>> data_file = open('us_cities.txt', 'r')

とします.次に,繰り返し処理を施し,

>>> for line in data_file :# data_fileの中で,line(行)ごとに処理を行う
>>>     city, population = line.split(':') # Tupleをunpackする
>>>     city = city.title()# 都市のの頭文字を大文字にする
>>>     population = '{0:,}'.format(int(population))# 数字に','を入れる
>>>     print(city.ljust(15) + population)

title() は単語の先頭を大文字に,する組み込み関数です.

例えば,

>>> 'But cOme ye bAck wHen Summers in the meaDow.'.tilte()
'But Come Ye Back When Summers In The Meadow.'

となります.

5.4.1. Looping without Indices

気付いている人もいると思いますが,Pythonでは,ループ処理(looping)を行うとき index を使わない方が好まれます.

例えば,

>>> for x in x_values:
        print  x*x

というコードのほうが,

>>> for i in range(len(x_values)):
>>>     print x_values[i] 8 x_values[i]

というコードよりも良いことが,2つを比べてみればわかると思います.

Pythonでは,1つ目のコードのように,変数に対して i のような index を付けずにループ(looping)処理をシンプルにします.

その例の一つとして, zip() という関数を説明しましょう. zip() は2つの列の要素を対応させていく関数です.

>>> zip((1,2,3,4),("a","b","c"))
[(1, 'a'), (2, 'b'), (3, 'c')]

というように,この場合,2つの tuple を要素ごとに組み合わせて, tuple を作り, list をつくります.

この関数を使って,県庁所在地を対応させるコードを書いてみましょう.(教科書では,国と首都を対応させるコードですが,ここで紹介するのもと同じものです)

まずは,2つのデータ, 都道府県のデータ;prefectures県庁所在地のデータ;cities を用意します.

>>> prefectures =('北海道(ほっかいどう)','青森県(あおもり)','岩手県(いわて)','宮城県(みやぎ)')
>>> cities = ('札幌(さっぽろ)','青森(あおもり)','盛岡(もりおか)','仙台(せんだい)')

次に,この2つを zip() で対応させていきます.

>>> prefectures =('北海道(ほっかいどう)','青森県(あおもり)','岩手県(いわて)','宮城県(みやぎ)')
>>> cities = ('札幌(さっぽろ)','青森(あおもり)','盛岡(もりおか)','仙台(せんだい)')
>>> for prefecture, city in zip(prefectures, cities):
        print '{0}の県庁所在地は,{1}です'.format(prefecture, city)

すると,出力は,

北海道(ほっかいどう)の県庁所在地は,札幌(さっぽろ)です
青森県(あおもり)の県庁所在地は,青森(あおもり)です
岩手県(いわて)の県庁所在地は,盛岡(もりおか)です
宮城県(みやぎ)の県庁所在地は,仙台(せんだい)です

'{0}の県庁所在地は,{1}です'{}zip() で作った,対応を当てはめていく場所です. {0} と書くと,そこに, zip(0, 1) とした第0番目の要素が入ります. {0} の中に,何も書かなかった場合は zip(0, 1) の要素の順番通りに変数が当てはめられていきます.

例えば,

>>> print '{}の県庁所在地は,{}です'.format(prefecture, city)

としても,結果は変わらず,

北海道(ほっかいどう)の県庁所在地は,札幌(さっぽろ)です
青森県(あおもり)の県庁所在地は,青森(あおもり)です
岩手県(いわて)の県庁所在地は,盛岡(もりおか)です
宮城県(みやぎ)の県庁所在地は,仙台(せんだい)です

となります.

また.

>>> print '{1}の県庁所在地は,{0}です'.format(prefecture, city)

とすれば,:

札幌(さっぽろ)の県庁所在地は,北海道(ほっかいどう)です
青森(あおもり)の県庁所在地は,青森県(あおもり)です
盛岡(もりおか)の県庁所在地は,岩手県(いわて)です
仙台(せんだい)の県庁所在地は,宮城県(みやぎ)です

となり,都道府県と県庁所在地の順番が入れ替わります.

5.4.2. zip()とdictionaries

zip()``は ``dictionary を作るのに便利です.

例えば,

>>> names = ['Tom', 'John']
>>> marks = ['E', 'F']
>>> dict(zip(names, marks))
{'John': 'F', 'Tom': 'E'}

というように,簡単にdictionary type のデータを作ることが出来ました.

5.4.3. それでも.indexが必要な時は

index がなくても,ループ処理ができるといっても,実際には index が必要な時があります.

そのような場合は, enumerate() を用いて, index を割り当てていきましょう.

>>> prefecture_list=['北海道(ほっかいどう)','青森県(あおもり)','岩手県(いわて)','宮城県(みやぎ)']
>>> for index, prefecture in enumerate(prefecture_list):
>>>     print '日本の東から{0}番目の都道府県は,{1}です'.format(index, prefecture)

とすれば,

日本の東から0番目の都道府県は,北海道(ほっかいどう)です
日本の東から1番目の都道府県は,青森県(あおもり)です
日本の東から2番目の都道府県は,岩手県(いわて)です
日本の東から3番目の都道府県は,宮城県(みやぎ)です

と出力されます.ここから, {0}index が割り当てられていることがわかります.

ところで,この, enumerate() では, index の開始番号を指定することができましょう.

'日本の東から0番目の都道府県' という日本語は,違和感があるので, enumerate(prefecture_list,1) とすれば.

>>> for index, prefecture in enumerate(prefecture_list,1):
>>>     print '日本の東から{0}番目の都道府県は,{1}です'.format(index, prefecture)

日本の東から1番目の都道府県は,北海道(ほっかいどう)です
日本の東から2番目の都道府県は,青森県(あおもり)です
日本の東から3番目の都道府県は,岩手県(いわて)です
日本の東から4番目の都道府県は,宮城県(みやぎ)です

というように出力されることから,indexが1から割り当てられていることがわかります.

5.5. Comparisons and Logical Operators

5.5.1. Comparisons

Booolean values(True or False で評価されるvalues) として,処理されるものはたくさんあります.

ここでは,代表的なものを紹介します.

まずは,不等式.

>>> a,b,c,d,e,f = 1,1,2,3,5,8
>>> a<=b<c<d<e<f
True

等式は,Pythonでは, == で表現されます.

>>> x=1
>>> x==20
False

ノットイコール (neq) は, != で表現されます.

>>> 1!=20
True

if を使うと,ある条件が満たされるかどうかで,場合分けすることができます.

>>> x= 'yes' if 5>2 else 'no'
x
'yes'

この条件式には,Pythonで認められているものであれば,どんなものでも許容されます.

if のあとに,つねに成立するような命題をいれてみます.

>>> x= 'yes' if 42 else 'no'
>>> x
'yes'

常に満たされない,ものとして, if のあとに空集合を入れてみましょう.

>>> x= 'yes' if [] else 'no'
>>> x
'no'

また, if の後に 0 を入れても同じ結果になります.

つまり, 0 だけではなく, [] にも false が割り当てられているということです.

bool() を用いて,確認してみましょう.

>>>print(bool(0))
False
>>>print(bool(42))
True
>>>print(bool([]))
False
>>>print(bool([1,2,3]))
True

条件式は and を用いて,複数の命題を組み合わせることもできます.

>>> 1<2  and  'あ'  in  'あきら'
True

5.6. More Functions

今まで紹介していないfunctionとして, max() , min() , ramge() などがあります.

range()引数に指定した長さのlistを作ります.例えば,

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

となり,指定しない場合は0から始まるlistを作ります.

始めと終わりの数字を指定することもできます,

>>> range(-10,10)
[-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

次に, max()listtuple といった引数の最大値を返す関数です.今までの,条件式や range() を組み合わせて例を作ると

>>> max(range(100))>98
True

となります. range(100) は0から99までの list を作るので,

>>> max(range(100))>99
False

となることが分かります.

最後に, str() は引数に指定した値の文字列を作ります.

>>> str(12)
'12'

5.7. Why Write Functions?

関数(function)を書くことで,コードを明瞭に書くことができます.

なぜなら,

  • ロジックごとにまとまりを作れる
  • 一度書けば,そのコードを何回も呼び出して使える

からです.

functionの定義の仕方を示しましょう.

引数の正負を調べる関数を定義します

def f(x):
        if x < 0:
                return 'nagetive'
        return 'nonnegative'

とすれば,

>>> f(-5)
'nagetive'

lamba を使うと,一行でfunctionを書くこともできます.

>>> f = lambda x:  x**2

とすると,

>>> f(x=5)
25

のような関数を定義できます.

しかし,functionの内容を明確化するためにも, def をつかったfunctionの定義を用いるほうが望ましいでしょう.

5.8. Exercises

5.8.1. Exercise1 Par1

zip() を使って内積を求める問題

適当な list の内積を求める

>>> x=[1.0,1.0,2.0,3.0,5.0,8.0,13.0,21.0,34.0,55.0,89]
>>> y=[1,1,2,3,5,8,13,21.0,34.0,55.0,89.0]

この2つの list の内積を求めてみよう

>>> S=0 #Sを0にしておく
>>> for a,b in zip(x,y): #zip()でx,yの要素をa,bに入れる
>>>     S=S + a*b        #for を使って各要素を足しあわせていく
>>> print(S) #Sの値を出す
12816.0

問題では zip() を用いましたが,一行で書くこともできます.

sum() の中に条件式を入れることもできるので

>>> sum(a*b for a,b in zip(x,y))
12816.0

とかける.

5.8.2. Exercise1 Par2

一行で0から99の偶数の数を計算する.

ちなみに,

>>> x%2

という操作が,xを2で割った余りを返すことがヒントです.

まず,コードの内容を把握するために,複数行で書いてみる.

>>> s=0
>>> for x in range(100):
>>>     if x%2==0:
>>>             s=s+1
>>>     print(s)
50

これを,一行にまとめる.

Exercise1 Par1のように sum() を用いて表現する.

range(100) のなかで, x%2==0true の回数を数えればいいので,

>>> sum(1 for x in range(100) if x%2==0)
50

xが偶数の時に x%2 が0になることを利用する方法もある.

0にはFalseが割り当てられるので,xが偶数なら not x%2true になる.

true には1が割り当てられているので,

>>> sum(not x%2 for x in range(100))
50

5.8.3. Exercise1 Par3

pairs = ((2, 5), (4, 2), (9, 8), (12, 10)) というリストの tuple の(a,b)のうち,a,bのどちらもが偶数の tuple の数を数える.

(a,b)のa,bに関してそれぞれ条件式をかくので,(a,b)を分ける必要がある.

それは,

>>> for a,b in pairs

とすればよい.あとは条件式をかいてそれが満たされる回数を数えれば良いので,

>>> pairs=((2,5),(4,2),(9,8),(12,10))
>>> n = 0
>>> for a,b in pairs:
>>>     if a%2==0 and b%2==0:
>>>             n=n+1
>>>     print(n)
2

とすれば良い.

これも,一行でかけて,

>>> sum(1 for a,b in pairs if a % 2==0 and b%2==0 )
2

となる.

5.8.4. Exercise2

Polynominal(多項)の式を計算する関数を作るExersise.

式は以下の通り,

\[p(x) = a_0 + a_1 x +a_2x^2+ ... + a_nx^n = \sum^n_{i=0} a_i x^i\]

所与の数列 \(a_nとx^n\) の掛け算を足し合わせてできる関数p(x)を作れば良い.

数列が所与といっても,それはPolynominal(多項)の式自体がそうなのであって,計算するにはその具体的な値が必要です.なので,関数の引数としては,xの値と,数列anを list として指定します.

数列のnとxの乗数が共通しているのでそれを利用して書いてみましょう.一番,思いつきやすいのは,項をterとおいて,足しあわせていき,足し合わせるごとのnを1増やしていく方法だと思います.

>>> def p(x, coeff):
>>>     n, ter = 0, 0
>>>     for a in coeff:
>>>             ter = ter + (a * (x**n))
>>>             n += 1
>>>     return ter

しかし,先に習ったように, enumerate() を用いれば, index をわざわざ作る必要はありません.

>>> def p2(x, coeff):
>>>     ter = 0
>>>     for n,a in enumerate(coeff):
>>>             ter = ter + (a * (x**n))
>>>     return ter

もう少し工夫すれば,この関数を2行で書くこともできます.

>>> def p3(x, coeff):
>>>      return sum(a*(x**n) for n,a in enumerate(coeff))

5.8.5. Exercise3

英文の文字列の中の大文字の数を数える関数を作る問題です.

ヒントとして,文字列を大文字にして返す関数, .upper() が与えられているので,これを上手く使いましょう.

関数の流れとしては,文字列, strings の中の文字xを一文字づつ取り出して,その文字の大文字を .upper() で作ります.

そして,それが取り出したxと同じかどうかを調べ,同じだった回数を数えればよいでしょう.

>>> def f(string):
>>>     n=0
>>>     for x in string:
>>>             if x == x.upper() and x.isalpha():
>>>                     n=n+1
>>>     return n

ただし,この関数の定義をみればわかるように, x.isalpha() というmethodが用いられています.

これは,文字が英字であるかどうかを調べるmethodです.

もし,これがないと,スペースやピリオドといった,英字でないものに対してもTrueを返してしまい,正確にstringsの中の大文字の数を数えることはできません.

しかし,このような教科書に出ていないmethodを使わなくても,工夫して同じ関数を定義することができます.

>>> upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> 'A' in upper
True

このように,あらかじめ,大文字の英字を文字列として与えてこのなかのどれかと,stringの文字が同じだった時,Trueを返すようにすればよいのです.

また,Trueの値が1であるということを思い出せば,直接Trueを合計すればいいことにも気づくでしょう.

>>> def count_upper(s):
>>>     upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>>     return sum(c in upper for c in s)

5.8.6. Exercise4

2つの流列(sequence)a,b,が与えられていて,aの要素すべてが,bの要素に含まれていたらTrueを返す関数をつくる問題.

ここでいう,流列とはlistやtuple,stringを意味する.

また,setsやset methodを使わずに関数を定義するように指示されています.

seq_aの要素を1つずつ取り出して,それが,seq_bの中に入っていなければFalse,それ以外ならばTrueをかえす関数を書いてみましょう.

>>> def f(seq_a,seq_b):
>>>     for a in seq_a:
>>>             if a not in seq_b:
>>>                     return False
>>>             return True

この関数の問題点は,seq_aの要素全てに対して,それがseq_bに属しているかを調べている点です.

問題文では,“の要素すべてが,bの要素に含まれていたらTrue”とあるので,seq_aなかで,一つでもseq_bに入っていないものを見つけたら,すぐにFalseになる関数を書けば,より効率的な関数を書くことができます.

そのような,関数を定義するために,all()という組み込み関数を用います.

all()は,iterable の全ての要素が真ならば (もしくは iterable が空ならば) True を返すので,bにないものが見つかった時点で,動作をやめます.

つまり,seq_aの要素全てに対して,それがseq_bに属しているかを調べている最初の解答例よりも早く処理が終わります.

>>> def f(seq_a,seq_b):
>>>     return all(a in seq_b for a in seq_a)