第14回:まとめ¶
前回は Web スクレイピングで、インターネット上のページから欲しい情報を取り出した。これで、プログラミング言語 I の復習から始まった本科目の内容は一通り出そろった。最終回の今回は、この半期に学んだことをかるく振り返り、最後に総まとめの課題に取り組む。
この半期で学んだことを、あとから見返せるように順に振り返る。各テーマの「代表的な書き方」と「つまずきやすい点」を思い出しながら読んでほしい。
リスト・タプルと基本型(第 2 回)¶
複数の値をまとめて扱う入れ物として、リスト([]、あとから中身を変更できる)と タプル(()、一度作ったら変更できない)を学んだ。
scores = [80, 55, 91] # リスト:追加・変更できる
scores.append(62) # → [80, 55, 91, 62]
point = (135, 240) # タプル:座標などの「変えない組」に向く
- リストは
append(追加)・scores[0](番号で取り出し)・len(scores)(個数)が基本 - 「変更してよいか」でリストとタプルを使い分ける
int(整数)・float(小数)・str(文字列)・bool(真偽)といった基本型と、int()/str()による型変換もここで確認した
ユーザ定義関数(第 3・4 回)¶
同じ処理を何度も書かずに済むよう、処理をひとまとめにして名前を付けるのが 関数。def で定義し、引数で値を受け取り、return で戻り値を返す。
returnした値は、呼び出した側で受け取って使えるreturnを書かない関数はNoneを返す(画面表示のprintと、値を返すreturnは別物)- 引数には初期値を付けられる(
def f(x, n=1):)
高階関数とラムダ式(第 5 回)¶
関数そのものを引数として渡す関数を高階関数という。渡す相手として、その場で使い捨ての短い関数を書けるのが ラムダ式。
def apply_list(func, items):
return [func(x) for x in items]
print(apply_list(lambda x: x * 2, [1, 2, 3])) # → [2, 4, 6]
- 書き方は
lambda 引数: 戻り値。defを使わず 1 行で書ける sorted(データ, key=lambda x: x[1])のように、並べ替えの基準を関数で渡す使い方が代表的
クラス・メソッド・継承(第 6〜8 回)¶
データと処理をひとまとめにする設計図が クラス。__init__ で初期値を用意し(第 6 回)、クラスの中に書く関数=メソッドで動きを持たせる(第 7 回)。第 1 引数の self は「自分自身」を指す。
class Character:
def __init__(self, name, hp):
self.name = name # インスタンスごとのデータ
self.hp = hp
def show(self): # メソッド
print(self.name, 'HP:', self.hp)
hero = Character('勇者', 100) # インスタンスを作る
hero.show() # 勇者 HP: 100
継承(第 8 回)は、既存のクラスを土台に新しいクラスを作るしくみ。共通部分を親クラスにまとめ、違う部分だけ子クラスで書く。
インスタンス.メソッド()の形で呼ぶ。メソッドの中では自分のデータをself.〇〇で参照する- 子クラスで同じ名前のメソッドを書くと、親の動きを上書きできる
例外処理(第 9 回)¶
エラーが起きてもプログラムを止めずに対処するのが try / except。想定できる失敗を種類ごとに受け止める。
try:
n = int(input('数を入力: '))
print(10 / n)
except ValueError:
print('数字を入れてください') # 数に変換できないとき
except ZeroDivisionError:
print('0 では割れません') # 0 で割ったとき
tryの中でエラーが出ると、対応するexceptに飛ぶfinallyに書いた処理は、エラーの有無にかかわらず必ず実行される(後片付けに使う)
ファイル操作(第 10 回)¶
プログラムを閉じると変数の中身は消える。残したいデータはファイルに保存し、次回読み込む。with open(...) as f: で開くと、閉じ忘れがない。
# 書き込み(セーブ):改行 \n は自分で付ける
with open('save.txt', 'w', encoding='UTF-8') as f:
f.write('勇者' + '\n')
f.write('100' + '\n')
# 読み込み(ロード):1 行ずつ処理する
with open('save.txt', 'r', encoding='UTF-8') as f:
for line in f:
print(line.strip()) # strip() で行末の改行を落とす
- モードは
'w'(書き込み・上書き)と'r'(読み込み) - 読み込んだ 1 行には末尾に改行が付くので、
strip()で落とすことが多い - 今回の課題の
seiseki_data.txtも、この「1 行ずつ読む」処理が土台になる
データの集計とグラフ描画(第 11 回)¶
集めた数値は、表にするだけでなくグラフにすると傾向が見える。外部ライブラリ matplotlib を使う。
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Yu Gothic' # 日本語が化けない対策(描画より前に書く)
plt.bar(['月', '火', '水'], [3, 7, 2]) # 棒グラフ
plt.show()
plt.bar(ラベル, 値)が棒グラフ、plt.plot(値のリスト)が折れ線グラフplt.show()で表示、plt.savefig('graph.png')でファイルに保存plt.xlabel(...)やplt.legend()(凡例)で見た目を整えられる
画像処理(第 12 回)¶
写真も、プログラムなら大量にまとめて加工できる。外部ライブラリ OpenCV(import cv2)を使う。
import cv2
img = cv2.imread('block.jpg') # 読み込み
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 白黒に変換
cv2.imwrite('gray.jpg', gray) # 保存
cv2.imreadで読み込み、cv2.imwriteで保存cv2.resize(img, (幅, 高さ))でサイズ変更、cv2.cvtColor(..., cv2.COLOR_BGR2GRAY)で白黒化、cv2.Canny(img, 50, 100)で輪郭抽出- 画面に出すときは
cv2.imshowのあとcv2.waitKey(0)を忘れない
Web スクレイピング(第 13 回)¶
インターネット上のページから欲しい情報を取り出す。ページの取得は requests、中身の解析は BeautifulSoup。
import requests
from bs4 import BeautifulSoup
result = requests.get('https://www.shoeisha.co.jp/book/ranking')
soup = BeautifulSoup(result.text, 'html.parser')
for sec in soup.select('section'): # 該当タグをすべて回す
if sec.select_one('h2'): # 先頭の1つを取り出す
print(sec.select_one('h2').text) # .text で中の文字列だけ
select('タグ名')は該当要素をすべて(リスト)、select_one('タグ名')は先頭の 1 つを返す.textを付けるとタグを外して「中の文字列だけ」になる- 欲しい情報が「どのタグの中にあるか」を見つけるのがコツ
総まとめの課題について¶
最終回の課題は、ファイルからデータを読み込み、条件で選び出して集計するという、この半期の土台になった処理の総復習である。seiseki_data.txt を題材にする。上のリンクからダウンロードし、プログラムと同じフォルダに置くこと。
seiseki_data.txt は、氏名と 4 科目(国語・算数・理科・社会)の科目名と点数が、順に改行で並んだデータになっている。
つまり 1 人分は 9 行(氏名 → 「国語」→ 点数 → 「算数」→ 点数 → 「理科」→ 点数 → 「社会」→ 点数)で、これがくり返される。
人数分のリストは作らない
基本・応用ともに、全員のデータをリストに溜めてはいけない。一人分を読んで処理したら次へ進む、という書き方にすること(1 人分だけ変数に持てば十分)。
授業内演習¶
seiseki_data.txt を読み込み、実行例のとおり、先頭 1 人分の氏名と 4 科目の点数を表示するプログラムを作成しなさい(課題に進む前のウォームアップ)。
実行例:
確認の観点:
with open('seiseki_data.txt', 'r', encoding='UTF-8') as f:で開く- 1 人分は 9 行。
f.readline()で 1 行ずつ読むか、forで読みながら数える - 点数は文字列で読まれる。数として使うときは
int()で変換する(今回は表示だけなのでそのままでもよい)
基本課題¶
seiseki_data.txt を読み込んで、算数が 85 点以上、かつ国語が 85 点以上の人だけを抽出し、実行例のとおり、該当する氏名と合計人数を、画面とファイルの両方に出力するプログラムを作成しなさい。
なお、人数分のリストは作成しないプログラムとすること(一人分のデータを処理したら次に進めばよいので、全員のデータをリストにとっておく必要はない)。
実行例:
ヒント:
- 1 人分は 9 行。「氏名 → 国語 → 点数 → 算数 → 点数 → 理科 → 点数 → 社会 → 点数」の順で並んでいる
- 点数の比較は数として行う。読み込んだ点数は
int()で数に変換してから比べる - 条件(算数 ≥ 85 かつ 国語 ≥ 85)を満たしたら、氏名を表示し、数える用の変数を 1 増やす
- 最後に合計人数を出す
- 「画面とファイルの両方」に出す。ファイルへは、画面に
printするのと同じ内容をf.write()で書けばよい(printした文字列をそのまま書き込む)
応用課題¶
seiseki_data.txt において、このデータには理系タイプと文系タイプが存在するだろうか? それを調べるプログラムを作成しなさい。
「考え方や工夫した点」で、得られた結論と、なぜそのように考えたのかを説明すること。基本課題と同様、人数分のリストは作成しないプログラムとすること。
考え方のヒント(ここからは自分なりに):
- まず「理系タイプ」「文系タイプ」を自分で定義してみよう。たとえば「理科・算数が高く、国語・社会が低い人=理系タイプ」「その逆=文系タイプ」など、基準は自分で決めてよい
- 決めた基準に当てはまる人が何人いるかを数えれば、「存在するか」を判断できる
- あるいは、科目どうしの関係を見る手もある(例:算数が高い人は理科も高い傾向があるか? 国語が高い人は社会も高い傾向があるか?)。傾向があれば「タイプが存在する」根拠になる
- どんな基準・観点を選んだとしても、なぜそう考えたのかを「工夫した点」に必ず書くこと。結論そのものより、考え方の筋道が評価される