본문 바로가기
[파이썬]/데이터 분석

[데이터 분석] 판다스를 활용한 데이터프레임 재구성

by sung min_Kim 2023. 11. 30.

(데이터프레임 생성에 관한 글은 아래의 링크를 참고하시라.)
2023.11.29 - [[파이썬]/데이터 분석] - [데이터 분석] 판다스를 활용한 데이터프레임 생성 및 개념

 


데이터프레임 재구성

 


 한국전력거래소에서 수집한 "시간별 전력수요량" 공공데이터를 새로운 데이터프레임으로 재구성하려고 한다. 이러한 작업을 수행하는 이유는 데이터의 구조가 보기 불편하다고 느껴지기 때문이다.
(왜 이렇게 생각하는지에 대해서는 아래에 명시하였다.)


· 데이터 조회

 

import pandas AS pd

# 데이터 불러오기
file_path = "./01_data/new_data.csv"

# CSV 파일을 읽어 데이터프레임 객체로 변환
df = pd.read_csv(file_path)

# 호출
df


 우선 데이터프레임을 재구성하기 위해서는 판다스 라이브러리를 사용하여 수집한 위의 파일을 호출해야 한다. 판다스 라이브러리를 사용하면 위 파일이 저장된 위치를 읽고, 그 파일을 열어 전체 데이터를 데이터프레임 객체로 변환시켜 주는 작업을 처리할 수 있다.

 판다스 라이브러리는 'pd.read' 함수를 제공하며, 이 함수를 사용하여 파일로부터 데이터를 불러와 데이터프레임 객체를 생성할 수 있다.

 호출하고자 하는 파일의 형식은 CSV이며, 판다스 라이브러리는 'pd.read_csv(file_path)' 구문을 통해 'file_path'에 저장된 파일을 불러와, 해당 데이터를 데이터프레임 객체로 변환하는 기능을 수행한다.

 이렇게 생성된 데이터프레임 객체를 'df'라는 변수에 할당하면, 이후 'df'를 통해 데이터프레임에 접근할 수 있다.

 

new_data.csv 데이터 구조

 위 데이터프레임에 저장된 데이터들을 조회하였다. 이 데이터프레임은 "날짜별로 각 시간대의 데이터"를 저장하고 있다. 위의 구조를 한눈에 보았을 때 보기 편한가? 개인적으로 불편하게 느껴진다. 이는 일반적으로 시간에 따른 발전량 데이터가 나열되어야 하는데, 현재는 "날짜"와 "시간"이 열에, "발전량"이 행에 나열된 구조이기 때문에, 다소 복잡하게 느껴졌기 때문이다. 따라서, 글쓴이는 이 데이터를 "날짜별"이 아닌 "각 시간대별로 날짜별 데이터"를 갖는 구조로 재정의하려고 한다.

 


· 데이터 검증


 구조를 바꾸기에 앞서 위의 데이터가 결측치나 이상치가 있는 데이터인지 검증 단계를 거쳐야 한다. 여기서 말하는 '결측치'란 데이터가 누락되어 없는 값을 의미하며, '이상치'는 나머지 데이터와 크게 벗어나거나 일반적인 범위에서 벗어난 값'을 의미한다.

 먼저 'info()' 함수를 사용하여 결측치를 확인하고, 이후에 'describe()' 함수를 사용하여 이상치를 확인함으로써 데이터에 대한 검증을 수행하려 한다.

 

검증1_결측치 데이터의 존재 유무 확인

  이 데이터는 결측치가 존재하지 않는 데이터이다. 그 이유는 'RangeIndex'의 값과 'non-null'의 값이 '365'로 일치하기 때문이다. 그렇다면 각 각은 무엇을 의미할까? 'RangeIndex'는 전체 행의 개수를 나타내고, 'non-null'은 각 열에서 결측치가 아닌 데이터의 개수를 나타낸다.

 따라서, 이 정보를 통해 전체 행의 개수와 각 열의 개수가 일치하면 "결측 데이터가 없다."는  것을 의미하고, 일치하지 않으면 "결측 데이터가 있다."는 것을 알 수 있다. 위의 데이터프레임은 결측치가 존재하지 않는 데이터프레임이다.


 그럼 이제 Describe 함수를 사용하여 이상치 데이터가 존재하는지 유무를 확인하여 주도록 하자.

 

검증2_이상치 데이터의 존재 유무 확인

 이상치 데이터인지 확인하는 일반적인 방법은 주어진 데이터 집합의 평균값(mean) 또는 중앙값(50%)을 확인하는 것이다.  이 평균값이나 중앙값에서 크게 벗어난 값들은 분포를 왜곡시키거나, 통계적 분석을 방해하는 요소가 될 수 있다. 더불어 최소값(min)이 마이너스인지도 확인하여 준다.

 이 데이터는 값이 마이너스인 경우도 없고, 다른 값들과 크게 벗어나는 데이터도 없으므로, 이상치가 존재할 확률이 적다고 예상할 수 있다. 이러한 검증 단계를 앞서 수행함으로써 데이터의 무결성을 보장할 수 있다.


 위와 같은 단계를 수행함으로써, 이 데이터를 사용하기 위한 준비 과정을 마쳤다. 이제 해당 데이터를 주입할, "날짜", "시간", "발전량"을 열로 갖는 새로운 데이터프레임을 생성하여 줄 것이다. 

 


· 데이터프레임 생성 및 컬럼명 지정

 

# 빈 데이터 프레임 생성
result_df = pd.DataFrame()

# 컬럼명 부여
result_df.columns = ['컬럼명1', '컬럼명2', '컬럼명3']


 빈 데이터프레임에 컬럼명을 부여하려면, 데이터프레임을 생성할 때부터 컬럼명을 지정해 주어야 한다. 데이터가 없는 빈 데이터프레임에 컬럼명을 부여하려고 하면 'ValueError' 오류가 발생하기 때문이다. 이는 데이터프레임에 아무런 데이터도 없기 때문에 발생하는 오류이다. 따라서, 아래와 같은 방식으로 컬럼명을 가진 데이터프레임을 생성해야 한다.

 

# 컬럼명과 함께 빈 데이터 프레임 생성
result_df = pd.DataFrame(columns=["년도", "시간", "전력량"])


 판다스의 Columns 속성을 사용하면 데이터프레임을 생성하면서 컬럼명을 지정할 수 있다. 이를 이용하면 "년도", "시간", "전력량"이라는 컬럼을 가진 ' result_df ' 데이터프레임을 생성할 수 있다.

새 데이터프레임 생성

 


 · 데이터 추출 및 새 데이터프레임에 주입


 이제 새로운 데이터프레임을 생성했으니, 본격적으로 기존 데이터를 새 데이터프레임으로 옮겨주도록 하자.
데이터를 옮기기 위해서는 우선 기존의 데이터프레임으로부터 데이터를 추출하는 작업을 수행해야 한다.
 
 첫 번째로, 기존의 데이터프레임 'df'에서 각 컬럼의 데이터를 추출하여 변수에 저장해 두도록 하자. 

기본 데이터프레임의 컬럼 추출

 변수 'col_list'에는 기존 데이터프레임의 컬럼을 추출한 값을 가지고 있다. 이 변수는 새로 생성한 데이터프레임 'result_df'의 각 컬럼에 데이터를 주입할 때 사용된다.

 두 번째로, 위 'col_list' 변수에 저장된 행 단위 데이터를 추출하여 준다.

 

# 행 단위 데이터 추출
For index, row in df.iterrows() :

	# 년도 데이터
	ymd = row[col_list[0]]

	# 시간과 전력량 데이터
	data = row[col_list[1:]]

 


 이때에는 Iterrows() 함수를 사용하여, 기존 데이터프레임에서 'col_list'에 저장된 각 컬럼의 데이터를 순회하며 특정 컬럼의 값을 추출한다. Iterrows 함수는 데이터프레임의 각 행을 순회하면서 반환되는 행은 시리즈(Series) 객체로 반환되는데, 이 객체에 대괄호 []를 적용하면 해당 행에서 특정 컬럼의 값을 추출할 수 있다.

 따라서, 'col_list'의 첫 번째 컬럼인 "년도" 데이터를 추출하여 'ymd' 변수에 저장하며, 두 번째 컬럼부터 마지막 컬럼에 해당하는 "시간"과 "전력량"의 데이터는 'data' 변수에 저장한다. 이렇게 추출한 데이터는 "년도", "시간", "전력량"등의 정보를 가져오는 데 사용된다.

"시간"과 "발전량" 값을 갖는 변수 'data'


 여기까지의 과정을 정리해 보자면, 우선 기존의 데이터프레임에서 컬럼의 값들을 추출하여, 특정 행의 "년도", "시간", "전력량" 정보를 가져온다. 이렇게 가져온 정보는 새로운 데이터프레임 'result_df'에 추가되기 위한 준비 과정이다. 이렇게 행 단위로 데이터를 추출하고 처리함으로써, 데이터프레임의 구조를 원하는 형태로 구성할 수 있다.

 

 

# '시간'과 '전력량'의 행 데이터를 각 각 추출
For time, value In data.items():
	
    # 최종 데이터프레임(result_df)에 행 데이터를 추가하기 위한 임시 데이터프레임 생성
    df_temp = pd.DataFrame({"년도":[ymd], "시간":[time], "전력량":[value]})
	
    # 최종 데이터프레임에 행 데이터 추가하기
    result_df = pd.concat([result_df, df_temp], axis=0, ignore_imdex=True)


 세 번째로, "시간"과 "발전량"의 데이터를 가지고 있는 변수 'data'에 대해 Items 함수를 사용하여 "시간"과 "전력량"을 각 각 추출한다. 이렇게 추출한 데이터를 기반으로 새로운 데이터프레임 'df_temp'을 생성한다.

 'df_temp' 데이터프레임의 역할은 "년도(ymd)", "시간(time)", "전력량(value)"의 행 데이터를 임시로 저장하고, 이를 'result_df' 데이터프레임 추가하는 것이다.


임시로 행 데이터를 보관할 데이터프레임 생성


 마지막 단계에서는, 'df_temp' 데이터프레임에 저장된 행 데이터를 'result_df' 데이터프레임에 추가한다. 이때 'pd.concat' 함수를 사용한다.
 Concat 함수는 데이터프레임과 데이터프레임을 행 단위(axis=0) 또는 열 단위(axis=1)로 추가할 때 사용하는 함수이다.
 추가로 'ignore_index=True' 옵션을 통해 새로운 행이 추가될 때마다 인덱스를 자동으로 증가시킨다. 이 과정을 통해 'result_df' 데이터프레임은 "년도", "시간", "전력량"을 컬럼으로 가지며, 각 컬럼에 대한 데이터를 행으로 가지는 구조로 재구성되었다.



 끝으로 생성한 데이터프레임은 꼭 파일 또는 데이터베이스에 저장하여 필요할 때 사용할 수 있도록 하자. 이때 주의해야 할 점으로는 저장하려는 파일명이 저장 위치에 이미 존재하면 값을 덮어쓴다는 점이다.

# 지정할 경로 지정
save_path = "./01_data/new_data.csv"

result_df.to_csv(save_path, index=False)
## index=False : 인덱스 번호값 생략

 



지금까지 데이터 수집부터 검증, 그리고 약간의 전처리 과정을 거쳐
기존에 수집한 데이터를 새로운 데이터프레임으로 재구성하는 작업을 수행하였다.

이 과정에서는 판다스 라이브러리의 다양한 기능들을 활용하여 데이터를 효율적으로 관리하고 가공하였다.

이렇게 재구성된 데이터프레임은 분석이나 모델링 작업을 수행하는데 활용할 수 있다.

이 전의 데이터와 비교하여 더 깔끔하고 관리하기 쉬운 형태로 데이터가 변환되었음을 확인할 수 있다.