Pandas 소개 2

GonsSu24 내용에 이어서 Pandas 라이브러리를 소개한다.

먼저 GongSu24를 임포트 한다.


In [3]:
from GongSu24_Pandas_Introduction_1 import *

뷰 방식 이해

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

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


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

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


In [6]:
dframe5 = DataFrame(pop)
dframe5


Out[6]:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6

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


In [86]:
frame3T = frame3.T.copy()
frame3T


Out[86]:
2000 2001 2002
Nevada NaN 2.4 2.9
Ohio 1.5 1.7 3.6

전치행렬: 뷰 방식을 따름. 대신에 copy() 함수를 사용하면 관계를 끊을 수 있음.


In [87]:
frame3['Nevada'].iloc[0] = 1.0
frame3


Out[87]:
Nevada Ohio
2000 1.0 1.5
2001 2.4 1.7
2002 2.9 3.6

In [88]:
frame3T


Out[88]:
2000 2001 2002
Nevada NaN 2.4 2.9
Ohio 1.5 1.7 3.6

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


In [41]:
DataFrame(pop, index=[2001, 2002, 2003])


Out[41]:
Nevada Ohio
2001 2.4 1.7
2002 2.9 3.6
2003 NaN NaN

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


In [42]:
pdata = {'ohio' : frame3['Ohio'][:-1],
         'Nevada': frame3['Nevada'][:2]}
DataFrame(pdata)


Out[42]:
Nevada ohio
2000 NaN 1.5
2001 2.4 1.7

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


In [43]:
frame3.index.name = 'year'; frame3.columns.name = 'state'
frame3


Out[43]:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6

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


In [44]:
frame3.values


Out[44]:
array([[ nan,  1.5],
       [ 2.4,  1.7],
       [ 2.9,  3.6]])

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


In [45]:
frame2.values


Out[45]:
array([[2000, 'Ohio', 1.5, nan],
       [2001, 'Ohio', 1.7, -1.2],
       [2002, 'Ohio', 3.6, nan],
       [2001, 'Nevada', 2.4, -1.5],
       [2002, 'Nevada', 2.9, -1.7]], dtype=object)

색인 개체

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


In [46]:
obj = Series(range(3), index=['a', 'b', 'c'])
index = obj.index
index


Out[46]:
Index([u'a', u'b', u'c'], dtype='object')

In [47]:
index[1:]


Out[47]:
Index([u'b', u'c'], dtype='object')

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


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


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-48-676fdeb26a68> in <module>()
----> 1 index[1] = 'd'

/Users/gslee/anaconda/lib/python2.7/site-packages/pandas/core/indexes/base.pyc in __setitem__(self, key, value)
   1618 
   1619     def __setitem__(self, key, value):
-> 1620         raise TypeError("Index does not support mutable operations")
   1621 
   1622     def __getitem__(self, key):

TypeError: Index does not support mutable operations

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


In [49]:
index = pd.Index(np.arange(3))
index


Out[49]:
Int64Index([0, 1, 2], dtype='int64')

In [50]:
obj2= Series([1.5, -2.5, 0], index=index)
obj2.index is index


Out[50]:
True

특수한 목적으로 축을 색인하는 기능을 개발하기 위해 Index 클래스의 서브클래스를 만들 수 있다.

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


In [51]:
frame3


Out[51]:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6

In [52]:
'Ohio' in frame3.columns


Out[52]:
True

In [53]:
2003 in frame3.index


Out[53]:
False

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

핵심 기능

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

재색인

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


In [54]:
obj = Series([4.5, 7.2, -5.3, 3.6], index= ['d', 'b', 'a', 'c'])
obj


Out[54]:
d    4.5
b    7.2
a   -5.3
c    3.6
dtype: float64

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


In [55]:
obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
obj2


Out[55]:
a   -5.3
b    7.2
c    3.6
d    4.5
e    NaN
dtype: float64

In [56]:
obj.reindex(['a','b','c','d','e'], fill_value=0.0)


Out[56]:
a   -5.3
b    7.2
c    3.6
d    4.5
e    0.0
dtype: float64

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


In [58]:
obj3 = Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj3.reindex(range(6), method='ffill')


Out[58]:
0      blue
1      blue
2    purple
3    purple
4    yellow
5    yellow
dtype: object

현재 누락된 값을 앞이나 뒤에 채워 넣는 것보다 좀 더 세련된 방법으로 보간할 수 있는 방법을 추후 추가할 필요가 있다.

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


In [66]:
frame = DataFrame(np.arange(9).reshape((3, 3)), index=['a', 'b', 'd'],\
                    columns= ['Ohio', 'Texas', 'California'])
frame


Out[66]:
Ohio Texas California
a 0 1 2
b 3 4 5
d 6 7 8

In [67]:
frame2 = frame.reindex(['a', 'b', 'c', 'd'])
frame2


Out[67]:
Ohio Texas California
a 0.0 1.0 2.0
b 3.0 4.0 5.0
c NaN NaN NaN
d 6.0 7.0 8.0

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


In [69]:
states = ['Texas', 'Utah', 'California']
frame.reindex(columns=states)


Out[69]:
Texas Utah California
a 1 NaN 2
b 4 NaN 5
d 7 NaN 8

보간은 로우에 대해서만 이루어진다 (axis 0).


In [74]:
frame.reindex(index=['a', 'b', 'c', 'd'], method='ffill')


Out[74]:
Ohio Texas California
a 0 1 2
b 3 4 5
c 3 4 5
d 6 7 8

reindex는 기존 자료를 변경하지 않는다.


In [75]:
frame


Out[75]:
Ohio Texas California
a 0 1 2
b 3 4 5
d 6 7 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 5 pandas 시작하기 (0) 2016.03.25 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 posted by 신퐁 0 0 2016.03.11 17:45 Python for data analysis Chapter 4. Numpy 기본 : 배열과 벡터 계산 Chapter 4. Numpy : 배열과 벡터 계산

Numerical Python의 줄임말인 Numpy는 고성능의 과학계산 컴퓨팅과 데이터 분석에 필요한 기본 패키지다. Numpy는 이 책에서 사용하는 거의 모든 종류의 고수준의 도구를 작성하는 데 토대가 되는 패키지로 제공하는 기능은 다음과 같다.

-빠르고 메모리를 효율적으로 사용하며 벡터 산술연산과 세련된 브로드캐스팅 기능을 제공하는 다차원 배열인 ndarray -반복문을 작성할 필요 없이 전체 데이터 배열에 대해 빠른 연산을 제공하는 표준 수학 함수 -배열 데이터를 디스크에 쓰거나 읽을 수 있는 도구와 메모리에 올려진 파일을 사용하는 도구 -선형대수, 난수 발생기, 푸리에 변환 기능 -C, C++, 포트란으로 쓰여진 코드를 통합하는 도구

마지막 항목은 생태계 관점에서 봤을 때 가장 중요한 기능으로 Numpy는 사용하기 편한 C API를 제공하며 데이터를 다른 저수준 언어로 쓰여진 외부 라이브러리에 쉽게 전달 할 수 있다. 또한 외부 라이브러리에서 반환된 데이터를 파이썬의 NumPy 배열 형태로 불러올 수도 있다.

NumPy 그 자체로는 고수준의 데이터 분석 기능을 제공하지 않으므로 NumPy 배열과 배열 기반의 컴퓨팅에 대한 이해를 한다면 pandas 같은 도구를 좀 더 효율적으로 사용할 수 있다.

대부분 데이터 분석 애플리케이션에서 중요하게 사용되는 기능

  • 벡터 배열상에서 데이터 개조, 정제, 부분 집합, 필터링, 변형, 다른 종류 연산의 빠른 수행
  • 정렬, 유일 원소 찾기, 집합연산 같은 일반적인 배열 처리 알고리즘
  • 통계의 효과적인 표현과 데이터의 수집/요약
  • 다른 종류의 데이터 묶음을 병합하고 엮기 위한 데이터 정렬과 데이터 간의 관계 조작
  • if - elif - else를 포함하는 반복문 대신 사용할 수 있는 조건절을 표현할 수 있는 배열 표현 (comprehension)
  • 데이터 그룹 전체에 적용할 수 있는 수집, 변형, 함수 적용 같은 데이터 처리.

import numpy as np NumPy는 이런 연산을 위한 기본 라이브러리를 제공한다.

4.1 NumPy ndarray: 다차원 배열 객체

NumPy의 핵심 기능 중 하나는 N차원의 배열 객체 또는 ndarray로 파이썬에서 사용할 수 있는 대규모 데이터 집합을 담을 수 있는 빠르고 유연한 자료 구조다. In[2]: import numpy as np In[3]: data = np.random.randn(2, 3) In[4]: data Out[4]: array([[-0.56285369, -0.06812205, 0.75793005], [ 0.71350143, 0.114842 , -0.78867801]]) In[6]: data * 10 Out[6]: array([[-5.62853693, -0.68122054, 7.5793005 ], [ 7.13501431, 1.14842001, -7.88678008]]) In[7]: data + data Out[7]: array([[-1.12570739, -0.13624411, 1.5158601 ], [ 1.42700286, 0.229684 , -1.57735602]]) ndarray는 같은 종류의 데이터를 담을 수 있는 포괄적인 다차원 배열이며, ndarray의 모든 원소는 같은 자료형이어야만 한다.

모든 배열은 각 차원의 크기를 알려주는 shape라는 튜플과 배열에 저장된 자료형을 알려주는 dtype라는 객체를 가지고 있다.

In[8]: data.shape Out[8]: (2L, 3L) In[9]: data.dtype Out[9]: dtype('float64') 이 장에서는 NumPy의 배열을 사용하는 기초 방법에 대해 소개한다.

배열 위주 프로그래밍과 생각하는 방법에 능숙해지는 것이 파이썬을 이용한 과학계산의 고수가 되는 지름길이다.

4.1.1 ndarray 생성

배열을 생성하는 가장 쉬운 방법은 array함수를 이용하는 것이다. 순차적인 객체(다른 배열도 포함하여)를 받아 넘겨받은 데이터가 들어잇는 새로운 NumPy 배열을 생성한다. 예를 들어 파이썬의 리스트는 변환하기 좋은 예다.

In[10]: data1 = [6, 7.5, 8, 0, 1] In[11]: arr1 = np.array(data1) In[12]: arr1 Out[12]: array([ 6. , 7.5, 8. , 0. , 1. ]) 같은 길이의 리스트가 담겨있는 순차 데이터는 다차원 배열로 변환이 가능하다.

In[23]: data2 = [[1,2,3,4],[5,6,7,8]] In[24]: arr2 = np.array(data2) In[25]: arr2 Out[25]: array([[1, 2, 3, 4], [5, 6, 7, 8]]) In[26]: arr2.ndim
Out[26]: 2
In[27]: arr2.shape Out[27]: (2L, 4L)

명시적으로 지정하지 않는 한 np.array는 생성될 때 적절한 자료형을 추정한다. 그렇게 추정된 자료형은 dtype 객체에 저장되는데, 먼저 살펴본 예로 들어 보면, In[28]: arr1.dtype Out[28]: dtype('float64') In[29]: arr2.dtype Out[29]: dtype('int32') 또한 np.array는 새로운 배열을 생성하기 위한 여러 함수를 가지고 있는데, 예를 들면 zeros와 ones는 주어진 길이나 모양에 각각 0과 1이 들어있는 배열을 생성한다. empty함수는 초기화되지 않은 배열을 생성하는데, 이런 메서드를 사용해서 다차원 배열을 생성하려면 원하는 형태의 튜플을 넘기면 된다.

In[30]: np.zeros(10) Out[30]: array([ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) In[31]: np.zeros((3, 6)) # 6개의 0으로 구성된 리스트 3개 Out[31]: array([[ 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0.]]) In[32]: np.empty((2, 3, 2)) '''2개의 초기화되지 않은 값으로 채워진 배열을 가진 리스트 3개를 가진 리스트를 2개 생성 ''' Out[32]: array([[[ 8.74496193e-322, 0.00000000e+000], [ 0.00000000e+000, 0.00000000e+000], [ 0.00000000e+000, 0.00000000e+000]],

   [[  0.00000000e+000,   0.00000000e+000],
    [  0.00000000e+000,   0.00000000e+000],
    [  0.00000000e+000,   0.00000000e+000]]])

arange는 파이썬의 range 함수의 배열 버전이다. In[33]: np.arange(15) Out[33]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) 배열 생성 함수 목록은 page 119 표 4-1 참조

4.1.2 ndarray의 자료형

자료형, dtype은 ndarray가 특정 데이터를 메모리에서 해석하기 위해 필요한 정보를 담고 있는 특수한 객체다. In[34]: arr1 = np.array([1, 2, 3], dtype=np.float64) In[35]: arr2 = np.array([1, 2, 3], dtype=np.int32) In[36]: arr1.dtype Out[36]: dtype('float64') In[37]: arr2.dtype Out[37]: dtype('int32') dtype가 있기에 NumPy가 강력하면서도 유연한 도구가 될 수 있었는데, 대부분의 데이터는 디스크에서 데이터를 읽고 쓰기 편하도록 하위 레벨의 표현에 직접적으로 맞춰져 있으며, C나 포트란 같은 저수준 언어로 작성된 코드와 쉽게 연동이 가능하다. 산술 데이터의 dtype는 float, int 같은 자료형의 이름과 하나의 원소가 차지하는 비트 수로 이루어진다. 파이썬의 float 객체에서 사용되는 표준 배정밀도 부동소수점 값은 8바이트 혹은 64비트로 이루어지는데, 이 자료형은 NumPy에서 float64로 표현된다.

NumPy 자료형 목록은 page 120 표 4-2 참조

NOTE NumPy의 모든 dtype을 외울 필요는 없다. 주로 사용하게 될 자료형의 일바적인 종류만 신경 쓰면 된다. 주로 대용량 데이터가 메모리나 디스크에 저장되는 방식을 제어해야 할 필요가 있을 때 알아 두면 좋다.

ndarray의 astype 메서드를 사용해서 dtype을 다른 형으로 명시적 변경이 가능하다.

In[41]: arr = np.array([1, 2, 3, 4, 5]) In[42]: arr.dtype Out[42]: dtype('int32') In[43]: float_arr = arr.astype(np.float64) #int32에서 float64로 자료형 변경 In[44]: float_arr.dtype # 변경됨을 확인, 정수형을 부동소수점으로 변환하였다. Out[44]: dtype('float64') 만약 부동소수점 숫자를 정수형으로 변환하면 소수점 아랫자리는 버려질 것이다.

In[45]: arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1]) In[46]: arr Out[46]: array([ 3.7, -1.2, -2.6, 0.5, 12.9, 10.1]) In[47]: arr.astype(np.int32) Out[47]: array([ 3, -1, -2, 0, 12, 10]) # 소숫점 아랫자리가 버려졌다. 숫자 형식의 문자열을 담고 있는 배열이 있다면 astype을 사용하여 숫자로 변환 할 수 있다.

In[50]: numericstrings = np.array(['1.25', '-9.6', '42'], dtype=np.string) #문자열을 가지고 있는 배열 In[51]: numeric_strings.astype(float) #astype명령을 사용해 문자열을 float으로 바꾸어줌, float이라고해도 알아서 바꿔줌 Out[51]: array([ 1.25, -9.6 , 42. ]) int_array = np.arange(10) calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64) int_array.astype(calibers.dtype) #int_array배열의 자료형을 calibers 배열의 dtype인 float64형태로 맞추어줌 , 결과는 생략 4.1.3 배열과 스칼라 간의 연산

배열은 for 반복문을 작성하지 않고 데이터를 일괄처리할 수 있기 때문에 중요하다. 이를 벡터화라고 하는데, 같은 크기의 배열 간 산술연산은 배열의 각 요소 단위로 적용된다. (element wise *) In[52]: arr = np.array([[1., 2., 3.], [4., 5., 6.]]) In[53]: arr Out[53]: array([[ 1., 2., 3.], [ 4., 5., 6.]]) In[54]: arr arr Out[54]: array([[ 1., 4., 9.], [ 16., 25., 36.]]) In[55]: arr- arr Out[55]: array([[ 0., 0., 0.], [ 0., 0., 0.]]) 스칼라 값에 대한 산술연산은 각 요소로 전달된다.

크기가 다른 배열 간의 연산은 브로드캐스팅이라고 한다. 이에 대해서는 12장에서 하게 된다.

4.1.4 색인과 슬라이싱 기초 NumPy 배열 색인에 대해서는 다룰 주제가 많다. 데이터의 부분 집합이나 개별 요소를 선택 하기 위한 수많은 방법이 존재한다. 1차원 배열은 단순하며 표면적으로는 파이썬의 리스트와 유사하게 동작한다.

In[56]: arr = np.arange(10) In[57]: arr Out[57]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In[58]: arr[5] #5번째 수 불러오기 Out[58]: 5 In[59]: arr[5:8] # 6번째에서부터 8번째까지 Out[59]: array([5, 6, 7]) In[60]: arr[5:8] = 12 # 6~8번째 수를 12로 치환 In[61]: arr Out[61]: array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9]) 이처럼 배열 조각에 스칼라 값을 대입하면 12가 선택 영역 전체로 전파된다. (이후로는 브로드캐스팅이라고 한다.) 리스트와의 중요한 차이점은 배열 조각은 원본 배열의 뷰라는 점이다. 즉, 데이터는 복사되지 않고 뷰에 대한 변경은 그대로 원본 배열에 반영된다는 것이다.

In[62]: arr_slice = arr[5:8] In[63]: arr_slice[1] Out[63]: 12 In[64]: arr_slice[1] = 12345 In[65]: arr Out[65]: array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8, 9])

여기서 보면 arr_slice의 변수에서 슬라이싱하고 조작했던게 arr에도 반영된다. 연동되어있다.

NumPy는 대용량 데이터 처리를 염두에 두고 설계되었기 때문에 만약 NumPy가 데이터 복사를 남발한다면 성능과 메모리 문제에 직면할 것이다.

NOTE : 만약에 뷰 대신 ndrray 슬라이스의 복사본을 얻고 싶다면 arr[5:8].copy()를 사용해서 명시적으로 배열을 복사하면 된다.

다차원 배열을 다루려면 좀 더 많은 옵션이 필요하다. 2차원 배열에서 각 색인에 해당하는 요소는 스칼라 값이 아니라 1차원 배열이 된다.

In[66]: arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) In[67]: arr2d[2] Out[67]: array([7, 8, 9]) 따라서 개별 요소는 재귀적으로 접근해야 한다다음의 두 표현은 같다 하지만 그렇게 하기는 귀찮으니 구분된 색인 리스트를 넘기면 된다. In[68]: arr2d[0][2] Out[68]: 3 In[69]: arr2d[0, 2] Out[69]: 3

In[73]: arr3d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) In[74]: arr3d Out[74]: array([[[ 1, 2, 3], [ 4, 5, 6]],

   [[ 7,  8,  9],
    [10, 11, 12]]])

In[75]: arr3d[0] Out[75]: array([[1, 2, 3], [4, 5, 6]]) In[76]: old_values = arr3d[0].copy() In[77]: arr3d[0] = 42 In[78]: arr3d Out[78]: array([[[42, 42, 42], [42, 42, 42]],

   [[ 7,  8,  9],
    [10, 11, 12]]])

In[79]: arr3d[0] = old_values In[80]: arr3d Out[80]: array([[[ 1, 2, 3], [ 4, 5, 6]],

   [[ 7,  8,  9],
    [10, 11, 12]]])

슬라이스 색인

파이썬의 리스트 같은 1차원 객체처럼 ndarray는 익숙한 문법으로 슬라이싱할 수 있다.

In[87]: arr[1:6] Out[87]: array([ 1, 2, 3, 4, 64]) In[88]: arr2d Out[88]: array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) In[89]: arr2d[:2] Out[89]: array([[1, 2, 3], [4, 5, 6]]) In[90]: arr2d[:2, 1:] Out[90]: array([[2, 3], [5, 6]]) In[91]: arr2d[1, :2] Out[91]: array([4, 5]) In[92]: arr2d[2, :1] Out[92]: array([7])

물론 슬라이싱 구문에 값을 대입하면 선택 영역 전체에 값이 할당된다.

4.1.5 불리언 색인 중복된 이름이 포함된 배열이 있다고 하자. 그리고 numpy.random 모듈에 있는 randn 함수를 사용해서 임의의 표준정규분포 데이터를 생성하자.

In[93]: names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe']) In[94]: data = np.random.randn(7, 4) # 4개의 난수를 갖는 리스트를 7개 뱉어라 In[95]: names Out[95]: array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='|S4') In[96]: data Out[96]: array([[-0.5099588 , -0.26549335, 0.47190496, 1.28825708], [-0.22645075, -0.33822525, 1.15948599, 0.62708074], [-1.05545525, -0.29507054, 1.8157602 , -0.23001171], [-0.2477215 , 0.16012906, 1.1783344 , 1.8553623 ], [-0.82402224, 1.02722829, 0.54261483, -0.20464688], [ 1.22017486, 1.25984082, 1.39068215, -0.61045326], [ 1.40302308, -0.73592691, 1.28374355, 0.08466187]]) 각각의 이름은 data 배열의 각 로우에 대응한다고 가정하자. 만약에 전체 로우에서 'Bob'과 같은 이름을 선택하려면 산술연산과 마찬가지로 배열에 대한 비교연산(== 같은) 도 벡터화 된다.

이 불리언 배열은 배열의 색인으로 사용할 수 있다.

이 불리언 배열은 반드시 색인하려는 축의 길이와 동일한 길이를 가져야 한다. 불리언 배열 색인도 슬라이스 또는 숫자 색인과 함께 혼용할 수 있다.

In[101]: names == 'Bob' # names에서 'Bob' 인 것들은 True 아니면 False Out[101]: array([ True, False, False, True, False, False, False], dtype=bool) In[102]: data[names == 'Bob'] # 0, 3에서 True Out[102]: array([[-0.5099588 , -0.26549335, 0.47190496, 1.28825708], [-0.2477215 , 0.16012906, 1.1783344 , 1.8553623 ]]) In[103]: data[names == 'Bob', 2:] # 0,3 의 배열중에 3번째 부터 끝까지 뱉어라 Out[103]: array([[ 0.47190496, 1.28825708], [ 1.1783344 , 1.8553623 ]]) Q) 0,3 번째를 참조하는 값 어케?

'Bob'이 아닌 요소를 선택하려면 != 연산자를 사용하거나 -를 사용해서 조건절을 부정하면 된다.

name != 'Bob' data[-(names == 'Bob')]

세 가지 이름 중에서 두 가지 이름을 선택하려면 &(and) 와 |(or) 같은 논리연산자를 사용한 여러 개의 불리언 조건을 조합하여 사용하면 된다.

In[107]: mask = (names == 'Bob') | (names == 'Will')

In[108]: mask #1,3,4,5만 True

In[107]: mask = (names == 'Bob') | (names == 'Will') In[108]: mask #1,3,4,5만 True Out[108]: array([ True, False, True, True, True, False, False], dtype=bool) In[109]: data[mask] # 1,3,4,5 값만 내놔라 Out[109]: array([[-0.5099588 , -0.26549335, 0.47190496, 1.28825708], [-1.05545525, -0.29507054, 1.8157602 , -0.23001171], [-0.2477215 , 0.16012906, 1.1783344 , 1.8553623 ], [-0.82402224, 1.02722829, 0.54261483, -0.20464688]]) 배열에 불리언 색인을 이용해서 데이터를 선택하면 반환되는 배열의 내용이 바뀌지 않더라도 항상 데이터 복사가 이루어진다.

불리언 배열에 값을 대입하는 것은 상식선에서 이루어지며, data에 저장된 모든 음수를 0으로 대입하려면 아래와 같이 하면 된다. data[data <0] = 0 data[names != 'Joe'] = 7

4.1.6 팬시 색인 팬시 색인은 정수 배열을 사용한 색인을 설명하기 위해 NumPy에서 차용한 단어다. 8X4 크기의 배열이 있다고 하자.

In[120]: arr = np.empty((8, 4)) #초기화 되지 않은 4개의 값을 같는 리스트 8개로 된 배열만들기 In[121]: for i in range(8): # 0~7까지의 숫자를 i에 반복문, array 색인 마다 같은 번호 부여 ... arr[i] = i In[123]: arr Out[121]: array([[ 0., 0., 0., 0.], [ 1., 1., 1., 1.], [ 2., 2., 2., 2.], [ 3., 3., 3., 3.], [ 4., 4., 4., 4.], [ 5., 5., 5., 5.], [ 6., 6., 6., 6.], [ 7., 7., 7., 7.]]) 특정한 순서로 로우를 선택하고 싶다면 그냥 원하는 순서가 명시된 정수가 담긴 ndarray나 리스트를 넘기면 된다.

In[125]: arr[[4, 3, 0, 6]] # array중 5번째, 4번째, 1번째, 7번째 참조 Out[123]: array([[ 4., 4., 4., 4.], [ 3., 3., 3., 3.], [ 0., 0., 0., 0.], [ 6., 6., 6., 6.]])

다차원 색인 배열을 넘기는 것은 조금 다르게 동작하며, 각각의 색인 튜플에 대응하는 1차원 배열이 선택된다.

In[131]: arr = np.arange(32).reshape((8,4)) # 4개의 값을 가진 리스트 8개를 반환하는데 0~31의 값을 갖게 한다. In[132]: arr Out[130]: array([[ 0, 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]]) In[133]: arr[[1, 5, 7, 2], [0, 3, 1, 2]] '''2번째 배열에서 첫 번째 값, 6번째 배열에서 4번째 값, 8번째 베열에서 2번째 값, 3번째 배열에서 3번째 값''' Out[131]: array([ 4, 23, 29, 10]) 이 예제를 잠시 살펴보자 (1,0), (5,3), (7,1) (2,2)에 대응하는 요소가 선택 되었다. 이 예제에서 팬시 색인은 우리의 예상과는 조금 다르게 동작했다.

행렬의 행과 열에 대응하는 사각형 모양의 값이 선택되기를 기대했는데 사실 그렇게 하려면 다음처럼 선택해야 한다.

In[135]: arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]] # 2, 6, 8, 3 행에서 1, 4, 2, 3순서로 담은 배열을 갖게 해라 Out[133]: array([[ 4, 7, 5, 6], [20, 23, 21, 22], [28, 31, 29, 30], [ 8, 11, 9, 10]]) np.ix_ 함수를 사용하면 같은 결과를 얻을 수 있는데, 1차원 정수 배열 2개를 사각형 영역에서 사용할 색인으로 변환해준다.

In[138]: arr[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])] Out[135]: array([[ 4, 7, 5, 6], [20, 23, 21, 22], [28, 31, 29, 30], [ 8, 11, 9, 10]]) 팬시 색인은 슬라이싱과는 달리 선택된 데이터를 새로운 배열로 복사한다.

4.1.7 배열 전치와 축 바꾸기 배열 전차는 데이터를 복사하지 않고 데이터 모양이 바귄 뷰를 반환하는 특별한 기능이다. ndarray는 transpose 메서드와 T라는 이름의 특수한 속성을 가지고 있다.

In[139]: arr = np.arange(15).reshape((3, 5)) In[140]: arr Out[137]: array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) In[141]: arr.T Out[138]: array([[ 0, 5, 10], [ 1, 6, 11], [ 2, 7, 12], [ 3, 8, 13], [ 4, 9, 14]]) 행렬 계산을 할 때 자주 사용하게 될 텐데, 예를 들면 행렬의 내적 는 np.dot을 이용해서 구할 수 있다. In[143]: arr = np.random.randn(6, 3) In[144]: np.dot(arr.T, arr) Out[141]: # (3,6) X (6,3) array([[ 6.22450725, 1.06558449, -0.83079853], [ 1.06558449, 2.82535758, 1.06919888], [-0.83079853, 1.06919888, 4.34671241]]) 다차원 배열의 경우 transpose 메서드는 튜플로 축 번호를 받아서 치환한다. 실제로 계산하려면 머리에 쥐가 날지도 모른다.

In[145]: arr = np.arange(16).reshape((2, 2, 4)) In[146]: arr Out[143]: array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]],

   [[ 8,  9, 10, 11],
    [12, 13, 14, 15]]])

In[147]: arr.transpose((1,0,2)) Out[144]: array([[[ 0, 1, 2, 3], [ 8, 9, 10, 11]],

   [[ 4,  5,  6,  7],
    [12, 13, 14, 15]]])

.T 속성을 이용하는 간단한 전치는 축을 뒤바꾸는 특별한 경우다. ndarray에는 swapaxes 메서드가 있는데 2개의 축 번호를 받아서 배열을 뒤바꾼다.

In[148]: arr Out[145]: array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]],

   [[ 8,  9, 10, 11],
    [12, 13, 14, 15]]])

In[149]: arr.swapaxes(1, 2) Out[146]: array([[[ 0, 4], [ 1, 5], [ 2, 6], [ 3, 7]],

   [[ 8, 12],
    [ 9, 13],
    [10, 14],
    [11, 15]]])

swapaxes도 마찬가지로 데이터를 복사하지 않고 원래 데이터에 대한 뷰를 반환한다.

4.2 유니버설 함수 ufunc라고 불리는 유니버설 함수는 ndarray 안에 있는 데이터 원소별로 연산을 수행하는 함수다. 유니버설 함수는 하나 이상의 스칼라 값을 받아서 하나 이상의 스칼라 결과 값을 반환하는 간단한 함수를 고속으로 수행할 수 있는 벡터화된 래퍼 함수라고 생각하면 된다.

sqrt나 exp같은 간단한 변형을 전체 원소에 적용할 수 있다.

이 경우는 단항 유니버설 함수라 하고,

add나 maximum처럼 2개의 인자를 취해서 단일 배열을 반환하는 함수를 이항 유니버설 함수라고 한다.

In[150]: x = np.random.randn(8) In[151]: y = np.random.randn(8) In[152]: x Out[149]: array([ 0.57483741, 0.39598833, -0.35815866, -1.73518055, 1.09899127, 0.81740202, 0.82778791, -0.34151437]) In[153]: y Out[150]: array([ 0.92638336, 0.70822114, 0.66838903, 0.76546891, 0.22617098, -1.29688062, 1.9109471 , 2.54965647]) In[154]: np.maximum(x, y) Out[151]: array([ 0.92638336, 0.70822114, 0.66838903, 0.76546891, 1.09899127, 0.81740202, 1.9109471 , 2.54965647])

배열 여러개를 반환 저작자 표시 신고 'Python for data analysis' 카테고리의 다른 글 Chapter 5 pandas 시작하기 (0) 2016.03.25 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 posted by 신퐁 0 0 2016.03.11 14:36 Python for data analysis Chapter 3. IPython 소개 Chapter 3. IPython 소개

무위로 행하고, 편안하게 일하라, 작은 것을 크게 여기고, 적은 것을 많게 여겨라, 어려운 일은 쉬울 때 처리하고 큰 일은 작은 것부터 처리하라. - 노자, 도덕경

작가는 'IPython과 텍스트 편집기'를 사용한다고 한다.

근본적으로 IPython은 대화형 컴퓨팅과 소프트웨어 개발 두 가지 모두를 위해 최적의 생산성을 얻도록 설계 되었다. 그리고 다른 프로그래밍 언어와 달리 '편집-컴파일-실행'방식보다 '실행-탐색'방식을 장려하고 있다. 또한 운영체제의 셸, 파일 시스템과도 잘 통합되어 있다.

이런 특징 덕분에 데이터 분석 프로그래밍에서 많은 부분을 차지하는 데이터 탐색, 실험, 오류 판독, 반복 등을 IPython에서는 빠르게 처리할 수 있다.

IPython 프로젝트는 진보된 대화형 파이썬 셸 그 이상이며, GUI 콘솔에서 바로 확인할 수 있는 표와 웹 기반의 대화형 노트북 형식, 가볍고 빠른 병렬 컴퓨팅 엔진을 포함한다. 또한 개발자를 위한 다른 도구와 마찬가지로 개인화가 가능하다.

Ipython은 cmd에서 실행 가능하다.

3.1.1 탭 자동 완성

겉으로 보기에 IPython은 대화형 파이썬 인터프리터와는 조금 다르다. 매스태티카 사용자라면 번호가 붙어있는 입 출력 프롬프트가 친숙하게 느껴질 것이다. 셸에서 입력을 하는 동안 을 누르면 네임스페이스에서 그 시점까지 입력한 내용과 맞아떨어지는 변수(객체, 함수 등)를 자동으로 찾아준다.

NOTE (TAB)을 눌렀을 때 화면에 출력 결과가 너무 많으면 초보자는 헷갈릴 수 있는데, IPython은 아예_로 시작하는 내부 메서드와 속성을 제외시키고 보여준다. 먼저 _를 입력하면 해당 메서드와 속성을 선택할 수 있다. 기본적으로 이런 메서드를 탭 자동 완성에 넣고 싶다면 IPython 환경 설정에서 설정할 수 있다.

나중에 살펴볼 %run 명령어와 함께 조합해서 사용하면 키 입력을 대폭 줄일 수 있다. 또한 자동 완성 기능을 사용하면 함수에서 이름을 가진 인자도 = 기호까지 포함해서 보여준다.

3.1.2 자기관찰 변수 이름 앞이나 뒤에 ? 기호를 붙이면 그 객체에 대한 일반 정보를 출력한다. 함수에도 마찬가지이다. ??를 사용하면 함수의 소스코드를 보여준다.

또 ?는 표준 유닉스나 윈도우 명령행에서와 마찬가지로 IPython의 네임스페이스를 검색하는 데 사용할 수도 있다. *기호로 둘러싸인 문자열과 포함하는 모든 이름을 보여준다. 예를 들어 Numpy의 최상단 네임스페이스 안에서 load를 포함하는 모든 함수 목록을 가져올 수 있다.

3.1.3 %run 명령어 %run 명령어를 사용하면 IPython 세션 안에서 파이썬 프로그램 파일을 불러와 실행할 수 있다.

실행 중인 코드 중지하기 %run을 통해 스크립트가 실행되고 있거나 오랜 실행 시간이 필요한 코드가 실행되고 있는 중간에 <ctrl+C> 를 누르면 KeyboardInterrupt 예외를 발생시킨다. 이 예외는 몇몇 특수한 경우를 제외한 거의 모든 파이썬 프로그램을 즉시 멈추도록 한다.

TIP. 실행 중인 파이썬 코드가 확장 모듈을 호출한 경우에는 <Ctrl +C>를 눌러도 프로그램이 즉각 멈추지 않는데, 이런 경우에는 프로그램의 제어권이 파이썬 인터프리터로 되돌아올 때까지 기다리거나 심각한 경우에는 운영체제의 작업관리자 메뉴를 통해 파이썬 프로세스를 강제로 종료해야 한다.

3.1.4 클립보드에 있는 코드 실행하기

IPython에서 쉽고 빠르게 코드를 실행하려면 클립보드에 있는 내용을 붙여넣어 실행하면 된다. 이 방법은 다소 번거로워 보이지만 실제로 매우 유용하다.

예를 들어, 복잡하거나 실행 시간이 오래 걸리는 애플리케이션을 개발하면서 스크립트를 단위별로 조각내어 실행해보고 싶은 경우나 매 단계별로 현재까지 읽어들인 데이터와 결과를 확인해보고 싶은 경우, 혹은 인터넷에서 찾은 코드를 실행해보고 싶은데 새로 .py 파일을 만들기는 귀찮을 때 유용하게 사용할 수 있다.

<Ctrl + Shift + V >를 누르면 클립보드 내용이 붙여넣기 된다. 클립 보드의 각 줄을 IPython에 한 줄씩 입력하는 방식으로 붙여넣기 한다. 줄 바꿈을 으로 처리한다.

x =5 y = 7 if x > 5: x += 1

y = 8

이 스크립트를 붙여 넣기 한다면 indentical 오류가 발생한다. 이 경우 %paste 혹은 %cpaste 매직 함수를 이용한다.

%paste는 클립보드에 있는 텍스트를 단일 블록으로 실행한다.

%cpaste는 %paste와 유사하지만 코드를 붙여넣을 때 특수한 프롬프트를 제공한다. 실수로 잘못된 코드를 붙여 넣었다면 <Ctrl+C>를 눌러 %cpaste 프롬프트를 빠져나올 수 있다.

3.1.5

저작자 표시 신고 'Python for data analysis' 카테고리의 다른 글 Chapter 5 pandas 시작하기 (0) 2016.03.25 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 posted by 신퐁 0 0 2016.03.04 17:13 Python for data analysis Chapter 2 사례 소개 이 책은 데이터를 생산적으로 다루는 여러 가지 파이썬 도구를 사용하는 방법을 설명한다.

저마다 데이터를 처리하는 목적은 다르겠지만 대개는 다음과 같은 작업을 위해 사용할 것이다.

외부 자료 활용 : 여러 가지 파일 형식과 데이터베이스를 읽고 쓰는 작업 데이터 준비 : 데이터 정비, 수집, 집계, 정규화, 개조, 분리, 분석을 위한 이터 변형 데이터 변형 : 모아놓은 데이터에 수학적, 통계학적 연산을 적용해 새로운 데이터를 도출. 모델링과 계산 : 통계 모델, 기계 학습 알고리즘 또는 다른 계산 도구와 데이터 연결하기 데이터 표현 : 정적 혹은 인터랙티브한 도식화 또는 원문 요약

2.1 bit.ly의 1.usa.gov 데이터

http://1usagov.measuredvoice.com/2012/

에 가서 usagov_bitly_data2012-03-16-1331923249를 받고 압축을 푼뒤 txt파일로 변환하여 주었다.

json모듈의 loads 함수는 내려받은 샘플 파일을 한 줄 씩 읽는다.

path = 'E:\Pfd\usagov_bitly_data2012-03-16-1331923249.txt' open(path).readline() import json path = 'usagov_bitly_data2012-03-16-1331923249.txt' records = [json.loads(line) for line in open(path)] # json 모듈의 loads 함수는 내려받은 샘플 파일을 한 줄 씩 읽는다. records[0] records[0]['tz'] # record의 개별 아이템에서 접근하려는 값의 키를 문자열로 넘겨서 쉽게 읽어올 수 있다. print records[0]['tz'] # 'America/New_York을 출력 형식으로 2.1.1 순수 파이썬으로 표준시간대 세어보기

가장 빈도가 높은 표준시간대 (tz 필드)를 구한다고 가정하자.

2.1.1

time_zones = [rec['tz'] for rec in records] #수행하면 오류 - records의 아이템이 모두 표준시간대 필드를 포함하지 않음. time_zones = [rec['tz'] for rec in records if 'tz' in rec] # 위의 문제를 해결, tz 필드가 있는지 검사 time_zones[:10] # 처음 10개의 표준시간대 보기, 몇 개는 비어있다. 그냥 두고 표준시간대를 셀 때 pandas를 사용.

재사용이 쉽도록 이 로직을 함수로 만들고 time_zones 리스트를 함수에 넘겨서 사용

def get_counts(sequence): counts = {} for x in sequence: if x in counts: counts[x] += 1 else: counts[x] = 1 return counts # time_zones를 순회하면서 표준시간대를 센 후 자료 구조에 저장

from collections import defaultdict def get_counts2(sequence): counts = defaultdict(int) # 값은 0부터 시작할 것임 for x in sequence: counts[x] += 1 return counts

counts = get_counts(time_zones) counts['America/New_York'] # 표준시간대 중 America/New_York의 표준 시간대 개수 len(time_zones) #time_zones의 문자 길이

가장 많이 등장하는 상위 10개의 표준시간대를 알고 싶을때, 세련된 방법으로 사전 이용

def top_counts(count_dict, n=10): value_key_pairs = [(count, tz) for tz, count in count_dict.items()] value_key_pairs.sort() # 오름차순 정렬 return value_key_pairs[-n:]

top_counts(counts) # 가장 많이 등장하는 상위 10개 표준시간대 알 수 있다.

collections.Counter 클래스를 이용하면 지금까지 수행했던 작업을 훨씬 쉽게 할 수 있다.

from collections import Counter

counts = Counter(time_zones) counts.most_common(10) 2.1.2 pandas로 표준시간대 세어보기

pandas의 주요 자료 구조는 DataFrame인데 표나 스프레드시트 같은 형태라고 생각하면 된다. 다음은 records를 가지고 DataFrame을 만드는 방법으로, 매우 간단하다.

In[27]: from pandas import DataFrame, Series import pandas as pd; import numpy as np

In[28]: from pandas import DataFrame, Series

In[29]: import pandas as pd; import numpy as np

In[30]: frame = DataFrame(records)

In[31]: frame

Out[31]: heartbeat a \ 0 NaN Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKi...
1 NaN GoogleMaps/RochesterNY
2 NaN Mozilla/4.0 (compatible; MSIE 8.0; Windows NT ...
3 NaN Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8)...
4 NaN Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKi...
5 NaN Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKi...
In[33]: frame['tz'][:10]

Out[33]: 0 America/New_York 1 America/Denver 2 America/New_York 3 America/Sao_Paulo 4 America/New_York 5 America/New_York 6 Europe/Warsaw 7
8
9
Name: tz, dtype: object In[34]: tz_counts = frame['tz'].value_counts() # 각 시간대 별 갯수를 센다 tz_counts[:10] # 상위 10개 정렬

Out[34]: America/New_York 1251 521 America/Chicago 400 America/Los_Angeles 382 America/Denver 191 Europe/London 74 Asia/Tokyo 37 Pacific/Honolulu 36 Europe/Madrid 35 America/Sao_Paulo 33 Name: tz, dtype: int64 matplotlib 라이브러리로 이 데이터의 그래프 그려보기.

In[38]: clean_tz = frame['tz'].fillna('Missing') # fillna로 없는 값을 대체 In[39]: clean_tz[clean_tz ==''] = 'Unknown' # 비어있는 값은 불리언 배열 색인을 통해 대체 In[40]: tz_counts = clean_tz.value_counts() # tz_count에서 비어있는 표준시간대를 다른 이름으로 바꿔줌 In[41]: tz_counts[:10] Out[41]: America/New_York 1251 Unknown 521 America/Chicago 400 America/Los_Angeles 382 America/Denver 191 Missing 120 Europe/London 74 Asia/Tokyo 37 Pacific/Honolulu 36 Europe/Madrid 35 Name: tz, dtype: int64 tz_counts[:10].plot(kind= 'barh', rot=0) # plot 메서드 이용, counts 객체에 대한 수평 막대 그래프를 만든다.

다음처럼 URL을 축약하는 데 사용한 브라우저, 단말기, 애플리케이션에 대한 정보를 담은 필드가 있다.

In[45]: frame['a'][1] Out[45]: u'GoogleMaps/RochesterNY' In[46]: frame['a'][50] Out[46]: u'Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2' In[47]: frame['a'][51] Out[47]: u'Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; LG-P925/V10e Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1' 'agent'라고 하는 이 흥미로운 문자열 정보를 분석하는 일이 어려워보일 수도 있다. 하지만 다행히도 파이썬의 내장 문자열 함수와 정규표현식을 익히고 나면 식은 죽 먹기다.

이 둘을 이용하면 대략적인 브라우저 종류를 알 수 있는 첫번째 문자열 토큰을 잘라내고 사용자 행동에 대한 또 다른 개요를 만들 수 있다.

In[50]: results = Series([x.split()[0] for x in frame.a.dropna()]) # a에서 공백으로 분리, split() In[51]: results[:5] # 5개 보여주기 Out[51]: 0 Mozilla/5.0 1 GoogleMaps/RochesterNY 2 Mozilla/4.0 3 Mozilla/5.0 4 Mozilla/5.0 dtype: object In[52]: results.value_counts()[:8] # 8개 종류를 쓰는 순서대로 보여주기 Out[52]: Mozilla/5.0 2594 Mozilla/4.0 601 GoogleMaps/RochesterNY 121 Opera/9.80 34 TEST_INTERNET_AGENT 24ㅗ GoogleProducer 21 Mozilla/6.0 5 BlackBerry8520/5.0.0.681 4 dtype: int64 표준시간대 순위표를 윈도우 사용자와 비윈도우 사용자 그룹으로 나눠보기

agent 문자열이 'Windows'를 포함하면 윈도우 사용자라고 가정, agent값이 없는 경우 제외 시킨다.

cframe = frame[frame.a.notnull()] #agent값이 없는 경우 제외

각 행이 윈도우인지 아닌지 검사한다.

2.2 MovieLens의 영화평점 데이터

GroupLens 연구소는 1990년대부터 2000년대 초까지 방대한 영화 평점 데이터를 제공하고 있다. 이 데이터에는 영화평점과 영화에 대한 정보(장르, 개봉연도) 그리고 사용자에 대한 정보( 나이, 우편번호, 성별, 직업)가 포함되어있다. 기계 학습 기법을 여기서 소개하기는 어렵고 이런 종류의 데이터를 요구사항에 맞게 잘 쪼개는 방법만 소개하겠다.

MovieLens 1M( 백만 개) 데이터 셋은 약 6천여 명의 사용자들로부터 수집한 4000여 편의 영화에 대한 백만 개의 영화 평점을 담고 있다.

이 데이터 셋은 평점, 사용자 정보, 영화 정보, 이 세 가지 테이블로 나뉘어 있는데, zip 파일을 풀어서 각 테이블을 pandas.read_table 함수를 사용해 DataFrame 객체로 불러오자.

저작자 표시 신고 'Python for data analysis' 카테고리의 다른 글 Chapter 5 pandas 시작하기 (0) 2016.03.25 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 posted by 신퐁 0 0 2016.03.04 12:20 Python for data analysis Chaper 1 시작하기 전에 1.1 이 책은? 파이썬으로 데이터를 다루는 다양한 기본적인 방법을 소개.

1.2 왜 데이터 분석을 위한 파이썬인가?

파이썬은 스크립트 언어라고 한다.

이름 자체에 중요한 소프트웨어를 만드는 데는 사용하지 못한다는 의미를 함축한다.

과확계산 컴퓨팅 커뮤니티에서 사용된다.

1.2.1 접착제처럼 사용하는 파이썬

C, C++, 포트란 코드와의 통합이 쉽다.

프로그램은 실행 시간의 대부분을 차지하는 작은 부분의 코드와 실행 시간을 얼마 차지하지 않는 많은 양의 '글루 코드'로 이루어져 있다.

1.2.2 한 가지 언어만 사용

파이썬은 연구를 하거나 프로토 타입을 만드는 데 적합한 언어일 분만 아니라 실제 시스템을 개발하는 데도 적합하기에 갈수록 인기를 더하고 있다.

1.2.3 파이썬을 사용하면 안 되는 경우.

파이썬은 인터프리터 언어라 Java나 C++같은 컴파일 언어보다 많이 느리다.

동시다발적인 멀티스레드를 처리하거나 CPU에 집중된 많은 스레드를 처리하는 애플리케이션에 적합한 언어는 아니다. GIL때문인데, 이 메커니즘은 인터프리터가 한 번에 하나의 파이썬 바이트 코드 명령만 실행하도록 한다.

1.3 필수 파이썬 라이브러리 지금부터 사용할 라이브러리와 과학계산용 파이썬 환경에 익숙하지 않은 독자를 위해 간단히 라이브러리를 소개한다.

1.3.1 NumPy NumPy는 Numerical Python의 줄임말로, 과학계산용 파운데이션 패키지다.

기능: 빠르고 효율적인 다차원 배열 객체 ndarray 배열 원소를 다루거나 배열 간의 수학 계산을 수행하는 함수 디스크로부터 배열 기반의 데이터를 읽거나 쓸 수 있는 도구 선형대수 계산, 푸이에 변환, 난수 발생기 파이썬과 C, C++그리고 포트란 코드를 통합하는 도구

파이썬에 빠른 배열 처리 기능을 제공한다. 데이터 분석에서는 아록르짐에 사용할 데이터 컨테이너의 역하을 한다. 수치 데이터라면 NumPy 배열은 파이썬 기본 자료 구조보다 훨씬 효율적인 방법으로 데이터를 저장하고 다룰 수 있다. 또한 C나 포트란 같은 저수준 언어로 이뤄진 라이브러리는 NumPy 배열에 저장된 데이터를 복사하지 않고 사용할 수 있다.

1.3.2 pandas pandas : 구조화된 데이터를 빠르고 쉬우면서도 다양한 형식으로 가공할 수 있는 풍부한 자료 구조와 함수를 제공한다. 파이썬을 강력하고 생산적인 데이터 분석 환경으로 만드는 데 꼭 필요하다.

주로 pandas의 주요 객체인 DataFrame을 다룰 텐데, 이 객체는 2차원 표 또는 행과 열을 나타내는 자료 구조다.

NumPy의 고성능 배열 계산 기능과 스프레드 시트, SQL 같은 관계형 데이터베이스의 유연한 데이터 조작 기능을 조합한 것이다. 세련된 인덱싱 기능으로 쉽게 데이터를 재배치하고 잘게 조각내거나 집계하고 부분집합을 구할 수도 있다.

1.3.3 matplotlib 그래프나 2차원 데이터 시각화를 생성하는 유명한 파이썬 라이브러리. 출판물에 필요한 그래프를 만드는 데 맞춰졌으며 IPython에 통합되어 있어 편리하게 데이터를 살펴보고 그래프를 만들어 낼 수 있다.

1.3.4 IPython IPython은 표준 과학계산용 파이썬 도구 모음에 포함된 컴포넌트이며, 인터랙티브하고 강력한 생산적인 환경을 제공한다. 파이썬 코드를 작성하고 테스트하고 디버깅을 할 수 있는 향상된 파이썬 셸을 제공한다. 특히 데이터를 처리하고 matplotlib으로 데이터를 시각화 하는 데 매우 유용하다. 실행, 디버깅, 테스트 같은 파이썬을 필요로 하는 작업을 대부분 수행할 수 있다.

IPython을 웹브라우저와 연결할 수 있는 매스메티카 스타일의 HTML 노트북 기능 그래프를 즉시 그려보거나 여러 줄을 편집할 수 있는 기능 그리고 문법 강조 기능을 가진 Qt 프레임워크 기반의 GUI 콘솔

병렬 분산 컴퓨팅을 위한 기반 구조

1.3.5 SciPy 과학계산 컴퓨팅 영역의 여러 기본 문제를 다루는 패키지 모음이다.

scipy.integrate : 수치적분 루틴과 미분방정식 해법기 scipy.linalg: numpy.linalg에서 제공하는 것보다 더 확장된 선형대수 루틴과 매트릭스 분해 scipy.optimize : 함수 최적화기와 방정식의 근을 구하는 알고리즘 scipy.signal : 시그널 프로세싱 도구 scipy.sparse : 희소 행렬과 희소 선형 시스템 풀이법 scipy.special : 감마 함수처럼 흔히 사용되는 수학 함수를 구현한 포트란 라이브러리인 SPECFUN 확장 scipy.stats : 표준 연속/이산 확률 분포와 다양한 통계 테스트, 그리고 좀 더 기술적인 통계 도구 scipy.weave : 배열 계산을 빠르게 하기 위해 인라인 C++ 코드를 사용하는 도구

NumPy와 SciPy를 함께 사용하면 확장 애드온을 포함한 MATLAB를 완벽하게 대체 가능하다.

1.4 설치와 설정 1.4.1 윈도우 1.4.2 애플 OS X 1.4.3 리눅스 1.4.4 파이썬 2.x와 파이썬 3.x 1.4.5 통합 개발 환경 1.5 커뮤니티와 컨퍼런스 1.6 이 책을 살펴보는 방법 1.6.1 예제코드 1.6.2 예제에 사용된 데이터

1.6.3 import 컨벤션 import numpy as np import pandas as pd import matplotlib..pyplot as plt

1.6.4 용어

의사 코드 : 알고리즘이나 과정을 실제 유효한 소스 코드는 아니지만 형식을 코드처럼 표현한 것

신태틱 슈거 : 새로운 기능은 아니지만 기존에 비해 좀 더 편리하고 타이핑도 간편해지는 프로그래밍 문법

1.7 감사의 말

저작자 표시 신고 'Python for data analysis' 카테고리의 다른 글 Chapter 5 pandas 시작하기 (0) 2016.03.25 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 posted by 신퐁 0 0 prev 1 next RSS FEED 신퐁's Blog is powered by DAUM / designed by TISTORY

Tistory로그인

출처: http://sinpong.tistory.com/category/Python for data analysis [새로운 바람]