プログラミング入門II
2025.06.18
ファイル処理

Back to index page



  1. 本日の作業内容

  2. 確認テストについて
  3. 授業の最初にユーザ定義関数・モジュールに関する確認テストを行いますので,しっかり準備してきてください.

  4. 前回の宿題について

    今回はエラーになるものが出てきてしまいました.エラーの原因はエラーメッセージにかいてありますので,ちゃんと修正してから出しましょう.

    実行時エラー: 24_18 24_55

    出力時番号間違い: 24_12

    以下は例によって問題あるプログラムの例です.参考にしてください.

    data = make_data()
    

    モジュールの呼び出しが指定と違いますので,エラーになります.

    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
    

  5. 前回の復習

    モジュールについていろいろと学習しました.基本的には教科書にある通りですが,実際の開発作業では必須のものではあるものの,一人で作業しているときにはあまり有難味を感じないのも事実です.これまでに作りためたプログラムを活用していくようなことを少し取り入れていければと思います.

  6. ファイル処理

    今回はファイル処理です.実際に実験などで得られたCSVファイルを読み込んで処理したり,計算結果をCSVファイルとして書き出したりすることは頻繁に行われることです.今回はCSV形式のテキストファイルについての読み込みと書き出しについて学習します.

    CSV とは Comma Separeted Value の略で,カンマ(コンマ)で区切られたデータ群のことです.ただし,区切り記号はカンマ以外にもスペースやタブなどが使われることもよくありますし,データを2重引用符 " で括る方式と括らない方式など,いくつかバリエーションもあります.今回はデータテキストはそのまま,区切り記号はカンマに限定して作業します.
    CSVデータの扱い

    Python は現在機械学習などを始めとしてデータ処理に多く利用されています.この分野では処理したデータをCSVファイルとして保存したり,CSVファイルを読み込んで処理したり,という作業が多くなります.そのため,Python には pandas や NumPy というデータ解析に便利なライブラリやツールが準備されていますが,今回は標準で用意されている CSV モジュールを使った方法を学習しましょう.

    1. モジュールの読み込み

      次のように import 文を用意します.

      import csv
      

    2. with 文によるファイル読み込み

      以下のような内容のデータが 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()
      

      使用するエディタによって色分けなどが違いはしますが,上のソースですと,色が付いている部分が予約語や関数です.それ以外の部分は自分で適当に名前をつけてよい変数やリストです.

    3. 読み込みデータの数値化

      先程のコードを実行すると,出力結果が以下のようになったはずです.

      [['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 についての操作を行っています.

    4. データ書き込み

      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() を使います.
  7. 演習

    今回の演習問題です.

  8. 宿題

    いつものようにMoodleを利用します.授業当日の18:00から閲覧可能で,締切りは23日月曜日の15:00です.

  9. 次回の予習範囲

    次回もファイル処理です.教引き続き科書のp.354-373の範囲を学習しますので,予習をしてきてください.


目次ページに戻る