プログラミング入門II
2025.06.18
ファイル処理
モジュールの呼び出しが指定と違いますので,エラーになります.
b = (sumy*sumx^2-sumx*sumxy)/bunnbo |
Python では演算子の ^ はビット排他的論理和です.
from make_list import * from operator import mul print('Student number: s******') print() import operator import make_list ........... sum_x2 = sum(map(operator.mul, x_list, x_list)) sum_xy = sum(map(operator.mul, x_list, y_list)) |
元々解答用紙にこのようにインポートするようにと import 文が入っていました.それを無視していますね.
from make_list import * from operator import mul print('Student number: s******') print() print('Data generated') def make_list(): b = randint(2, 4) x_data = [i + gauss(0, 0.1) for i in range(1, 11)] y_data = [i * b + gauss(0, 0.5) for i in range(1, 11)] lst = [x_data, y_data] return lst for i in range(0, 2): print(*make_list()[i]) |
make_list はモジュールとしてインポートするよう指定しています.
for i in range(10): print(x[i], y[i]) print() print(f'Fritted line: y={a}+{b}x') |
出力する値の桁数指定がありません.なので,結果は以下のようになり,見にくいことこの上ないです.
Data generated: 1.099693079661548 3.244647938714833 1.9611382230826497 8.456359213217752 3.0267892785784 11.862206143547567 3.9964079301074844 15.732435571935088 5.038019924714554 20.669641301090213 6.005199140283092 25.03511046317064 7.0584773708722475 28.11692208440032 8.157408828095807 32.54111594018556 9.048865813276453 36.06112819749263 9.9490776631628 39.805899804454356 Fritted line: y=-0.2060602453813823+4.040146672508243x |
print(f'Fitted line: y = {b:6.3f} + {a:6.3f}x') |
係数 a と b がいつのまにか入れ替わっていますので,当然出力結果も傾きと切片が逆になっています.直線の傾きと切片を理解していますか?
Fitted line: y = 2.002 + -0.175x |
for x, y in zip(x_data, y_data): print(f"{x:.3f} {y:.3f}") |
出力の桁数指定は小数部だけでは不十分です.結果として下のようにずれていますよ.
0.890 1.850 2.306 4.215 3.057 6.423 4.046 8.607 4.999 10.363 6.119 12.541 7.204 13.984 8.134 15.326 8.847 17.969 10.124 19.816 |
for pair in data: print(f'{pair[0]:.3f} {pair[1]:.3f}') |
出力する値の指定がおかしいので,下のように2個ずつしか出ません.
Data generated: 1.178 2.117 4.144 7.116 Fitted line: y = 0.133 + 1.685 x |
さて,以下に載せているものは,今回の条件指定を完全に無視しているもので,よくありません.自分できちんと作っていくことにもう少し取り組んでください.
def make_list(): b = randint(2, 4) x_data = [i + gauss(0, 0.1) for i in range(1, 11)] y_data = [i * b + gauss(0, 0.5) for i in range(1, 11)] return [x_data, y_data] def main(): data = make_list() x_data = data[0] y_data = data[1] n = len(x_data) sum_x = sum(x_data) sum_y = sum(y_data) sum_xx = sum(map(lambda x: x * x, x_data)) sum_xy = sum(map(mul, x_data, y_data)) b = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x ** 2) a = (sum_y - b * sum_x) / n print("Data generated:") for x, y in zip(x_data, y_data): print(f"{x:6.3f} {y:6.3f}") print(f"\nFitted line: y = {a:.3f} + {b:.3f} x") if __name__ == "__main__": main() |
これ自身がモジュールになっていますねえ.どうしてこういう書き方をするのでしょうか.
def l_s(): x_list, y_list = make_list() n = len(x_list) sum_x = sum(x_list) sum_y = sum(y_list) sum_xy = sum(map(mul, x_list, y_list)) sum_x2 = sum(x ** 2 for x in x_list) b = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x ** 2) a = (sum_y - b * sum_x) / n print(f'Data generated:') for x,y in zip(x_list, y_list): print(f'{x:6.3f} {y:6.3f}') print() print(f'Fitted line: y = {a:.3f} + {b:.3f}x') l_s() |
メインで実行するスクリプトそのものを関数にする必要は無いです.
import random import operator def make_data(): x_list = [round(random.uniform(0.5, 10.5), 3) for _ in range(10)] y_list = [round(2 * x + random.uniform(-1, 1), 3) for x in x_list] return [x_list, y_list] data = make_data() x_list = data[0] y_list = data[1] print('Data generated:') for i in range(10): print(f'{x_list[i]:6.3f} {y_list[i]:7.3f}') print() n = len(x_list) sum_x = sum(x_list) sum_y = sum(y_list) sum_xx = sum(x * x for x in x_list) sum_xy = sum(map(operator.mul, x_list, y_list)) denominator = n * sum_xx - sum_x ** 2 b = (n * sum_xy - sum_x * sum_y) / denominator a = (sum_y - b * sum_x) / n print(f'Fitted line: y = {a:.3f} + {b:.3f} x') |
そもそも自分で勝手にデータを作っていて,出力結果も全然違うので,どうしてこういうことになるのでしょうか.
Data generated: 9.766 20.192 2.926 5.845 10.297 20.529 5.369 11.292 2.168 4.079 3.580 6.408 8.152 16.821 4.978 9.997 3.172 6.476 1.712 4.139 Fitted line: y = -0.053 + 2.040 x |
モジュールについていろいろと学習しました.基本的には教科書にある通りですが,実際の開発作業では必須のものではあるものの,一人で作業しているときにはあまり有難味を感じないのも事実です.これまでに作りためたプログラムを活用していくようなことを少し取り入れていければと思います.
今回はファイル処理です.実際に実験などで得られたCSVファイルを読み込んで処理したり,計算結果をCSVファイルとして書き出したりすることは頻繁に行われることです.今回はCSV形式のテキストファイルについての読み込みと書き出しについて学習します.
CSV とは Comma Separeted Value の略で,カンマ(コンマ)で区切られたデータ群のことです.ただし,区切り記号はカンマ以外にもスペースやタブなどが使われることもよくありますし,データを2重引用符 " で括る方式と括らない方式など,いくつかバリエーションもあります.今回はデータテキストはそのまま,区切り記号はカンマに限定して作業します.CSVデータの扱い
Python は現在機械学習などを始めとしてデータ処理に多く利用されています.この分野では処理したデータをCSVファイルとして保存したり,CSVファイルを読み込んで処理したり,という作業が多くなります.そのため,Python には pandas や NumPy というデータ解析に便利なライブラリやツールが準備されていますが,今回は標準で用意されている CSV モジュールを使った方法を学習しましょう.
次のように import 文を用意します.
import csv |
以下のような内容のデータが numbers.csv というファイル名でカレントディレクトリにあるとするとき,そのデータを読み込んで numbers というリストに保存する処理を考えます.
1,2,3,4 5,6,7,8 |
教科書 p.360 にある with 文を使用して以下のようにしてみます.
import csv with open('numbers.csv', 'r') as f: data = csv.reader(f) numbers = [n for n in data] print(numbers) f.close() |
使用するエディタによって色分けなどが違いはしますが,上のソースですと,色が付いている部分が予約語や関数です.それ以外の部分は自分で適当に名前をつけてよい変数やリストです.
先程のコードを実行すると,出力結果が以下のようになったはずです.
[['1', '2', '3', '4'], ['5', '6', '7', '8']] |
ここで,reader という関数がデータを行単位で読み込んでくれています.
データはちゃんと読み込めましたが,数値はシングルクオート ' ' で囲まれていますので,文字列です.計算などで使用するためには数値でないと困ります.
EXCEL で読み込んだ場合はテキストでも自動で数値化してくれるので計算に使用できます.教科書 p.36 で int 関数が紹介されていますが,それを使用することで整数に変換することができます.ということで,数値化してみましょう.
import csv with open('numbers.csv', 'r') as f: data = csv.reader(f) num_txt = [n for n in data] numbers = [[int(num_txt[i][j]) for j in range(0,4)] for i in range(0,2)] print(numbers) f.close() |
上のソースの中では,7行目のリスト numbers の作成における内包表記表現の添字 i と j に注意してください.内包表現では内側のリストの添字を j とし,外側のリストの添字を i とすることが慣例ですので,まず j についての値を操作し,次に外側で i についての操作を行っています.
numbers というリストは2次元のリストになっていますが,内側のリストの和を要素として持つ num_sum というリストを作り,それを num_sum.csv というファイルに書き込んで保存することを行ってみましょう.
import csv with open('numbers.csv', 'r') as f: data = csv.reader(f) num_txt = [n for n in data] numbers = [[int(num_txt[i][j]) for j in range(0,4)] for i in range(0,2)] num_sum = [sum(numbers[i]) for i in range(len(numbers))] with open('num_sum.csv', 'w') as ff: save_data = csv.writer(ff) save_data.writerow(num_sum) f.close() ff.close() |
再び with 文が登場しますが,今回は書き込みです.writer() 関数を使用しますが,1行書き込む writerow で書き込んでいます.
書き込むデータが複数行に渡るときは最後の行は writerows() を使います.
今回の演習問題です.
いつものようにMoodleを利用します.授業当日の18:00から閲覧可能で,締切りは23日月曜日の15:00です.
次回もファイル処理です.教引き続き科書のp.354-373の範囲を学習しますので,予習をしてきてください.