Pythonプログラミング(ステップ7・リスト・基本的なデータ処理)
このステップの目標
- リスト(配列)要素の並べ替え操作を伴う処理のアルゴリズムとコードの動作を理解できる
- 「総和のパターン」に基づいた統計計算のプログラムが書ける
1.リストを使ったデータの整列(ソーティング)
リストを使ったデータ処理のうち、実用的にも重要な例として、リスト中の数値の並び順を、大きいものから(あるいは小さいものから)順に並べる方法を考えよう。 統計処理をする際に、データの順位付けが必要となる場合が多いが、そのような際、あらかじめデータを整列しておく必要がある。
以下は、リストを小さい方から大きい方へ順に(昇順に)並べ変え、出力するPythonコードの例である:
例題9a(ex9a.py)
リストの整列(ソーティング)
# coding: utf-8 X=[2.1, 5.3, 7.3, 4.7, 9.2, 1.2, 3.1, 5.3, 8.3, 4.8] for i in range(0,len(X)-1,1): for j in range(len(X)-1,i,-1): if X[j-1]>X[j]: w=X[j] X[j]=X[j-1] X[j-1]=w for i in range(0,len(X),1): print(X[i])
例題 9aのアルゴリズム
Input: 10件のデータが入った配列
Output: 昇順に整列された配列
1: 配列
2: 位置
3: 位置
4: もし 隣同士が
5: 反復ここまで
6: 反復ここまで
7: 配列の値を先頭から順に出力する
8: 終了する
プログラムの見所
ソーティングについての 補足説明はこちら
トランプのカードをバブルソートで並べ替える様子の動画(亀場を使用)
例題9の5〜10行目にかけてが、リスト(配列)データの並べ変え(整列、ソーティング)を行っている部分である。
x[j]
で
ソーティングの仕組みについては、別ページにもう少し詳しく説明した。 また、参考書 例題2.25 (p.30)も併せて参照のこと。
Pythonの標準機能
Pythonの機能を使うと、リストX
の整列はX.sort()
(降順の場合はX.sort(reverse=True)
)で済んでしまう。
ここではアルゴリズムの学習を兼ねて、敢えて、リストの要素毎に処理するコードを考えることにする。
sort()メソッドを使って書き直した例題9a
# coding: utf-8 X=[2.1, 5.3, 7.3, 4.7, 9.2, 1.2, 3.1, 5.3, 8.3, 4.8] X.sort() for i in range(0,len(X),1): print(X[i])
このように、リストを操作するための「便利機能」がPythonには沢山備わっている。 こちらのページにいくつかをまとめたので参照のこと。
練習:データの整列(降順)
例題9a(ex9a.py)のコードに変更を加え、以下の80件のデータ(東北楽天ゴールデンイーグルスに登録されている選手の身長)の並びについて、値が大きいものから小さくなる順(降順)に整列されるよう改修してみなさい。
X=[ 174,175,180,185,178,184,179,185,170,178,184,183,180,191,180,180,174,188,181,182, 187,178,175,177,185,178,175,175,174,169,174,173,185,178,180,180,183,191,179,178, 174,185,185,180,193,181,178,181,177,176,174,180,186,180,180,180,185,174,194,175, 183,176,192,185,186,193,171,172,175,186,180,172,175,175,177,181,178,180,176,174 ]
ヒント
昇順から降順に切り替えるには、プログラム中の1箇所を変更するのみである。 データの件数が変わると、プログラムのどの箇所が影響されるかについても、よく考えること。
練習:平均とメジアン
上の課題(「データの整列」)で作成したプログラムにさらにコードを追加し、以下の例に倣って、平均値と中央値(median)を出力するよう改修してみなさい。
... 172 172 169 平均身長= ... 中央値= ...
ヒント
平均の計算は、このページの例題9b(ex9b.py)も参照のこと。
あらかじめ昇順に整列された//
)。
練習:最頻値
上記の野球選手の身長データについて、最頻値(モード)を求めるコードを作成し、動作を確認してみなさい。
ヒント
いろいろなアプローチが考えられるが、データが整列されていれば、データを順に調べ、同じ値が連続する回数(の最大値)を調べれば良さそうだ。
練習:バブルソートに要する手間
バブルソートを行なう際に、その「手間」あるいは「工数」を見積もる指標として
- A. 隣同士のデータの大小を比較する回数
- B. 隣同士のデータを入れ替える回数
を考えるのが良さそうである。もちろん、回数が少ないほど効率よく短時間で仕事が完了するはずである。 上の データの整列 で作成したプログラムを元にして、1,2の回数をプログラム内でカウントし、
比較の回数= xxx回 入れ替えの回数 xxx回
のように出力するプログラムを作成しなさい。
2.リスト(配列)を使った統計計算
以前のステップで行った統計計算プログラムを、リストを使ったバージョンに変更してみよう。 先にまず、全データをリストに格納して、その後で、平均の計算を行っている。 リストが登場する以外には、特に目新しい箇所は無いはずだ。
例題9b(ex9b.py)
# coding: utf-8 X=[ ] n=int(input("データの件数を入れてください:")) for i in range(n): data=float(input("{0}番目のデータ:".format(i+1))) X.append(data) s_x=0 for i in range(len(X)): s_x=s_x + X[i] print("データの総和は",s_x,"です") mu = s_x/n print("平均値は",mu,"です")
例題9bの見所
- 必要なリストの大きさはわからないので、とりあえず、空のリストから始める(
X=[ ]
)。 - リストの要素は、0番目から始まる。それに対応して、ループ変数の
i
を0からlen(X)-1
まで動かしている。 ここでlen(X)
はリストの要素数。 - 何番目のデータかを表示する際、「0番目」という表現は多少違和感があるので、
i+1
の値をプリントするようにしてある。 range(n)
はrange(0,n,1)
の省略形。
練習:標準偏差の計算
例題9bを発展させて,標本平均だけでなく(標本)標準偏差も計算して,両方を表示するプログラムを書いてみなさい。
ヒント
標本標準偏差を計算は以下の手順で行えばよい:
- データの件数を
とし、データが に格納されているとする - データの総和を求める
- データの2乗の総和を求める
- 標本平均
を求める - 標本分散
を求める - 標本標準偏差
標準偏差の計算方法については、総和のパターンの練習も参照のこと。
総和と二乗の総和は、ひとつのforループで「まとめて」計算できるはず。
解説 ファイルに格納されたデータの利用
データの件数が多いと、数値をキーボードから入力するのは大変な手間となる。 そこであらかじめテキストエディタ等で、
80 174 175 180 185 178 184 ... |
のようなデータファイルをあらかじめ作成しておいて(ファイル名をsample.txtとすると)、 それをPythonのコードで処理させれば効率的である。 そんな場合は、ファイルのデータをリスト(配列)に読み込ませればよい。 例題9bを修正し、キーボードからではなく、ファイルからデータを読み込むようにした例を以下に示す:
ファイルからデータを読み込むには、まず 変数=open("ファイル名")で「ファイルを開いた」後、
内容を読み込む動作( 変数.readlines() ) を行う。
ファイルへのアクセスが終わったら、最後に閉じる( 変数.close() )。
データファイル: sample.txt
# coding: utf-8 import math file=open("sample.txt") # ファイル"sample.txt"を開く lines=file.readlines() # ファイルからリストに行を読み込む file.close() # ファイルを閉じる n=int(lines[0]) # ファイルの1行目の数値をnにセットする s_x=0.0 for i in range(1,n+1,1): x=float(lines[i]) # ファイルのi行目を実数に変換してxにセットする s_x=s_x+x print("データの総和は",s_x,"です") mu=s_x/n print("平均値は",mu,"です")
補足
上のコードで、ファイルの読み込みの箇所は
with open("sample.txt") as file: lines=file.readlines()
と書くのが今流である。
解説: 分散(標準偏差)の推定
調査や実験から
ところが、上の練習問題や以前の練習問題では、サンプルの分散(標本分散)を
平均
ここの箇所、統計学の教科書には
いま、対象としているデータの「真の」平均(母平均)が
一方、データの本来の分散は、「真の平均値
全てのデータが等しい重みでサンプルされるとすれば、この関係はほとんど自明であろう。
で評価できる。そして、
次に、上記の量
つまり、「分母を
以上を定性的に再解釈すると
- サンプルデータは、真の平均と比較して、より大きな値、あるいはより小さな値に偏っている可能性がある。その程度は、サンプル数
が少ないほど顕著である。 - そうしたばらつきは本来分散に反映されなければならないが、例えば、値が「全体的に大きめ」であるようなサンプルに対しては、そのサンプル平均
も 当然(真の平均より)大きめとなるので、 からの平均自乗距離(すなわち標本分散)は、真の平均 からの平均自乗距離よりも小さく見積もられてしまう。 - そうした偏り(bias)の効果まで勘定に入れて「補正」すると、分母のところが
ではなく となる。
というわけである。
練習:標本分散と不偏分散のシミュレーションによる比較
以下は、『平均0、分散1のガウス(正規)分布に従う乱数を5回サンプルし、その標本分散と不偏分散を求める計算』を1000回試行し、 それぞれの分散の平均(期待値)を計算するシミュレーション用コードの例である。
# coding: utf-8 import random n_trial=1000 # 試行回数 n_sample=5 # 標本の数 var_sample=0 var_unbiased=0 for k in range(n_trial): sample=[ ] s_x = 0 for i in range(n_sample): x = random.gauss(0,1) # 平均0 分散1のガウス分布 sample.append(x) s_x = s_x + x s_x = s_x/n_sample # 標本平均 s_xx=0 for i in range(n_sample): s_xx = s_xx + (sample[i]-s_x)**2 var_sample = var_sample + s_xx/n_sample # 標本分散(の総和) var_unbiased = var_unbiased + s_xx/(n_sample-1) # 不偏分散(の総和) var_sample = var_sample/n_trial # 標本分散の平均 var_unbiased = var_unbiased/n_trial # 不偏分散の平均 print("標本分散の平均=",var_sample) print("不偏分散の平均=",var_unbiased)
- コードを実行し、母分散(この場合は1)に近い値が得られるを確認しなさい。また、標本の数(
n_sample
の値)を小さく(大きく)して、 標本分散と不偏分散との違いにどのように反映されるかシミュレーションしてみなさい。 -
母集団の分布型を(ガウス分布ではなく)
の一様分布に変えて、同様の比較を行いなさい。
ヒント
random.gauss(0,1)
の箇所を、random.uniform(-1,1)
に変更すると、
区間