Learn & Record

Python (CSV, CSV객체, 연습문제, 공공데이터활용) 본문

Dev/Python

Python (CSV, CSV객체, 연습문제, 공공데이터활용)

Walker_ 2024. 3. 4. 17:23

1. CSV 

 - 쉼표로 구분된 값 comma

# CSV 파일 입출력

# 1. CSV 파일이란

# 쉼표로 구분된 값 comma-separated values를 의미
# 일반 텍스트 파일처럼 저장된 간단한 스프레드 시트
# 파이썬의 csv 모듈로 CSV 파일을 쉽게 구문 분석 가능

# 각 줄은 스프레드 시트의 행을 의미하고, 쉼표는 행에서 셀을 구분하는 용도로 사용

# * 단점
# 값에 유형이 없음. 모든 것은 다 문자열
# 글꼴 크기나 색상을 지정할 수 없음
# 여러 개의 워크시르틀 가질 수 없음
# 셀의 너비나 높이를 지정할 수 없음
# 셀을 병합할 수 없음
# 그림이나 차트를 포함 할 수 없음

# Comma Separated Values 의 약자로, '쉼표로 분리한 값들'
# db나 스프레드시트 데이터를 저장하기 위해 사용하는 형식
# 내부는 실제 쉼표(,)를 사용하여 모든 항목이 구분되어 있으며 쉼표로 구성된 각 항목들이 테이블을 구성하는 각각의 데이터가 되는 방식
# 메모장에서는 텍스트로, 엑셀에서는 각 셀로 나누어서 보인다
#
# UTF - 8 형식으로 저장된 CSV 의 경우, 엑셀에서는 버전에 따라서 기본읽기로는 한글이 깨지는 경우가 있다
# 한컴 Cell 에서는 에러없이 잘 열린다.

# CSV 파일은 쉼표로 데이터가 구분되어 있어 문자열 처리 메소드를 화용하면
# 별도의 외부 모듈이 없어도 충분히 읽을 수있다:
# 1. 한줄에 한 데이터가 있으므로 readline() 메소드로 한 줄씩 읽는다
# 2. 쉼표로 분리하기 위해 split() 메소드 이용

student_list = []
with open('./input/학생명단.csv', 'rt') as file:
    file.readline()  # 학번 이름 주소 연락처
    while True:
        line = file.readline()
        if not line:  # 더 이상 읽을 내용물이 없으면 반복문을 빠져나온다.
            break
        student = line.split(',')
        student_list.append(student)
print(student_list)

# CSV 파일을 사용할 때 큰 따옴표를 이용해서 텍스트를 묶는다.
# 큰 따옴표를 제거하기 위해 회원명에 추가로 strip() 메소드 사용.
member_list = []
with open('./input/회원명단.csv', 'rt') as file:
    file.readline()  # 학번 이름 주소 연락처
    while True:
        line = file.readline()
        if not line:  # 더 이상 읽을 내용물이 없으면 반복문을 빠져나온다.
            break
        member = line.split(',')
        member[0] = member[0].strip('"')
        # member[0].strip('"') 으로 큰 따옴표 제거
        # member[0] 에는 큰 따옴표가 포함된 회원명이 저장되어 있으므로
        # 위와 같은 형식으로 작업할 경우 아래와 같이 추가적인 따옴표 발생.
        # [['"강나라"', '필라테스', '25일\n'], ['"나유라"', '수영', '25일\n'], ['"이상기"', '헬스', '15일\n']]
        member_list.append(member)
print(member_list)

# member[0]에는 큰 따옴표가 포함된 회원명이 저장되어 있기 때문에 member[0].strip('"')으로
# 큰 따옴표를 제거

# 영어는 문제가 없는데 한글의 경우 표현하는 방식이 2가지
# cp949 : 윈도우의 기본 인코딩. 예전 방식. 한글에만 특수화된 한국에서만 사용. 모든 한글을 표현하지 못함.
# utf-8 : 파이참의 기본 인코딩. 상대적으로 새로운 방식. 한글 및 기타 외국이 문자를 하나의 인코딩으로 모두 표현하기 개발

import csv

# w모드로 열고 생성된 파일 객체를 csv, writer() 메소드에 전달
# 그러면 CSV 파일을 생성할 수 있는 객체가 생성되는데 이 객체를 이용해서
# writerow() 메소드를 호출하면 csv 파일에 데이터를 저장할 수 있슴

with open('./output/차량관리_01.csv', 'w') as file:
    # delimiter = ',':
    csv_maker = csv.writer(file, delimiter=',')
    csv_maker.writerow([1, '08라1234', '2020-10-20,14:00'])
    csv_maker.writerow([2, '25다1234', '2020-10-20,14:10'])
    csv_maker.writerow([3, '28하1234', '2020-10-20,14:20'])
print('차량관리_01.csv 파일이 생성되었습니다.')

# 불필요한 라인이 하나씩 추가되어 있는데 이를 막기 위해서 새로운 라인을
# 추가하지 못하도록 newline 옵션을 사용할 수 있음
# 줄 바꿈이 되지 않도록 newline 옵션에 빈 문자열을 지정하고 이를 코드에 반영

import csv

with open('./output/차량관리_02.csv', 'w', newline='') as file:
    csv_maker = csv.writer(file, delimiter=',')
    csv_maker.writerow([1, '08라1234', '2020-10-20,14:00'])
    csv_maker.writerow([2, '25다1234', '2020-10-20,14:10'])
    csv_maker.writerow([3, '28하1234', '2020-10-20,14:20'])
print('차량관리_02.csv 파일이 생성되었습니다.')

# csv 모듈로 CSV 파일 읽기

# CSV 파일을 읽기 위해서는 r모드로 파일을 열고 생성된 파일 객체를 csv.reader() 메소드에 전달
# csv.reader() 메소드는 CSV파일을 읽고 그 내용을 읽고 저장한 객체 iterator를 반환

with open('./output/차량관리_02.csv', 'r', newline='') as file:
    csv_reader = csv.reader(file, delimiter=',', quotechar='"')
    for line in csv_reader:
        print(line)

 

2. CSV 객체

# 1. CSV 파일이란

# 쉼표로 구분된 값 comma-separated values를 의미
# 일반 텍스트 파일처럼 저장된 간단한 스프레드 시트
# 파이썬의 csv 모듈로 CSV 파일을 쉽게 구문 분석 가능

# 각 줄은 스프레드 시트의 행을 의미하고, 쉼표는 행에서 셀을 구분하는 용도로 사용

# * 단점
# 값에 유형이 없음. 모든 것은 다 문자열
# 글꼴 크기나 색상을 지정할 수 없음
# 여러 개의 워크시르틀 가질 수 없음
# 셀의 너비나 높이를 지정할 수 없음
# 셀을 병합할 수 없음
# 그림이나 차트를 포함 할 수 없음

# * 장점
# 단순함
# 많은 프로그램에서 지원을 하고, 텍스트 편집기에서 볼 수 있으며, 스프레드 시트 데이터를 표현하는 간단한 방법

# * 주의점
# 텍스트로 구성이 되어 있어서, 각 줄에 대해 split(',')을 호출하여 쉼표로 구분된 값에서 문자열 리스트를 얻을 수 있음
# 그러나 CSV 파일의 모든 쉼표가 두 셀의 경계를 나타내지 않고, 값의 일부인 경우도 있음
# 이런 잠재적인 문제 때문에 CSV 파일을 읽거나 쓸 때 CSV 모듈을 사용하는 것이 좋음

# 1. reader 객체
# csv 모듈은 별도의 설치없이 사용가능
import csv

# csv 모듈을 사용해서 csv 파일을 읽으려면 다른 텍스트 파일처럼 open() 함수로 파일을 열어야 함
example_file = open('./input/example.csv') # mode를 생략하면 rt가 기본값

# read() 대신 csv.reader() 함수에 전달. reader() 객체가 반환
example_reader = csv.reader(example_file)

# list로 변환하여 값에 접근
print(example_reader)
example_data = list(example_reader)
print(example_data)
print(example_data[0][1]) # Apples
example_file.close()

# 2. for 반복문을 이용해 reader 객체로부터 데이터 읽기
# CSV 파일이 큰 경우에는, 전체 파일을 한 번에 메모리에 로드하지 말고
# for 반복문에서 reader 객체 사용
# reader 객체는 한 번만 사용가능하기 때문에 다시 사용할려면 새로 reader 객체 생성
example_file = open('./input/example.csv')
example_reader = csv.reader(example_file)
#print(example_reader)
print('example.csv 출력')
for row in example_reader:
    # 각 행의 번호를 얻으려면 reader 객체의 line_one 변수를 사용
    print(f'Row #{str(example_reader.line_num)} {str(row)} {str(row[0])}')
example_file.close()
print('=' * 20)

# 3. writer 객체
# writer 객체를 사용하면 데이터를 csv 파일에 저장할 수 있음

output_file = open('./output/output.csv', 'w', newline='') # newline'' : 빈줄이 들어가는 것 방지
output_writer = csv.writer(output_file) # csv.writer에 전달할 객체 생성
output_writer.writerow(['spam', 'eggs', 'bacon', 'han']) # writer.writerow() : 리스트에 인자를 전달
output_writer.writerow(['hello, world!', 'eggs', 'bacon', 'ham'])
output_writer.writerow([1,2,3,3.141592,4])
output_file.close()

# 4. 키워드 인자 delimiter와 lineterminator
# 쉼표가 아닌 탭문자로 셀을 구분하고 줄 간격을 한 줄씩 띄우려는 상황을 가정
# 구분자 (delimiter)와 줄 끝 문자 (lineterminator)를 변경
# delimiter의 기본값은 쉼표이고, lineterminator의 기본값은 개행문자
# 셀들이 탭으로 구분되어 있기 때문에 탭으로 구분된 값을 의미하는 .tsv 파일 확장자 사용
csv_file = open('./output/example.tsv', 'w', newline='')
csv_writer = csv.writer(csv_file, delimiter='\t', lineterminator='\n\n')
csv_writer.writerow(['apples', 'oranges', 'grapes'])
csv_writer.writerow(['eggs', 'bacon', 'ham'])
csv_writer.writerow(['spam','spam','spam','spam','spam','spam',])
csv_file.close()

# 5. CSV 객체의 DictReader와 DictWriter
# 헤더 행이 있는 csv 파일의 경우 reader나 writer 객체보다 DictReader나 DictWriter 객체를 사용하는 것이 작업하기 편함
example_file = open('./input/example_with_header.csv')
example_dict_reader = csv.DictReader(example_file)
# DictReader 객체는 1) row를 딕셔너리 객체로 설정하고, 2) 첫 번째 행에 있는 정보를 건너 뜀
# 3) 첫 번째 행에 있는 정보를 키값으로 사용
print('example_with_header.csv 출력')
for row in example_dict_reader:
    print(f'{row["Timestamp"]}, {row["Fruit"]}, {row["Quantity"]} ')
print('=' * 20)

# 헤더 정보가 없는 파일의 경우 DictReaer() 생성자의 두 번째 인자로 헤더 이름을 지어서 전달
example_file = open('./input/example_with_header.csv')
example_dict_reader = csv.DictReader(example_file, ['time', 'name', 'amount'])
print('example.csv 출력')
for row in example_dict_reader:
    print(f'{row["time"]}, {row["name"]}, {row["amount"]}')
print('=' * 20)

# DictWriter 객체는 csv 파일을 생성하기 위해 딕셔너리를 사용
output_file = open('./output/output_with_header.csv', 'w', newline='')
output_dict_writer = csv.DictWriter(output_file, ['Name', 'Pet', 'Phone'])
# 파일에 헤더 행을 넣고 싶으면 writeheader()를 호출
output_dict_writer.writeheader()
# writerow() 메서드를 호출하여 각 행을 쓸 수 있는데, 이 때 딕셔너리를 사용.
# 딕셔너리의 키는 헤더이고, 딕셔너리의 값은 파일에 쓰려는 데이터가 들어감
output_dict_writer.writerow({'Name' : 'Alice', 'Pet' : 'cat', 'Phone' : '555-1234'})
output_dict_writer.writerow({'Name' : 'Bob', 'Phone' : '555-9999'}) # 누란된 키는 빈 상태로 나옴
output_dict_writer.writerow({'Phone': '555-5555', 'Name': 'Carol', 'Pet':'dog'}) # 순서는 중요하지 않음
output_file.close()

 

3. 연습문제

# 다음 지시사항에 따라 서울특별시 마포구에 설치된 CCTV의 개수를 구하는 프로그램을 구현하세요.
#
# 지시사항
# 1. cctv.csv 파일을 읽습니다.
# 2. 모든 라인에 존재하는 카메라 개수를 합한 결과를 출력합니다.
# 5번째 데이터가 카메라 대수 입니다.
#
# 실행 예 :
# 서울특별시 마포구에 설치된 CCTV는 총 2167대입니다.


# DictReader 이용할 것

import csv

with open('./input/cctv.csv', 'r') as file:
    datas = csv.DictReader(file) # dict로 읽음
    total_cctv = 0 # 카메라 대수를 저장하기 위한 변수
    for data in datas:
        total_cctv += int(data['카메라대수']) # value가 문자열이여서 형변환을 해야함

print(f'서울특별시 마포구에 설치된 CCTV는 총 {format(total_cctv, ",")}대입니다.')


# 개발절차
# 1. pop_seoul.csv 파일을 읽어서 딕셔너리로 출력
# 2. 반복문을 돌려서 한 행씩 출력
# 3. 한 행씩 출력하는 코드에서 외국인 비율을 구해서 출력
# 4. 조건애 해당하는 데잍

import csv

# 1. pop_seoul.csv 파일을 읽어서 딕셔너리로 출력
example_file = open('./input/pop_seoul.csv')
example_dict_reader = csv.DictReader(example_file) # dict 형식으로 파일을 읽기
print(list(example_dict_reader))
example_file.close()

# 2. 반복문을 돌려서 한 행씩 출력
example_file = open('./input/pop_seoul.csv')
example_dict_reader = csv.DictReader(example_file) # dict 형식으로 파일을 읽기
# print(list(example_dict_reeader))
for row in example_dict_reader:
    print(row)
example_file.close()

# 3. 한 행씩 출력하는 코드에서 외국인 비율을 구해서 출력
# 외국인 비율 : 외국인 / 총 인구수(한국인 + 외국인) * 100

import csv
new_datas: list = []
with open('./input/pop_seoul.csv', 'r') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:

        # 외국인 비율 : 외국인 / 총 인구수(한국인 + 외국인) * 100
        # 구별 총 인구수 출력 : 데이터가 문자열이라서 콤마(,)를 제거해야 정수로 형변환 가능
        total_population = int(row['Korean'].replace(',','')) + int(row['Foreigner'].replace(',',''))
        print(f'{row["Gu"]}의 총 인구수 : {total_population}')

        # 외국인 비율
        rate_foreigner: float = int(row['Foreigner'].replace(',','')) / total_population * 100
        print(f'{row["Gu"]}의 외국인 비율: {rate_foreigner}')

        if rate_foreigner >= 3.0:
            new_data: dict = dict()
            new_data['Gu'] = row['Gu']
            new_data['Korean'] = row['Korean']
            new_data['Foreigner'] = row['Foreigner']
            new_data['Rate'] = round(rate_foreigner, 1)
            new_datas.append(new_data)

print(new_datas)

with open('./output/pop_seoul.csv', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=['Gu', 'Korean', 'Foreigner', 'Rate'])
    writer.writeheader()
    for data in new_datas:
        writer.writerow(data)

 

4. 공공데이터 활용

https://rt.molit.go.kr/

 

국토교통부 실거래가공개시스템

새로운 소식 국토교통부 실거래가 공개시스템의 새로운 소식을 확인하실 수 있습니다. 보도자료 공지사항 FAQ

rt.molit.go.kr

 - 링크 접속 > 자료 제공 클릭 

 - 전국 1달 분량, 지역은 지정하면 1년 분량 제공

 - 대구 1년 분량 다운로드

 - 계약일자는 기본 값, 파일 구분은 CSV, 나머지는 기본 값 사용

 - 다운 받은 후 메모장에서 파일을 열고 15행까지는 삭제

import csv
from typing import List

# example_file = open('./input/아파트(매매)_실거래가_20240304154554.csv')
# example_reader = csv.DictReader(example_file)
# print('출력')
# for row in example_reader:
#     print(f'{row}')
# print('=' * 20)

# 조건 : 래미안 단지만 검색
# 출력조건: 지역, 단지명, 크기, 층수, 거래금액
# 저장 파일명 : apt_2403_조건1.csv
# with로 파일 처리

new_datas: List[dict] = list()
with open('./input/아파트(매매)_실거래가_20240304154554.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        row: dict
        if row['단지명'].find('래미안') != -1:
            print(f'{row["단지명"]}')
            new_data: dict = dict()
            new_data['시군구'] = row['시군구']
            new_data['단지명'] = row['단지명']
            new_data['전용면적(㎡)'] = row['전용면적(㎡)']
            new_data['층'] = row['층']
            new_data['거래금액(만원)'] = row['거래금액(만원)']
            new_datas.append(new_data)
print(new_datas)
header: list = list(new_datas[0].keys())

file_name = 'apt_2403_조건1.csv'
with open(f'./output/{file_name}', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=header)
    writer.writeheader()
    for data in new_datas:
        writer.writerow(data)
print(f'{file_name}파일이 생성 되었습니다.')


# 조건 : 롯데캐슬 거래건만 검색
# 출력조건 : 구(구만 나오도록 할 것), 단지명, 전용면적, 층, 거래금액
# 저장 파일명 : apt_2403_조건3.csv
# with로 파일 처리
# 예) 달서구, 롯데캐슬레이크, 84, 91, 3, "32.109"

new_datas: List[dict] = list()
with open('./input/아파트(매매)_실거래가_20240304154554.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        row: dict
        if row['단지명'].find('롯데캐슬') >= 0:
            print(f'{row["시군구"].split(" ")[1]} / {row["단지명"]}')
            new_data: dict = dict()
            new_data['구'] = row['시군구'].split(" ")[1]
            new_data['시군구'] = row['시군구']
            new_data['단지명'] = row['단지명']
            new_data['전용면적(㎡)'] = row['전용면적(㎡)']
            new_data['층'] = row['층']
            new_data['거래금액(만원)'] = row['거래금액(만원)']
            new_datas.append(new_data)
print(new_datas)
header: list = list(new_datas[0].keys())

file_name = 'apt_2403_조건2.csv'
with open(f'./output/{file_name}', 'w', newline='') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=header)
    writer.writeheader()
    for data in new_datas:
        writer.writerow(data)
print(f'{file_name}파일이 생성 되었습니다.')