Python for data analysis

Chapter 5 pandas 시작하기

표독's 2016. 3. 25. 16:54

Chapter 5 pandas 시작하기



pandas는 앞으로 가장 자주 살펴볼 라이브러리다. pandas는 고수준의 자료 구조와 파이썬을 통한 빠르고 쉬운 데이터 분석 도구를 포함한다.


pandas는 NumPy 기반에서 개발되어 NumPy를 사용하는 애플리케이션에서 쉽게 사용할 수 있다.


-자동적으로 혹은 명시적으로 축의 이름에 따라 데이터를 정렬할 수 있는 자료 구조, 잘못 정렬된 데이터에 의한 일반적인 오류를 예방하고 다양한 소스에서 가져온 다양한 방식으로 색인되어 있는 데이터를 다룰 수 있는 기능

-통합된 시계열 기능

-시계열 데이터와 비시계열 데이터를 함께 다룰 수 있는 통합 자료 구조

-산술연산과 한 축의 모든 값을 더하는 등의 데이터 축약연산은 축의 이름 같은 메타데이터로 전달될 수 있어야 한다.

-누락된 데이터를 유연하게 처리할 수 있는 기능

-SQL같은 일반 데이터베이스처럼 데이터를 합치고 관계연산을 수행하는 기능


from pandas import Series, DataFrame
import pandas as pd
import numpy as np

pd는 pandas를 지칭하며 Series와 DataFrame은 로컬 네임스페리스로 import하는 것이 편해서 이렇게 사용함.


5.1 pandas 자료 구조 소개


pandas에 대해서 알아보려면 Series와 DataFrame, 이 두 가지 자료 구조에 익숙해져야 한다.


5.1.1 Series


Series는 일련의 객체를 담을 수 있는 1차원 배열 같은 자료 구조다. (어떤 NumPy 자료형이라도 담을 수 있다.) 그리고 색인이라고 하는 배열의 데이터에 연관된 이름을 가지고 있다. 가장 간단한 Series객체는 배열 데이터로부터 생성할 수 있다.

In[6]: obj = Series([4, 7, -5, 3])
In[7]: obj
Out[7]:
0 4
1 7
2 -5
3 3
dtype: int64

Series 객체의 문자열 표현은 왼쪽에 색인을 보여주고 오른쪽에 해당 색인의 값을 보여준다.

데이터 색인 디폴트 : 기본 색인인 정수 0에서 N-1까지의 숫자가 표시된다.


Series의 배열과 색인 객체는 각각 values와 index 속성을 통해 얻을 수 있다.


In[8]: obj.values
Out[8]: array([ 4, 7, -5, 3], dtype=int64)
In[9]: obj.index
Out[9]: Int64Index([0, 1, 2, 3], dtype='int64')

각각의 데이터를 지칭하는 색인을 지정해 Series 객체를 생성해야 할 때는 다음처럼 생성한다..


In[10]: obj2 = Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
In[11]: obj2
Out[11]:
d 4
b 7
a -5
c 3
dtype: int64
In[12]: obj2.index
Out[12]: Index([u'd', u'b', u'a', u'c'], dtype='object')

배열에서 값을 선택하거나 대입할 때는 색인을 이용해서 접근한다.


In[13]: obj2['a']
Out[13]: -5
In[14]: obj2['d'] = 6
In[15]: obj2[['c', 'a', 'd']]
Out[15]:
c 3
a -5
d 6
dtype: int64

불리언 배열을 사용해서 값을 걸러내거나 산술 곱셈을 수행하거나 또는 수학 함수를 적용하는 등 NumPy 배열연산을 수행해도 색인- 값 연결은 유지된다.


In[17]: obj2[obj2>0]
Out[17]:
d 6
b 7
c 3
dtype: int64
In[18]: obj2 * 2
Out[18]:
d 12
b 14
a -10
c 6
dtype: int64
In[19]: np.exp(obj2)
Out[19]:
d 403.428793
b 1096.633158
a 0.006738
c 20.085537
dtype: float64


Series를 이해하는 다른 방법은 고정 길이의 정렬된 사전형이라고 이해하는 것이다. Series는 색인 값에 데이터 값을 매핑하고 있으므로 파이썬의 사전형과 비슷하다. Series 객체는 파이썬의 사전형을 인자로 받아야 하는 많은 함수에서 사전형을 대체하여 사용할 수 있다.


In[20]: 'b' in obj2
Out[20]: True
In[21]: 'e' in obj2
Out[21]: False

파이썬 사전형에 데이터를 저장해야 한다면 파이썬 사전 객체로부터 Series 객체를 생성할 수도 있다.


In[22]: sdata = {'Ohio':35000, 'Texas': 71000, 'Oregon':16000, 'Utah':5000}
In[23]: obj3 = Series(sdata)
In[24]: obj3
Out[24]:
Ohio 35000
Oregon 16000
Texas 71000
Utah 5000
dtype: int64

 

사전 객체만 가직 Series 객체를 생성하면 생성된 Series 객체의 색인은 사전의 키 값이 순서대로 들어간다.


In[28]: states = ['California', 'Ohio', 'Oregon', 'Texas']
In[29]: obj4 = Series(sdata, index=states)
In[30]: obj4
Out[30]:
California NaN
Ohio 35000
Oregon 16000
Texas 71000
dtype: float64

이 예제를 보면 sdata에 있는 값 중 3개마 확인할 수 있는데, 이는'California'에 대한 값을 찾을 수 없기 때문이다. 이 값은 NaN (not a number)으로 표시되고 pandas에서는 누락된 값 혹은 NA값으로 취급된다. 


pandas의 isnull과 notnull 함수는 누락된 데이터를 찾을 때 사용된다.


In[31]: pd.isnull(obj4)
Out[31]:
California True
Ohio False
Oregon False
Texas False
dtype: bool
In[32]: pd.notnull(obj4)
Out[32]:
California False
Ohio True
Oregon True
Texas True
dtype: bool

이 메서드는 Series의 인스턴스 메서드이기도 하다.


In[33]: obj4.isnull()

Out[33]:
California True
Ohio False
Oregon False
Texas False
dtype: bool

누락된 데이터를 처리하는 방법은 이 장의 끝부분에서 좀 더 자세히 살펴보기로 하자. 가장 주요한 Series의 기능은 다르게 색인된 데이터에 대한 산술연산이다.


In[34]: obj3 In[35]: obj4
Out[34]: Out[35]:
Ohio 35000 California NaN
Oregon 16000 Ohio 35000
Texas 71000 Oregon 16000
Utah 5000 Texas 71000
dtype: int64 dtype: float64


In[36]: obj3 + obj4
Out[36]:
California NaN
Ohio 70000
Oregon 32000
Texas 142000
Utah NaN
dtype: float64


데이터 정렬에 대한 내용은 다른 주제를 다룰 때 살펴보겠다.

Series 객체와 Series의 색인은 모두 name 속성이 있는데, 이 속성은 pandas의 기능에서 중요한 부분을 차지하고 있다.


In[49]: obj4.name = 'population'

In[50]: obj4.index.name = 'state'
In[51]: obj4
Out[51]:
state
California NaN
Ohio 35000
Oregon 16000
Texas 71000
Name: population, dtype: float64

#Series의 색인은 대입을 통해 변경할 수 있다.

In[52]: obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']
In[53]: obj
Out[53]:
Bob 4
Steve 7
Jeff -5
Ryan 3
dtype: int64

5.1.2 DataFrame

DataFrame은 표 같은 스프레드시트 형식의 자료 구조로 여러 개의 칼럼이 있는데, 각 칼럼은 서로 다른 종류의 값(숫자, 문자열, 불리언 등)을 담을 수 있다. DataFrame은 로우와 칼럼에 대한 색인이 있는데, 이 DataFrame은 색인의 모양이 같은 Series 객체를 담고 있는 파이썬 사전으로 생각하면 편하다. R의 data.frame 같은 다른 DataFrame과 비슷한 자료 구조와 비교했을 때, DataFrame에서의 로우 연산과 칼럼 연산은 거의 대칭적으로 취급된다. 내부적으로 데이터는 리스트나 사전 또는 1차원 배열을 담고 있는 다른 컬렉션이 아니라 하나 이상의 2차원 배열에 저장된다.


DataFrame 객체는 다양한 방법으로 생성할 수 있지만 가장 흔하게 사용되는 방법은 같은 길이의 리스트에 담긴 사전을 이용하거나 NumPy 배열을 이용하는 방법이다.


In[57]: data = {'state' : ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],

... 'year' : [2000, 2001, 2002, 2001, 2002],

... 'pop' : [1.5, 1.7, 3.6, 2.4, 2.9]}

...
In[60]: frame = DataFrame(data)

In[62]: frame
Out[60]:
pop state year
0 1.5 Ohio 2000
1 1.7 Ohio 2001
2 3.6 Ohio 2002
3 2.4 Nevada 2001
4 2.9 Nevada 2002

만들어진 DataFrame의 색인은 Series와 같은 방식으로 자동으로 대입되며 칼럼은 정렬되어 저장된다.


원하는 순서대로 columns를 지정하면 원하는 순서를 가진 DataFrame 객체가 생성된다.

In[63]: DataFrame(data, columns=['year', 'state', 'pop'])
Out[61]:
year state pop
0 2000 Ohio 1.5
1 2001 Ohio 1.7
2 2002 Ohio 3.6
3 2001 Nevada 2.4
4 2002 Nevada 2.9


Series와 마찬가지로 data에 없는 값을 넘기면 NA 값이 저장된다.


In[64]: frame2 = DataFrame(data, columns=['year', 'state', 'pop', 'debt'], index=['one','two','three','four','five'])
In[65]: frame2
Out[63]:
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 NaN
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 NaN
five 2002 Nevada 2.9 NaN
In[66]: frame2.columns
Out[64]: Index([u'year', u'state', u'pop', u'debt'], dtype='object')

DataFrame의 칼럼은 Series처럼 사전 형식의 표기법으로 접근하거나 속성 형식으로 접근할 수 있다.

In[67]: frame2['state']
Out[65]:
one Ohio
two Ohio
three Ohio
four Nevada
five Nevada
Name: state, dtype: object
In[68]: frame2.year
Out[66]:
one 2000
two 2001
three 2002
four 2001
five 2002
Name: year, dtype: int64


반환된 Series 객체가 DataFrame 같은 색인을 가지면 알맞은 값으로 name 속성이 채워진다.

로우는 위치나 ix 같은 몇 가지 메서드를 통해 접근할 수 있다.


In[69]: frame2.ix['three']
Out[67]:
year 2002
state Ohio
pop 3.6
debt NaN
Name: three, dtype: object

칼럼은 대입이 가능하다. 예를 들면 현재 비어있는 'debt' 칼럼에 스칼라 값이나 배열의 값을 대입할 수 있다. 

In[70]: frame2['debt'] = 16.5
In[71]: frame2
Out[69]:
year state pop debt
one 2000 Ohio 1.5 16.5
two 2001 Ohio 1.7 16.5
three 2002 Ohio 3.6 16.5
four 2001 Nevada 2.4 16.5
five 2002 Nevada 2.9 16.5
In[72]: frame2['debt'] = np.arange(5.)
In[73]: frame2
Out[71]:
year state pop debt
one 2000 Ohio 1.5 0
two 2001 Ohio 1.7 1
three 2002 Ohio 3.6 2
four 2001 Nevada 2.4 3
five 2002 Nevada 2.9 4

리스트나 배열을 칼럼에 대입할 때는 대입하려는 값의 길이가 DataFrame의 크기와 같아야 한다. Series를 대입하면 DataFrame의 색인에 따라 값이 대입되며 없는 색인에는 값이 대입되지 않는다.


In[74]: val = Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
In[75]: frame2['debt'] = val
In[76]: frame2
Out[74]:
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 -1.2
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 -1.5
five 2002 Nevada 2.9 -1.7

없는 칼럼을 대입하면 새로운 칼럼이 생성된다. 파이썬 사전형에서와 마찬가지로 del예약어를 사용해서 칼럼을 삭제할 수 있다.

In[77]: frame2['eastern']= frame2.state == 'Ohio'
In[78]: frame2
Out[76]:
year state pop debt eastern
one 2000 Ohio 1.5 NaN True
two 2001 Ohio 1.7 -1.2 True
three 2002 Ohio 3.6 NaN True
four 2001 Nevada 2.4 -1.5 False
five 2002 Nevada 2.9 -1.7 False
In[79]: del frame2['eastern']
In[80]: frame2.columns
Out[78]: Index([u'year', u'state', u'pop', u'debt'], dtype='object')

**DataFrame의 색인을 이용해서 생성된 칼럼은 내부 데이터에 대한 뷰(view)이며 복사가 이루어지지 않는다. 따라서 이렇게 얻은 Series 객체에 대한 변경은 실제 DataFrame에 반영된다. 복사본이 필요할 때는 Series의 copy 메서드를 이용하자.


또한 중첩된 사전을 이용하여 데이터를 생성할 수 있는데, 다음과 같은 중첩한 사전이 있다면.

In[81]: pop = {'Nevada' : {2001: 2.4, 2002: 2.9},
'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}

바깥에 있는 사전의 키 값이 칼럼이 되고 안에 있는 키는 로우가 된다.


In[81]: pop = {'Nevada' : {2001: 2.4, 2002: 2.9},
'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
In[82]: frame3 = DataFrame(pop)
In[83]: frame3
Out[81]:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6


NumPy에서와 마찬가지로 결과 값의 순서를 뒤집을 수 있다.

In[84]: frame3.T
Out[82]:
2000 2001 2002
Nevada NaN 2.4 2.9
Ohio 1.5 1.7 3.6

중첩된 사전을 이용해서 DataFrame을 생성할 때 안쪽에 있는 사전 값은 키 값별로 조합되어 결과의 색인이 되지만 색인을 직접 지정한다면 지정된 색인으로 DataFrame을 생성한다.


In[85]: DataFrame(pop, index=[2001, 2002, 2003])
Out[83]:
Nevada Ohio
2001 2.4 1.7
2002 2.9 3.6
2003 NaN NaN

Series 객체를 담고 있는 사전 데이터도 같은 방식으로 취급된다.


In[86]: pdata = {'ohio' : frame3['Ohio'][:-1],
'Nevada': frame3['Nevada'][:2]}
In[87]: DataFrame(pdata)
Out[85]:
Nevada ohio
2000 NaN 1.5
2001 2.4 1.7

DataFrame 생성자에 넘길 수 있는 자료형의 목록은 [표 5-1]을 참고하자.


In[88]: frame3.index.name = 'year'; frame3.columns.name = 'state'
In[89]: frame3
Out[87]:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6


Series와 유사하게 values 속성은 DataFrame에 저장된 데이터를 2차원 배열로 반환한다.

In[90]: frame3.values
Out[88]:
array([[ nan, 1.5],
[ 2.4, 1.7],
[ 2.9, 3.6]])


DataFrame의 칼럼에 서로 다른 dtype이 있다면 모든 칼럼을 수용하기 위해 그 칼럼 배열의 dtype이 선택된다.


In[91]: frame2.values
Out[89]:
array([[2000L, 'Ohio', 1.5, nan],
[2001L, 'Ohio', 1.7, -1.2],
[2002L, 'Ohio', 3.6, nan],
[2001L, 'Nevada', 2.4, -1.5],
[2002L, 'Nevada', 2.9, -1.7]], dtype=object)


5.1.3 색인 개체

pandas의 색인 객체는 표 형식의 데이터에서 각 로우와 칼럼에 대한 이름과 다른 메타데이터(축의 이름등)를 저장하는 객체다. Series나 DataFrame 객체를 생성할 때 사용되는 배열이나 혹은 다른 순차적인 이름은 내부적으로 색인으로 변환된다.


In[92]: obj = Series(range(3), index=['a', 'b', 'c'])
In[93]: index = obj.index
In[94]: index
Out[92]: Index([u'a', u'b', u'c'], dtype='object')
In[95]: index[1:]
Out[93]: Index([u'b', u'c'], dtype='object')

색인 객체는 변경할 수 없다.

In[96]: index[1] = 'd'


Traceback (most recent call last):

  File "C:\Users\Jusung\Anaconda2\lib\site-packages\IPython\core\interactiveshell.py", line 3066, in run_code

    exec(code_obj, self.user_global_ns, self.user_ns)

  File "<ipython-input-94-676fdeb26a68>", line 1, in <module>

    index[1] = 'd'

  File "C:\Users\Jusung\Anaconda2\lib\site-packages\pandas\core\index.py", line 1130, in __setitem__

    raise TypeError("Index does not support mutable operations")

TypeError: Index does not support mutable operations


색인 객체는 변경될 수 없기에 자료 구조 사이에서 안전하게 공유될 수 있다.


In[101]: index = pd.Index(np.arange(3))
In[102]: index
Out[100]: Int64Index([0, 1, 2], dtype='int64')
In[103]: obj2= Series([1.5, -2.5, 0], index=index)
In[104]: obj2.index is index
Out[102]: True


[표 5-2]에 pandas에서 사용한ㄴ 내장 색인 클래스가 정리되어 있다. 특수한 목적으로 축을 색인하는 기능을 개발하기 위해 Index 클래스의 서브클래스를 만들 수 있다.


또한 배열과 유사하게 Index 객체도 고정 크기로 동작한다.


In[107]: frame3
Out[105]:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
In[108]: 'Ohio' in frame3.columns
Out[106]: True
In[109]: 2003 in frame3.index
Out[107]: False

각각의 색인은 담고 있는 데이터에 대한 정보를 취급하는 여러 가지 메서드와 속성을 가지고 있다. [표 5-3]을 참고하자.


5.2 핵심 기능

이 절에서는 Series나 DataFrame에 저장된 데이터를 다루는 기본 방법을 설명한다. 앞으로 pandas를 이용한 데이터 분석과 조작에 관한 좀 더 자세한 내용을 살펴볼 것이다. 이 책은 pandas 라이브러리에 대한 완전한 설명은 자제하고 중요한 기능에만 초점을 맞추고 있다. 


5.2.1 재색인

pandas객체의 기막힌 기능 중 하나인 reindex는 새로운 색인에 맞도록 객체를 새로 생성하는 기능이다.

In[110]: obj = Series([4.5, 7.2, -5.3, 3.6], index= ['d', 'b', 'a', 'c'])
In[111]: obj
Out[109]:
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64

이 Series 객체에 대해 reindex를 호충하면 데이터를 새로운 색인에 맞게 재배열하고, 없는 값이 있다면 비어있는 값을 새로 추가한다.


In[112]: obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
In[113]: obj2
Out[111]:
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
In[114]: obj.reindex(['a','b','c','d','e'], fill_value=0.0)
Out[112]:
a -5.3
b 7.2
c 3.6
d 4.5
e 0.0
dtype: float64

시계열 같은 순차적인 데이터를 재색인할 때 값을 보간하거나 채워 넣어야 할 경우가 있다. 이런 경우 method 옵션을 이용해서 해결할 수 있으며, ffill 메서드를 이용하면 앞의 값으로 누락된 값을 채워넣을 수 있다. 


In[115]: obj3 = Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
In[116]: obj3.reindex(range(6), method='ffill')
Out[114]:
0 blue
1 blue
2 purple
3 purple
4 yellow
5 yellow
dtype: object

[표 5-4]에 사용 가능한 method 옵션을 소개했다. 현재 누락된 값을 앞이나 뒤에 채워 넣는 것보다 좀 더 세련된 방법으로 보간할 수 있는 방법을 추후 추가할 필요가 있다.


DataFrame에 대한 reindex는 (로우) 색인, 칼럼 또는 둘 다 변경이 가능하다. 그냥 순서만 전달하면 로우가 재색인된다.


In[117]: frame = DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'b', 'd'], columns= ['Ohio', 'Texas', 'California'])
In[118]: frame
Out[116]:
Ohio Texas California
a 0 1 2
b 3 4 5
d 6 7 8
In[119]: frame2 = frame.reindex(['a', 'b', 'c', 'd'])
In[120]: frame2
Out[118]:
Ohio Texas California
a 0 1 2
b 3 4 5
c NaN NaN NaN
d 6 7 8

열은 columns 예약어를 사용해서 재색인할 수 있다.

In[121]: states = ['Texas', 'Utah', 'California']
In[122]: frame.reindex(columns=states)
Out[120]:
Texas Utah California
a 1 NaN 2
b 4 NaN 5
d 7 NaN 8

로우와 칼럼은 모두 한 번에 재색인할 수 있지만 보간은 로우에 대해서만 이루어진다 (axis 0).


In[123]: frame.reindex(index=['a', 'b', 'c', 'd'], method='ffill', columns=states)
Out[121]:
Texas Utah California
a 1 NaN 2
b 4 NaN 5
c 4 NaN 5
d 7 NaN 8

재색인은 ix를 이용해서 라벨로 색인하면 좀 더 간결하게 할 수 있다.


In[124]: frame.ix[['a', 'b', 'c', 'd'], states]
Out[122]:
Texas Utah California
a 1 NaN 2
b 4 NaN 5
c NaN NaN NaN
d 7 NaN 8


5.2.2 하나의 로우 또는 칼럼 삭제하기


색인 배열 또는 삭제하려는 로우나 칼럼이 제외된 리스트를 이미 가지고 있다면 로우나 칼럼을 쉽게 삭제할 수 있는데, 이 방법은 데이터의 모양을 변경하는 작업이 필요하다.

drop 메서드를 사용하면 선택한 값이 삭제된 새로운 개체를 얻을 수 있다.


In[125]: obj = Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
In[126]: new_obj = obj.drop('c')
In[127]: new_obj
Out[125]:
a 0
b 1
d 3
e 4
dtype: float64

In[128]: obj.drop(['d', 'c'])
Out[126]:
a 0
b 1
e 4
dtype: float64

DataFrame에서는 로우와 칼럼 모두에서 값을 삭제할 수 있다.

In[130]: data.drop(['Colorado', 'Ohio'])
Out[128]:
one two three four
Utah 8 9 10 11
New York 12 13 14 15
In[131]: data.drop('two', axis=1)
Out[129]:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
In[132]: data.drop(['two', 'four'], axis=1)
Out[130]:
one three
Ohio 0 2
Colorado 4 6
Utah 8 10
New York 12 14


5.2.3 색인하기, 선택하기, 거르기


Series의 색인 (obj[...])은 NumPy 배열의 색인과 유사하게 동작하는데, Series의 색인은 정수가 아니어도 된다는 점이 다르다.  


라벨 이름으로 슬라이싱하는 것은 시작점과 끝점을 포함한다는 점이 일반 파이선에서 슬라이싱과 다른 점이다.


In[136]: obj = Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
In[137]: obj['b':'c']
Out[135]:
b 1
c 2
dtype: float64

슬라이싱 문법으로 선택된 영역에 값을 대입하는 것은 예상한 대로 동작한다.


In[138]: obj['b':'c'] = 5
In[139]: obj
Out[137]:
a 0
b 5
c 5
d 3
dtype: float64

앞에서 확인한대로 색인으로 DataFrame에서 칼럼의 값을 하나 이상 가져올 수 있다.


In[140]: data = DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns = ['one', 'two', 'three', 'four'])
In[141]: data
Out[139]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15

In[142]: data['two']
Out[140]:
Ohio 1
Colorado 5
Utah 9
New York 13
Name: two, dtype: int32
In[143]: data[['three', 'one']]
Out[141]:
three one
Ohio 2 0
Colorado 6 4
Utah 10 8
New York 14 12


슬라이싱으로 로우를 선택하거나 불리언 배열로 칼럼을 선택할 수 있다.


In[144]: data[:2]
Out[142]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
In[145]: data[data['three'] > 5]
Out[143]:
one two three four
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15


이 문법에 모순이 있다고 생각할 수 있지만, 실용성에 기인한 것일 뿐이다.


또 다른 사례는 스칼라 비교를 통해 생성된 불리언 DataFrame을 사용해서 값을 선택하는 것이다.


In[146]: data < 5
Out[144]:
one two three four
Ohio True True True True
Colorado True False False False
Utah False False False False
New York False False False False
In[147]: data[data < 5] = 0
In[148]: data
Out[146]:
one two three four
Ohio 0 0 0 0
Colorado 0 5 6 7
Utah 8 9 10 11
New York 12 13 14 15

이 예제는 DataFrame을 ndarray와 문법적으로 비슷하게 보이도록 의도한 것이다.


DataFrame의 칼럼에 대해 라벨로 색인하는 방법으로, 특수한 색인 필드인 ix를 소개한다. ix는 NumPy와 비슷한 방식에 추가적으로 축의 라벨을 사용하여 DataFrame의 로우와 칼럼을 선택할 수 있도록 한다. 앞에서 언급했듯이 이 방법은 재색인을 좀 더 간단하게 할 수 있는 방법이다.

In[149]: data.ix['Colorado', ['two', 'three']]
Out[147]:
two 5
three 6
Name: Colorado, dtype: int32
In[150]: data.ix[['Colorado', 'Utah'], [3,0,1]]
Out[148]:
four one two
Colorado 7 0 5
Utah 11 8 9
In[151]: data.ix[2]
Out[149]:
one 8
two 9
three 10
four 11
Name: Utah, dtype: int32
In[152]: data.ix[:'Utah', 'two']
Out[150]:
Ohio 0
Colorado 5
Utah 9
Name: two, dtype: int32
In[153]: data.ix[data.three > 5, :3]
Out[151]:
one two three
Colorado 0 5 6
Utah 8 9 10
New York 12 13 14

지금까지 살펴봤듯이 pandas 객체에서 데이터를 선택하고 재배열하는 방법은 여러 가지가 있다. [표 5-6]에 다양한 방법을 정리해두었다. 나중에 살펴볼 계층적 색인을 이용하면 좀 더 다양한 방법을 사용할 수 있다.


5.2.4 산술연산과 데이터 정렬

pandas에서 중요한 기능은 색인이 다른 객체 간의 산술연산이다. 객체를 더할 때 짝이 맞지 않는 색인이 있다면 결과에 두 색인이 통합된다.


In[159]: s1 = Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd','e'])
In[160]: s2 = Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])
In[161]: s1 + s2
Out[159]:
a 5.2
c 1.1
d NaN
e 0.0
f NaN
g NaN
dtype: float64


서로 겹치는 색인이 없다면 데이터는 NA 값이 된다. 산술연산 시 누락된 값은 전파되며, DataFrame에서는 로우와 칼럼 모두에 적용된다.

In[162]: df1 = DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
index=['Ohio', 'Texas', 'Colorado'])
In[163]: df2 = DataFrame(np.arange(12.).reshape((4,3)), columns=list('bde'),

index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In[164]: df1 + df2
Out[162]:
b c d e
Colorado NaN NaN NaN NaN
Ohio 3 NaN 6 NaN
Oregon NaN NaN NaN NaN
Texas 9 NaN 12 NaN
Utah NaN NaN NaN NaN

산술연산 메서드에 채워 넣을 값 지정하기


서로 다른 색인을 가지는 객체 간의 산술연산에서 존재하지 않는 축의 값을 특수한 값( 0 같은)으로 지정하고 싶을 때는 다음과 같이 할 수 있다.


In[168]: df1 = DataFrame(np.arange(12.).reshape((3,4)), columns=list('abcd'))
df2 = DataFrame(np.arange(20.).reshape((4,5)), columns=list('abcde'))
In[169]: df1
Out[167]:
a b c d
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
In[170]: df2
Out[168]:
a b c d e
0 0 1 2 3 4
1 5 6 7 8 9
2 10 11 12 13 14
3 15 16 17 18 19
In[171]: df1 + df2
Out[169]:
a b c d e
0 0 2 4 6 NaN
1 9 11 13 15 NaN
2 18 20 22 24 NaN
3 NaN NaN NaN NaN NaN

이 둘을 더했을 때 겹치지 않는 부분의 값이 NA값이 된 것을 알 수 있다.


df1의 add메서드로 df2와 fill_value 값을 인자로 전달한다.


In[172]: df1.add(df2, fill_value=0)

Out[170]:
a b c d e
0 0 2 4 6 4
1 9 11 13 15 9
2 18 20 22 24 14
3 15 16 17 18 19
In[173]: df1.reindex(columns=df2.columns, fill_value=0)

Out[171]:
a b c d e
0 0 1 2 3 0
1 4 5 6 7 0
2 8 9 10 11 0


Series나 DataFrame을 재색인할 때 역시 fill_value를 지정할 수 있다.


DataFrame과 Series 간의 연산


NumPy 배열의 연산처럼 DataFrame과 Series 간의 연산도 잘 정의되어 있다. 먼저 2차원 배열과 그 배열 중 한 칼럼의 차이에 대해서 생각할 수 있는 예제를 살펴보자.


In[175]: arr
Out[173]:
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
In[176]: arr[0]
Out[174]: array([ 0., 1., 2., 3.])
In[177]: arr - arr[0]
Out[175]:
array([[ 0., 0., 0., 0.],
[ 4., 4., 4., 4.],
[ 8., 8., 8., 8.]])

이 예제는 브로드캐스팅에 대한 예제로 자세한 내용은 12장에서 살펴볼 것이다. DataFrame과 Series간의 연산은 이와 유사하다.


In[6]: frame = DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In[7]: series = frame.ix[0]
In[8]: frame
Out[8]:
b d e
Utah 0 1 2
Ohio 3 4 5
Texas 6 7 8
Oregon 9 10 11
In[9]: series
Out[9]:
b 0
d 1
e 2
Name: Utah, dtype: float64

기본적으로 DataFrame과 Series 간의 산술 연산은 Series의 색인을 DataFrame의 칼럼에 맞추고 아래 로우로 전파한다.


In[10]: frame - series
Out[10]:
b d e
Utah 0 0 0
Ohio 3 3 3
Texas 6 6 6
Oregon 9 9 9

만약 색인 값을 DataFrame의 칼럼이나 Series의 색인에서 찾을 수 없다면 그 객체는 형식을 맞추기 위해 재색인된다.


In[11]: series2 = Series(range(3), index = list('bef'))
In[12]: frame + series2
Out[12]:
b d e f
Utah 0 NaN 3 NaN
Ohio 3 NaN 6 NaN
Texas 6 NaN 9 NaN
Oregon 9 NaN 12 NaN

만약 각 로우에 대해 연산을 수행하고 싶다면 산술연산 메서드를 사용하면 된다.

In[17]: series3 = frame['d']

In[18]: frame

Out[18]:
b d e
Utah 0 1 2
Ohio 3 4 5
Texas 6 7 8
Oregon 9 10 11
In[19]: series3

Out[19]:
Utah 1
Ohio 4
Texas 7
Oregon 10
Name: d, dtype: float64
In[20]: frame.sub(series3, axis=0)

Out[20]:
b d e
Utah -1 0 1
Ohio -1 0 1
Texas -1 0 1
Oregon -1 0 1


5.2.5 함수 적용과 매핑

pandas 객체에도 NumPy의 유니버설 함수( 배열의 각 원소에 적용되는 메서드)를 적용할 수 있다.


In[21]: frame = DataFrame(np.random.randn(4,3), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
In[22]: frame
Out[22]:
b d e
Utah -1.077549 1.063706 1.300466
Ohio -0.028174 -0.805961 1.124684
Texas 0.884636 -0.271803 0.133528
Oregon -1.284909 0.136602 0.705397
In[23]: np.abs(frame) #절대값
Out[23]:
b d e
Utah 1.077549 1.063706 1.300466
Ohio 0.028174 0.805961 1.124684
Texas 0.884636 0.271803 0.133528
Oregon 1.284909 0.136602 0.705397

자주 사용되는 또 다른 연산은 각 로우나 칼럼의 1차원 배열에 함수를 적용하는 것이다.

DataFrame의 apply 메서드를 통해 수행할 수 있다.


In[24]: f = lambda x: x.max() - x.min()
In[25]: frame.apply(f)
Out[25]:
b 2.169545
d 1.869667
e 1.166938
dtype: float64
In[26]: frame.apply(f, axis=1)
Out[26]:
Utah 2.378015
Ohio 1.930645
Texas 1.156440
Oregon 1.990306
dtype: float64

배열의 합계나 평균 같은 일반적인 통계는 DataFrame의 메서드로 있으므로 apply 메서드를 사용해야만 하는 것은 아니다.


apply 메서드에 전달된 함수는 스칼라 값을 반환할 필요가 없으며, Series 또는 여러 값을 반환해도 된다.

In[27]: def f(x):
return Series([x.min(), x.max()], index=['min', 'max'])
In[28]: frame.apply(f)
Out[28]:
b d e
min -1.284909 -0.805961 0.133528
max 0.884636 1.063706 1.300466

배열의 각 원소에 적용되는 파이썬의 함수를 사용할 수도 있다. frame 객체에서 실수 값을 문자열 포맷으로 변환하고 싶다면 applymap을 이용해서 다음과 같이 해도 된다.


In[31]: format = lambda x: '%.2f' % x
In[32]: frame.applymap(format)
Out[32]:
b d e
Utah -1.08 1.06 1.30
Ohio -0.03 -0.81 1.12
Texas 0.88 -0.27 0.13
Oregon -1.28 0.14 0.71

이 메서드의 이름이 applymap인 이유는 Series가 각 원소에 적용할 함수를 지정하기 위한 map 메서드를 가지고 있기 때문이다.


In[35]: frame['e'].map(format)
Out[35]:
Utah 1.30
Ohio 1.12
Texas 0.13
Oregon 0.71
Name: e, dtype: object


5.2.6 정렬과 순위


어떤 기준에 근거해서 데이터를 정렬하는 것 역시 중요한 명령이다. 로우나 칼럼의 색인을 알파벳 순으로 정렬하려면 정렬된 새로운 객체를 반화하는 sort_index 메서드를 사용하면 된다.


In[35]: frame['e'].map(format)
Out[
35]:
Utah     
1.30
Ohio      1.12
Texas     0.13
Oregon    0.71
Name: e, dtype: object

 

5.2.6

어떤 기준에 근거해서 데이터를 정렬하는 것 역시 중요한 명령이다. 로우나 칼럼의 색인을 알파벳 순으로 정렬하려면 정렬된 새로운 객체를 반환하는 sort_index 메서드를 사용하면 된다.

 

In[36]: obj = Series(range(4), index=['d', 'a', 'b', 'c']) In[37]: obj.sort_index() Out[37]: a    1 b    2 c    3 d    0 dtype: int64

 

DataFrame은 로우나 칼럼 중 하나의 축을 기준으로 정렬할 수 있다.

In[40]: frame = DataFrame(np.arange(8).reshape((2,4)), index = ['three', 'one'], columns = ['d', 'a', 'b', 'c'])
In[
41]: frame.sort_index() Out[41]:        d  a  b  c one    4  5  6  7 three  0  1  2  3
In[42]: frame.sort_index(axis=1) Out[42]:        a  b  c  d three  1  2  3  0 one    5  6  7  4

 

데이터는 기본적으로 오름차순으로 정렬되지만 내림차순으로 정렬할 수도 있다.

In[43]: frame.sort_index(axis=1, ascending=False)
Out[
43]:
       d  c  b  a
three 
0  3  2  1
one    4  7  6  5

 

Series 객체를 값에 따라 정렬하고 싶다면 sort_values 메서드를 사용한다.

In[46]: obj.sort_values() Out[46]: 2   -3 3    2 0    4 1    7 dtype: int64 In[47]: obj = Series([4, 7, -3, 2]) In[48]: obj.sort_values() Out[48]: 2   -3 3    2 0    4 1    7 dtype: int64

 

정렬할 때 비어있는 값은 기본적으로 Series 객체에서 가장 마지막에 위치한다.

In[49]: obj = Series([4, np.nan, 7, np.nan, -3, 2]) In[50]: obj.sort_values() Out[50]: 4    -3 5     2 0     4 2     7 1   NaN 3   NaN dtype: float64

 

DataFrame에서는 하나 이상의 칼럼에 있는 값으로 정렬이 필요할 수 있다. 이럴 때는 by 옵션에 필요한 칼럼의 이름을 넘기면 된다.

In[34]: frame = DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
In[35]: frame
Out[33]:
a b
0 0 4
1 1 7
2 0 -3
3 1 2
In[36]: frame.sort_values(by='b')
Out[34]:
a b
2 0 -3
3 1 2
0 0 4
1 1 7

여러 개의 칼럼을 정렬하려면 칼럼의 이름이 담긴 리스트를 전달하면 된다.

In[37]: frame.sort_values(by=['a','b'])
Out[35]:
a b
2 0 -3
0 0 4
3 1 2
1 1 7

순위는 정렬과 거의 흡사하며, 1부터 배열의 유효한 데이터 개수까지 순위를 매긴다. 또한 순위는 numpy.argsort에서 반환하는 간접 정렬 색인과 유사한데, 동률인 순위를 처리하는 방식이 다르다. 기본적으로 Series와 DataFrame의 rank 메서드는 동점인 항목에 대해서는 평균 순위를 매긴다.


In[38]: obj = Series([7, -5, 7, 4, 2, 0 ,4])
In[39]: obj.rank()
Out[37]:
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64

데이터 상에서 나타나는 순서에 따라 순위를 매길 수도 있다. 

In[40]: obj.rank(method='first')
Out[38]:
0 6
1 1
2 7
3 4
4 3
5 2
6 5
dtype: float64

내림차순으로 순위를 매길 수도 있다.

In[41]: obj.rank(ascending=False, method='max')
# 'max' 는 같은 값을 가지는 그룹을 높은 순위로 매긴다.

Out[39]:
0 2
1 7
2 2
3 4
4 5
5 6
6 4
dtype: float64


5.2.7 중복 색인

지금까지 살펴본 모든 예제는 모두 축의 이름(색인 값)이 유일했다.

pandas의 많은 함수(reindex 같은) 에서 색인 값은 유일해야 하지만 강제 사항은 아니다. 이제 색인 값이 중복된 Series객체를 살펴보자.

In[42]: obj = Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
In[43]: obj
Out[41]:
a 0
a 1
b 2
b 3
c 4
dtype: int64

색인의 is_unique 속성은 해당 값이 유일한지 아닌지 알려준다.

In[44]: obj.index.is_unique
Out[42]: False


중복되는 색인 값이 있으면 색인을 이용한 데이터 선택은 다르게 동작하고 하나의 Series 객체를 반환한다. 하지만 중복되는 색인 값이 없으면 색인을 이용한 데이터 선택은 스칼라 값을 반환한다.


In[45]: obj['a']
Out[43]:
a 0
a 1
dtype: int64
In[46]: obj['c']
Out[44]: 4


DataFrame에서 로우를 선택하는 것도 동일하다.

In[47]: df = DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b','b'])
In[48]: df
Out[46]:
0 1 2
a 2.053621 0.432342 0.236329
a -0.843233 2.030160 0.603298
b -0.487331 0.949086 -0.163972
b -2.950345 -0.615662 -1.235880

In[49]: df.ix['b']

Out[47]:
0 1 2
b -0.487331 0.949086 -0.163972
b -2.950345 -0.615662 -1.235880

5.3 기술통계 계산과 요약


pandas 객체는 일반적인 수학 메서드와 통계 메서드를 가지고 있다. 이 메서드는 대부분 Series나 DataFrame 하나의 칼럼이나 로우에서 단일 값(합이나 평균 같은)을 구하는 축소 혹은 요약통계 범주에 속한다. 순수 NumPy 배열에서 제공하는 동일한 메서드와 비교하여 pandas의 메서드는 처음부터 누락된 데이터를 제외하도록 설계되었다. 다음과 같은 DataFrame을 생각해보자


In[50]: df = DataFrame([[1.4, np.nan],[7.1, -4.5],[np.nan, np.nan], [0.75, -1.3]],
index = ['a', 'b', 'c', 'd'],
columns=['one', 'two'])
In[51]: df
Out[49]:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3


DataFrame의 sum 메서드를 호출하면 각 칼럼의 합을 담은 Series를 반환한다.


In[53]: df.sum()
Out[51]:
one 9.25
two -5.80
dtype: float64

In[54]: df.sum(axis=1) #axis=1 옵션을 넘기면 각 로우의 합을 반환한다.
Out[52]:
a 1.40
b 2.60
c 0.00
d -0.55
dtype: float64

전체 로우나 칼럼의 값이 NA가 아니라면 계산 과정에서 NA 값은 제외시키고 계산된다. 이는 skipna 옵션을 통해 조정할 수 있다.


In[55]: df.mean(axis=1, skipna=False)
Out[53]:
a NaN
b 1.300
c NaN
d -0.275
dtype: float64


idxmin이나 idxmax 같은 메서드는 최소 혹은 최대 값을 가지고 있는 색인 값 같은 간접 통계를 반환한다.

In[59]: df.idxmax()

Out[57]:
one b
two d
dtype: object
In[60]: df.cumsum() # 누산 메서드

Out[58]:
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
In[61]: df.describe() # 축소나 누산도 아닌 다른 종류의 메서드로 여러 통계 결과를 만들어 낸다.

Out[59]:
one two
count 3.000000 2.000000
mean 3.083333 -2.900000
std 3.493685 2.262742
min 0.750000 -4.500000
25% 1.075000 -3.700000
50% 1.400000 -2.900000
75% 4.250000 -2.100000
max 7.100000 -1.300000


5.3.1 상관관계와 공분산

상관관계와 공분산 같은 요약통계 계산은 인자가 두 벌 필요하다. 야후! 금융사이트에서 구한 주식가격과 시가 총액을 담고 있는 다음 DataFrame에 대해 생각해보자.


In[62]: import pandas.io.data as web

C:\Users\Jusung\Anaconda2\lib\site-packages\pandas\io\data.py:33: FutureWarning:
The pandas.io.data module is moved to a separate package (pandas-datareader) and will 
be removed from pandas in a future version.
After installing the pandas-datareader package (https://github.com/pydata/pandas-datareader)
, you can change the import ``from pandas.io import data, wb`` to ``
from pandas_datareader import data, wb``.

FutureWarning)
In[63]: all_data = {}

In[64]: for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']:

... all_data[ticker] = web.get_data_yahoo(ticker)

...
In[66]: price = DataFrame({tic: data['Adj Close'] for tic, data in all_data.items()})
In[67]: volume = DataFrame({tic: data['Volume'] for tic, data in all_data.items()})

Future Warning이 뜨지만 (파이썬 2.7버전) 괜찮다. 

각 주식의 퍼센트 변화율을 계산해보겠다.

In[68]: returns = price.pct_change()

In[69]: returns.tail()
Out[66]:
AAPL GOOG IBM MSFT
Date
2016-03-31 -0.005203 -0.007435 0.020484 0.003270
2016-04-01 0.009175 0.006658 0.007065 0.006156
2016-04-04 0.010274 -0.006161 -0.002950 -0.002519
2016-04-05 -0.011789 -0.010050 -0.013612 -0.015695
2016-04-06 0.010473 0.010694 0.000133 0.010264


corr 메서드는 NA가 아니고 정렬된 색인에서 연속하는 두 Series에 대해 상관관계를 계산하고 cov 메서드는 공분산을 계산한다.

In[73]: returns.MSFT.corr(returns.IBM)

Out[70]: 0.50044758994882854
In[74]: returns.MSFT.cov(returns.IBM)

Out[71]: 8.9984395351248766e-05


반면에 DataFrame에서 corr과 cov메서드는 DataFrame 행렬상에서 상관관계와 공분산을 계산한다.

In[75]: returns.corr()

Out[72]:
AAPL GOOG IBM MSFT
AAPL 1.000000 0.409961 0.393292 0.397760
GOOG 0.409961 1.000000 0.399543 0.455731
IBM 0.393292 0.399543 1.000000 0.500448
MSFT 0.397760 0.455731 0.500448 1.000000
In[76]: returns.cov()

Out[73]:
AAPL GOOG IBM MSFT
AAPL 0.000284 0.000112 0.000081 0.000099
GOOG 0.000112 0.000263 0.000079 0.000109
IBM 0.000081 0.000079 0.000148 0.000090
MSFT 0.000099 0.000109 0.000090 0.000219


DataFrame의 corrwith 메서드를 사용하면 다른 Series나 DataFrame과의 상관관계를 계산한다. Series를 넘기면 각 칼럼에 대해 계산한 상관관계를 담고 있는 Series를 반환한다.


In[77]: returns.corrwith(returns.IBM)

Out[74]:
AAPL 0.393292
GOOG 0.399543
IBM 1.000000
MSFT 0.500448
dtype: float64


DataFrame을 넘기면 맞아 떨어지는 칼럼의 이름에 대한 상관관계를 계산한다. 여기서는 시가 총액의 퍼센트 변화율에 대한 상관관계를 계산해보았다.

In[78]: returns.corrwith(volume)

Out[75]:
AAPL -0.083129
GOOG -0.003435
IBM -0.200958
MSFT -0.084664
dtype: float64

axis=1 옵션을 넘기면 각 칼럼에 대한 상관관계와 공분산을 계산한다. 모든 경우 데이터는 상관관계를 계산하기 전에 색인의 이름 순서대로 정렬된다.


5.3.2 유일 값, 값, 세기, 멤버십

또 다른 종류의 메서드로는 1차원 Series에 담긴 값의 정보를 추출하는 메서드가 있다. 다음 예제를 살펴보자.

In[79]: obj = Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
In[80]: uniques = obj.unique()
In[81]: uniques
Out[78]: array(['c', 'a', 'd', 'b'], dtype=object)


유일 값은 정렬된 순서로 반환되지 않지만 필요하다면 uniques.sort()를 이용해서 나중에 정렬 할 수 도 있다. 그리고 value_counts 는 Series에서 도수를 계산하여 반환한다.


In[82]: obj.value_counts()

Out[79]:
c 3
a 3
b 2
d 1
dtype: int64


value_counts에서 반환하는 Series는 담고 있는 값으로 내림차순 정렬된다. 또한 value_counts 메서드는 pandas의 최상위 메서드로, 어떤 배열이나 순차 자료 구조에서도 사용할 수 있다.


마지막으로 isin 메서드는 어떤 값이 Series에 있는지 나타내는 불리언 벡터를 반환하는데, Series나 DataFrame의 칼럼에서 값을 골라내고 싶을 때 유용하게 사용할 수 있다.

In[87]: mask = obj.isin(['b', 'c'])

In[88]: mask

Out[85]:
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
In[89]: obj[mask]

Out[86]:
0 c
5 b
6 b
7 c
8 c
dtype: object


DataFrame의 여러 로우에 대해 히스토그램을 구해야 하는 경우가 있다. 다음 예제를 보자.


In[91]: data = DataFrame({'Qu1' : [1, 3, 4, 3, 4],
'Qu2' : [2, 3, 1, 2, 3],
'Qu3' : [1, 5, 2, 4, 4]})
data

Out[88]:
Qu1 Qu2 Qu3
0 1 2 1
1 3 3 5
2 4 1 2
3 3 2 4
4 4 3 4


그런 경우 DataFrame의 apply 함수에 pandas.value_counts를 넘기면 다음과 같은 결과를 얻을 수 있다. value_counts 메서드의 결과가 DataFrame의 칼럼 크기보다 작을 수 있기 때문에 fillna(0) 함수를 이용해서 비어있는 값은 0으로 채워준다. 

In[94]: result = data.apply(pd.value_counts).fillna(0)

In[95]: result

Out[92]:
Qu1 Qu2 Qu3
1 1 1 1
2 0 2 1
3 2 2 0
4 2 0 2
5 0 0 1


5.4 누락된 데이터 처리하기


누락된 데이터를 처리하는 일은 데이터 분석 애플리케이션에서 흔히 있는 일이다. pandas의 설계 목표 중 하나는 누락 데이터를 가능한 한 쉽게 처리할 수 있도록 하는 것이다. 예를 들어 앞에서 살펴봤듯이 pandas 객체의 모든 기술통계는 누락된 데이터를 배제하고 처리한다.


pandas는 누락된 데이터를 실수든 아니든 모두 NaN(Not a Number)으로 취급한다. 그래서 누락된 값을 쉽게 찾을 수 있게 하는 파수병 역할을 한다.


In[96]: string_data = Series(['aardvark', 'artichoke', np.nan, 'avocado'])
In[97]: string_data
Out[94]:
0 aardvark
1 artichoke
2 NaN
3 avocado
dtype: object
In[98]: string_data.isnull()
Out[95]:
0 False
1 False
2 True
3 False
dtype: bool

파이썬의 내장 None 값 또한 NA 값으로 취급된다.


In[99]: string_data[0] = None

In[100]: string_data.isnull()

Out[97]:
0 True
1 False
2 True
3 False
dtype: bool

pandas에서 NA 값을 표기하는 것이 최선이라는 주장을 하려는 것은 아니지만 pandas에서 사용하는 방법이 더 간단하고 일관적이다. 성능 면에서도 훌륭하며 NumPy 자료형에는 존재하지 않는 진짜 NA 자료형이나 비트 패턴 위에서 만든 간단한 API를 제공한다. NumPy는 계속 개발 중인 프로젝트이므로 앞으로 변경될 가능성이 있다. 


5.4.1 누락된 데이터 골라내기

누락된 데이터를 골라내는 방법에는 여러 가지가 있는데, 직접 손으로 제거하는 것도 한 방법이지만 dropna를 사용하는 것도 매우 유용한 방법이다 . Series에 대해 dropna 메서드를 적용하면 실제 데이터가 들어있는 색인 값과 데이터를 Series 값으로 반환한다.


In[101]: from numpy import nan as NA

In[102]: data = Series([1, NA, 3.5, NA, 7])

In[103]: data.dropna()

Out[100]:
0 1.0
2 3.5
4 7.0
dtype: float64


불리언 색인을 이용해서 직접 계산하는 것도 물론 가능하다.

In[104]: data[data.notnull()]

Out[101]:
0 1.0
2 3.5
4 7.0
dtype: float64


DataFrame 객체의 경우는 조금 복잡한데, 모두 NA인 로우나 칼럼을 제외하든가 하나라도 NA인 값을 포함하고 있는 로우나 칼럼을 제외시킬 수도 있다. dropna는 기본적으로 NA 값이 하나라도 있는 로우는 제외시킨다.


In[106]: data = DataFrame([[1., 6.5, 3.], [1., NA, NA],
[NA, NA, NA], [NA, 6.5, 3.]])

In[107]: Cleaned = data.dropna()
In[108]: data
Out[105]:
0 1 2
0 1 6.5 3
1 1 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3
In[109]: Cleaned
Out[106]:
0 1 2
0 1 6.5 3


how='all' 옵션을 주면 모든 값이 NA인 로우만 제외시킨다.

In[110]: data.dropna(how='all')
Out[107]:
0 1 2
0 1 6.5 3
1 1 NaN NaN
3 NaN 6.5 3

칼럼을 제외시키는 방법은 옵셔내으로 axis=1을 주면 로우를 제외시키는 것과 동일한 방식으로 동작한다.


In[118]: data[4] = NA

In[119]: data

Out[116]:
0 1 2 4
0 1 6.5 3 NaN
1 1 NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN 6.5 3 NaN
In[120]: data.dropna(axis=1, how='all')

Out[117]:
0 1 2
0 1 6.5 3
1 1 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3


DataFrame의 로우를 제외시키는 방법은 주로 시계열 데이터에 사용되는 경향이 있다. 몇 개 이상의 값이 들어있는 로우만 살펴보고 싶다면 thresh 인자에 원하는 값을 넘기면 된다.

df = DataFrame(np.random.randn(7, 3))
In[123]: df

Out[120]:
0 1 2
0 0.674988 NaN NaN
1 0.206816 NaN NaN
2 1.890366 NaN NaN
3 1.207395 NaN 0.513191
4 -1.933233 NaN 1.315672
5 -0.743783 0.009746 0.478140
6 0.945863 0.781102 -1.704737
In[124]: df.dropna(thresh=3)

Out[121]:
0 1 2
5 -0.743783 0.009746 0.478140
6 0.945863 0.781102 -1.704737

5.4.2 누락된 값 채우기


누락된 값을 제외시키지 않고 (잠재적으로 다른 데이터도 함께 버려질 가능성이 있다.) 데이터 상의 '구멍'을 어떻게는 메우고 싶은 경우가 있는데, 이런 경우에는 fillna 메서드를 활용하면 된다. 즉, fillna 메서드에 채워 넣고 싶은 값을 넘겨주면 된다.

In[127]: df.fillna(0)

Out[124]:
0 1 2
0 0.674988 0.000000 0.000000
1 0.206816 0.000000 0.000000
2 1.890366 0.000000 0.000000
3 1.207395 0.000000 0.513191
4 -1.933233 0.000000 1.315672
5 -0.743783 0.009746 0.478140
6 0.945863 0.781102 -1.704737

fillna에 사전 값을 넘겨서 각 칼럼마다 다른 값을 채워 넣을 수도 있다.

In[128]: df.fillna({1: 0.5, 3: -1})

Out[125]:
0 1 2
0 0.674988 0.500000 NaN
1 0.206816 0.500000 NaN
2 1.890366 0.500000 NaN
3 1.207395 0.500000 0.513191
4 -1.933233 0.500000 1.315672
5 -0.743783 0.009746 0.478140
6 0.945863 0.781102 -1.704737


fillna는 새로운 객체를 반환하지만 다음처럼 기존 객체를 변경할 수도 있다.

In[131]: _ = df.fillna(0, inplace=True) # _는 이전에 다루던 객체를 의미

In[132]: df

Out[129]:
0 1 2
0 0.674988 0.000000 0.000000
1 0.206816 0.000000 0.000000
2 1.890366 0.000000 0.000000
3 1.207395 0.000000 0.513191
4 -1.933233 0.000000 1.315672
5 -0.743783 0.009746 0.478140
6 0.945863 0.781102 -1.704737

재색인에서 사용 가능한 보간 메서드는 fillna 메서드에서도 사용이 가능하다.


In[133]: df = DataFrame(np.random.randn(6,3))

In[134]: df.ix[2:, 1] = NA; df.ix[4:, 2] = NA

In[135]: df

Out[132]:
0 1 2
0 2.321233 0.243797 -0.049593
1 -0.768766 -0.639125 -0.522648
2 0.580564 NaN 0.334274
3 0.757897 NaN -0.482256
4 0.122206 NaN NaN
5 1.561499 NaN NaN
In[136]: df.fillna(method='ffill')

Out[133]:
0 1 2
0 2.321233 0.243797 -0.049593
1 -0.768766 -0.639125 -0.522648
2 0.580564 -0.639125 0.334274
3 0.757897 -0.639125 -0.482256
4 0.122206 -0.639125 -0.482256
5 1.561499 -0.639125 -0.482256
In[137]: df.fillna(method='ffill', limit = 2) #두 개까지만 채운다. 남용금지?

Out[134]:
0 1 2
0 2.321233 0.243797 -0.049593
1 -0.768766 -0.639125 -0.522648
2 0.580564 -0.639125 0.334274
3 0.757897 -0.639125 -0.482256
4 0.122206 NaN -0.482256
5 1.561499 NaN -0.482256


조금만 창의적으로 생각하면 fillna를 이용해서 매우 다양한 일을 할 수 있는데, 예를 들면 Series의 평균 값이나 중간 값을 전달할 수도 있다.

In[138]: data = Series([1., NA, 3.5, NA, 7])

In[139]: data.fillna(data.mean())

Out[136]:
0 1.000000
1 3.833333
2 3.500000
3 3.833333
4 7.000000
dtype: float64

5.5 계층적 색인


계층적 색인은 pandas의 중요한 기능으로, 축에 대해 다중 색인 단계를 지정할 수 있도록 해준다. 약간 추상적으로 말하면 ㅏ원이 높은 데이터를 낮은 차원의 형식으로 다룰 수 있게 해주는 기능이다. 간단한 예제를 하나 살펴보자. 우선 리스트를 담고 있는 리스트나 배열을 가진 Series하나를 생성하자.


In[140]: data = Series(np.random.randn(10),
index=[['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'd','d'],
[1, 2, 3, 1, 2, 3, 1, 2, 2, 3]])

In[141]: data

Out[138]:
a 1 0.750448
2 -0.823054
3 0.562675
b 1 -0.629406
2 -1.099839
3 1.614658
c 1 -1.297442
2 0.232058
d 2 -0.753372
3 -0.176555
dtype: float64


지금 생성한 객체는 MultiIndex를 색인으로 하는 Series로, 색인의 계층을 보여주고 있다. 바로 위 단계의 색인을 이용해서 하위 계층을 직접 접근할 수 있다.


In[142]: data.index

Out[139]:
MultiIndex(levels=[[u'a', u'b', u'c', u'd'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 1, 2, 0, 1, 1, 2]])


계층적으로 색인된 객체는 데이터의 부분집합을 부분적 색인으로 접근 하는 것이 가능하다.

In[143]: data['b']

Out[140]:
1 -0.629406
2 -1.099839
3 1.614658
dtype: float64
In[144]: data['b':'c']

Out[141]:
b 1 -0.629406
2 -1.099839
3 1.614658
c 1 -1.297442
2 0.232058
dtype: float64
In[145]: data.ix[['b', 'd']]

Out[142]:
b 1 -0.629406
2 -1.099839
3 1.614658
d 2 -0.753372
3 -0.176555
dtype: float64


하위 계층의 객체를 선택하는 것도 가능하다.


In[146]: data[:, 2]

Out[143]:
a -0.823054
b -1.099839
c 0.232058
d -0.753372
dtype: float64


계층적인 색인은 데이터를 재형성하고 피벗 테이블 생성 같은 그룹 기반의 작업을 할 때 중요하게 사용된다. 예를 들어 위에서 만든 DataFrame 객체에 unstack 메서드를 사용해서 데이터를 새롭게 배열할 수 도 있다.

In[147]: data.unstack()

Out[144]:
1 2 3
a 0.750448 -0.823054 0.562675
b -0.629406 -1.099839 1.614658
c -1.297442 0.232058 NaN
d NaN -0.753372 -0.176555
In[148]: data.unstack().stack()

Out[145]:
a 1 0.750448
2 -0.823054
3 0.562675
b 1 -0.629406
2 -1.099839
3 1.614658
c 1 -1.297442
2 0.232058
d 2 -0.753372
3 -0.176555
dtype: float64

unstack의 반대되는 작업은 stack메서드로 수행한다.


stack과 unstack 메서드는 7장에서 더 자세히 알아보기로 하자.


DataFrame에서는 두 축 모두 계층적 색인을 가질 수 있다.


In[149]: frame = DataFrame(np.arange(12).reshape((4,3)),
index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
columns=[['ohio', 'Ohio', 'Colorado',],
['Green', 'Red', 'Green']])

In[150]: frame

Out[147]:
ohio Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11


계층적 색인의 각 단계는 이름(문자열이나 어떤 파이썬 객체라도 가능하다)을 가질 수 있고, 만약 이름이 있다면 콘솔 출력 시에 함께 나타난다. ( 색인의 이름과 축의 라벨을 혼동하지 말자!).


In[151]: frame.index.names = ['key1', 'key2']

In[152]: frame.columns.names = ['state', 'color']

In[153]: frame

Out[150]:
state ohio Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11


칼럼의 부분집합을 부분적 색인으로 접근하는 것도 로우에 대한 부분적 색인과 비슷하게 사용하면 된다.


In[154]: frame['Ohio']

Out[151]:
color Red
key1 key2
a 1 1
2 4
b 1 7
2 10

MultiIndex는 따로 생성한 다음에 재사용이 가능하다. 위에서 살펴본 DataFrame의 칼럼 계층의 이름은 다음처럼 생성할 수 있다.


In[162]: pd.MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']],
names=['state', 'color'])

Out[159]:
MultiIndex(levels=[[u'Colorado', u'Ohio'], [u'Green', u'Red']],
labels=[[1, 1, 0], [0, 1, 0]],
names=[u'state', u'color'])

5.5.1 계층 순서 바꾸고 정렬하기 

계층적 색인에서 계층 순서를 바꾸거나 지정된 계층에 따라 데이터를 정렬해야 하는 경우도 있다. swaplevel은 넘겨받은 2개의 계층 버호나 이름이 뒤바뀐 새로운 객체를 반환한다(하지만 데이터는 변경되지 않는다.)


In[164]: frame.swaplevel('key1', 'key2')

Out[161]:
state ohio Ohio Colorado
color Green Red Green
key2 key1
1 a 0 1 2
2 a 3 4 5
1 b 6 7 8
2 b 9 10 11


반면에 sortlevel 메서드는 단일 계층에 속한 데이터를 정렬한다. swaplevel을 사용해서 계층을 바꿀 때 대개는 sortlevel을 사용해서 결과도 사전식으로 정렬한다.


In[165]: frame.sortlevel(1)

Out[162]:
state ohio Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
b 1 6 7 8
a 2 3 4 5
b 2 9 10 11
In[166]: frame.swaplevel(0, 1).sortlevel(0)

Out[163]:
state ohio Ohio Colorado
color Green Red Green
key2 key1
1 a 0 1 2
b 6 7 8
2 a 3 4 5
b 9 10 11

5.5.2 단계별 요약통계

DataFrame과 Series의 많은 기술통계와 요약통계는 level 옵션을 가지고 있는데, 이는 어떤 한 축에 대해 합을 구하고 싶은 단계를 지정할 수 있는 옵션이다. 앞에서 살펴본 DataFrame에서 로우나 칼럼을 아래처럼 단계별로 정렬하여 합을 구할 수 잇다.


In[167]: frame.sum(level = 'key2')

Out[164]:
state ohio Ohio Colorado
color Green Red Green
key2
1 6 8 10
2 12 14 16


In[168]: frame.sum(level='color', axis=1)

Out[165]:
color Green Red
key1 key2
a 1 2 1
2 8 4
b 1 14 7
2 20 10

내부적으로는 pandas의 groupby기능을 이용해서 구현되었는데 자세한 내용은 앞으로 더 살펴 보겠다.


5.5.3 DataFrame의 칼럼 사용하기


DataFrame에서 로우를 선택하기 위한 색인으로 하나 이상의 칼럼을 사용하는 것은 드물지 않은 일이다. 아니면 로우의 색인을 DataFrame의 칼럼으로 옮기고 싶을 것이다. 다음과 같은 DataFrame이 있다.


In[169]: frame = DataFrame({'a': range(7), 'b':range(7, 0, -1),
'c': ['one', 'one', 'one', 'two', 'two', 'two', 'two'],
'd': [0,1, 2, 0, 1, 2, 3]})

In[170]: frame

Out[167]:
a b c d
0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
3 3 4 two 0
4 4 3 two 1
5 5 2 two 2
6 6 1 two 3

DataFrame의 set_index 함수는 하나 이상의 칼럼을 색인으로 하는 새로운 DataFrame을 생성한다.


In[171]: frame2 = frame.set_index(['c', 'd'])

In[172]: frame2

Out[169]:
a b
c d
one 0 0 7
1 1 6
2 2 5
two 0 3 4
1 4 3
2 5 2
3 6 1

다음처럼 칼럼을 명시적으로 남겨두지 않으면 DataFrame에서 삭제된다.


In[173]: frame.set_index(['c', 'd'], drop=False)

Out[170]:
a b c d
c d
one 0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
two 0 3 4 two 0
1 4 3 two 1
2 5 2 two 2
3 6 1 two 3


반면에 reset_index 함수는 set_index와 반대되는 개념으로, 계층적 색인 단계가 칼럼으로 이동한다.

In[174]: frame2.reset_index()

Out[171]:
c d a b
0 one 0 0 7
1 one 1 1 6
2 one 2 2 5
3 two 0 3 4
4 two 1 4 3
5 two 2 5 2
6 two 3 6 1

5.6 pandas와 관련된 기타 주제


지금부터는 pandas의 부수적인 내용에 대해서 알아보자.


5.6.1 정수 색인

pandas 객체를 정수로 색인해서 사용하는 일은 파이썬에서 리스트나 튜플 같은 기본 자료 구조에서 사용되는 색인의 의미와 약간 달라서 초보자들은 종종 실수를 한다.


In[175]: ser = Series(np.arange(3.))

ser[-1]

# 난리가남


이때 pandas는 정수 색인에 대한 '대비책'을 세울 수 있지만 알아내기 쉽지 않은 버그가 생기지 않을 만한 안전하고 일반적인 방법은 내가 알기론 없다. 여기 ser 객체는 0, 1, 2 색인을 가지고 있지만 사용자가 원하는 것이 위치 색인인지 이름 색인인지 알아 맞히는 것은 어려운 일이다.

In[177]: ser

Out[174]:
0 0
1 1
2 2
dtype: float64

반면에 정수 색인이 아니라면 그리 어렵지는 않다.


In[178]: ser2 = Series(np.arange(3.), index=['a', 'b', 'c'])

In[179]: ser2[-1]

Out[176]: 2.0

일관성을 유지하기 위해 색인 값을 가진 축 색인이 있을 경우 정수 데이터는 항상 이름을 지향한다. 이는 ix 슬라이스에도 마찬가지로 적용된다.

In[180]: ser.ix[:1]

Out[177]:
0 0
1 1
dtype: float64


만일 색인의 종류에 상관없이 위치 기반의 색인이 필요하다면 Series의 iget_value 메서드와 DataFrame의 irow, icol 메서드를 사용하면 된다.


In[183]: frame = DataFrame((np.arange(6).reshape(3, 2)), index=[2, 0, 1])

In[184]: frame.iloc[0]

Out[181]:
0 0
1 1
Name: 2, dtype: int32


5.6.2 Panel 데이터

이 책에서 중요하게 다룰 주제는 아니지만 pandas에는 Panel 이라고 하는 자료 구조가 있는데, Panel은 DataFrame의 3차원 버전이라고 이해하면 된다.


형식의 데이터를 다루는 데 초점을 맞추고 있고 계층적 색인을 이용하면 대개의 경우 N차원 배열은 불필요하다.


Panel은 DataFrame 객체를 담고 있는 사전이나 3차원 ndarray를 통해 생성할 수 있다. 


import pandas.io.data as web

pdata = pd.Panel(dict((stk, web.get_data_yahoo(stk)) for stk in ['AAPL', 'GOOG', 'MSFT'
'DELL']))

Panel의 각 항목( DataFrame에서 칼럼이라고 생각하면 된다)은 DataFrame이다.


In[187]: pdata

Out[184]:
<class 'pandas.core.panel.Panel'>
Dimensions: 4 (items) x 1595 (major_axis) x 6 (minor_axis)
Items axis: AAPL to MSFT
Major_axis axis: 2010-01-04 00:00:00 to 2016-04-06 00:00:00
Minor_axis axis: Open to Adj Close
In[188]: pdata = pdata.swapaxes('items', 'minor')

In[189]: pdata['Adj Close']

Out[186]:
AAPL DELL GOOG MSFT
Date
2010-01-04 28.313195 14.06528 313.062468 26.227603
2010-01-05 28.362145 14.38450 311.683844 26.236076
2010-01-06 27.911008 14.10397 303.826685 26.075067
2010-01-07 27.859412 14.23940 296.753749 25.803894
2010-01-08 28.044630 14.36516 300.709808 25.981851
2010-01-11 27.797232 14.37483 300.255255 25.651358
2010-01-12 27.481038 14.56830 294.945572 25.481874
2010-01-13 27.868673 14.57797 293.252243 25.719151
2010-01-14 27.707269 14.22005 294.630868 26.236076
2010-01-15 27.244224 13.92985 289.710772 26.151335
2010-01-19 28.449462 14.32646 293.516976 26.354715
2010-01-20 28.011555 14.03626 289.915587 25.922532
2010-01-21 27.527342 13.92017 291.199286 25.431029
2010-01-22 26.162022 13.18982 274.730736 24.541239
2010-01-25 26.865850 13.43650 269.730740 24.846310
2010-01-26 27.245547 13.13662 270.939526 24.998845
2010-01-27 27.502206 13.08825 270.779695 25.142907
2010-01-28 26.365761 12.84641 266.878565 24.710723
2010-01-29 25.409244 12.47882 264.705742 23.880253
2010-02-01 25.762481 12.78837 266.244198 24.075159
2010-02-02 25.911978 12.86576 265.295156 24.117529
2010-02-03 26.357823 12.92380 270.140309 24.261591
2010-02-04 25.407921 12.58523 263.127321 23.592131
2010-02-05 25.859059 12.80772 265.380075 23.744666
2010-02-08 25.681779 12.95282 266.468996 23.490440
2010-02-09 25.955637 13.10760 267.952522 23.736192
2010-02-10 25.814078 13.30107 266.958496 23.719243
2010-02-11 26.283736 13.49454 267.932539 23.829409
2010-02-12 26.509966 13.38813 266.294170 23.668399
2010-02-16 26.909508 13.67834 270.380071 24.136659
... ... ... ...
2016-02-24 96.099998 NaN 699.559998 51.360001
2016-02-25 96.760002 NaN 705.750000 52.099998
2016-02-26 96.910004 NaN 705.070007 51.299999
2016-02-29 96.690002 NaN 697.770020 50.880001
2016-03-01 100.529999 NaN 718.809998 52.580002
2016-03-02 100.750000 NaN 718.849976 52.950001
2016-03-03 101.500000 NaN 712.419983 52.349998
2016-03-04 103.010002 NaN 710.890015 52.029999
2016-03-07 101.870003 NaN 695.159973 51.029999
2016-03-08 101.029999 NaN 693.969971 51.650002
2016-03-09 101.120003 NaN 705.239990 52.840000
2016-03-10 101.169998 NaN 712.820007 52.049999
2016-03-11 102.260002 NaN 726.820007 53.070000
2016-03-14 102.519997 NaN 730.489990 53.169998
2016-03-15 104.580002 NaN 728.330017 53.590000
2016-03-16 105.970001 NaN 736.090027 54.349998
2016-03-17 105.800003 NaN 737.780029 54.660000
2016-03-18 105.919998 NaN 737.599976 53.490002
2016-03-21 105.910004 NaN 742.090027 53.860001
2016-03-22 106.720001 NaN 740.750000 54.070000
2016-03-23 106.129997 NaN 738.059998 53.970001
2016-03-24 105.669998 NaN 735.299988 54.209999
2016-03-28 105.190002 NaN 733.530029 53.540001
2016-03-29 107.680000 NaN 744.770020 54.709999
2016-03-30 109.559998 NaN 750.530029 55.049999
2016-03-31 108.989998 NaN 744.950012 55.230000
2016-04-01 109.989998 NaN 749.909973 55.570000
2016-04-04 111.120003 NaN 745.289978 55.430000
2016-04-05 109.809998 NaN 737.799988 54.560001
2016-04-06 110.959999 NaN 745.690002 55.119999

[1595 rows x 4 columns]

ix를 이용한 라벨 색인을 통한 접근은 3차원에도 일반화되어 특정 날짜나 어떤 기간 동안의 모든 데이터를 다음처럼 선택 할 수 있다.



In[190]: pdata.ix[:, '6/1/2012', :]

Out[187]:
Open High Low Close Volume Adj Close
AAPL 569.159996 572.650009 560.520012 560.989983 130246900 74.218116
DELL 12.150000 12.300000 12.045000 12.070000 19397600 11.675920
GOOG 571.790972 572.650996 568.350996 570.981000 6138700 285.205295
MSFT 28.760000 28.959999 28.440001 28.450001 56634300 25.598227
In[191]: pdata.ix['Adj Close', '5/22/2012':, :]

Out[188]:
AAPL DELL GOOG MSFT
Date
2012-05-22 73.686282 14.58765 300.100412 26.776915
2012-05-23 75.484211 12.08221 304.426106 26.192070
2012-05-24 74.790973 12.04351 301.528978 26.156079
2012-05-25 74.390105 12.05319 295.470050 26.147081
2012-05-28 NaN 12.05319 NaN NaN
2012-05-29 75.710442 12.24666 296.873645 26.596962
2012-05-30 76.623304 12.14992 293.821674 26.399015
2012-05-31 76.432797 11.92743 290.140354 26.264051
2012-06-01 74.218116 11.67592 285.205295 25.598227
2012-06-04 74.654700 11.60821 289.006480 25.688202
2012-06-05 74.461551 11.76298 284.920579 25.652212
2012-06-06 75.603286 11.81619 289.995487 26.408013
2012-06-07 75.637681 11.73396 288.826666 26.300040
2012-06-08 76.775446 11.72429 289.935570 26.677940
2012-06-11 75.564914 11.47278 283.966519 26.003119
2012-06-12 76.225086 11.57919 282.268201 26.354027
2012-06-13 75.695894 11.87423 280.265216 26.210064
2012-06-14 75.612542 11.93711 279.246220 26.399015
2012-06-15 75.956519 11.89841 281.973510 27.010853
2012-06-18 77.497794 12.01449 285.140358 26.848896
2012-06-19 77.713448 11.78233 290.475012 27.622691
2012-06-20 77.492502 11.89841 288.467038 27.829636
2012-06-21 76.424856 11.60821 282.323161 27.118824
2012-06-22 77.010939 11.80168 285.455033 27.622691
2012-06-25 75.512000 11.55500 280.070407 26.875889
2012-06-26 75.678696 11.53565 282.058429 27.010853
2012-06-27 76.005469 11.92743 284.366112 27.145817
2012-06-28 75.284441 11.55984 281.873596 26.911879
2012-06-29 77.262308 12.10155 289.745749 27.523717
2012-07-02 78.389489 11.98064 289.945546 27.496724
... ... ... ...
2016-02-24 96.099998 NaN 699.559998 51.360001
2016-02-25 96.760002 NaN 705.750000 52.099998
2016-02-26 96.910004 NaN 705.070007 51.299999
2016-02-29 96.690002 NaN 697.770020 50.880001
2016-03-01 100.529999 NaN 718.809998 52.580002
2016-03-02 100.750000 NaN 718.849976 52.950001
2016-03-03 101.500000 NaN 712.419983 52.349998
2016-03-04 103.010002 NaN 710.890015 52.029999
2016-03-07 101.870003 NaN 695.159973 51.029999
2016-03-08 101.029999 NaN 693.969971 51.650002
2016-03-09 101.120003 NaN 705.239990 52.840000
2016-03-10 101.169998 NaN 712.820007 52.049999
2016-03-11 102.260002 NaN 726.820007 53.070000
2016-03-14 102.519997 NaN 730.489990 53.169998
2016-03-15 104.580002 NaN 728.330017 53.590000
2016-03-16 105.970001 NaN 736.090027 54.349998
2016-03-17 105.800003 NaN 737.780029 54.660000
2016-03-18 105.919998 NaN 737.599976 53.490002
2016-03-21 105.910004 NaN 742.090027 53.860001
2016-03-22 106.720001 NaN 740.750000 54.070000
2016-03-23 106.129997 NaN 738.059998 53.970001
2016-03-24 105.669998 NaN 735.299988 54.209999
2016-03-28 105.190002 NaN 733.530029 53.540001
2016-03-29 107.680000 NaN 744.770020 54.709999
2016-03-30 109.559998 NaN 750.530029 55.049999
2016-03-31 108.989998 NaN 744.950012 55.230000
2016-04-01 109.989998 NaN 749.909973 55.570000
2016-04-04 111.120003 NaN 745.289978 55.430000
2016-04-05 109.809998 NaN 737.799988 54.560001
2016-04-06 110.959999 NaN 745.690002 55.119999

[988 rows x 4 columns]


통계 모델에 알맞게 Panel 데이터를 출력하는 다른 방법은 DataFrame을 쌓아 놓는 것이다.


... stacked = pdata.ix[:, '5/30/2012':, :].to_frame()

In[193]: stacked

Out[190]:
Open High Low Close Volume \
Date minor
2012-05-30 AAPL 569.199997 579.989990 566.559990 579.169998 132357400
DELL 12.590000 12.700000 12.460000 12.560000 19787800
GOOG 588.161028 591.901014 583.530999 588.230992 3827600
MSFT 29.350000 29.480000 29.120001 29.340000 41585500
2012-05-31 AAPL 580.740021 581.499985 571.460022 577.730019 122918600
DELL 12.530000 12.540000 12.330000 12.330000 19955600
GOOG 588.720982 590.001032 579.001013 580.860990 5958800
MSFT 29.299999 29.420000 28.940001 29.190001 39134000
2012-06-01 AAPL 569.159996 572.650009 560.520012 560.989983 130246900
DELL 12.150000 12.300000 12.045000 12.070000 19397600
GOOG 571.790972 572.650996 568.350996 570.981000 6138700
MSFT 28.760000 28.959999 28.440001 28.450001 56634300
2012-06-04 AAPL 561.500008 567.499985 548.499977 564.289978 139248900
DELL 12.110000 12.112500 11.800000 12.000000 17015700
GOOG 570.220958 580.491016 570.011006 578.590973 4883500
MSFT 28.620001 28.780001 28.320000 28.549999 47926300
2012-06-05 AAPL 561.269989 566.470001 558.330002 562.830025 97053600
DELL 11.950000 12.240000 11.950000 12.160000 15620900
GOOG 575.451008 578.131003 566.470986 570.410999 4697200
MSFT 28.510000 28.750000 28.389999 28.510000 45715400
2012-06-06 AAPL 567.770004 573.849983 565.499992 571.460022 100363900
DELL 12.210000 12.280000 12.090000 12.215000 20779900
GOOG 576.480979 581.970971 573.611004 580.570966 4207200
MSFT 28.879999 29.370001 28.809999 29.350000 46860500
2012-06-07 AAPL 577.290009 577.320023 570.500000 571.720001 94941700
DELL 12.320000 12.410000 12.120000 12.130000 20074000
GOOG 587.601014 587.891038 577.251006 578.230986 3530100
MSFT 29.639999 29.700001 29.170000 29.230000 37792800
2012-06-08 AAPL 571.599998 580.580017 568.999992 580.319984 86879100
DELL 12.130000 12.225000 12.020000 12.120000 18155600
... ... ... ... ...
2016-03-23 AAPL 106.480003 107.070000 105.900002 106.129997 25452600
GOOG 742.359985 745.719971 736.150024 738.059998 1421900
MSFT 54.110001 54.240002 53.740002 53.970001 19905300
2016-03-24 AAPL 105.470001 106.250000 104.889999 105.669998 25480900
GOOG 732.010010 737.747009 731.000000 735.299988 1564800
MSFT 53.840000 54.330002 53.730000 54.209999 18842700
2016-03-28 AAPL 106.000000 106.190002 105.059998 105.190002 19303600
GOOG 736.789978 738.989990 732.500000 733.530029 1299800
MSFT 54.209999 54.290001 53.330002 53.540001 16988200
2016-03-29 AAPL 104.889999 107.790001 104.879997 107.680000 30774100
GOOG 734.590027 747.250000 728.760010 744.770020 1902100
MSFT 53.660000 54.860001 53.450001 54.709999 23375000
2016-03-30 AAPL 108.650002 110.419998 108.599998 109.559998 45159900
GOOG 750.099976 757.880005 748.739990 750.530029 1781000
MSFT 54.930000 55.639999 54.900002 55.049999 22920300
2016-03-31 AAPL 109.720001 109.900002 108.879997 108.989998 25685700
GOOG 749.250000 750.849976 740.940002 744.950012 1712400
MSFT 54.950001 55.590000 54.860001 55.230000 26173800
2016-04-01 AAPL 108.779999 110.000000 108.199997 109.989998 25626200
GOOG 738.599976 750.340027 737.000000 749.909973 1574900
MSFT 55.049999 55.610001 54.570000 55.570000 24298600
2016-04-04 AAPL 110.419998 112.190002 110.269997 111.120003 37333500
GOOG 750.059998 752.799988 742.429993 745.289978 1134200
MSFT 55.430000 55.660000 55.000000 55.430000 18909100
2016-04-05 AAPL 109.510002 110.730003 109.419998 109.809998 26495300
GOOG 738.000000 742.799988 735.369995 737.799988 1129800
MSFT 55.189999 55.299999 54.459999 54.560001 19148800
2016-04-06 AAPL 110.230003 110.980003 109.199997 110.959999 26047800
GOOG 735.770020 746.239990 735.559998 745.690002 1050200
MSFT 54.360001 55.200001 54.209999 55.119999 21032100

Adj Close
Date minor
2012-05-30 AAPL 76.623304
DELL 12.149920
GOOG 293.821674
MSFT 26.399015
2012-05-31 AAPL 76.432797
DELL 11.927430
GOOG 290.140354
MSFT 26.264051
2012-06-01 AAPL 74.218116
DELL 11.675920
GOOG 285.205295
MSFT 25.598227
2012-06-04 AAPL 74.654700
DELL 11.608210
GOOG 289.006480
MSFT 25.688202
2012-06-05 AAPL 74.461551
DELL 11.762980
GOOG 284.920579
MSFT 25.652212
2012-06-06 AAPL 75.603286
DELL 11.816190
GOOG 289.995487
MSFT 26.408013
2012-06-07 AAPL 75.637681
DELL 11.733960
GOOG 288.826666
MSFT 26.300040
2012-06-08 AAPL 76.775446
DELL 11.724290
...
2016-03-23 AAPL 106.129997
GOOG 738.059998
MSFT 53.970001
2016-03-24 AAPL 105.669998
GOOG 735.299988
MSFT 54.209999
2016-03-28 AAPL 105.190002
GOOG 733.530029
MSFT 53.540001
2016-03-29 AAPL 107.680000
GOOG 744.770020
MSFT 54.709999
2016-03-30 AAPL 109.559998
GOOG 750.530029
MSFT 55.049999
2016-03-31 AAPL 108.989998
GOOG 744.950012
MSFT 55.230000
2016-04-01 AAPL 109.989998
GOOG 749.909973
MSFT 55.570000
2016-04-04 AAPL 111.120003
GOOG 745.289978
MSFT 55.430000
2016-04-05 AAPL 109.809998
GOOG 737.799988
MSFT 54.560001
2016-04-06 AAPL 110.959999
GOOG 745.690002
MSFT 55.119999

[3277 rows x 6 columns]

이를 위해 DataFrame에는 to_panel 메서와 그 반대인 to_frame 메서드가 있다.

In[195]: stacked.to_panel()

Out[192]:
<class 'pandas.core.panel.Panel'>
Dimensions: 6 (items) x 982 (major_axis) x 4 (minor_axis)
Items axis: Open to Adj Close
Major_axis axis: 2012-05-30 00:00:00 to 2016-04-06 00:00:00
Minor_axis axis: AAPL to MSFT


'Python for data analysis' 카테고리의 다른 글

Chapter 4. Numpy 기본 : 배열과 벡터 계산  (0) 2016.03.11
Chapter 3. IPython 소개  (0) 2016.03.11
Chapter 2 사례 소개  (0) 2016.03.04
Chaper 1 시작하기 전에  (0) 2016.03.04