見出し画像

Python学習~アウトプット編①~


はじめに

アウトプット編では、Pythonを使って様々なプログラムを作成し、論理的思考力を養いながらコーディングに慣れていくことを目指します。本記事では、特に「ファイル操作」に焦点を当てて学習します。CSV、json ファイルについて理解の浅い方は以下の記事を読んで頂くと本学習がより理解しやすいかと思います。

成果物について

本記事では、「geojsonファイルから特定のデータを抽出し、CSVファイルとして特定のフォルダに出力する」というプログラムを作成します。今回は国土交通省のサイトから高速バス停留所のデータをダウンロードし、学習に使用します。

事前準備①

まず、国土数値情報から「東京都(シェープ、geojson形式)」のデータをダウンロードし、「P36-23_13.geojson」というファイルを任意のフォルダに保存します。今回はデスクトップ上に「geojson」というフォルダを作成し、その中にファイルを格納しました。

事前準備②

次に、作成したCSVファイルを格納するためのフォルダを用意します。今回はデスクトップ上に「CSV」というフォルダを作成します。

コーディング

ここからは実際にコーディングを行います。今回はgeojsonファイルから「バス停名、事業者名、バス系統1〜17」のデータを抽出し、CSVファイルとして出力します。

まず、コーディングを始める前に今回の要件を分解していきましょう。以下が要件を分解した内容です。

  1. 指定のフォルダからファイルを取得する。

  2. 取得したファイルからデータを抽出する。

  3. 抽出したデータをCSVファイルに出力する。

コーディング①

先ほど分解した要件の「1」を実装していきます。
以下が実際のコードです。

import glob
from pathlib import Path

geojson_dir = Path(r"C:\Users\user\Desktop\geojson")     ----(1)
files_path = glob.glob(os.path.join(geojson_dir, '*'))   ----(2)

コードの解説をしていきます。まず、(1)でgeojsonファイルを格納しているフォルダパスを指定します。(2)で、(1)で指定したフォルダに格納されているファイルパスを取得します。このとき、取得されるファイルパスはリスト型で「files_path」に保持されます。

コーディング②

続いて、分解した要件の「2」を実装していきます。この実装において、さらに要件を細分化します。①:CSVに出力する際のファイル名を用意します。②:geojsonファイルに含まれているデータを抽出します。③:②で抽出した内容から「バス停名、事業者名、バス系統1〜17」の項目に紐づくデータのみを抽出します。以下が①の実際のコードです。

for file_path in files_path:
        file_name_with_ext = os.path.basename(file_path)             ----(1)
        file_name = f"{os.path.splitext(file_name_with_ext)[0]}.csv" ----(2)

コードの解説をしていきます。コーディング①では、geojsonフォルダに格納されたファイルパスが「files_path」というリスト形式で保持されています。forループを使用して、ファイルパスを1つずつ取り出します。ここで、取り出したファイルパスに対して(1)でファイル名を抽出します。その後、(2)で(1)で抽出したファイル名を使用して、CSVファイルを出力する際のファイル名を作成します。

続いて②~③のコードです。
今回は関数としてコーディングしていきます。

def read_file(file_path):
    output_data_list = []  ----(1)
    with open(file_path, 'r',encoding='utf-8') as geojson_file:  ----(2)
        geojson_data = json.load(geojson_file)  ----(3)
        for feature in geojson_data['features']:  ----(4)
            bus_system = []
            bus_stop_name = feature['properties']['P36_001'] ----(5)
            company_name = feature['properties']['P36_002']  ----(6)
            bus_system = [feature['properties'][f"P36_003_{id:02}"] for id in range(1, 18)] ----(7)
            tmp_read_data_list = [bus_stop_name, company_name] + bus_system ----(8)
            output_data_list.append(tmp_read_data_list)
    return output_data_list 

コードの解説をします。関数「read_file」は、geojsonファイルから抽出したデータをCSVファイルに整形して出力するためのもので、二重配列「output_data_list」を戻り値として返します。まず、(1)で「output_data_list」用の空のリストを準備します。次に、(2)で引数として受け取ったgeojsonファイルを開きます。その後、(3)でgeojsonファイルに記載されているデータをjson形式で変数「geojson_data」に格納します。(4)では、「geojson_data」に格納されたデータから、keyが['features']のデータを抽出します。(5)と(6)では、今回geojsonから抽出する「バス停名」と「事業者名」のデータを取り出します。これについては、紐づいているkeyが国土数値情報のサイトに記載されています。(7)では、「バス系統1~17」のデータを抽出しています。ここで利用するkeyは「P36_003_01~P36_003_17」までの連番です。そのため、「for i in range(数値)」を使用して、keyを作成しています。CSVに出力するデータは二重配列である必要があるため、(8)で一次元配列のリストに(3)から(7)までのデータを格納し、最終的に「output_data_list」にまとめていきます。

コーディング③

最後に要件の「3」を実装していきます。
以下が実際のコードです。こちらも関数としてコーディングしていきます。

def output_csv(csv_file_path, output_data_list):
    with open(csv_file_path, 'w', newline='',encoding='utf-8') as csv_file: ----(1)
        csv_writer = csv.writer(csv_file)
        csv_writer.writerow(['バス停名', '事業者名', 'バス系統1', 'バス系統2', 'バス系統3', 'バス系統4',
                             'バス系統5', 'バス系統6', 'バス系統7', 'バス系統8', 'バス系統9', 'バス系統10',
                             'バス系統11', 'バス系統12', 'バス系統13', 'バス系統14', 'バス系統15', 'バス系統16', 'バス系統17'
                             ]) ----(2)
        for data in output_data_list:
            csv_writer.writerow(data) ----(3)

コードの解説をしていきます。

(1)で出力用にCSVファイルを開きます。
(2)でCSVファイルに出力するヘッダを準備します。
(3)でコーディング②の関数で作成したoutput_data_listをCSVファイルに書き込みしていきます。output_data_listは二重配列の為forループを使用して、一次元配列の要素を順に取り出しています。

コード全量

先ほどのコーディング①~コーディング③をまとめコードを以下に記載しています。

import os
import glob
import csv
import json
from pathlib import Path

def read_file(file_path):
    output_data_list = []
    with open(file_path, 'r',encoding='utf-8') as geojson_file:
        geojson_data = json.load(geojson_file)
        for feature in geojson_data['features']:
            bus_system = []
            bus_stop_name = feature['properties']['P36_001']
            company_name = feature['properties']['P36_002']
            bus_system = [feature['properties'][f"P36_003_{id:02}"] for id in range(1, 18)]
            tmp_read_data_list = [bus_stop_name, company_name] + bus_system
            output_data_list.append(tmp_read_data_list)
    return output_data_list 


def output_csv(csv_file_path, output_data_list):
    with open(csv_file_path, 'w', newline='',encoding='utf-8') as csv_file:
        csv_writer = csv.writer(csv_file)
        csv_writer.writerow(['バス停名', '事業者名', 'バス系統1', 'バス系統2', 'バス系統3', 'バス系統4',
                             'バス系統5', 'バス系統6', 'バス系統7', 'バス系統8', 'バス系統9', 'バス系統10',
                             'バス系統11', 'バス系統12', 'バス系統13', 'バス系統14', 'バス系統15', 'バス系統16', 'バス系統17'
                             ])
        for data in output_data_list:
            csv_writer.writerow(data)


def main():
    geojson_dir = Path(r"C:\Users\user\Desktop\geojson")
    csv_dir = Path(r"C:\Users\user\Desktop\csv")

    files_path = glob.glob(os.path.join(geojson_dir, '*'))
    for file_path in files_path:
        file_name_with_ext = os.path.basename(file_path)
        file_name = f"{os.path.splitext(file_name_with_ext)[0]}.csv"
        output_data_list = read_file(file_path)
        csv_file_path = os.path.join(csv_dir, file_name)
        output_csv(csv_file_path, output_data_list)

if __name__ == "__main__":
    main()
        

まとめ

本記事では、Pythonでのファイル操作について学習しました。プログラミングにおいて、コードには様々な書き方があり、絶対的な正解はありません。(可読性や保守性は重要ですが。)ここでは、まず「要件からコーディングする」というプロセスに慣れていただければと思います。


この記事が気に入ったらサポートをしてみませんか?