본 글은 판다스(pandas)의 기본 사용법을 소개해 놓은 10 Minutes to pandas 을 번역한 내용(DANDYRILLA)을 다시 편집하여 요약한 내용입니다. 처음 pandas를 공부하며 원글을 읽고 도전해봤지만 10분 만에 전체 과정을 마무리하긴 힘들었습니다. 그래서 제 기준에서 pandas를 처음 접하는 사람들이 pandas에 대해 이해하고 시작하기에 충분할 것 같은 내용들만 모아 요약해 봤습니다.
0. 시작하기에 앞서
pandas 를 사용하기 위해서 다음과 같이 모듈을 임포트(import) 합니다.
1
2
3
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
1. 데이터 오브젝트 생성하기
- 데이터 오브젝트는 ‘데이터를 담고 있는 그릇’입니다.
- 대표적으로 Series 와 DataFrame 이 있습니다.
- Series 는 1차원 배열, DataFrame 은 2차원 배열로 데이터를 담고 있습니다.
- Series 와 DataFrame 데이터 오브젝트를 만들어 보는 실습을 해 보겠습니다.
1.1 Series
Series는 기본적으로 리스트를 넘겨주어 만들 수 있습니다. 또한 값이 위치하고 있는 정보인 인덱스(index)가 Series 에 같이 저장되게 됩니다.
1
2
3
4
5
6
7
8
9
s = pd.Series([1, 3, 5, np.nan, 6, 8])
# 0 1.0
# 1 3.0
# 2 5.0
# 3 NaN
# 4 6.0
# 5 8.0
# dtype: float64
1.2 DataFrame - numpy array
DataFrame은 여러 형태의 데이터를 받아 생성할 수 있는데 그 중 한 방법으로 아래와 같이 numpy array 를 받아 생성이 가능합니다. DataFrame 은 2차원 배열의 형태를 띄고 있습니다. 따라서 우리가 자주 보는 표 형태와 같이 두 가지의 기준에 따라 데이터를 담고 있습니다.
DataFrame 을 만들기 위해서는 pd.DataFrame()
라는 클래스 생성자를 사용하며, 일반적으로pd.DataFrame(data, index, columns)
의 형태로 사용합니다.
1
2
3
4
5
6
7
8
9
10
11
dates = pd.date_range('20130101', periods=6)
cols = ['A', 'B', 'C', 'D']
df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=cols)
# A B C D
# 2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
# 2013-01-04 0.721555 -0.706771 -1.039575 0.271860
# 2013-01-05 -0.424972 0.567020 0.276232 -1.087401
# 2013-01-06 -0.673690 0.113648 -1.478427 0.524988
1.3 DataFrame - dict
DataFrame 을 생성하는 또 다른 방법으로 아래와 같이 딕셔너리(dict)를 넣어주어 만들 수 있습니다. dict 의 key 값이 열을 정의하는 컬럼이 되며, 인덱스는 0부터 1씩 증가하는 정수 인덱스가 사용됩니다.
1
2
3
4
5
6
7
8
9
10
11
df2 = pd.DataFrame({'A': 1.,
'B': pd.Timestamp('20130102'),
'C': pd.Series(1, index=list(range(4)), dtype='float32'),
'D': np.array([3]*4, dtype='int32'),
'E': pd.Categorical(['test', 'train', 'test', 'train']),
'F': 'foo'})
# A B C D E F
# 0 1.0 2013-01-02 1.0 3 test foo
# 1 1.0 2013-01-02 1.0 3 train foo
# 2 1.0 2013-01-02 1.0 3 test foo
# 3 1.0 2013-01-02 1.0 3 train foo
2. 데이터 확인하기 (Viewing Data)
2.1 head(), tail()
DataFrame 에 들어있는 자료들을 확인하기 위해 .head()
와 .tail()
메소드를 사용하면 됩니다. 기본적으로 상위 또는 하위 5 개의 자료를 보여주는데, 메소드의 인자로 보고싶은 데이터의 개수를 숫자를 넣어줄 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
## 첫 5개 행의 데이터를 보여줍니다.
df.head()
# A B C D
# 2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
# 2013-01-04 0.721555 -0.706771 -1.039575 0.271860
# 2013-01-05 -0.424972 0.567020 0.276232 -1.087401
## 마지막 3개 행의 데이터를 보여줍니다.
df.tail(3)
# A B C D
# 2013-01-04 0.721555 -0.706771 -1.039575 0.271860
# 2013-01-05 -0.424972 0.567020 0.276232 -1.087401
# 2013-01-06 -0.673690 0.113648 -1.478427 0.524988
2.2 index, columns, values
DataFrame의 인덱스를 보려면 .index
속성을, 컬럼을 보려면 .columns
속성을, numpy 데이터를 보려면 .values
속성을 통해 확인하면 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
df.index
# DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
# '2013-01-05', '2013-01-06'],
# dtype='datetime64[ns]', freq='D')
df.columns
# Index(['A', 'B', 'C', 'D'], dtype='object')
df.values
# [[ 0.4691, -0.2829, -1.5091, -1.1356],
# [ 1.2121, -0.1732, 0.1192, -1.0442],
# [-0.8618, -2.1046, -0.4949, 1.0718],
# [ 0.7216, -0.7068, -1.0396, 0.2719],
# [-0.425 , 0.567 , 0.2762, -1.0874],
# [-0.6737, 0.1136, -1.4784, 0.525 ]]
2.3 describe()
.describe()
메소드는 생성했던 DataFrame 의 간단한 통계 정보를 보여줍니다.
1
2
3
4
5
6
7
8
9
10
df.describe()
# A B C D
# count 6.000000 6.000000 6.000000 6.000000
# mean 0.073711 -0.431125 -0.687758 -0.233103
# std 0.843157 0.922818 0.779887 0.973118
# min -0.861849 -2.104569 -1.509059 -1.135632
# 25% -0.611510 -0.600794 -1.368714 -1.076610
# 50% 0.022070 -0.228039 -0.767252 -0.386188
# 75% 0.658444 0.041933 -0.034326 0.461706
# max 1.212112 0.567020 0.276232 1.071804
2.4 T
.T
속성은 DataFrame 에서 index 와 column 을 바꾼 형태의 DataFrame 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 열과 행을 바꾼 형태의 데이터프레임입니다.
df.T
# 2013-01-01 2013-01-02 2013-01-03 2013-01-04 2013-01-05 2013-01-06
# A 0.469112 1.212112 -0.861849 0.721555 -0.424972 -0.673690
# B -0.282863 -0.173215 -2.104569 -0.706771 0.567020 0.113648
# C -1.509059 0.119209 -0.494929 -1.039575 0.276232 -1.478427
# D -1.135632 -1.044236 1.071804 0.271860 -1.087401 0.524988
## 다음과 같이 메소드로 호출한다면 에러를 냅니다.
df.T()
# Traceback (most recent call last):
# File "./main.py", line 5, in __main__
# dfT = df.T()
# TypeError: 'DataFrame' object is not callable
2.5 sort_index()
행과 열 이름을 정렬하여 나타낼 수 있습니다.
axis
(정렬 대상 축) :axis=0
라고 써주면 인덱스 기준 정렬(기본값),axis=1
라고 써주면 컬럼을 기준으로 정렬합니다.ascending
(정렬 방향) :ascending=True
는 오름차순 정렬(기본값),ascending=False
는 내림차순 정렬을 하겠다는 의미입니다.
1
2
3
4
5
6
7
8
df.sort_index(axis=1, ascending=False)
# D C B A
# 2013-01-01 -1.135632 -1.509059 -0.282863 0.469112
# 2013-01-02 -1.044236 0.119209 -0.173215 1.212112
# 2013-01-03 1.071804 -0.494929 -2.104569 -0.861849
# 2013-01-04 0.271860 -1.039575 -0.706771 0.721555
# 2013-01-05 -1.087401 0.276232 0.567020 -0.424972
# 2013-01-06 0.524988 -1.478427 0.113648 -0.673690
또한 DataFrame 내부에 있는 값으로 정렬할 수도 있습니다. 다음은 B 컬럼에 대해 정렬한 결과를 보여줍니다.
1
2
3
4
5
6
7
8
df.sort_values(by='B')
# A B C D
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
# 2013-01-04 0.721555 -0.706771 -1.039575 0.271860
# 2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236
# 2013-01-06 -0.673690 0.113648 -1.478427 0.524988
# 2013-01-05 -0.424972 0.567020 0.276232 -1.087401
3. 데이터 선택하기 (Selection)
데이터프레임의 슬라이싱은 []
, .loc
, .at
, .iloc
가 있습니다. (처음에는 복잡하다고 느끼시겠지만 pandas를 사용하다보면 주로 사용하는 슬라이싱이 몇 가지로 압축될 것입니다. 개인적으로 저는 df[컬럼명]
, .iloc
를 많이 사용하는 것 같습니다.)
3.1 df[컬럼명]
1
2
3
4
5
6
7
8
9
10
11
12
## A컬럼의 데이터만 갖고옵니다.
df['A']
# 2013-01-01 0.469112
# 2013-01-02 1.212112
# 2013-01-03 -0.861849
# 2013-01-04 0.721555
# 2013-01-05 -0.424972
# 2013-01-06 -0.673690
# req: D, Name: A, dtype: float64
type(df['A'])
# <class 'pandas.core.series.Series'>
3.2 df[시작인덱스:끝인덱스+1]
1
2
3
4
5
6
7
8
## 맨 처음 3개의 행을 가져옵니다.
df[0:3]
# A B C D
# 2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
# df[0:3] 이라고 입력했지만 3번째 행을 가져오지 않음에 유의합니다.
3.3 df[시작인덱스명:끝인덱스명]
이 때에는 숫자를 이용하여 슬라이싱 할 때와 달리 처음과 끝의 행이 모두 포함된 결과를 가져옵니다.
1
2
3
4
5
6
## 인덱스명에 해당하는 값들을 가져옵니다.
df['20130102':'20130104']
# A B C D
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
# 2013-01-04 0.721555 -0.706771 -1.039575 0.271860
3.4 .loc[인덱스명]
라벨의 이름을 이용하여 선택할 수 있는 .loc
를 이용할 수도 있습니다.
1
2
3
4
5
6
df.loc[dates[0]] # df.loc['20130101'], df.loc['2013-01-01'] 로 써도 동일하게 동작합니다.
# A 0.469112
# B -0.282863
# C -1.509059
# D -1.135632
# Name: 2013-01-01 00:00:00, dtype: float64
3.5 .loc[:,[‘컬럼명1’, ‘컬럼명2’…]]
컬럼 ‘A’와 컬럼 ‘B’에 대한 모든 값 가져오기.
1
2
3
4
5
6
7
8
df.loc[:,['A','B']]
# A B
# 2013-01-01 0.469112 -0.282863
# 2013-01-02 1.212112 -0.173215
# 2013-01-03 -0.861849 -2.104569
# 2013-01-04 0.721555 -0.706771
# 2013-01-05 -0.424972 0.567020
# 2013-01-06 -0.673690 0.113648
3.6 .loc[시작인덱스명:끝인덱스명, [‘컬럼명1’, ‘컬럼명2’…]]
1
2
3
4
5
df.loc['20130102':'20130104',['A','B']]
# A B
# 2013-01-02 1.212112 -0.173215
# 2013-01-03 -0.861849 -2.104569
# 2013-01-04 0.721555 -0.706771
3.7 .loc[인덱스명, [‘컬럼명1’, ‘컬럼명2’…]]
1
2
3
4
df.loc[dates[0], ['A','B']]
# A 1.212112
# B -0.173215
# Name: 2013-01-02 00:00:00, dtype: float64
3.8 .at
특정 인덱스 값과 특정 컬럼에 있는 값 가져오기. 이는 .at
을 이용할 수도 있습니다.
1
2
3
4
5
df.loc[dates[0],'A']
# 0.46911229990718628
df.at[dates[0], 'A']
# 0.46911229990718628
3.9 .iloc
.iloc
는 위치를 이용하여 데이터를 선택할 수 있습니다. 여기서 인덱스 번호는 python 에서 사용하는 인덱스와 같은 개념으로 이해하시면 됩니다.
1
2
3
4
5
6
df.iloc[3] # 4번째 행
# A 0.721555
# B -0.706771
# C -1.039575
# D 0.271860
# Name: 2013-01-04 00:00:00, dtype: float64
인덱스 번호로 행 위치 뿐만 아니라 열 위치도 선택할 수 있습니다.
1
2
3
4
df.iloc[3:5,0:2] # 3~4행, 0~1열
# A B
# 2013-01-04 0.721555 -0.706771
# 2013-01-05 -0.424972 0.567020
또한 행과 열의 인덱스를 리스트로 넘겨줄 수도 있습니다.
1
2
3
4
5
df.iloc[[1,2,4],[0,2]]
# A C
# 2013-01-02 1.212112 0.119209
# 2013-01-03 -0.861849 -0.494929
# 2013-01-05 -0.424972 0.276232
명시적으로 행이나 열 선택 인자에 :
슬라이스를 전달하면 다음과 같이 행 또는 열 전체를 가져올 수도 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
df.iloc[1:3,:]
# A B C D
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
df.iloc[:,1:3]
# B C
# 2013-01-01 -0.282863 -1.509059
# 2013-01-02 -0.173215 0.119209
# 2013-01-03 -2.104569 -0.494929
# 2013-01-04 -0.706771 -1.039575
# 2013-01-05 0.567020 0.276232
# 2013-01-06 0.113648 -1.478427
값 하나를 선택하기 위해서는 특정 행과 열을 지정하는 방식으로 하면 됩니다. 아래의 두 방법 모두 동일한 방법입니다.
1
2
3
4
df.iloc[1,1]
# -0.17321464905330858
df.iat[1,1]
# -0.17321464905330858
loc
와 iloc
가 많이 헷갈리실텐데
loc
: 이름으로 슬라이싱iloc
: 위치로 슬라이싱
정도로 기억해 두시면 됩니다.
3.10 조건을 이용하여 선택하기
- 특정한 열의 값 기준 조건
1
2
3
4
5
df[df.A > 0]
# A B C D
# 2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236
# 2013-01-04 0.721555 -0.706771 -1.039575 0.271860
- 각 값 기준 조건 이 때에는 행이 선택되는 것이 아니라 조건에 맞는 값들만 그대로 보여지고 나머지 값들은 결측치(missing value)로 나타나게 됩니다.
1
2
3
4
5
6
7
8
df[df > 0]
# A B C D
# 2013-01-01 0.469112 NaN NaN NaN
# 2013-01-02 1.212112 NaN 0.119209 NaN
# 2013-01-03 NaN NaN NaN 1.071804
# 2013-01-04 0.721555 NaN NaN 0.271860
# 2013-01-05 NaN 0.567020 0.276232 NaN
# 2013-01-06 NaN 0.113648 NaN 0.524988
- 필터링 조건
isin()
은 필터링 하려는 값 리스트를 인자로 전달하면 해당되는 행을 리턴합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df2 = df.copy()
df2['E'] = ['one', 'one','two','three','four','three']
df2
# A B C D E
# 2013-01-01 0.469112 -0.282863 -1.509059 -1.135632 one
# 2013-01-02 1.212112 -0.173215 0.119209 -1.044236 one
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804 two
# 2013-01-04 0.721555 -0.706771 -1.039575 0.271860 three
# 2013-01-05 -0.424972 0.567020 0.276232 -1.087401 four
# 2013-01-06 -0.673690 0.113648 -1.478427 0.524988 three
df2[df2['E'].isin(['two', 'four'])] # E 컬럼의 값이 two, four인 행 선택
# A B C D E
# 2013-01-03 -0.861849 -2.104569 -0.494929 1.071804 two
# 2013-01-05 -0.424972 0.567020 0.276232 -1.087401 four
3.11 데이터 변경하기
우리가 선택했던 데이터 프레임의 특정 값들을 다른 값으로 변경할 수 있습니다. 이에 대한 방법을 알아봅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Series 오브젝트 생성
s1 = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range('20130102', periods=6))
s1
# 2013-01-02 1
# 2013-01-03 2
# 2013-01-04 3
# 2013-01-05 4
# 2013-01-06 5
# 2013-01-07 6
# Freq: D, dtype: int64
# DataFrame에 F컬럼과 데이터 추가
df['F'] = s1
데이터 프레임의 특정 값 하나를 선택하여 다른 값으로 바꿀 수 있습니다.
1
df.at[dates[0], 'A'] = 0
앞서 배운 값의 위치(인덱스 번호)를 이용한 변경도 가능합니다.
1
df.iat[0,1] = 0
여러 값을 한꺼번에 바꾸고 싶을 때는 데이터의 사이즈만 잘 맞춰 주면 됩니다. 다음은 NumPy array를 이용한 방법입니다.
1
df.loc[:,'D'] = np.array([5] * len(df))
앞에서 바꾼 데이터들을 모두 적용하여 데이터 프레임의 값들을 한번 살펴보겠습니다.
1
2
3
4
5
6
7
8
df
# A B C D F
# 2013-01-01 0.000000 0.000000 -1.509059 5 NaN
# 2013-01-02 1.212112 -0.173215 0.119209 5 1.0
# 2013-01-03 -0.861849 -2.104569 -0.494929 5 2.0
# 2013-01-04 0.721555 -0.706771 -1.039575 5 3.0
# 2013-01-05 -0.424972 0.567020 0.276232 5 4.0
# 2013-01-06 -0.673690 0.113648 -1.478427 5 5.0
앞서 배운 조건을 이용해 특정 조건에 만족하는 값들만 변경할 수도 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
## 양수 값을 음수로 바꾸는 예제
df2 = df.copy()
df2[df2 > 0] = -df2
df2
# A B C D F
# 2013-01-01 0.000000 0.000000 -1.509059 -5 NaN
# 2013-01-02 -1.212112 -0.173215 -0.119209 -5 -1.0
# 2013-01-03 -0.861849 -2.104569 -0.494929 -5 -2.0
# 2013-01-04 -0.721555 -0.706771 -1.039575 -5 -3.0
# 2013-01-05 -0.424972 -0.567020 -0.276232 -5 -4.0
# 2013-01-06 -0.673690 -0.113648 -1.478427 -5 -5.0
4. 결측치 (Missing Data)
여러가지 이유로 우리는 데이터를 전부 다 측정하지 못하는 경우가 종종 발생합니다. 이처럼 측정되지 못하여 비어있는 데이터를 ‘결측치’라고 합니다. pandas 에서는 결측치를 np.nan
으로 나타냅니다. pandas 에서는 결측치를 기본적으로 연산에서 제외시키고 있습니다. Working with missing data 항목을 참고하기 바랍니다.
재인덱싱(reindex)은 해당 축에 대하여 인덱스를 변경/추가/삭제를 하게됩니다. 이는 복사된 데이터프레임을 반환합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ['E'])
df1
# A B C D F E
# 2013-01-01 0.000000 0.000000 -1.509059 5 NaN NaN
# 2013-01-02 1.212112 -0.173215 0.119209 5 1.0 NaN
# 2013-01-03 -0.861849 -2.104569 -0.494929 5 2.0 NaN
# 2013-01-04 0.721555 -0.706771 -1.039575 5 3.0 NaN
df1.loc[dates[0]:dates[1], 'E'] = 1
df1
# A B C D F E
# 2013-01-01 0.000000 0.000000 -1.509059 5 NaN 1.0
# 2013-01-02 1.212112 -0.173215 0.119209 5 1.0 1.0
# 2013-01-03 -0.861849 -2.104569 -0.494929 5 2.0 NaN
# 2013-01-04 0.721555 -0.706771 -1.039575 5 3.0 NaN
결측치가 하나라도 존재하는 행들을 버리고 싶을 때는 dropna()
메소드를 이용합니다. 결과적으로 결측치가 하나도 없는 두번째 행만 남고 나머지 행들은 사라졌습니다.
1
2
3
df1.dropna(how='any')
# A B C D F E
# 2013-01-02 1.212112 -0.173215 0.119209 5 1.0 1.0
만약 결측치가 있는 부분을 다른 값으로 채우고 싶다면 fillna()
메소드를 이용하세요.
1
2
3
4
5
6
df1.fillna(value=5)
# A B C D F E
# 2013-01-01 0.000000 0.000000 -1.509059 5 5.0 1.0
# 2013-01-02 1.212112 -0.173215 0.119209 5 1.0 1.0
# 2013-01-03 -0.861849 -2.104569 -0.494929 5 2.0 5.0
# 2013-01-04 0.721555 -0.706771 -1.039575 5 3.0 5.0
그리고 해당 값이 결측치인지 아닌지의 여부를 알고싶다면 isna()
메소드를 이용하면 됩니다. 결측치이면 True, 값이 있다면 False 로 나타납니다.
1
2
3
4
5
6
pd.isna(df1)
# A B C D F E
# 2013-01-01 False False False False True False
# 2013-01-02 False False False False False False
# 2013-01-03 False False False False False True
# 2013-01-04 False False False False False True
5. 연산 (Operations)
5.1 통계적 지표들 (Stats)
평균 구하기. 일반적으로 결측치는 제외하고 연산을 합니다.
1
2
3
4
5
6
7
df.mean()
# A -0.004474
# B -0.383981
# C -0.687758
# D 5.000000
# F 3.000000
# dtype: float64
다른 축에 대해서 평균 구하기. mean()
함수의 인자로 1을 주게 되면 컬럼이 아닌 인덱스를 기준으로 연산을 합니다.
1
2
3
4
5
6
7
8
df.mean(1)
# 2013-01-01 0.872735
# 2013-01-02 1.431621
# 2013-01-03 0.707731
# 2013-01-04 1.395042
# 2013-01-05 1.883656
# 2013-01-06 1.592306
# Freq: D, dtype: float64
서로 차원이 다른 두 오브젝트 간의 연산의 예제입니다. pandas 는 맞추어야 할 축만 지정해 준다면 자동으로 해당 축을 기준으로 맞추어 연산을 수행합니다. 아래는 인덱스를 기준으로 연산이 수행되고 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
s = pd.Series([1, 3, 5, np.nan, 6, 8], index=dates).shift(2)
# 2013-01-01 NaN
# 2013-01-02 NaN
# 2013-01-03 1.0
# 2013-01-04 3.0
# 2013-01-05 5.0
# 2013-01-06 NaN
# Freq: D, dtype: float64
## df - s 연산. s의 0,1,5 인덱스에 값이 없어 NaN 으로 결과가 나옴
df.sub(s, axis='index')
# A B C D F
# 2013-01-01 NaN NaN NaN NaN NaN
# 2013-01-02 NaN NaN NaN NaN NaN
# 2013-01-03 -1.861849 -3.104569 -1.494929 4.0 1.0
# 2013-01-04 -2.278445 -3.706771 -4.039575 2.0 0.0
# 2013-01-05 -5.424972 -4.432980 -4.723768 0.0 -1.0
# 2013-01-06 NaN NaN NaN NaN NaN
5.2 함수 적용하기 (Apply)
데이터프레임에 함수를 적용할 수 있습니다. 기존에 존재하는 함수를 사용하거나 사용자가 정의한 람다 함수를 사용할 수도 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df.apply(np.cumsum) # cumsum => 컬럼기준 누적 합계
# A B C D F
# 2013-01-01 0.000000 0.000000 -1.509059 5 NaN
# 2013-01-02 1.212112 -0.173215 -1.389850 10 1.0
# 2013-01-03 0.350263 -2.277784 -1.884779 15 3.0
# 2013-01-04 1.071818 -2.984555 -2.924354 20 6.0
# 2013-01-05 0.646846 -2.417535 -2.648122 25 10.0
# 2013-01-06 -0.026844 -2.303886 -4.126549 30 15.0
df.apply(lambda x: x.max() - x.min())
# A 2.073961
# B 2.671590
# C 1.785291
# D 0.000000
# F 4.000000
# dtype: float64
5.3 히스토그램 구하기 (Histogramming)
데이터의 값들의 빈도를 조사하여 히스토그램을 만들 수 있습니다. Histogramming and Discretization에서 더 많은 정보를 찾아보세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
s = pd.Series(np.random.randint(0, 7, size=10))
# 0 4
# 1 2
# 2 1
# 3 2
# 4 6
# 5 4
# 6 4
# 7 6
# 8 4
# 9 4
# dtype: int64
s.value_counts()
# 4 5 # 4의 갯수 5개
# 6 2 # 6의 갯수 2개
# 2 2 # 2의 갯수 2개
# 1 1 # 1의 갯수 1개
# dtype: int64
5.4 문자열 관련 메소드들 (String methods)
아래의 예제처럼 시리즈(Series)는 배열의 각 요소에 쉽게 적용이 가능하도록 str
이라는 속성에 문자열을 처리할 수 있는 여러가지의 메소드들을 갖추고 있습니다. 문자열 내에서의 패턴을 찾기 위한 작업들은 일반적으로 기본적으로 정규표현식을 사용하는 것에 유의합니다. (몇몇의 경우에는 항상 정규표현식을 사용합니다.) 더 많은 정보는 Vectorized String Methods 에서 찾아보세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
s.str.lower()
# 0 a
# 1 b
# 2 c
# 3 aaba
# 4 baca
# 5 NaN
# 6 caba
# 7 dog
# 8 cat
# dtype: object
6. 합치기 (Merging)
다양한 정보를 담은 자료들이 있을 때 이들을 합쳐 새로운 자료를 만들어야 할 때가 있습니다.
concat
: 같은 형태의 자료들을 이어 하나로 만들어준다.merge
: 다른 형태의 자료들을 한 컬럼을 기준으로 합친다.append
: 기존 데이터 프레임에 하나의 행을 추가한다.
6.1 Concat
아래는 concat 을 이용하여 pandas 오브젝트들을 일렬로 잇는 예제입니다.
pandas 에 있는 concat 메소드를 이용하여 원래대로 다시 합칠 수 있다는 것을 보여줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 임의의 수를 담고있는 n x 4 형태의 데이터 프레임 생성
df1 = pd.DataFrame(np.random.randn(3, 4))
df2 = pd.DataFrame(np.random.randn(4, 4))
df3 = pd.DataFrame(np.random.randn(3, 4))
df1
# 0 1 2 3
# 0 -0.548702 1.467327 -1.015962 -0.483075
# 1 1.637550 -1.217659 -0.291519 -1.745505
# 2 -0.263952 0.991460 -0.919069 0.266046
df2
# 0 1 2 3
# 0 -0.709661 1.669052 1.037882 -1.705775
# 1 -0.919854 -0.042379 1.247642 -0.009920
# 2 0.290213 0.495767 0.362949 1.548106
# 3 -1.131345 -0.089329 0.337863 -0.945867
df3
# 0 1 2 3
# 0 -0.932132 1.956030 0.017587 -0.016692
# 1 -0.575247 0.254161 -1.143704 0.215897
# 2 1.193555 -0.077118 -0.408530 -0.862495
# 합치기
pd.concat([df1, df2, df3])
# 0 1 2 3
# 0 -0.548702 1.467327 -1.015962 -0.483075
# 1 1.637550 -1.217659 -0.291519 -1.745505
# 2 -0.263952 0.991460 -0.919069 0.266046
# 3 -0.709661 1.669052 1.037882 -1.705775
# 4 -0.919854 -0.042379 1.247642 -0.009920
# 5 0.290213 0.495767 0.362949 1.548106
# 6 -1.131345 -0.089329 0.337863 -0.945867
# 7 -0.932132 1.956030 0.017587 -0.016692
# 8 -0.575247 0.254161 -1.143704 0.215897
# 9 1.193555 -0.077118 -0.408530 -0.862495
6.2 Merge
데이터베이스에서 사용하는 SQL 의 join 기능입니다. merge 메소드를 통해 이루어집니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
left = pd.DataFrame({'key': ['foo', 'foo'], 'lval': [1, 2]})
# key lval
# 0 foo 1
# 1 foo 2
right = pd.DataFrame({'key': ['foo', 'foo'], 'rval': [4, 5]})
# key rval
# 0 foo 4
# 1 foo 5
merged = pd.merge(left, right, on='key')
# key lval rval
# 0 foo 1 4
# 1 foo 1 5
# 2 foo 2 4
# 3 foo 2 5
또 다른 예제로는 이런 것이 있을 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
left = pd.DataFrame({'key': ['foo', 'bar'], 'lval': [1, 2]})
# key lval
# 0 foo 1
# 1 bar 2
right = pd.DataFrame({'key': ['foo', 'bar'], 'rval': [4, 5]})
# key rval
# 0 foo 4
# 1 bar 5
merged = pd.merge(left, right, on='key')
# key lval rval
# 0 foo 1 4
# 1 bar 2 5
위의 예제와 아래의 예제는 key 값을 중복으로 가질 때와 그렇지 않을 때의 merge 메소드의 작동방식을 설명해줍니다. 위의 예제에서는 key 값으로 모두 ‘foo’ 라는 문자열을 가지고 있고, 아래의 예제에서는 key 값으로 ‘foo’ 또는 ‘bar’ 를 가지고 있습니다. 보통 key 로 사용하는 값은 중복일 경우가 잘 없지만, 만약에 중복된 값이 있을 때에는 모든 경우의 수를 만들어내는 작동방식을 보여주고 있습니다.
6.3 Append
데이터프레임의 맨 뒤에 행을 추가합니다. 아래의 예제는 4번째 행을 기존의 데이터프레임의 맨 뒤에 한번 더 추가하는 방법을 보여주고 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
df = pd.DataFrame(np.random.randn(8, 4), columns=['A', 'B', 'C', 'D'])
# A B C D
# 0 1.346061 1.511763 1.627081 -0.990582
# 1 -0.441652 1.211526 0.268520 0.024580
# 2 -1.577585 0.396823 -0.105381 -0.532532
# 3 1.453749 1.208843 -0.080952 -0.264610
# 4 -0.727965 -0.589346 0.339969 -0.693205
# 5 -0.339355 0.593616 0.884345 1.591431
# 6 0.141809 0.220390 0.435589 0.192451
# 7 -0.096701 0.803351 1.715071 -0.708758
s = df.iloc[3]
df.append(s, ignore_index=True)
# A B C D
# 0 1.346061 1.511763 1.627081 -0.990582
# 1 -0.441652 1.211526 0.268520 0.024580
# 2 -1.577585 0.396823 -0.105381 -0.532532
# 3 1.453749 1.208843 -0.080952 -0.264610 # 3번 행
# 4 -0.727965 -0.589346 0.339969 -0.693205
# 5 -0.339355 0.593616 0.884345 1.591431
# 6 0.141809 0.220390 0.435589 0.192451
# 7 -0.096701 0.803351 1.715071 -0.708758
# 8 1.453749 1.208843 -0.080952 -0.264610 # 추가된 3번 행
7. 묶기 (Grouping)
‘그룹화 (group by)’는 다음과 같은 처리를 하는 과정들을 지칭합니다.
- 어떠한 기준을 바탕으로 데이터를 나누는 일 (splitting)
- 각 그룹에 어떤 함수를 독립적으로 적용시키는 일 (applying)
- 적용되어 나온 결과들을 통합하는 일 (combining)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar',
'foo', 'bar', 'foo', 'foo'],
'B': ['one', 'one', 'two', 'three',
'two', 'two', 'one', 'three'],
'C': np.random.randn(8),
'D': np.random.randn(8)})
# A B C D
# 0 foo one -1.202872 -0.055224
# 1 bar one -1.814470 2.395985
# 2 foo two 1.018601 1.552825
# 3 bar three -0.595447 0.166599
# 4 foo two 1.395433 0.047609
# 5 bar two -0.392670 -0.136473
# 6 foo one 0.007207 -0.561757
# 7 foo three 1.928123 -1.623033
A 컬럼의 값을 기준으로 합계를 구하는 sum()
함수를 적용해 봅시다.
1
2
3
4
5
6
7
df.groupby('A').sum()
# C D
# A
# bar -2.802588 2.42611
# foo 3.146492 -0.63958
# 기존의 0~7의 인덱스가 사라지고 A 컬럼이 인덱스가 됨에 유의
그룹을 묶을 때 여러 컬럼을 기준으로 이용할 수도 있습니다.
1
2
3
4
5
6
7
8
9
10
11
df.groupby(['A', 'B']).sum()
# C D
# A B
# bar one -1.814470 2.395985
# three -0.595447 0.166599
# two -0.392670 -0.136473
# foo one -1.195665 -0.616981
# three 1.928123 -1.623033
# two 2.414034 1.600434
# A, B가 계층구조의 인덱스가 됨을 확인할 수 있다.
8. 변형하기 (Reshaping)
8.1 Pivot Tables
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
df = pd.DataFrame({'A': ['one', 'one', 'two', 'three'] * 3,
'B': ['A', 'B', 'C'] * 4,
'C': ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2,
'D': np.random.randn(12),
'E': np.random.randn(12)})
df
# A B C D E
# 0 one A foo 1.418757 -0.179666
# 1 one B foo -1.879024 1.291836
# 2 two C foo 0.536826 -0.009614
# 3 three A bar 1.006160 0.392149
# 4 one B bar -0.029716 0.264599
# 5 one C bar -1.146178 -0.057409
# 6 two A foo 0.100900 -1.425638
# 7 three B foo -1.035018 1.024098
# 8 one C foo 0.314665 -0.106062
# 9 one A bar -0.773723 1.824375
# 10 two B bar -1.170653 0.595974
# 11 three C bar 0.648740 1.167115
위와 같은 데이터 프레임의 형식을 피벗 테이블 기능을 이용하여 아래와 같이 쉽게 변형할 수 있습니다. 찾지 못한 값은 NaN 으로 표시됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'])
# C bar foo
# A B
# one A -0.773723 1.418757 # D의 값 중 A=one, B=A, C=bar 인 값 | D의 값 중 A=one, B=A, C=foo 인 값
# B -0.029716 -1.879024
# C -1.146178 0.314665
# three A 1.006160 NaN
# B NaN -1.035018
# C 0.648740 NaN
# two A NaN 0.100900
# B -1.170653 NaN
# C NaN 0.536826
9. 그래프로 표현하기 (Plotting)
다음과 같은 데이터가 있을 때, 그래프 그리기는 다음과 같이 plot()
메소드 하나만으로 완성할 수 있다.
1
2
df = pd.DataFrame({'lab':['A', 'B', 'C'], 'val':[10, 30, 20]})
df.plot.bar()
plot()
메소드는 다양한 형태의 그래프를 그릴 수 있도록 도와주지만, seaborn과 같이 더 다양한 기능의 시각화 라이브러리들이 있고 사용도 쉬우니 차차 다른 라이브러리들을 익혀볼 것을 추천한다.
10. 데이터 입/출력 (Getting Data In/Out)
데이터를 다양한 형식의 파일에 읽고 쓰는 방법을 알아봅니다.
10.1 CSV
데이터 프레임을 CSV 형식으로 저장하기.
1
2
df.to_csv('foo1.csv')
df.to_csv('foo2.csv', index=False) # 추천! csv 방식의 저장은 자동으로 인덱스를 추가하여 저장하기 때문
CSV 형식으로 된 파일로부터 데이터 프레임의 형식으로 읽어오기. CSV 형식으로 부터 저장할 때 주의할 점은 새로운 인덱스가 추가된다는 것입니다. 따라서 저장할 당시에는 3개였던 열의 개수가 4개가 되어있는 것을 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
pd.read_csv('foo1.csv')
# Unnamed: 0 lab val
# 0 0 A 10
# 1 1 B 30
# 2 2 C 20
# index=False로 저장한 파일
pd.read_csv('foo2.csv')
# lab val
# 0 A 10
# 1 B 30
# 2 C 20
10.2 HDF5
HDF5 형식으로 저장하기.
1
df.to_hdf('foo.h5', 'df')
HDF5 형식으로부터 읽어오기.
1
2
3
4
5
pd.read_hdf('foo.h5', 'df')
# lab val
# 0 A 10
# 1 B 30
# 2 C 20
10.3 Excel
데이터 프레임을 엑셀 파일로 저장하기.
1
df.to_excel('foo.xlsx', sheet_name='Sheet1', index=False)
엑셀 파일로부터 데이터 프레임 읽어오기.
1
2
3
4
5
pd.read_excel('foo.xlsx', 'Sheet1', index_col=None, na_values=['NA'])
# lab val
# 0 A 10
# 1 B 30
# 2 C 20