260107 [ Day 7 ] - Data (1)

TaeHyun·2026년 1월 7일

TIL

목록 보기
130/184

시작하며

오늘은 본격적인 데이터 분석에 앞서 데이터 파일의 입출력에 대한 내용과 공공데이터포털 API를 사용해서 데이터를 수집하는 방법을 실습해보았다.

작업 경로

  • Python에서 외부 파일을 입출력할 때 입출력 함수의 괄호 안에 ‘경로/파일명.확장자’ 형태의 문자열을 인수로 지정
  • Windows : ‘C:\Users\Account\Documents\project\code’
  • MacOS : ‘/Users/Acount/Documents/project/code’

윈도우에서의 이스케이프

  • 이스케이프()는 기능이 있는 메타문자의 기능을 해제하는 메타문자
  • 따옴표는 여러 글자를 문자열로 생성하는 기능이 있음
    • 역슬래시 2번 사용 \
    • 일반 슬래시 사용 /
    • raw string : 문자열 안 이스케이프의 기능을 일괄 해제(r'C:\Users’)

절대 경로

  • 시작부터 끝까지 전체 경로를 표시한 것
  • 현재 사용 중인 컴퓨터에서 유일한 경로
  • 너무 길면 코딩할 때 다소 불편할 수 있음
  • 항상 같은 결과를 얻을 수 있음

상대 경로

  • 현재 작업 중인 위치를 기준으로 한 상대적인 경로
  • 현재 작업 경로가 달라지면 코드를 실행할 때 에러 발생할 수 있음

OS 라이브러리

  • 폴더와 파일을 처리하는 함수를 포함한 표준 라이브러리
구분상세 내용구분상세 내용
os.getcwd()현재 작업 경로를 문자열로 반환os.path.exists(path)지정한 경로의 존재 여부를 True, False로 반환
os.chdir(path)작업 경로를 변경os.path.isfile(path)지정한 경로가 파일이면 True, 아니면 False
os.mkdir(path)새 폴더를 생성os.path.isdir(path)지정한 경로가 폴더이면 True, 아니면 False
os.rmdir(path)빈 폴더를 삭제(빈 폴더가 아니면 에러 발생)os.path.abspath(path)지정한 경로에 대한 절대 경로를 반환
os.listdir(path)지정한 경로에 있는 폴더명과 파일명을 문자열 리스트로 반환shutil.copy(src, dst)파일을 복사
os.remove(path)지정한 파일을 삭제(폴더 지정 시 에러 발생)shutil.copytree(src, dst)폴더에 있는 모든 파일과 하위 폴더를 통째로 복사
os.rename(src, dst)폴더명 또는 파일명 변경shutil.move(src, dst)파일을 옮김
os.path.join(*paths)여러 경로명을 하나로 결합shutil.rmtree(path)폴더에 있는 모든 파일과 하위 폴더를 통째로 삭제(사용X)

작업 경로 확인 및 변경

  • 현재 작업 경로
os.getcwd()
  • 상위 폴더로 이동
os.chdir('..')
  • 현재 경로를 변경시커 데이터 폴더에 접근
    • MacOS 에서는 sorted() 사용
os.chdir('../../data')
os.getcwd()
sorted(os.listdir())

데이터 입출력

특정 문자열을 포함하는 파일 선택

files = []
for file in sorted(os.listdir()):
    if 'xlsx' in file:
        files.append(file)
  • 리스트 컴프리헨션으로 사용
[file for file in sorted(os.listdir()) if 'xlsx' in file]

특정 문자열을 포함하는 파일 삭제

files = [file for file in sorted(os.listdir()) if 'test' in file]
for file in files:
    os.remove(file)

Excel 데이터

read_excel 함수 주요 옵션

  • sheet_name : 시트의 인덱스 또는 이름을 지정
    • None 지정 시 모든 시트를 읽고 딕셔너리로 반환
    • 키가 시트명이고, 값은 데이터프레임
  • usecols : 선택할 열의 정수 인덱스 도는 열이름을 리스트
  • skiprows : 열이름 위로 생략할 행 개수를 지정
  • thousands : 천 단위 구분자를 지정(문자열 → 숫자)
    • 생략 시 콤마가 있는 숫자를 문자열로 인식

여러 시트 데이터를 하나의 데이터프레임으로 가져오기

dfs = pd.read_excel('APT_Price_Seoul_Split_2020~2024.xlsx',
                    sheet_name=None,
                    usecols=range(1,16),
                    skiprows=3,
                    thousands=','
                    )

딕셔너리 → 데이터프레임으로 변환(pd.concat)

  • ignore_index=True : 기존 데이터프레임의 인덱스를 무시하고 초기화
df1 = pd.concat(dfs, ignore_index=True)

새로운 엑셀 파일로 저장(to_excel)

  • index=False : 인덱스 생략
df1.to_excel('test.xlsx', index=False)

데이터프레임 미리보기

  • info()
df1.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 231904 entries, 0 to 231903
# Data columns (total 15 columns):
#  #   Column  Non-Null Count   Dtype         
# ---  ------  --------------   -----         
#  0   거래금액    231904 non-null  int64         
#  1   지역코드    231904 non-null  int64         
#  2   아파트     231904 non-null  object        
#  3   시도명     231904 non-null  object        
#  4   시군구     231904 non-null  object        
#  5   법정동     231904 non-null  object        
#  6   지번      231904 non-null  object        
#  7   건축년도    231904 non-null  int64         
#  8   년       231904 non-null  int64         
#  9   월       231904 non-null  int64         
#  10  일       231904 non-null  int64         
#  11  등기일자    79535 non-null   datetime64[ns]
#  12  매도자     56193 non-null   object        
#  13  전용면적    231904 non-null  float64       
#  14  층       231904 non-null  int64         
# dtypes: datetime64[ns](1), float64(1), int64(7), object(6)
# memory usage: 26.5+ MB
  • head() : 처음 5행 확인
  • tail() : 마지막 5행 확인
  • sample() : 무작위 5행 확인
    • random_state 매개변수에 시드를 지정할 수 있음

CSV 데이터

  • Comma Separated Values
  • 표 형태의 데이터를 텍스트 파일로 저장한 파일 포맷

인코딩 방식 확인

  • 컴퓨터는 사람이 사용하는 자연어 문자를 이해할 수 없음
  • 인코딩 : 자연어 문자를 컴퓨터가 이해할 수 있는 코드로 변환하는 것
  • 디코딩 : 컴퓨터가 이해하는 코드를 자연어로 변환한 것
# 원하는 파일만 선택하여 리스트에 저장
files = [file for file in sorted(os.listdir()) if 'Split' in file and 'csv' in file]
  • with open() 사용
    • mode 매개변수에 읽기 모드를 설정
      • 'r'은 문자열(str)로 읽기, 'rb'는 바이츠(bytes)로 읽기
      • bytes는 아스키로 인코딩된 문자열
    • bytes 타입은 따옴표 왼쪽에 b가 있음
with open(file = files[0], mode='rb') as file:
    text = file.read()
  • 전체 텍스트를 조회하면 시간이 오래걸리니 처음 일부 글자만 확인후 'EUC-KR'로 나오면 'CP949'로 지정
    • confidence : 신뢰도
chardet.detect(text[:100])
# {'encoding': 'EUC-KR', 'confidence': 0.99, 'language': 'Korean'}
chardet.detect(text)
# {'encoding': 'CP949', 'confidence': 0.99, 'language': 'Korean'}

CSV 파일 읽기

pd.read_csv(files[0], encoding='CP949')

read_csv 함수 주요 옵션

  • encoding : 인코딩 방식 지정(CP949, UTF-8 등)
  • parse_dates : 날짜시간형으로 읽을 열이름을 리스트로 지정
    • 기본 문자열 방식(yyyy-mm-dd HH:MM:SS)일 때 정상 실행
    • 날짜만 있으면 이각을 00:00:00 으로 자동 적용
  • thousands : 천 단위 구분자를 지정(문자열 → 숫자)

CSV 파일 입출력

df2 =pd.DataFrame()

for file in files:
    df = pd.read_csv(file,
                    encoding='CP949',
                    parse_dates=['등기일자'],
                    thousands=','
                    )
    df2 = pd.concat([df2, df], ignore_index=True)
df2.to_csv('test.csv', index=False, sep=',', encoding='UTF-8')
  • DtypeWarning : 파일을 읽고 메모리에 저장할 때 저장해둔 데이터 타입이 섞여있음(결측)
    • low_memory=False 속성(메모리 제한 해제) 적용으로 해결 가능
df3 = pd.read_csv('test.csv')
# DtypeWarning: Columns (11,12) have mixed types

df3 = pd.read_csv('test.csv', low_memory=False)

Pickle 데이터

  • 처리 속도 빠름
  • 자료형 유지
  • 여러 객체를 하나의 pickle 파일로 저장 가능
  • pandas 버전이 다르면 pickle 파일 오류

pickle 파일 입출력

df2.to_pickle('test.pkl')
df4 = pd.read_pickle('test.pkl')

여러 개의 객체를 하나의 피클 파일로 저장

pd.to_pickle([df1, df2, df3], filepath_or_buffer='test_all.pkl')
  • 여러 객체를 하나의 pickle 파일로 저장했다면 읽을 때 해당 개수만큼 변수에 할당해야 편리함
    • pickle 파일을 읽으면 리스트를 반환
df1_1, df2_1, df3_1 = pd.read_pickle('test_all.pkl')
  • 저장한 pickle 파일의 개수를 모를때
objs = pd.read_pickle('test_all.pkl')
len(objs) # 3

딕셔너리를 활용한 pickle 관리

  • 저장하려는 파이썬 객체의 변수명을 키로, 변수를 값으로 하여 딕셔너리를 만들고 딕셔너리를 pikcle 파일로 저장
objs = {
    'df1': df1,
    'df2': df2,
    'df3': df3
}

pd.to_pickle(objs, filepath_or_buffer='test_all.pkl')

dct = pd.read_pickle('test_all.pkl')
  • globals() : 전역 변수 목록을 보여줌
  • update() : 딕셔너리에서 원소를 추가
    • pickle 파일 딕셔너리의 키로 전역변수 생성
globals().update(dct)

공공데이터포털 API

사용 라이브러리

import requests, xmltodict, json
from pprint import pprint
from tqdm.notebook import tqdm
import pandas as pd
  • requests : HTTP 요청(API 호출 등)
  • xmltodict : XML → Python 딕셔너리 변환
  • json : json → Python 딕셔너리 변환
  • tqdm : 주피터 노트북용 진행도 표시줄
  • pprint : 딕셔너리를 보기 좋게 출력

기본 구조

  • 요청 url 및 api키 추가
URL = 'https://apis.data.go.kr/'
API_KEY = ''
  • 요청 쿼리 추가(조회 데이터마다 값이 다름)
query = {
    'LAWD_CD': '11680',
    'DEAL_YMD': '202512',
    'serviceKey': API_KEY,
    'numOfRows': 9999,
    'pageNo': 1
}
  • HTTP Request(요청)
res = requests.get(url=URL, params=query)
  • 응답 상태 코드 확인
res.status_code # 200
  • 응답 헤더 중 콘텐츠 타입 확인
res.headers
# {'Content-Type': 'application/xml;charset=utf-8', 'Date': 'Wed, 07 Jan 2026 11:59:45 GMT', 'Server': 'Apache', 
# 'Set-Cookie': 'JSESSIONID=92CE385C24CC20B8BECB226BAB288EA1; Path=/oasis; Secure; HttpOnly', 
# 'Vary': 'Origin, Access-Control-Request-Method, Access-Control-Request-Headers', 'X-Frame-Options': 'SAMEORIGIN', 'Transfer-Encoding': 'chunked'}
  • 데이터 타입과 인코딩 확인
res.headers['content-type']
# 'application/xml;charset=utf-8'
  • xml 형태의 문자열을 딕셔너리로 변환
data = xmltodict.parse(xml_input=res.text)
  • 데이터프레임으로 변환
df = pd.DataFrame(data['response']['body']['items']['item'])

함수로 생성

def apt_price(areaCd, ymonth, nrow=9999, page=1):
    # url 주소 및 api 키
    URL = 'https://apis.data.go.kr/'
    API_KEY = ''
    # 요청 변수
    query = {
        'LAWD_CD': areaCd,
        'DEAL_YMD': ymonth,
        'serviceKey': API_KEY,
        'numOfRows': nrow,
        'pageNo': page
    }
    # requests 가져오기
    res = requests.get(url=URL, params=query)
    # xml 데이터 추출
    data = xmltodict.parse(xml_input=res.text)
    # 데이터프레임으로 변환
    df = pd.DataFrame(data['response']['body']['items']['item'])
    
    return df

일정 기간의 데이터 수집

ymonths = [str(i) for i in range(202501, 202513)]
print(ymonths)
# ['202501', '202502', '202503', '202504', '202505', '202506', '202507', '202508', '202509', '202510', '202511', '202512']

dfs = pd.DataFrame()

for ymonth in tqdm(ymonths):
    # ymonth를 순회하면서 df 생성
    df = apt_price(areaCd='11680', ymonth=ymonth)
    # 생성 된 df를 dfs에 병합
    dfs = pd.concat([dfs, df], ignore_index=True)

마치며

이전 프로젝트를 진행하면서 조금씩 다뤘던 os와 공공데이터포털 API에 대해서 더 배워보고 싶었는데, 오늘 배울 수 있어서 정말 좋았고 재밌었다. 혼자 알아보면서 공부할 때는 어렵게만 느껴졌는데, 막상 배우니까 쉽게 느껴져서 다음에도 많이 활용할 수 있을 것 같다.

profile
Hello I'm TaeHyunAn, Currently Studying Data Analysis

0개의 댓글