2.2.10 데이터 로딩과 저장
2.2.10.1 파일 읽기
pandas에는 표 형식으로 되어있는 포맷으로 되어진 파일을 읽을 수 있도록 기능을 제공하고 있다. 그 중에서 주로 사용하는 read_csv와 read_table만 사용해보자.
해당 예제파일은 쉼표로 구분 되어있기 때문에 read_csv을 사용해서 dataframe으로 읽어올 수 있다. Read_table의 경우는 구분자를 쉼표로 지정해서 읽어올 수 있다.
In [1]:
!cat ex1.csv
Out [2]:
,a,b,c,d
0,1,2,3,4
1,5,6,7,8
2,9,10,11,12
3,13,14,15,16
In [3]:
df = pd.read_csv('ex1.csv', index_col = 0)
In [4]:
df
Out [5]:
a b c d
0 1 2 3 4
1 5 6 7 8
2 9 10 11 12
3 13 14 15 16
In [6]:
df_1.drop(['Unnamed: 0'], axis = 1, inplace = True)
In [7]:
df_1
Out [8]:
a b c d
0 1 2 3 4
1 5 6 7 8
2 9 10 11 12
3 13 14 15 16
이때 의도하지 않은 Unnamed: 0과 같은 컬럼이 추가될 수 있다. 이유는 맨 처음 cat을 통해 확인한 결과를 보게 되면 맨 앞에 쉼표로 되어 있는 것을 볼 수 있는데, 앞에 빈 값을 인덱스로 인식해서 그런 것이다. 이렇게 되어있는 데이터일 경우 생기게 된다.(A1셀에 column이 빈경우)
Index_col = 0을 추가해주면서 제거가 가능하다.
In [9]:
df = pd.read_csv('ex1.csv', index_col = 0)
In [10]:
df
Out [11]:
a b c d
0 1 2 3 4
1 5 6 7 8
2 9 10 11 12
3 13 14 15 16
또는 drop메소드를 통해서도 제거 가능하다.
In [12]:
df_1.drop(['Unnamed: 0'], axis = 1, inplace = True)
In [13]:
df_1
Out [14]:
a b c d
0 1 2 3 4
1 5 6 7 8
2 9 10 11 12
3 13 14 15 16
2.2.10.2 대용량 파일 읽기
Csv 파일 용량이 큰 경우
간혹 용량이 큰 csv파일을 읽게 됐을 때 메모리가 부족하거나 여러 이유로 문제가 생길 수 있다.
한 개의 csv 파일이 대용량일 경우 한 번에 1000개의 데이터를 가져오는 식으로 핸들링을 하면 된다.
chunksize = 10 **3
for cnt, chunk in enumerate(pd.read_csv(filename, chunksize = chunksize)):
do_something(chunk)
csv 파일이 많은 경우
날짜별로 csv파일이 되어있을 경우 대용량의 파일처럼 데이터를 끊어서 가져올 필요는 없지만, 하나씩 가져와서 concatenate를 할 필요가 있는데 그거를 하기 위한 방법이다.
디렉토리에 존재하는 파일들만큼 반복문을 실행하고, csv파일인 경우 읽어오는 방법이다.
for filename in os.listdir(dir):
if filename.endswith('.csv'):
df = pd.read_csv(dir + '/' + filename)
2.2.11 데이터 정제 및 준비
데이터 분석 및 모델링을 하는 것은, 데이터를 불러온 뒤, 정제 및 원하는 값으로 처리한 뒤 재정렬하는 과정에서 많은 시간이 소요되기 마련이다. 이러한 작업은 분석 및 학습보다 더 많은 시간이 소요될 수도 있고, 이러한 처리를 통해 결과가 달라지기 때문에 매우 중요한 과정이다. 이번 챕터에서는 결측치 처리, 중복 데이터 처리, 같은 자료형으로 처리, 데이터 병합, 재배열을 해보고자 한다.
결측치란?
결측치(missing value)는 직역하면 값이 없는 것이다. 결측치는 언어마다 여러가지로 표현되지만 시계열에선 NaN(not a number)으로 숫자가 아닌 것을 의미한다.
2.2.11.1 누락된 데이터에 따른 처리 방법
시계열 데이터에서는 누락된 데이터가 자주 발생할 수 있다. 그렇기 때문에 누락된 데이터를 처리하는 것은 자주 발생하는 일이기도 한다. 간단하게 처리는 가능하지만, pandas에서 제공하는 결측치를 처리하는 방식이 완벽하진 않기 때문에 분석 및 학습 결과가 달라질 수 있으니 상황에 맞게 결측치를 처리할 줄 알아야한다.
예를 들어 결측치를 모두 제거할 경우, 막대한 데이터 손실이 생길 수 있고, 결측치를 잘 못 대체할 경우, 데이터에 편향(bias)이 생길 수 있다.
결측치에 대한 충분한 해석이 이루어졌다면, 해당 특성을 삭제할 것인지, 새로운 특성으로 변환할 것인지, 기존 특성을 유지하면서 결측치를 치환할 것인지 판단해야 되며, 보통 비율과 데이터 특성에 따라서 다음 표와 같이 처리할 수 있다.
결측치 비율 | 처리 방법 |
10% 미만 | 제거 또는 치환 |
10% 이상 20% 미만 | 모델 기반 처리 |
20% 이상 | 모델 기반 처리 |
- 치환
말 그대로 적당한 방법으로 대체하는 것이다. (합리적 접근) 데이터의 특성에 맞게 적당한 평균, 중간값등으로 채울 수도 있고, 다른 특성과 상관관계가 있을 경우 그에 맞는 값을 넣어 줄 수도 있다.
- 모델 기반 처리
결측치를 예측하는 새로운 모델을 구성하고, 이를 기반으로 결측치를 채워나가는 방식
2.2.11.2 누락된 값 확인하기
가장 먼저 Nan 값이 있나 확인하는 방법이다 isnull()을 이용해 어느 인덱스에 Nan 값이 있는지 확인이 가능하며 데이터가 커졌을 경우 총 개수나 어느 인덱스에 있는지 확인한 뒤 처리해주면 된다.
In [15]:
series = pd.Series([2,4,6,np.nan,10,np.nan])
In [16]:
series
Out [17]:
0 2.0
1 4.0
2 6.0
3 NaN
4 10.0
5 NaN
dtype: float64
In [18]:
series.fillna(1)
Out [19]:
0 2.0
1 4.0
2 6.0
3 1.0
4 10.0
5 1.0
dtype: float64
In [20]:
series.isnull()
Out [21]:
0 False
1 False
2 False
3 True
4 False
5 True
dtype: bool
In [22]:
series.isnull().sum()
Out [23]:
2
In [24]:
np.where(series.isnull())
Out [25]:
(array([3, 5]),)
2.2.11.3 결측치 처리하기
결측치의 경우 따로 처리를 하지 않으면, 기존 데이터와 데이터 타입이 달라 분석이나 모델링을 동작시킬 때 오류가 발생 할 수 있다. 같은 타입으로 바꿔주거나 제거해주도록 하자.
결측치 처리 관련 메소드
df.fillna(num) | Nan을 num으로 모두 채워서 리턴 |
df.dropna() | 결측치 있는 행/열 제거 |
df.interpolate() | 선형 방식으로 인덱스를 무시하고 주변 값들을 이용해 같은 간격으로 처리 후 리턴 |
pd.Isnull(df), df.notnull() | 전체의 결측값 여부 확인 |
2.2.11.4 중복 제거
-중복 데이터 확인
Duplicated를 통해 각 row가 중복인지 series로 반환된 값을 확인할 수 있다.
첫 번째 row와 마지막 row가 중복되는지 true를 통해 알 수 있다.
In [26]:
df = pd.DataFrame(np.arange(12).reshape(6,2), index=[1,2,2,3,3,4], columns=['a','b'])
In [27]:
df
Out [28]:
a b
1 0 1
2 2 3
2 4 5
3 6 7
3 8 9
4 10 11
In [29]:
df.iat[5,0] = 0
df.iat[5,1] = 1
In [30]:
df
Out [31]:
a b
1 0 1
2 2 3
2 4 5
3 6 7
3 8 9
4 0 1
In [32]:
df.duplicated()
Out [33]:
1 False
2 False
2 False
3 False
3 False
4 True
dtype: bool
추가적으로 keep= ‘last’로 지정해주면 이후 중복되는 데이터가 아닌 앞에 위치에 true를 줄 수도 있다.
In [34]:
df.duplicated(keep = 'last')
Out [35]:
1 True
2 False
2 False
3 False
3 False
4 False
dtype: bool
-중복 데이터 삭제
Drop_duplicates()로 중복되는 데이터를 삭제 가능하다. duplicated가 아닌 duplicates인걸 주의하자.
In [36]:
df.drop_duplicates()
Out [37]:
a b
1 0 1
2 2 3
2 4 5
3 6 7
3 8 9
마찬가지로 keep =’last’를 주면 뒤에 데이터를 남기고 앞에 데이터를 삭제가 가능하다. 컬럼명을 적어주면 중복 확인과 제거가 가능하다. 여러 컬럼을 선택하고 싶다면 리스트에 추가해서 가능하다.
In [38]:
df
Out [39]:
a b
1 0 1
2 2 3
2 4 5
3 6 7
3 8 9
4 10 1
In [40]:
df.duplicated('b')
Out [41]:
1 False
2 False
2 False
3 False
3 False
4 True
dtype: bool
In [42]:
df.drop_duplicates('b')
Out [43]:
a b
1 0 1
2 2 3
2 4 5
3 6 7
3 8 9
2.2.11.5 함수나 매핑을 이용해서 데이터 변형하기
dataframe을 다루다 보면 값들을 다른 타입이나 새로운 값을 추가해야 될 때가 있다.
예시로 한 데이터 프레임에서 한 행의 평균을 내고 싶지만, 영어 점수가 string 타입으로 되어 있어 계산이 되지 않을 때 타입을 변경해주어야 되고, 특히 datetime의 경우 자주 변경해야 될 때가 있다.
In [44]:
df = pd.DataFrame([[70, 80, 50],[30, str(30), 40],[76,43,54],[55,75,95]],
columns = ['국어','영어','수학'])
In [45]:
df
Out [46]:
국어 영어 수학
0 70 80 50
1 30 30 40
2 76 43 54
3 55 75 95
In [47]:
df['영어'].str.contains('0')
Out [48]:
0 NaN
1 True
2 NaN
3 NaN
Name: 영어, dtype: object
2.2.11.6 문자열 치환 여부 판단하기
pandas에서 제공하는 contains 메소드를 사용해서 series안에 있는 값들이 어떤 문자열을 포함하고 있는지를 알 수 있다.
영어라는 column안에 string 값으로 30이 들어가 있는데, str.contains() 메소드를 통해 ‘0’이 있는 있는지 검사가 가능하다. 영어의 경우 case = True일 경우 대소문자를 구분한다
In [49]:
df = pd.DataFrame([[70, 80, 50],[30, str(30), 40],[76,43,54],[55,75,95]],
columns = ['국어','영어','수학'])
In [50]:
df
Out [51]:
국어 영어 수학
0 70 80 50
1 30 30 40
2 76 43 54
3 55 75 95
In [52]:
df['영어'].str.contains('0')
Out [53]:
0 NaN
1 True
2 NaN
3 NaN
Name: 영어, dtype: object
- 값 치환하기
Series 또는 daraframe에 저장한 값을 치환하고 싶은 경우 기본 문자열을 변경할 때 사용하는 replace와 같다. 마찬가지로 딕셔너리나 바꿔주고 싶은 하나의 변수만으로도 가능하다.
In [54]:
df = pd.DataFrame([[70, 80, 50],[30, np.NAN, 40],[76,43,54],[55,75,95]],
columns = ['국어','영어','수학'])
In [55]:
df
Out [56]:
국어 영어 수학
0 70 80.0 50
1 30 NaN 40
2 76 43.0 54
3 55 75.0 95
In [57]:
df.replace([np.NaN, 30], 0)
Out [58]:
국어 영어 수학
0 70 80.0 50
1 0 0.0 40
2 76 43.0 54
3 55 75.0 95
2.2.11.7 데이터 조합하기
대부분의 데이터는 여러 파일들이나 데이터베이스로 날짜 등으로 기록하기 때문에 나눠져 있다.
분석 또는 모델링에 사용을 하려면 원하는 결과가 나오게 조합을 할 줄 알아야 된다. 그리고 앞으로 데이터를 다루면서 가장 기본적이기도 하고 많이 사용하게 될 부분이라 필수라고 생각한다.
- dataframe 붙이기: pd.concat()
Pd.concat 함수의 경우 dataframe을 물리적으로 이어 붙여주는 함수이다. 그렇기 때문에 따로 중복제거는 되지 않는다.
합쳐질 때 default값으로 axis=0이 적용되어 있기 때문에 아래로 dataframe을 이어 붙인다. 그런데 df1에는 column d가 없어서 NaN으로 채워진 것을 볼 수 있다. 그냥 붙이는 것이기 때문에 인덱스가 겹치는데 ignore_index=True를 추가해줌으로써 인덱스를 리셋할 수 있다.
In [59]:
df1 = pd.DataFrame({'a':['a0','a1','a2','a3'],
'b':['b0','b1','b2','b3'],
'c':['c0','c1','c2','c3']},
index = [0,1,2,3])
df2 = pd.DataFrame({'a':['a2','a3','a4','a5'],
'b':['b2','b3','b4','b5'],
'c':['c2','c3','c4','c5'],
'd':['d2','d3','d4','d5']},
index = [2,3,4,5])
In [60]:
df1
Out [61]:
a b c
0 a0 b0 c0
1 a1 b1 c1
2 a2 b2 c2
3 a3 b3 c3
In [62]:
df2
Out [63]:
a b c d
2 a2 b2 c2 d2
3 a3 b3 c3 d3
4 a4 b4 c4 d4
5 a5 b5 c5 d5
In [64]:
pd.concat([df1,df2])
Out [65]:
a b c d
0 a0 b0 c0 NaN
1 a1 b1 c1 NaN
2 a2 b2 c2 NaN
3 a3 b3 c3 NaN
2 a2 b2 c2 d2
3 a3 b3 c3 d3
4 a4 b4 c4 d4
5 a5 b5 c5 d5
일반 pd.concat을 이용해서 합치게 되면 인덱스도 그대로 붙어버리게 되고, 옵션으로 ignore_index = Ture를 주면 reset_index가 된 인덱스를 사용할 수 있다.
In [66]:
pd.concat([df1,df2], ignore_index=True)
Out [67]:
a b c d
0 a0 b0 c0 NaN
1 a1 b1 c1 NaN
2 a2 b2 c2 NaN
3 a3 b3 c3 NaN
4 a2 b2 c2 d2
5 a3 b3 c3 d3
6 a4 b4 c4 d4
7 a5 b5 c5 d5
Join을 통해 겹치는 이터만 합칠 수 있다. 이어 붙이는 방식은 outer(합집합), inner(교집합)이 있다.
In [68]:
pd.concat([df1,df2], join= 'inner')
Out [69]:
a b c
0 a0 b0 c0
1 a1 b1 c1
2 a2 b2 c2
3 a3 b3 c3
2 a2 b2 c2
3 a3 b3 c3
4 a4 b4 c4
5 a5 b5 c5
- dataframe 병합 : pd.merge()
Merge()함수는 공통의 열을 기준으로 두 개의 dataframe을 합쳐준다. sql에서 join과 같은 역할이다. 보통 시간을 기준으로 여러 데이터를 합치고 싶을 때 사용한다.
기준열 name이 같을 때 pd.merge(df_left, df_right, how=’ 조인방식, on= 기준열).
In [70]:
df1 = pd.DataFrame(np.arange(10).reshape(5,2), columns = ['a','b'])
df2 = pd.DataFrame(np.arange(12).reshape(6,2), columns = ['a','c'])
In [71]:
df1
Out [72]:
a b
0 0 1
1 2 3
2 4 5
3 6 7
4 8 9
In [73]:
df2
Out [74]:
c d
0 0 1
1 2 3
2 4 5
3 6 7
4 8 9
5 10 11
In [75]:
pd.merge(df1,df2, left_on ='a', right_on='c', how = 'inner')
Out [76]:
a b c d
0 0 1 0 1
1 2 3 2 3
2 4 5 4 5
3 6 7 6 7
4 8 9 8 9
pd.merge의 경우 단점이 두 개의 dataframe만 합칠 수 있다는 것인데, 이를 reduce라는 함수를 통해 보안할 수 있다.
In [77]:
df1 = pd.DataFrame(np.arange(10).reshape(5,2), columns = ['a','b'])
df2 = pd.DataFrame(np.arange(12).reshape(6,2), columns = ['a','c'])
df3 = pd.DataFrame(np.arange(14).reshape(7,2), columns = ['a','d'])
df_list = [df1,df2,df3]
In [78]:
from functools import reduce
reduce(lambda left, right: pd.merge(left, right, on = ['a'], how = 'outer'), df_list)
Out [79]:
a b c d
0 0 1.0 1.0 1
1 2 3.0 3.0 3
2 4 5.0 5.0 5
3 6 7.0 7.0 7
4 8 9.0 9.0 9
5 10 NaN 11.0 11
6 12 NaN NaN 13
-dataframe 구조화 : pd.pivot()
데이터 열 중에서도 두 개의 열을 각각 행 인덱스, 열 인덱스로 사용하고, 데이터를 조회하여 펼쳐 놓는 것을 말한다. Dataframe.pivot(index = ‘인덱스로 사용될 열’, columns = ‘열로 사용될 열’, values = ‘값으로 입력될 열’)
In [80]:
df = pd.DataFrame({'id': [1, 1, 2, 2, 3, 3],
'col': ['x1', 'x2', 'x1', 'x2', 'x1', 'x2'],
'num': [30, 10, 70, 40, 20, 80],
'str': ['a', 'a', 'b', 'b', 'c', 'c']})
In [81]:
df
Out [82]:
id col num str
0 1 x1 30 a
1 1 x2 10 a
2 2 x1 70 b
3 2 x2 40 b
4 3 x1 20 c
5 3 x2 80 c
In [83]:
df.pivot('id','col','num')
Out [84]:
col x1 x2
id
1 a a
2 b b
3 c c
만약 행과 열로 pivot을 했을 때 value가 없게 되면 NaN으로 표시된다.
pd.pivot_table()
pd.pivot으로 하게 되면 에러가 나서 안되고, pd.pivot_table을 사용해야만 하는 경우가 몇 가지 있다. 그렇기 때문에 안되는 상황을 굳이 겪지 않으려면 pivot_table만 알고 있어도 무방하다. 추가적으로 pivot_table을 사용할 때 values는 문자형 데이터이면 에러가 발생한다. 이유는 pivot_table의 경우 aggfunc 인자를 추가적으로 받고 있는데 기본 값으로 mean으로 적용되어있기 때문이다. 하지만 aggunc = ‘first’로 명시적으로 설정해 줌으로써, default인 mean을 사용해서 집계하는 것이 아닌 재구조화하는 기준의 테이블에서 각 셀의 첫 번째 값(‘first’)를 그냥 가져오게끔 해서 대응이 가능하다.
In [85]:
pd.pivot_table(df, index = 'id', columns = 'col', values = 'str', aggfunc = 'first')
Out [86]:
col x1 x2
id
1 a a
2 b b
3 c c
글 | AI 1팀 김기중
'엑셈 경쟁력 > 시계열 데이터처리 AI 알고리즘' 카테고리의 다른 글
Chapter 2-3. 기초 시계열 분석 (0) | 2022.10.26 |
---|---|
Chapter 2-2. 시계열 데이터 전처리 (0) | 2022.09.28 |
Chapter 2. Pandas 2편 : Time Series 머신러닝을 위한 Python 필수 라이브러리 (0) | 2022.08.25 |
Chapter 2. Pandas 1편 : Time Series 머신러닝을 위한 Python 필수 라이브러리 (0) | 2022.08.25 |
Chapter 2. Numpy 2편 : Time Series 머신러닝을 위한 Python 필수 라이브러리 (0) | 2022.07.27 |
댓글