特別講義DS Ch7 アルゴリズムとPythonの基本構文
1 アルゴリズムとPythonの基本構文
データを適切に処理するためには,これまで扱ったpandasの技法だけでは足りません.ライブラリに含まれていない処理をPythonで自分で記述する必要があります.
Pythonで処理を記述するためにはそのための構文を学習する必要があります.
プログラムで記述される処理を,アルゴリズムといいます. アルゴリズムとは,有限回適用することで問題を解くことができる規則の集まりです. プログラミングとは,プログラミング言語のデータ型と構文を利用してアルゴリズムを記述することにほかなりません.
基本的に,アルゴリズムを構成する要素は以下のの3つしかありません.
- 逐次処理
手続き型言語は,プログラムは(基本的には)上から順番に実行されます. プログラムの記述に従って処理を順番に実行することを逐次処理といいます.
- 分岐
条件に従って処理が分岐します. Pythonでは,if
とcase
という構文によって実装されます.
- 反復
指定の回数または,条件が満たされるまで処理を繰り返すこと. Pythonでは,for
,while
によって実現します. より高度な実装方法として,再帰や高階関数なども利用できますがこの講義では扱いません.
この3つの組み合わせがプログラムで記述できれば,プログラムを利用してどのような問題でも解けます.
1.0.1 フローチャート
アルゴリズムを視覚的に表現する方法の一つにフローチャートがあります. 原理的には,フローチャートを描くことができれば,アルゴリズムが構築できますし,プログラムが記述できます.
フローチャートは,以下の図形と矢印の組み合わで記述されます(厳密には,ループやデータ代入などもっと細かいですが,ここでは簡単のために省略しています.)

例えば,逐次処理は以下のようなフローチャートで表現されます. 以下は,なにかの野菜を育てる手順をフローチャートで表現したものになります.矢印の順番で処理が行われます.

そこに,実がならなかった場合は刈り取るという分岐を加えると以下のようになります.

更に,実がならなかった場合には,肥料をやり,もう一度水を撒くところからやり直すという形で,前のステップに戻る反復を追加すると以下のようになります.

プログラムを用いてアルゴリズムを記述するには,①自然言語(日本語)で解きたい問題が書ける,②その解き方をフローチャートなどを利用してアルゴリズムに変換できる,③アルゴリズムをプログラムに変換できる,というステップを踏むのが一般的です.
最近小学生などの教育で良く耳にする論理的思考力やプログラミング的思考とは,この①から②への変換ができることを意味しているようです. プログラミングの学習では,①から②への変換は前提として,プログラムの知識,文法,記法,利用方法などを学習し,②から③への変換を扱います.
最近の小学生は,この学習のために,フローチャートを作ることでプログラムが書けるScratchなどを使っているようですが,この講義ではそこからやっている時間はないので,この章でフローチャートの作成と,そのプログラムへの変換の基礎を学習します.
- 演習問題
以下の処理を表すシンプルなフローチャートをPowerPointを利用して作成してください.
カレーの作り方 カレーの作り方を想像して,フローチャートで表現してください.
自動販売機 以下の条件で自動販売機を想定して, 自動販売機にお金が投入されてから商品が出てくるまでのフローチャートを作成してください.
100円の炭酸飲料と,150円のお茶を販売している自動販売機
商品を選択してくださいと最初にアナウンスする
選択された商品に応じた金額を投入してくださいとアナウンスする
投入された金額が足りている場合は商品を出しお釣りを返す
投入された金額が足りない場合は,投入されたお金を全部返して,最初に戻る
100個の1から100までのランダムな数字が書かれたボールが入っている箱Aから,1つずつボールを取り出し,偶数のボールを箱B,奇数のボールを箱Cに入れる作業を小学生に説明するためのフローチャートを作成してください.
1.1 input関数
これからアルゴリズムをプログラムで記述する練習をするにあたって,スクリプト内のプログラムと人間がやり取りをする場面が何度か出てきます.そのために利用される関数にinput()
があります. input()
はプログラム内で標準入力(Terminalなどに記述された文字列)を受け取る関数です. ()
内には標準入力を受け取る前に出力される文字列を記述します. 文字列が表示された後,ユーザーが入力した文字列が入力されます.
以下のプログラム(input_test.py
)を記述して実行してみましょう.
= input('あなたの名前を入力してください. \n')
x print(f'あなたの名前は{x}ですね. よろしく!')
以下のように,()
の中の文字列が表示されて,入力された文字列が代入されたx
が表示されるはずです.
❯ python3 input_test.py
あなたの名前を入力してください.
akagi
あなたの名前はakagiですね. よろしく!
1.2 逐次処理,インデント,ブロック
pythonは手続き型言語なので,基本的に上から1行ずつプログラムが実行されます. これによってアルゴリズムの構成要素における逐次処理が実現されています.
これまでにこの講義で扱ってきたプログラムは,基本的に1行に1処理を記述しており,処理は上から1行ずつ実行されていました.
しかし,プログラムがより複雑になると,一つの処理が1行では記述できなくなってきます. そこで,プログラムの中で処理の塊を作って,その処理の塊を順番に実行していくことになります. Pythonではこの処理の塊をブロック
と呼び,インデント
によって表現します.
def sample():
print('sample')
####
#↑def のブロックをインデントで表現
= 0
total for i in range(10):
+= i
total print(total)
####
#↑forのブロックをインデントで表現
for i in range(10):
for j in range(10):
print(i*j)
####
########
#↑ インデントの中でインデントを使って
# ブロックの中のブロックを表現
インデント
は,パワーポイントと同様に,行頭からの空白を意味します.半角スペース4つ
が一つの単位となります. TABキー
で入力されるTAB
をインデントとして利用することもできますが,プログラム中にタブとスペースが混在することになり編集が複雑になるので推奨しません.
しかし,いちいちスペースキーを4回テキストエディタで入力するのは面倒なので, テキストエディタの設定でタブをスペース4つに変換するように設定しておくと楽です.
Sublime Textでは, View > Indentation > Indent Using Spaces
をクリックして, TAB Width: 4
を選択すると,自動でタブがスペースになります.

1.3 分岐
Pythonで分岐を表す基本的な構文としてif
文があります. if
は英語の通り,もしXXならを表しており, if XXX:
のインデントブロックに,その条件下で行って欲しい処理を記述します.XXX
の部分には,Bool
型あるいはBool
型を返す論理式が入ります. 文の最後に:
を記述するのを忘れないようにしましょう. また,続けて, elif YYY:
と記述することで条件を追加することができます. 条件を網羅的に書かなくても, if
とelif
で指定された条件以外のすべての場合の処理をelse:
で指定することができます.

具体的に,ある変数x
の大きさによる分岐は以下のように書かれます. 以下の例では,変数x
と10
の大小比較の結果によって,表示する文字列を変更しています.

- 発展:Bool以外のデータ型とif文の挙動
if
文のif
,elif
以下の部分には基本的にBool型を記述しますが,Int
型やstr
型なども利用可能です. int
の場合1
はTrue
,それ以外の数値はFalse
とみなされます. str
の場合,''
(空白)のみがFalse
それ以外はTrue
となります.こちらのほうがTrue
やFalse
より高速なので,サンプルコードなどで利用されている場合がありますので注意しましょう.
例として下のプログラムを実行すると,1
,b
,c
とだけ表示されるはずですので,確認してみましょう.
if 1 :
print('1')
if 0:
print('0')
if '':
print('a')
if ' ':
print('b')
if 'c':
print('c')
それでは,具体的なプログラムを記述してみましょう.以下のプログラムについて考えてみます.
動物の名前をうけとって,それが'イヌ'
なら,'ワンワン'
,'ネコ'
なら,'ニャンニャン'
,どちらでもないなら'???'
と表示するプログラム
これを,まずは日本語のフローチャートで表現して,日本語の部分をプログラムの構文に書き換えてみると以下のようになります.これくらいのプログラムだと,フローチャートを経由して変換するのは面倒かと思いますが,今後より複雑なプログラムを書くにあたっての訓練ですので,取り組んでみましょう.

フローチャートが書けたら,そのままプログラムを上から書いてみましょう. else
の部分だけはフローチャートに現れていないので注意してください.
= input('動物の名前をカタカナで入力してください. \n')
kind
if kind == 'イヌ':
print('ワンワン')
elif kind == 'ネコ':
print('ニャンニャン')
else:
print('???')
実行して,意図通りに動くかを確認しましょう.
❯ python3 pet_name.py
動物の名前をカタカナで入力してください.
イヌ
ワンワン
❯ python3 pet_name.py
動物の名前をカタカナで入力してください.
サカナ
???
- 演習問題
以下の,処理のフローチャートと,プログラムを作成してください.
関東の都県を標準入力から受け取り,その都県の県庁所在地を返す.
input()
関数で数値を受け取って, 偶数なら偶数, 奇数なら奇数という文字列を返す.- ヒント:
input()
関数の返り値は文字列なので,int(input(‘数値を入力してください /n’))
のようにint()
関数を利用することで数値に変換できる.
- ヒント:
ランダムな1から10の数値を発生させて, その数値が5より大きければ
'BIG'
,小さければ'SMALL'
と表示する.- ヒント: 一行目に
import random
と記述してx = random.randint(1,10)
と書くと, 1から10のランダムな整数がxに入ります.
- ヒント: 一行目に
1.4 反復
Pythonにおいて,処理を繰り返す反復を実装する構文はいくつか存在しますが,代表的なものに while文
及び for文
があります. 利用頻度からしても,for文
の方が重要ですが, ここではフローチャートと相性が良く,意味が分かりやすいwhile文
で反復の感覚を掴んでからfor文
を学習しましょう.
1.4.1 While文
プログラムは,コンピュータに何かしらの命令をする文を書くものです. while文
もif
のように英文の意味に沿って,プログラムに命令を与えています.
例えば以下の英語の命令文について考えてみましょう.
- Keep working while the timer is running.
(タイマーが動いている間は,働いてください.)
- Water the plants every day while they are not bearing fruit.
(植物に実が成っていない間は毎日水やりをしてください.)
- Continue reading while the light is on.
(電気がついている間は, 読み続けてください.)
これらの文はいずれも, 命令 while 条件 という形を取っており, 条件が真である限り,命令を実行してくださいという意味になっています.
プログラムにおけるwhile文
は上の英文の順序を少し入れ替えて, while 条件: 命令
の形をとり,条件
が真である限り,命令
を実行するという意味のプログラムになります.

while文
は条件によって,プログラムの継続を判断するので,条件の真偽値が変更されない限り,プログラムが終了しません.
試しに以下のコードを実行してみましょう.
#実行されない
while False:
print('never printed')
#永遠に数字が増え続ける
#終了するには Ctrl + C
= 1
x while True:
+= 1
x print(x)
1つ目のwhile文
は,条件が最初からFalse
になっているため実行されません.
2つ目のwhile文
は,条件が永遠にTrue
なのでx
が1
から増え続けてprint()
によって標準出力され続けます(このように反復を表すプログラムでは, 変数への再代入を多用しますので,忘れている人は復習しましょう.) プログラムを強制終了するにはCtrl + C
を押しましょう.
ウェブサイトやソフトウェアの表示などではwhile文
を利用して永遠にプログラムを動かし続けることをしますが,通常の反復では,何かしらの条件の変更によってプログラムを終了するように条件部分を変更する必要があります.
先程のプログラムを少し変更して, x
の値が1
ずつ増えていき,10
になったら終了するようにしてみるとどうなるでしょうか.
while文
のような反復は, フローチャートでは,条件部分を表す分岐と,前に戻る矢印で表されます. 分岐のあとに,何かしら条件に関わる値が変更されることで,分岐が終了します.

= 1
x while x < 10:
+= 1
x print(x)
❯ python3 while_and_for.py
2
3
4
5
6
7
8
9
10
続いて,標準入力の結果によって反復の終了条件を判定するための,以下のフローチャートで表されるプログラムを考えてみましょう.

このフローチャートをプログラムに直すと以下のようになり, 問題文に正解しない限りプログラムが終了しません.
= 0
x while 2 != x:
= int(input('1 + 1 = ? \n')) x
❯ python3 while_and_for.py
1 + 1 = ?
3
1 + 1 = ?
4
1 + 1 = ?
2
1.4.1.1 while文
とif文
の組み合わせ
while文
の中でもif文
を利用した分岐が可能です. 先程のプログラムに少し加えて,ヒントを出すようにしてみましょう.

while文
のインデントの中で,更にif文
のインデントが組まれていることに注意しましょう.
= 0
x while 2 != x:
= int(input('1 + 1 = ? \n'))
x
if x > 2:
print('少し数が大きいかも')
elif x < 2:
print('少し数が小さいかも')
else:
print('大正解!!')
❯ python3 while_and_for.py
1 + 1 = ?
3
少し数が大きいかも
1 + 1 = ?
1
少し数が小さいかも
1 + 1 = ?
2
大正解!!
1.4.1.2 真偽値以外のwhile文
while文
では,if文
と同様にTrue,False以外の値を条件部分に用いることがあります.
while文
ではリスト
を条件部分に与えることが可能で, リストが空のときFalse
として判定されます.

以下のプログラムでは,リストの先頭要素を表示したのち,リストの先頭要素をpop()
メソッドによって削除しています. 削除を続けて,リストが空になるとプログラムが終了します.
= [1,2,3]
xs while xs:
print(xs[0])
#リストの先頭要素を削除(pop())
0) xs.pop(
❯ python3 while_and_for.py
1
2
3
- 演習問題
以下のプログラムのフローチャートを作成し,while文を利用してプログラムを記述してください.
x = 1
に3
ずつ数を足しながらx
の値をprint()
する.x
が1000
を超えたら終了する.1
から100
までの数の和を求める.- ヒント: 1ずつ値が増える変数とは別に,増えた値を足す変数を最初に作ろう.
100
から150
までの数のうち,5
で割り切れるかつ2
で割り切れる数の和を求める- ヒント
- まずは100から150の数を表示するプログラムを作ってみよう
- 次に, 100から150までの数のうち
5
と2
で割り切れる数を表示するプログラムを作ってみよう - 最後に,表示した和を足すようにしてみよう
1.4.2 for文
続いてもう一つの代表的な反復の表現方法であるfor文
について見ていきましょう while文
の説明の最後に扱った,リストから一つずつ値を取り出す操作のようになにかから値を取り出す操作を繰り返すことに特化したのが,for文
です.
for文
は, for x in y: z
の形で,yから一つずつxを抜き出して,zをしてくださいという意味になります.
英文では,以下の用にFor each x in y,の形で表されますが, プログラムではeachが省略されます.
- For each mandarin in the box, take it out and peel it
(みかんの入った箱から一つずつみかんを取り出して,皮を剥いてください)
- For each student in the class, collect the handout from them.
(クラスの学生一人ひとりからプリントを回収してください.)
- For each panda in the cage, take it out one by one and line them up.
(パンダの檻からパンダを一匹ずつ連れ出して並べてください.)
for文
をプログラムの世界で利用するには,現実を対象とした英語における,みかんの入った箱やパンダの檻のようになにか中身を取り出せる入れ物が必要になります.
python
では,この入れ物として,リストのように,中身を順番に取り出せるもの(オブジェクト)をイテラブルオブジェクト( iterable object)を利用します.
Iterable object の例
- リスト
xs = [1,2,3,4,5]
順番に取り出すと,
1,2,3,4,5
の順番で一つずつ出てくる- 辞書
animal_count = {'cat':2,'dog':4,'bird':8}
key
を順番に取り出すと,'cat','dog','bird'
が順番に一つずつ出てくる- タプル
xs = ('a','b','c','d')
順番に取り出すと,
'a','b','c','d'
の順番で一つずつ出てくる- DataFrameの列や行(
DataSeries
)
- リスト

for文
における終了条件は,iterable object
が空になることです.
リストから要素を取り出して,以下のフローチャートで表されるような合計を求める処理について考えてみましょう.

プログラムに直すと以下のようになります.
= [1,2,3,4,5]
xs = 0
total
for x in xs:
+= x
total
print(total)
❯ python3 while_and_for.py
15
xs から取り出した値がx
に毎回代入されていることに注意しましょう.なお,x
は変数名なので,任意の名前をつけることが可能です.
また,pop
を利用したwhile文
と違って,元のxs
の要素数は実際には減っていません.
range()
関数
上の例のように,m
からn
までの連続した数値が欲しい場合に,毎回リストを作っていると大変です. 単純に数値の列が欲しい場合はrange()
関数を使いましょう.
range(x)
は0
から始まるx
個のシーケンス
(インデックスで位置を指定できるイテラブルオブジェクト)を返します.
for x in range(5):
print(x)
0
1
2
3
4
range(始端,終端,ステップ)
の形で複数の引数を指定することで,始端,終端,ステップ(何個とばしにするか)を定めることができます.
終端は,一つ前の値までしか出力されないので注意しましょう.
for x in range(5,10):
print(x)
5
6
7
8
9
for x in range(5,10,2):
print(x)
5
7
9
1.4.2.1 for文
とif文
の組み合わせ
while文
と同様にfor文
の中でif文
を利用したり,if文
の中でfor文
を利用することができます.
以下のプログラムでは, 1
から5
までの値の中で偶数のものだけをprint()
しています.
for x in range(1,6):
if x % 2 == 0:
print(x)
❯ python3 while_and_for.py
2
4
1.4.2.2 多重ループ
for文
の中でfor文
を複数回繰り返して多重ループを実現できます. 以下のプログラムでは, リストxs
からリストx
を取り出し,取り出したリストx
から取り出した要素y
の合計値を求めています.
= [[1,2,3],[4,5,6],[7,8,9]]
xs = 0
total for x in xs:
for y in x:
+= y
total print(total)
❯ python3 while_and_for.py
45
- 演習
以下の処理のフローチャートとプログラムを作ってみよう.
x=0
に[1,3,5,7,9,12]
を順番に足して更新する.x
の値を更新するたびにprint()
する.人物の名前と成績を記録した辞書型
xs={'taro':'S','hanako':'B','yumi':'A','jiro':'D'}
から成績A以上の人物名だけをリストupper=[]
に追加し,upper
を表示する.100
から150
までの数のうち,5
で割り切れるかつ2
で割り切れる数の和を求めるxs = [[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20]]
として,for文
の多重ループを利用して,xs[0]
からxs[3]
の合計値が入ったリストを求めるFizzBuzz
とはプログラミングの動作確認でよく用いられる欧米圏の言葉遊びゲームです.. 以下のルールに則って1
から100
までの数を順番にFizzBuzz
の判定を行ってください.3の倍数ならFizzと表示する
5の倍数ならBuzzと表示する
両方の倍数ならFizzBuzz
どちらでもないならその数をPrintする.
1.4.2.3 発展: リスト内包表記
Pythonにはfor文
とwhile文
以外にも,反復を実現するための手法がいくつか存在します.
そのうち良く使われるものにリスト内包表記があります.
リスト内包表記は,集合論の記法を取り入れた書き方で,関数型言語からPythonに取り入れられました. リスト内包表記は,短い行で簡潔に書けるためリストを生成する際に良く用いられます.
例えば, 1~10までの数値のうち,偶数だけが入ったリストを生成することを考えます.
for文
を利用すると,例えば以下のようにして得ることができます.
= []
even_numbers for i in range(1,11):
if i % 2 range(1,11):
even_numbers.append(i)print('for文:', even_numbers)
#結果>>> for文: [2, 4, 6, 8, 10]
1つのリストを生成するのに4行かかっています. リスト内包表記で同じリストを作成してみます.
= [x for x in range(1,11) if x % 2 == 0]
even_numbers print('内包表記:',even_numbers)
#結果>>> 内包表記: [2, 4, 6, 8, 10]
同じ結果を一行で得ることができました.
リスト内包表記では[]
の中に
[得たい値 for 使いたい要素を代入した変数 in イテラブルオブジェクト if 条件]
という書き方で,for文
やif文
を利用します.
詳細は省きますが,これは数学の集合論における内包表記を真似た記述法になります.

なれるまで難しいかと思いますので,いくつか他の例も見てみましょう.
- 1から10までの数字のうち,奇数のものに3を足したリスト
= [x + 3 for x in range(1,11) if x % 2 == 0]
xs print(xs) #[5, 7, 9, 11, 13]
animals_small = ['cat','dog','bird']
を大文字に変換したリスト
= ['cat','dog','bird']
animals_small = [x.upper() for x in animals_small]
animals_large print(animals_large) #['CAT', 'DOG', 'BIRD']
['CAT', 'DOG', 'BIRD']
のうち文字数が3文字以下の文字列の頭文字のリスト
= [x[0] for x in animals_large if len(x) <=3 ]
xs print(xs) # ['C', 'D']
100
から150
までの数のうち,5
で割り切れるかつ2
で割り切れる数の和
print(sum([x for x in range(100,150) if x % 10 == 0]))
#600
1.4.2.4 発展: breakとcontinue
for文
やwhile文
の処理を途中で分岐させたい場合には break
, continue
, else
が利用できます.
continue
:continue
後の処理を行わずに反復の最初に戻るbreak
: 反復を抜け出すelse
:break
以外でwhile文
が終了したら実行
例として,あなたはなんの動物か尋ねてニンゲンですと答えないと終わらない以下のような処理を作ってみます.

この処理はユーザーに「あなたはなんの動物ですか?(カタカナで回答)」と質問し,正しい答え「ニンゲン」を得るまで質問を繰り返します.
まず,answer変数にユーザーの入力を格納します.whileループを使って,answerが「ニンゲン」でない限りループを続けます.
もし,ユーザーが「ウチュウジン」と答えた場合,「本当に!? 怖いのでさようなら!」と表示し,break
でループを終了します.
ユーザーが「カミサマ」と答えた場合,「わお!! 初めて会いました! … 馬鹿にしないでちゃんと答えてください!」と表示し,再度質問を行います.continue
で反復の最初に戻ります.
「ニンゲン」「ウチュウジン」「カミサマ」以外の答えの場合,「嘘をつかないで! XXX は喋れません.」と表示し,再度質問を行います.
ループが「ニンゲン」の答えで終了すると,else
で定義された「そうですよね!ニンゲンに決まっています!」と表示されます.
プログラムに直すと以下のようになります. それぞれの行がどのような条件で実行されるのか, 確認してみましょう.
# 先に変数を用意します
= input("あなたはなんの動物ですか?(カタカナで回答)\n")
answer
# while文でニンゲンですと答えるまで終わらないプログラムを書きます
while answer != "ニンゲン":
# 答えによっていろいろな反応を組み込んでみましょう
if answer == "ウチュウジン":
print("本当に!? 怖いのでさようなら!\n")
# while ループを終了します
break
elif answer == "カミサマ":
print("わお!! 初めて会いました! ... 馬鹿にしないでちゃんと答えてください!")
# もう一度値を更新して,ループの最初に戻ります
= input("本当はあなたはなんの動物ですか?\n")
answer continue
# ニンゲンとウチュウジン,カミサマ以外は多分喋れないのでもう一度訪ねます
else:
print("嘘をつかないで!" + answer + "は喋れません.")
# もう一度値を更新します
= input("本当はあなたはなんの動物ですか?\n")
answer
# これはcontinue も breakもされなかった場合だけ実行されます
print("今度は真面目に答えましたか?\n")
# breakで終わらなかった == ニンゲンだった場合の処理を書きます
else:
print("そうですよね!ニンゲンに決まっています!")
- 演習問題
質問に対する回答をinput関数で受け取り,それに対して返答をする簡単なBotプログラムを作成してください. なお,分岐は最低5つ以上とすること.
1.5 pandas
におけるfor文
PandasのDataFrameの処理においてもfor文
は良く利用されます.
例えば以下のようなデータについて考えてみます.
なお,こちらのデータは以下のコードで作成できます. コピーして使いましょう.
import pandas as pd
= [str(x) + '年' for x in range(1800,1820)]
years = ['101','187','150','117','','168'
values '195','140','151','123','192','137'
,'なし','184','136','192','150','163','141','122']
,
= pd.DataFrame({'year':years
df 'value':values})
,
print(df)
❯ python3 while_and_for.py
year value
0 1800年 101
1 1801年 187
2 1802年 150
3 1803年 117
4 1804年
5 1805年 168
6 1806年 195
7 1807年 140
8 1808年 151
9 1809年 123
10 1810年 192
11 1811年 137
12 1812年 なし
13 1813年 184
14 1814年 136
15 1815年 192
16 1816年 150
17 1817年 163
18 1818年 141
19 1819年 122
このデータはyear
列に'年'
がついており,value
列に空白や'なし'
があります.こういったデータを数値として処理できるようにすることを考えてみましょう.
まずは,print(df.dtypes)
でそれぞれのデータ型を確認して見ましょう.
year object
value object
dtype: object
すべて文字列型であることが分かります.
このまま,year
列をastype()
を利用してint
型に変換してみます.
'year'] = df['year'].astype('int') df[
Traceback (most recent call last):
File "/Users/akagi/Documents/Programs/Python/slds/while_and_for.py", line 74, in <module>
df['year'] = df['year'].astype('int')
^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid literal for int() with base 10: '1800年'
年
の部分が数値に変換出来ないというエラーが出ます.
では,year
列から'年'
を除外してみましょう.
先程のdf['year'] = df['year'].astype('int')
をコメントアウトしてから,以下を実行してみましょう.
# ------------------------------------------------
# year列をInt型にする
# ------------------------------------------------
# year列から"年"を抜かす
# 上から順番に処理するので
# 行番号をdf.indexで取得
for i in df.index:
# 一番右の文字をなくせば良い
'year'] = df.at[i, 'year'][:-1]
df.at[i,
# 確認してみる
print(df['year'])
# 年が消えたので,変換してみる
'year'] = df['year'].astype(int)
df[print(df['year'].dtype) #int64
0 1800
1 1801
2 1802
3 1803
4 1804
5 1805
6 1806
7 1807
8 1808
9 1809
10 1810
11 1811
12 1812
13 1813
14 1814
15 1815
16 1816
17 1817
18 1818
19 1819
Name: year, dtype: object
int64
上手く数値に変換できたようです.
続いて,value
列を数値のみにしてfloat
型にしてみましょう.
こちらも同じようにdf['value'] = df['value'].astype(float)
ではエラー(ValueError: could not convert string to float: 'なし'
)が出ます.
色々方法はありますが,NaN
と'なし'
の列は適当な値(150
)を入れることにしてみましょう.
数値に変換できる場合にTrue
を返すstr.isdecimal()
を利用して,上から一つひとつ数値に変換可能か識別して,できない場合”150”を入れてみます.
# ------------------------------------------------
# value列をfloat型にする
# ------------------------------------------------
for i in df.index:
# 数値型に変換可能か調べるには
# isdecimal関数を使う
# 変換可能でない場合'150'を入れる
if not(str.isdecimal(df.at[i, 'value'])):
'value'] = '150'
df.at[i,
# 確認
print(df['value'])
0 101
1 187
2 150
3 117
4 150
5 168
6 195
7 140
8 151
9 123
10 192
11 137
12 150
13 184
14 136
15 192
16 150
17 163
18 141
19 122
Name: value, dtype: object
無事に数値に変換できました.
このように, pandas
ではイテラブルオブジェクトとしてindex
やcolumns
を利用し,.at[]
などを利用してデータを編集する必要がある場合が多いです.
pandas
でfor文
,if文
などの組み合わせた処理に慣れておきましょう.
もう少し複雑な例についても見てみましょう. 以下のDataFrameにおいて,グループ別の平均点を求めてみましょう.
= {'group': [5, 5, 1, 2, 1, 5, 4, 3, 2, 1
data 5, 3, 4, 1, 2, 4, 5, 1, 1, 1
, 2, 3, 1, 2, 4, 4, 3, 4, 4]
, 'points': [ 45, 23, 58, 96, 38, 41, 33, 30, 82, 42
,42, 89, 66, 94, 25, 36, 52, 40, 93, 80
, 44, 39, 79, 67, 38, 43, 100, 62, 54]}
, = pd.DataFrame(data)
df print(df)
group points
0 5 45
1 5 23
2 1 58
3 2 96
4 1 38
5 5 41
6 4 33
7 3 30
8 2 82
9 1 42
10 5 42
11 3 89
12 4 66
13 1 94
14 2 25
15 4 36
16 5 52
17 1 40
18 1 93
19 1 80
20 2 44
21 3 39
22 1 79
23 2 67
24 4 38
25 4 43
26 3 100
27 4 62
28 4 54
一度, 以下の解答例を見ずに,自分でフローチャートとプログラムを考えてみましょう.
回答例
このような処理のプログラムが書けるかどうかは,どのような手順でデータを操作するかをイメージできるかで決まります. これは,プログラミングの文法知識などの問題ではなく,パズルを解くようないわゆる論理的思考や,プログラミング的思考と呼ばれる能力です.
こういった処理を「どのようにしたら思いつくことができるのか」はなかなか教えるのが難しいのですが,一つの手順として,一旦プログラムのことは忘れて,紙と鉛筆でどのように同じ問題を解くかを考えてみるというのが有効な場合があります.プログラムになれると,直接プログラムで記述することが出来るようになります. しかし,プログラム的思考に慣れていない人はそもそも何をすればいいのかわからないのでプログラムが書けません.
何をすればいいのか皆目検討がつかないという人は, 自分が,紙と鉛筆でどのように解くのかを考えて,それをアルゴリズムに変更し,プログラムに直すという手順を踏んでみましょう.
求めなければならないものをまずはイメージしてみましょう.求めるものは,以下のように,グループごとの値の合計値をグループの数で割ったものです.

この問題を紙と鉛筆で求める場合,どのような方法があるでしょうか.
一例として,上から1行ずつデータを確認し,グループ別にpoints
の値をメモする. すべての行を記録し終えたら,グループ別のメモの値を合計値とデータの数を求めて,平均値を計算するという方法でやってみましょう.
小学生でも順番を守れば可能な方法ですね.

まず,グループ別のメモをする場所を作成します.どのようなデータ型でも構いませんが,ここでは辞書型で作成してみます.
= {1:[]
memo 2:[]
,3:[]
,4:[]
,5:[]} ,
データを1行ずつ確認し,メモ帳に記録していきます.
for i in df.index:
'group']] += [df.at[i,'points']]
memo[df.at[i,
print(memo)
"""
{ 1: [58, 38, 42, 94, 40, 93, 80, 79]
, 2: [96, 82, 25, 44, 67]
, 3: [30, 89, 39, 100]
, 4: [33, 66, 36, 38, 43, 62, 54]
, 5: [45, 23, 41, 42, 52]}
"""
記録したメモを元に,一つひとつ合計値とデータ数を求め,平均値を計算してみます.
#グループ1からグループ5まで順番に作業する
for i in range(1,6):
= 0
total = 0
length #グループ別のリストを足していく
for x in memo[i]:
+= x
total += 1
length print(f'group:{i}, mean:{total/length}')
"""
group:1, mean:65.5
group:2, mean:62.8
group:3, mean:64.5
group:4, mean:47.42857142857143
group:5, mean:40.6
"""
これで無事に,グループ別のデータが求められました. 当然,もっと効率的で,簡単に計算する方法は沢山あります. 似た問題を検索し特定のメソッドを利用したり,AIに聞くことで,何も考えずに計算することも可能です.
例えば, 先程の平均値の計算部分に関しては,組み込み関数のsum()
やlen()
を利用することで,自分で書かなくても計算が可能です.
for i in range(1,6):
print(f'group:{i},mean:{sum(memo[i])/len(memo[i])}')
"""
group:1,mean:65.5
group:2,mean:62.8
group:3,mean:64.5
group:4,mean:47.42857142857143
group:5,mean:40.6
"""
また,以前扱った条件抽出を使えば,一つ一つデータを集める必要もありません. 以下の3行で同じことが可能です.
for i in range(1,6):
= df[df['group'] == i]['points'].mean()
group_mean print(f'group:{i},mean:{group_mean}')
"""
group:1,mean:65.5
group:2,mean:62.8
group:3,mean:64.5
group:4,mean:47.42857142857143
group:5,mean:40.6
"""
更に,pandas
の.groupby()
メソッドを使えば,1行で同じような処理が可能です.
print(df.groupby('group').mean())
"""
points
group
1 65.500000
2 62.800000
3 64.500000
4 47.428571
5 40.600000
"""
しかし,そのような方法ではいつまでたっても自分でプログラムが書けるようにはなりません.また,便利なメソッドが使えても,その背後で何をしているのかを理解していないと,計算した値の意味を理解したり,正しく利用することができません.
まずは自分の頭でどのように処理することで,求めたい値が計算できるのかの手順を考えて,プログラムが書けるようになったあとで,便利なメソッドなどの利用法を覚えることをおすすめします.
1.5.1 発展: 高階関数
Pythonで反復を実現するもう一つの方法として高階関数があります.高階関数とは,関数を引数にとる関数であり,こちらも関数型言語から取り入れられ,pandas
において各行に同じ処理を適用したい場合に良く用いられます.
本資料ではまだ,関数に関して扱っていませんが,発展的内容としてここだけで完結する範囲で簡単に説明します. 興味がある方は, 関数を学んだあとにもう一度こちらを見てみると理解が深まるかもしれません.
以下のDataFrame
を事例に考えてみましょう.
= pd.DataFrame({'kind':['cat','dog','fish']
df 'weight':[30,20,10]})
,print(df)
"""
kind weight
0 cat 30
1 dog 20
2 fish 10
"""
このデータのkind列
を大文字にすることを考えてみましょう.
for文
を利用すると以下のようになりますね.
for i in df.index:
'kind'] = df.at[i,'kind'].upper()
df.at[i,
print(df)
"""
kind weight
0 CAT 30
1 DOG 20
2 FISH 10
"""
この処理を,高階関数map()
を利用すると,以下のようになります.
'kind'] = df['kind'].map(lambda x : x.upper())
df[print(df)
"""
kind weight
0 CAT 30
1 DOG 20
2 FISH 10
"""
map()
は,与えられたDataFrame
の列(DataSeries
)の各行に対して,()
内の処理を適用した結果を返す高階関数です.
lambda x: x.upper()
を無名関数
やラムダ式
といい, lambda 変数: 変数に適用したい処理
の形で書きます. 今は,DataSeries
の各行をx
として,そのx
に.upper()
を適用しています.
条件に合致する場合の処理 if 条件 else 合致しない場合の処理
と書くことで条件を加えることも可能です.
以下の例では, weight
列の値が15
より大きければ2倍するという処理を行っています.
'weight'] = df['weight'].map(lambda x: x*2 if x > 15 else x)
df[print(df)
"""
kind weight
0 CAT 60
1 DOG 40
2 FISH 10
"""
高階関数を使った処理は,for文
より簡潔に書けて,かつ処理速度も高速です. 機会があれば積極的に使ってみましょう.
例えば,先程の以下のDataFrameで,year列
から年を削除し,value列
の空白を150
に置き換える処理は高階関数を利用すると以下のように書けます.
import pandas as pd
= [str(x) + '年' for x in range(1800,1820)]
years = ['101','187','150','117','','168'
values '195','140','151','123','192','137'
,'なし','184','136','192','150','163','141','122']
,
= pd.DataFrame({'year':years
df 'value':values})
,print(df)
'year'] = df['year'].map(lambda x : x[-1])
f['value'] = df['value'].map(lambda x: '150' if str.isdecimal(str(x)) else x) f[
- 演習問題
以下のURLから近世経済データのEXCELファイルをダウンロードし, 米相場の列に欠損値がないように変更し,データの基本的な構造を確認したのち米相場の西暦ごとの平均値を計算し,辞書型に格納してください.
https://www.rieb.kobe-u.ac.jp/project/kinsei-db/database_excel.html
1.6 関数とクラス
これまでは,基本的に記述したコードは,コード内で一度しか利用せず,上から順番に一つずつ行いたい処理を記述してきました. しかし,プログラミングを続けていると,同じ処理を何度も適用する場合が出てきます.その場合に,毎回処理を記述するのは労力がかかります.
ここでは, 同じ処理を再利用可能な形でまとめ,様々な場所で利用する方法を学びます.
Pythonでは,基本的に小さな一つの処理は関数にまとめて利用します.また,複数の関数やデータ型などをクラスという単位にまとめることも可能です.そのようなまとめた塊を組み合わせてプログラムを構築する手法をオブジェクト指向プログラミングといいます. 更に,いくつかの関数やクラスをまとめたものを再利用形なファイルにまとめることでモジュールが作られ,モジュールに階層構造を設定したものをライブラリといいます.
この講義では関数の利用法を学びますがクラスやオブジェクト指向プログラミング,モジュールの作成に関しては少し触れるだけにします. 興味がある方は, より専門的なプログラミングの講義などで学習してください.
1.6.1 関数
皆さんはこれまでにもいくつかの関数を利用してきました. 例えば,リストの要素数を求めるためのlen()
や,標準出力するためのprint()
などは関数です.
関数は基本的に()
の中に引数を与えられて,返り値を返します.
print(len([1,2,3])) #>>> 3
上のlen()
では,()
の中に引数としてリスト[1,2,3]
が与えられ,返り値として3
を返しています.
一方で,関数と似ているが異なる概念として,.mean()
のようにオブジェクトの後ろに.
を利用してつなげるメソッドもあります.
メソッドに関しては, クラスの部分で説明します.
= pd.DataFrame({'x':[1,2,3]})
df print(df.mean()) #>>> 2.0
このように,最初からPythonに実装されている関数を組み込み関数といいます.また,特定のライブラリで定義された関数もあります.
組み込み関数としては,len()
以外にも,合計値を返すsum()
や,文字列に変更するstr()
などを利用してきました. Pythonの組み込み関数はこちらで確認できます. 利用法のわからないものに関しては調べてみましょう.
これまでは,無名関数を除いて基本的にすでに作成された関数を利用してきましたが,関数は自分で作成することも可能です.
関数は, def 関数名(引数を表す変数):
という構文で定義することができます. インデントブロックの中で,引数に加えた処理を記述し, return 返り値
の形で,関数の返り値を定義します.
例えば,要素数を数えるlength()
という関数を実装してみましょう.
まずは,関数にする前に今まで通りにリストの要素数を数えるプログラムを書いてみましょう.
= [1,2,3]
xs = 0
count for x in xs:
+= 1
count print(count) >>> 3
これを関数にしてみます. def length(xs):
のインデントブロックに,行いたい処理を記述し,return count
でcount
を返します.
def length(xs):
= 0
count for x in xs:
+= 1
count return count
print(length([1,2,3])) #>>> 3
print(length(['a','b'])) #>>> 2
関数として定義することで,毎回数を数える処理を記述しなくても,様々なリストの要素数を数えることが可能になりました.
引数には複数の値を指定することも出来ます. 例えば以下の関数get_larger_than(xs,y)
はxs
の中からy
より大きな値のみを返します.
def get_larger_than(xs,y):
= []
result for x in xs:
if x > y:
result.append(x)return result
2,3,4,5,6],3) #>>>[4, 5, 6] get_larger_than([
引数名=デフォルト引数
と書くことで,引数にあらかじめ値を指定することも可能です. 関数を実行する際に,何も指定しなければ,デフォルト引数が利用されます.
def greet(name='guest', greeting="Hello"):
return f'{greeting}, {name}!'
# デフォルト引数を利用する場合
print(greet()) # Hello, guest!
# デフォルトの引数の一部を上書きする場合
print(greet(name='Taro')) # Hello, Taro!
print(greet(greeting='Good Morning')) # Good Morning, guest!
# 全ての引数を指定する場合
print(greet(name='Taro', greeting='Good Morning')) # Good Morning, Taro!
- 演習問題
与えられた数値のリストの合計値を返す関数
与えられた数値のリストの最大値を返す関数
与えられた数値にFizzBuzzの結果を,文字列で返す関数
組み込み関数の
filter()
の仕様を調べて自分で実装してください.
1.6.2 発展:クラスとインスタンス
先ほど関数を自分で定義して使う方法を学習しましたが,Pythonではデータ型も自分で定義することができます.
クラス(Class) とは, データ型,データ型の保有するデータ(属性)とデータ型に付随する機能(メソッド)を定義する機能です. クラスを具体化したものをインスタンスと呼びます. また,インスタンスが生成される際に実行されるメソッドをコンストラクタといいます.
これまでに見た例では,pandasのDataFrame
などはライブラリによって新たに定義されたデータ型です. .DataFrame()
というコンストラクタによって具体的なDataFrame
インスタンスが生成されます.
DataFrame
オブジェクトの属性として,shape
などを取得することができ,.to_csv()
などのDataFrame
に特有の機能(メソッド)を利用することができました.
それでは,具体的にクラスを作成してみましょう.
ここでは,これまでに何度か出てきた,FizzBuzz
専用のクラスを作成してみましょう.
新しいクラスを宣言するには class クラス名:
と記述します. インデントブロック内に,コンストラクタと,メソッドを記述します. クラス名は大文字で始めます.
コンストラクタは, def __init__(self,必要な情報):
の形で宣言します. __init__(self,
の部分は基本的にすべてのクラスで共通です.
FizzBuzz
クラスでは, 数値を引数に取ります. self
は,属性やメソッドが属するインスタンスを表す変数でコンストラクタやメソッドの第1引数は常にself
となります. self.属性
,self.メソッド()
などの形で,そのインスタンスの属性やメソッドを定義します.
各インスタンスに固有の属性をインスタンス属性といい,ここでは生成されたFizzBuzz
インスタンスは.number
という属性を持ちます. self.number = number
と買うことで生成時に引数として与えられた数値number
をインスタンス属性として保存します.
最後に,FizzBuzz
ゲームを実行するための機能,.evaluate()
を実行します.
# FizzBuzzというクラスを宣言します.
class FizzBuzz:
#コンストラクタの定義
#FizzBazzインスタンスを生成する際に必要となるデータを定義する
def __init__(self, number):
#インスタンス属性
self.number = number
#メソッド eveluateの定義
#FizzBuzzインスタンスは,evaluateすることで,`FizzBuzz`の
#ゲームが実行されます
def evaluate(self):
if self.number % 3 == 0 and self.number % 5 == 0:
return "FizzBuzz"
elif self.number % 3 == 0:
return "Fizz"
elif self.number % 5 == 0:
return "Buzz"
else:
return str(self.number)
# 使用例
# 単一の数値を評価
= FizzBuzz(15)
fb print(fb.evaluate()) # FizzBuzz
= FizzBuzz(9)
fb print(fb.evaluate()) # Fizz
= FizzBuzz(10)
fb print(fb.evaluate()) # Buzz
= FizzBuzz(7)
fb print(fb.evaluate()) # 7
クラスの重要な機能に継承があります. 継承は新しいクラス(サブクラス)を作成する際に,すでにあるクラス(スーパークラス)の属性やメソッドを引き継ぐことを意味します. これによって,コードの再利用や,拡張が用意になります.
以下では,先程定義した FizzBuzz
クラスを継承して,3
と5
以外の場合にも特別な挙動をするAdvancedFizzBuzz
クラスを定義してみましょう.
サブクラスを定義するには, class サブクラス名(スーパークラス名)
と記述します.
インデントブロック内では, super().属性
やsuper().メソッド
と書くことで,スーパークラスの属性やメソッドを利用することができます.
# 派生クラス AdvancedFizzBuzz
# ()内にスーパークラスを書きます
class AdvancedFizzBuzz(FizzBuzz):
def __init__(self, number, custom_message=None):
# スーパークラスの__init__を呼び出す
super().__init__(number)
self.custom_message = custom_message
def evaluate(self):
# スーパークラスのevaluateメソッドを拡張
# self.custom_message が定義されている場合のみ実行されます
if self.custom_message and self.number % 7 == 0:
return self.custom_message
else:
# スーパークラスのevaluateメソッドを呼び出す
return super().evaluate()
# サブクラスの使用例
= AdvancedFizzBuzz(21, custom_message="Hozz")
afb print(afb.evaluate()) # Hozz (7で割り切れるためカスタムメッセージ)
= AdvancedFizzBuzz(10)
afb print(afb.evaluate()) # Buzz (スーパークラスのメソッドが呼ばれる)
- 演習
FizzBuzzクラスを拡張して,
3
かつ5
かつ7
の倍数のときにHozz
と表示されるようにしてください.ジャンケンを行うためのクラスを定義してください.