노트2/메모

[Python] 데이터 전처리 : 문자열 포매팅, glob, concat, to_datetime(), 컬럼명변경, replace(), 정규표현식

Paige09 2022. 1. 26. 17:08

 

 

공공데이터포털에 업로드 할 주기성 데이터를 전처리하면서 사용했던

엑셀 함수와 파이썬 작업을 메모해두는 글. :-]

 

 


 

 

내가 전달받은 최초의 데이터는 아래 이미지와 같은 형태다.

키워드 별로 폴더에 담겨있고, 용량이 큰 파일들은  _1, _2, _3 과 같이 넘버링 꼬리를 달고 여러개로 쪼개져있다.

 

 

 

 

이 파일들을 데이터포털 업로드 양식에 맞게 수정해야했다.

작업 과정을 정리하면 다음과 같다.

 

1. 원하는 키워드, 원하는 연도의 자료를 불러온다. 여러 파일로 쪼개져있다면 합쳐서 불러온다.

2. 일자 컬럼 포맷을 변경한다. (ex. 19930101 ▶ 1993-01-01)

3. 컬럼명에 /같은 기호는 들어 갈 수 없으니, 다른 기호로 대체한다. (ex. 사건/사고 ▶ 사건_사고)

4. 맥락상 쓰인 물음표를 제외하고, 특수기호 오류로 입력된 물음표를 제거한다.

5. 신상정보를 포함할 수 없으므로 메일주소와 전화번호를 제거한다.

6. csv 파일로 저장한다.

 

 


 

 

1. 원하는 키워드, 원하는 연도의 자료를 불러온다. 여러 파일로 쪼개져있다면 합쳐서 불러온다.

 

처음에는 파일명을 복붙해서 불러오곤 했는데, 파일명 형식이 전부 같다는 점을 이용해서 더 편하게 가져오는 방법이 없을까 고민했다.

코드를 계속 다듬고 다듬어서 (블로그를 일찍 시작했다면 이 과정을 담을 수 있었을텐데..)

카테고리(category)와 연도(year) 변수에 값을 설정하는 것만으로 모든 코드가 알아서 굴러가게끔 만들었다.

 

문자열 포매팅으로 파일 위치와 파일명을 설정해주고 os.path.join 으로 파일위치+파일명을 합쳐 경로를 만들었다.

그리고 glob 함수로 해당 경로에 있는 파일 중에서, 내가 입력한 값과 일치하는 파일명을 리스트업 했다.

* glob에서 와일드카드 *은 0개 이상의 문자열을 의미. ?은 글자수 1개에 해당

 

예를 들어 아래와 같이 입력하면

category = '전염병'
year = 1993 

파일위치+파일명은 C:\Users\user\Desktop\전염병\전염병 (1993.01.01-1993.12.31)_*.csv 가 되고

이 형식에 해당하는 파일명을 glob 함수가 모아서 file_list 변수에 리스트업한다.

 

import pandas as pd
import glob
import os

category = '전염병' # 원하는 값을 입력
year = 1993 # 원하는 값을 입력
input_file = f'C:\\Users\\user\\Desktop\\{category}' # csv파일들이 있는 디렉토리 위치
file_name = f"{category} ({year}.01.01-{year}.12.31)_*.csv"
file_list = glob.glob(os.path.join(input_file, file_name)) # glob함수로 파일들을 모은다
output_file = input_file + "\\" + f"{category} ({year}.01.01-{year}.12.31).csv" 


alldata = []

if len(file_list) == 1:
    print(len(file_list),'개 파일 전처리 시작!')

    file = input_file + "\\" + f"{category} ({year}.01.01-{year}.12.31)_1.csv"
    df = pd.read_csv(file, encoding='cp949')
    df = df.fillna('')

else:
    print(len(file_list),'개 파일 통합 시작!')
    for file in file_list:
        df = pd.read_csv(file, encoding='cp949')
        df = df.fillna('')

        alldata.append(df) # 빈 리스트에 읽어 들인 내용을 추가한다

        df = pd.concat(alldata, axis=0, ignore_index=True) # concat함수를 이용해서 리스트의 내용을 병합
        # axis=0은 수직으로 병합함. axis=1은 수평. ignore_index=True는 인덱스 값이 기존 순서를 무시하고 순서대로 정렬되도록 한다.

print('데이터(row) :', len(df.index)) # dataCombine.shape[0]

 

전염병 (1993.01.01-1993.12.31)_1 과 같이 파일이 한개라면 그대로 가져오고,

전염병 (1993.01.01-1993.12.31)_2 와 같이 두개 이상의 파일이 존재한다면 concat 함수로 합쳐준다.

 

* len(df.index) 출력

데이터포털에 입력할 때 데이터의 수(행의 수)를 입력해야해서 편의상 출력했다.

 

 

 

 

2. 일자 컬럼 포맷을 변경한다. (ex. 19930101 ▶ 1993-01-01)

 

df['일자']=pd.to_datetime(df['일자'], format='%Y%m%d')

pandas.to_datetime(변환할 데이터, format="")

to_datetime() 함수로 문자열을 datetime 형식으로 변환한다.

나는 정해진 포맷이 있었기에 지정해주었는데, 별도 양식이 없다면 생략 가능하다. (변환 속도는 포맷을 지정하는 쪽이 훨씬 빠르다고 함.)

 

 

 

 

3. 컬럼명에 /같은 기호는 들어 갈 수 없으니, 다른 기호로 대체한다. (ex. 사건/사고 ▶ 사건_사고)

 

df.columns=[ 바꿀 컬럼 명 ]

df.columns = ['col1', 'col2', 'col3' ... ]

이름을 변경해야 할 컬럼이 많아서 아예 전체 컬럼명을 새로 지정했다.

 

특정 컬럼만 선택해서 바꾸려면 df.rename(columns={'Before':'After'}, inplace = True

 

 

 

 

4. 맥락상 쓰인 물음표를 제외하고, 특수기호 오류로 입력된 물음표를 제거한다.

 

제일 난감했던 부분...

아래 이미지처럼 파일 내에 맥락상 쓰인 물음표와, 오류로 입력된 물음표가 혼재되어 있다.

그렇다면 맥락상 필요한 물음표와 삭제해야하는 물음표를 어떻게 구분할 것인가..?

 

업무를 인수인계 받을 때 전임자는 엑셀로 '~?'를 검색해서 모든 셀을 들여다보고 수정했다고 했다.

하지만... 파일 하나에 몇십만건의 데이터가 있는데... 어떻게요....? ㅇ_ㅇ..?

 

우선 원본 파일을 열어보고 어떤 경우에 물음표가 입력되는지 케이스를 알아봤다.

그리고 파이썬에서 오류값으로 확인되는 물음표를 제거하고, 나머지 케이스만 엑셀로 확인했다.

 

1) 맥락상 물음표는 '제목'과 '본문' 컬럼에만 들어갈 수 있으니, 나머지 컬럼에 있는 물음표를 일괄 제거한다.

df.replace() 함수 이용.

q_columns = [ 제목, 본문을 제외한 컬럼명 ]

for column in q_columns:
    df[column] = df[column].str.replace("?","",regex=True)

 

그 중에서 내가 판단했을 때, 삭제해도 되는 물음표는 세종류. 

1) ....?... 형태로 문장 사이에 들어가있는 의미없는 물음표

2) 20?30과 같이 숫자 사이에 들어간 물음표

3) 한자 대신 오류값으로 들어간 물음표

df = df.replace(r'(\.\?\.)', '', regex=True) # 온점 사이에 있는 물음표 제거
df = df.replace(r'([0-9])\?([0-9])',r'\1\2', regex=True) # 번호 사이에 있는 물음표 제거
df = df.str.replace(pat=r'([一-龥])\s*\?+', repl=r'\1', regex=True) # 한자뒤에 오는 물음표 제거

 

이렇게 거른 뒤, 남은 물음표들은 엑셀로 확인했다.

 

 

 

 

5. 신상정보를 포함할 수 없으므로 메일주소와 전화번호를 제거한다.

 

df['기고자']=df['기고자'].str.replace(pat=r'[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-.]+', repl= r'', regex=True) # 이메일 제거
df['본문']=df['본문'].str.replace(pat=r'[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-.]+', repl= r'', regex=True) # 이메일 제거
df['본문']=df['본문'].str.replace(pat=r'[0-9]+-[0-9]+', repl= r'', regex=True) # 번호 제거

 

 

 

 

6. csv 파일로 저장한다.

 

df.to_csv(output_file, encoding='euc-kr', index = False

#참고로 아웃풋 파일 형식은
output_file = input_file + "\\" + f"{category} ({year}.01.01-{year}.12.31).csv"

 

 

이렇게 파이썬으로 1차 정제를 마치고,

csv파일을 열어서 엑셀로 2차 작업을 시작!

https://hjryu09.tistory.com/37