판다스(pandas) 시작하기¶
판다스는 파이썬에서 자료 정제 및 분석을 빠르고 쉽게 할 수 있는 자료
구조와 관리 도구들을 가지고 있다. 판다스는 수치 해석 도구들인 NumPy
,
SciPy
및 자료 분석 라이브러리인 statsmodels
,
scikit-learn
과 시각화 도구인 matplotlib
들과 함께 사용된다.
판다스는 NumPy
에서 사용되는 배열 기반 연산과 for
문 없이
계산하는 벡터화 계산을 기반으로 한다. 넘파이와 판다스의 큰 차이점은
넘파이는 같은 형에 대한 배열을 주로 다루는 반면 판다스는 이질적인
자료형을 다루는 것에 더 적합하다는 것이다.
앞으로 나오는 모든 코딩에서 다음과 같은 import
관례를 따른다.
import pandas as pd
pd.
을 사용하면 pandas.
를 의미하는 것이다.
판다스 자료 구조 소개¶
판다스를 다루기 위해서 2가지 중요한 자료 구조 시리즈(Series
)와
데이터프레임(DataFrame
)에 익숙해져야 한다.
시리즈(Series)¶
시리즈는 연속적인 값들로 이루어진 일차원 배열과 인덱스로 불리는 자료 라벨(label) 배열로 이루어진 객체이다. 리스트로부터 간단한 시리즈를 다음과 같이 만들 수 있다.
In [2]:
import pandas as pd
import numpy as np
rng = np.random.RandomState(1234)
In [3]:
obj = pd.Series([-5, 0, 10, 3])
obj
Out[3]:
0 -5
1 0
2 10
3 3
dtype: int64
출력된 시리즈에서 왼쪽은 인덱스이고 오른쪽 값이다. 인덱스를 지정하지 않으면 기본값으로 0부터 N - 1의 숫자가 설정된다. 여기서 N은 자료의 갯수이다. 시리즈 객체로부터 인덱스 객체와 값에 대한 넘파이 배열을 얻을 수 있다.
In [4]:
obj.index
Out[4]:
RangeIndex(start=0, stop=4, step=1)
In [5]:
obj.values
Out[5]:
array([-5, 0, 10, 3], dtype=int64)
종종 각각의 자료에 대응되는 자료 라벨을 직접 지정하여 시리즈를 만드는 것이 바람직할 때가 있다.
In [6]:
obj2 = pd.Series([-5, 0, 10, 3], index=['a', 'b', 'd', 'c'])
obj2
Out[6]:
a -5
b 0
d 10
c 3
dtype: int64
In [7]:
obj2.index
Out[7]:
Index(['a', 'b', 'd', 'c'], dtype='object')
넘파이와는 다르게 하나의 값 또는 여러 값들을 선택할 때 라벨을 사용할 수 있다.
In [8]:
obj2['a']
Out[8]:
-5
인덱스를 통해서 값을 변경할 수 있다.
In [9]:
obj2['c'] = -12
인덱스 리스트를 통해서 대응되는 값들을 선택할 수 있다.
In [10]:
obj2[['a', 'd', 'c']]
Out[10]:
a -5
d 10
c -12
dtype: int64
수학 함수, 스칼로 곱, 논리 배열에 대한 필터링과 같은 넘파이 함수 또는 연산들을 하더라도 인덱스 - 값 연결은 계속 보존된다.
In [11]:
obj2[obj2 < 0]
Out[11]:
a -5
c -12
dtype: int64
In [12]:
obj2 * 2
Out[12]:
a -10
b 0
d 20
c -24
dtype: int64
In [13]:
import numpy as np
np.exp(obj2)
Out[13]:
a 0.006738
b 1.000000
d 22026.465795
c 0.000006
dtype: float64
시리즈를 사전 형식으로 생각하여 열쇠(key)는 인덱스에, 값은 값에 대응하여 생각할 수 있다.
In [14]:
'a' in obj2
Out[14]:
True
사전형 자료를 다음과 같이 시리즈로 만들 수 있다.
In [15]:
인구 = {'서울': 9776, '부산':3429, '대전':1531, '세종':276, '충남':2148, '대구':2465}
시리3 = pd.Series(인구)
시리3
Out[15]:
대구 2465
대전 1531
부산 3429
서울 9776
세종 276
충남 2148
dtype: int64
사전형을 시리즈로 만들 때 사전의 열쇠에 대한 정렬 순서로 저장이된다. 순서를 바꿔 저장하고 싶으면 열쇠에 대한 리스트를 만들어 인덱스에 넘겨주면 된다.
In [16]:
도시들 = ['대전', '세종', '대구', '충남', '인천']
시리4 = pd.Series(인구, index=도시들)
시리4
Out[16]:
대전 1531.0
세종 276.0
대구 2465.0
충남 2148.0
인천 NaN
dtype: float64
인천을 제외한 도시들은 인구 리스트와 적절히 연결되어 값을 설정했다.
인천은 인구 리스트에 없기 때문에 NaN
을 출력했다. 이러한 값을
판다스에서는 소실값(missing values)이라고 부른다. isnull
과
notnull
함수들을 이용해 소실 자료들을 찾을 수 있다.
In [17]:
pd.isnull(시리4)
Out[17]:
대전 False
세종 False
대구 False
충남 False
인천 True
dtype: bool
In [18]:
pd.notnull(시리4)
Out[18]:
대전 True
세종 True
대구 True
충남 True
인천 False
dtype: bool
시리즈의 인스턴스 메소드로도 접근할 수 있다.
In [19]:
시리4.isnull()
Out[19]:
대전 False
세종 False
대구 False
충남 False
인천 True
dtype: bool
시리즈끼리의 산술 연산에서 자동으로 같은 인덱스끼리 계산을 해준다.
In [20]:
시리3
Out[20]:
대구 2465
대전 1531
부산 3429
서울 9776
세종 276
충남 2148
dtype: int64
In [21]:
시리4
Out[21]:
대전 1531.0
세종 276.0
대구 2465.0
충남 2148.0
인천 NaN
dtype: float64
In [22]:
시리3 + 시리4
Out[22]:
대구 4930.0
대전 3062.0
부산 NaN
서울 NaN
세종 552.0
인천 NaN
충남 4296.0
dtype: float64
시리즈 객체와 객체의 인덱스는 판다스의 다른 주요 영역과 통합되는
name
속성을 가지고 있다.
In [23]:
시리4.name = "인구통계"
시리4.index.name = "도시들"
시리4
Out[23]:
도시들
대전 1531.0
세종 276.0
대구 2465.0
충남 2148.0
인천 NaN
Name: 인구통계, dtype: float64
시리즈 인덱스는 다음과 같이 변경할 수 있다.
In [24]:
obj
Out[24]:
0 -5
1 0
2 10
3 3
dtype: int64
In [25]:
obj.index = ['김', '최', '윤', '홍']
obj
Out[25]:
김 -5
최 0
윤 10
홍 3
dtype: int64
데이터프레임(DataFrame)¶
데이터프레임은 같은 크기의 자료들로 이루어진 열들을 차례로 모아 놓은 직사각 모양의 자료형이다. 데이터프레임의 열과 행은 각각 인덱스를 가지고 있다. 데이터프레임을 만드는 방법은 여러가지고 있지만 가장 쉬운 방법은 같은 크기의 리스트 또는 넘파이 배열을 값으로 갖는 사전형을 사용하는 것이다.
In [26]:
데이터 = {'지역': ['대전', '대전', '대전', '서울', '서울', '서울'],
'년도': [2015, 2016, 2017, 2015, 2016, 2017],
'인구': [1542, 1535, 1531, 9941, 9852, 9776]}
프레임 = pd.DataFrame(데이터)
시리즈에서와 같이 데이터프레임도 자동으로 인덱스를 설정하고 열 인덱스 이름으로 정렬을 한다.
In [27]:
프레임
Out[27]:
년도 | 인구 | 지역 | |
---|---|---|---|
0 | 2015 | 1542 | 대전 |
1 | 2016 | 1535 | 대전 |
2 | 2017 | 1531 | 대전 |
3 | 2015 | 9941 | 서울 |
4 | 2016 | 9852 | 서울 |
5 | 2017 | 9776 | 서울 |
head
메소드를 통해 상위 5개의 자료들만 볼 수 있다.
In [28]:
프레임.head()
Out[28]:
년도 | 인구 | 지역 | |
---|---|---|---|
0 | 2015 | 1542 | 대전 |
1 | 2016 | 1535 | 대전 |
2 | 2017 | 1531 | 대전 |
3 | 2015 | 9941 | 서울 |
4 | 2016 | 9852 | 서울 |
선택 사항 columns
리스트를 지정함으로 원하는 순서로 열을 나열할 수
있다.
In [29]:
pd.DataFrame(데이터, columns=['지역', '인구', '년도'])
Out[29]:
지역 | 인구 | 년도 | |
---|---|---|---|
0 | 대전 | 1542 | 2015 |
1 | 대전 | 1535 | 2016 |
2 | 대전 | 1531 | 2017 |
3 | 서울 | 9941 | 2015 |
4 | 서울 | 9852 | 2016 |
5 | 서울 | 9776 | 2017 |
사전에 없는 열이름을 지정하면 소실값이 지정이 된다.
In [30]:
프레임2 = pd.DataFrame(데이터, columns=['지역', '인구', '년도', '자산'],
index=['하나', '둘', '셋', '넷', '다섯', '여섯'])
프레임2
Out[30]:
지역 | 인구 | 년도 | 자산 | |
---|---|---|---|---|
하나 | 대전 | 1542 | 2015 | NaN |
둘 | 대전 | 1535 | 2016 | NaN |
셋 | 대전 | 1531 | 2017 | NaN |
넷 | 서울 | 9941 | 2015 | NaN |
다섯 | 서울 | 9852 | 2016 | NaN |
여섯 | 서울 | 9776 | 2017 | NaN |
In [31]:
프레임2.columns
Out[31]:
Index(['지역', '인구', '년도', '자산'], dtype='object')
데이터프레임 열은 사전식으로 접근하거나 속성으로 접근할 수 있다. 다음은 사전식 열쇠를 통한 접근이다.
In [32]:
프레임2['지역']
Out[32]:
하나 대전
둘 대전
셋 대전
넷 서울
다섯 서울
여섯 서울
Name: 지역, dtype: object
다음은 속성을 통한 접근이다.
In [33]:
프레임2.지역
Out[33]:
하나 대전
둘 대전
셋 대전
넷 서울
다섯 서울
여섯 서울
Name: 지역, dtype: object
사전식으로 접근할 때는 열쇠 이름이 어떤 것이 와도 되지만 속성식으로
접근할 때는 파이썬 변수 이름 규칙을 따라야 한다. 행을 접근할 때는 나중에
자세히 나오겠지만 loc
속성을 통해 할 수 있다.
In [34]:
프레임2.loc['하나']
Out[34]:
지역 대전
인구 1542
년도 2015
자산 NaN
Name: 하나, dtype: object
열에 값을 지정하는 것은 스칼라 또는 배열을 통해 할 수 있다.
In [35]:
프레임2['자산'] = 20
프레임2
Out[35]:
지역 | 인구 | 년도 | 자산 | |
---|---|---|---|---|
하나 | 대전 | 1542 | 2015 | 20 |
둘 | 대전 | 1535 | 2016 | 20 |
셋 | 대전 | 1531 | 2017 | 20 |
넷 | 서울 | 9941 | 2015 | 20 |
다섯 | 서울 | 9852 | 2016 | 20 |
여섯 | 서울 | 9776 | 2017 | 20 |
In [36]:
프레임2.자산 = np.arange(6)
프레임2
Out[36]:
지역 | 인구 | 년도 | 자산 | |
---|---|---|---|---|
하나 | 대전 | 1542 | 2015 | 0 |
둘 | 대전 | 1535 | 2016 | 1 |
셋 | 대전 | 1531 | 2017 | 2 |
넷 | 서울 | 9941 | 2015 | 3 |
다섯 | 서울 | 9852 | 2016 | 4 |
여섯 | 서울 | 9776 | 2017 | 5 |
배열을 할당할 때는 배열의 크기가 데이터프레임의 행의 크기와 같아야 한다. 시리즈를 데이터프레임에 열로 지정할 수 있다. 이때 시리지 인덱스 이름과 데이터프레임 인덱스 이름이 같은 것만 값이 설정이 되고 나머지는 소실값이 된다.
In [37]:
시 = pd.Series([20, 19, 3], index=['둘', '넷', '다섯'])
프레임2['자산'] = 시
프레임2
Out[37]:
지역 | 인구 | 년도 | 자산 | |
---|---|---|---|---|
하나 | 대전 | 1542 | 2015 | NaN |
둘 | 대전 | 1535 | 2016 | 20.0 |
셋 | 대전 | 1531 | 2017 | NaN |
넷 | 서울 | 9941 | 2015 | 19.0 |
다섯 | 서울 | 9852 | 2016 | 3.0 |
여섯 | 서울 | 9776 | 2017 | NaN |
사전형식으로 데이터프레임의 열을 추가할 때 열 이름이 존재하지 않으면
새로운 열을 추가한다. 주의해야 할 것은 속성 프레임2.덤
을
이용해서는 새로운 열을 추가할 수 없다.
In [38]:
프레임2['덤'] = 프레임2.지역 == '대전'
프레임2
Out[38]:
지역 | 인구 | 년도 | 자산 | 덤 | |
---|---|---|---|---|---|
하나 | 대전 | 1542 | 2015 | NaN | True |
둘 | 대전 | 1535 | 2016 | 20.0 | True |
셋 | 대전 | 1531 | 2017 | NaN | True |
넷 | 서울 | 9941 | 2015 | 19.0 | False |
다섯 | 서울 | 9852 | 2016 | 3.0 | False |
여섯 | 서울 | 9776 | 2017 | NaN | False |
del
함수를 이용해서 열을 제거할 수 있다.
In [39]:
del 프레임2['덤']
프레임2
Out[39]:
지역 | 인구 | 년도 | 자산 | |
---|---|---|---|---|
하나 | 대전 | 1542 | 2015 | NaN |
둘 | 대전 | 1535 | 2016 | 20.0 |
셋 | 대전 | 1531 | 2017 | NaN |
넷 | 서울 | 9941 | 2015 | 19.0 |
다섯 | 서울 | 9852 | 2016 | 3.0 |
여섯 | 서울 | 9776 | 2017 | NaN |
속성을 이용해서 제거할 수는 없다.
In [40]:
del 프레임2.인구
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-40-b5d7c36d7aeb> in <module>()
----> 1 del 프레임2.인구
AttributeError: 인구
자주 사용되는 자료형으로 사전의 사전형 꼴이다.
In [41]:
인구1 = {'인구': {'하나': 1542, '여섯': 9776, '둘': 1535}, '지역': {'하나': '대전', '여섯': '서울', '둘': '대전', '셋':'대전'}}
프레임3 = pd.DataFrame(인구1)
프레임3
Out[41]:
인구 | 지역 | |
---|---|---|
둘 | 1535.0 | 대전 |
셋 | NaN | 대전 |
여섯 | 9776.0 | 서울 |
하나 | 1542.0 | 대전 |
판다스는 바깥 열쇠는 열 인덱스 이름으로 안쪽 열쇠는 행 인덱스 이름으로 해석한다.
In [42]:
프레임3.T
Out[42]:
둘 | 셋 | 여섯 | 하나 | |
---|---|---|---|---|
인구 | 1535 | NaN | 9776 | 1542 |
지역 | 대전 | 대전 | 서울 | 대전 |
데이터프레임의 인덱스와 열이 name
속성을 지정하면 그것이 표시된다.
In [43]:
프레임3.index.name = '번호'
프레임3.columns.name = '분류'
프레임3
Out[43]:
분류 | 인구 | 지역 |
---|---|---|
번호 | ||
둘 | 1535.0 | 대전 |
셋 | NaN | 대전 |
여섯 | 9776.0 | 서울 |
하나 | 1542.0 | 대전 |
시리즈와 비슷하게 데이터프레임의 values
속성을 이용하면 이차원 값
배열을 얻을 수 있다.
In [44]:
프레임3.values
Out[44]:
array([[1535.0, '대전'],
[nan, '대전'],
[9776.0, '서울'],
[1542.0, '대전']], dtype=object)
다음 표는 데이터프레임 첫번째 인자로 들어갈 수 있는 자료형들이다.
Type | Notes |
---|---|
2D ndarray | A matrix of data, passing optional row and column labels |
dict of arrays, lists, or tuples | Each sequence becomes a column in the DataFrame; all sequences must be the same length |
NumPy structured /record array | Treated as the “dict of arrays” case |
dict of Series | Each value becomes a column; indexes from each Series are unioned together to form the result’s row index if no explicit index is passed |
dict of dicts | Each inner dict becomes a column; keys are unioned to form the row index as in the “dict of Series” case |
List of dicts or Series | Each item becomes a row in the DataFrame; union of dict keys or Series indexes become the DataFrame’s column labels |
List of lists or tuples | Treated as the “2D ndarray” case |
Another DataFrame | The DataFrame’s indexes are used unless different ones are passed |
NumPy MaskedArra y | Like the “2D ndarray” case except masked values become NA/missing in the DataFrame result |
인덱스 객체(Index Objects)¶
판다스
인덱스(Index
)
객체는 축 라벨과 축 이름을 관리한다. 시리즈 또는 데이터프레임을 만들 때
선택 인자 index=
에 라벨 리스트를 대입하면 자동으로 인덱스 객체로
만든다.
In [45]:
시 = pd.Series(range(3), index=['a', 'b', 'c'])
시
Out[45]:
a 0
b 1
c 2
dtype: int64
In [46]:
인덱스 = 시.index
인덱스
Out[46]:
Index(['a', 'b', 'c'], dtype='object')
인덱스 객체는 그 자체로는 불변 객체이다.
In [47]:
인덱스[0] = 'A'
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-47-ed214f2ed8a2> in <module>()
----> 1 인덱스[0] = 'A'
~\Anaconda3\lib\site-packages\pandas\core\indexes\base.py in __setitem__(self, key, value)
1722
1723 def __setitem__(self, key, value):
-> 1724 raise TypeError("Index does not support mutable operations")
1725
1726 def __getitem__(self, key):
TypeError: Index does not support mutable operations
그러나 Index.values
속성을 이용하면 값을 변경할 수 있다.
In [48]:
인덱스.values
Out[48]:
array(['a', 'b', 'c'], dtype=object)
다음과 같이 첫번째 인덱스 라벨을 소문자 a
에서 대문자 A
로
변경할 수 있다.
In [49]:
인덱스.values[0] = 'A'
인덱스
Out[49]:
Index(['A', 'b', 'c'], dtype='object')
그러면 인덱스 객체를 사용하고 있는 시리즈 객체의 인덱스도 다음과 같이 변경이 되는 것을 볼 수 있다.
In [50]:
시
Out[50]:
A 0
b 1
c 2
dtype: int64
인덱스 객체를 배열처럼 다룰 수 있을뿐만아니라 집합처럼 다룰 수도 있다.
In [51]:
프레임3
Out[51]:
분류 | 인구 | 지역 |
---|---|---|
번호 | ||
둘 | 1535.0 | 대전 |
셋 | NaN | 대전 |
여섯 | 9776.0 | 서울 |
하나 | 1542.0 | 대전 |
In [52]:
프레임3.columns
Out[52]:
Index(['인구', '지역'], dtype='object', name='분류')
In [53]:
'인구' in 프레임3.columns
Out[53]:
True
In [54]:
'여섯' in 프레임3.index
Out[54]:
True
파이썬 집합과는 다르게 판다스 인덱스는 중복된 값을 가질 수 있다.
In [55]:
중복인덱 = pd.Index(['하나', '둘', '하나', '셋'])
중복인덱
Out[55]:
Index(['하나', '둘', '하나', '셋'], dtype='object')
중복된 인덱스 이름을 사용하면 같은 이름으로 된 모든 항목이 선택된다. 인덱스는 다음과 같은 집합 연산들이 가능하다.
Method | Description |
---|---|
append | Concatenate with additional Index objects, producing a new Index |
difference | Compute set difference as an Index |
intersecti on | Compute set intersection |
union | Compute set union |
isin | Compute boolean array indicating whether each value is contained in the passed collection |
delete | Compute new Index with element at index i deleted |
drop | Compute new Index by deleting passed values |
insert | Compute new Index by inserting element at index i |
is_monoto nic | Returns True if each element is greater than or equal to the previous element |
is_unique | Returns True if the Index has no duplicate values |
unique | Compute the array of unique values in the Index |
핵심 기능들¶
인덱싱 다시 하기¶
새로운 인덱스에 맞게 기존의 자료를 재배열하여 새로운 객체를 만들어내는 메소드 reindex는 판다스의 중요한 메소드 중의 하나이다. 예를 들어보자.
In [56]:
시 = pd.Series([3, -8, 2, 0], index=['d', 'b', 'a', 'c'])
시
Out[56]:
d 3
b -8
a 2
c 0
dtype: int64
시리즈 객체에서 reindex
메소드를 부르면 인자로 넘겨준 새로운
인덱스에 맞게 자료들을 재배열하고 만일 인덱스가 없으면 소실값(missing
value)을 설정한다.
In [57]:
시.reindex(['a', 'b', 'c', 'd', 'e'])
Out[57]:
a 2.0
b -8.0
c 0.0
d 3.0
e NaN
dtype: float64
시계열 자료일 경우 reindex
할 때 소실값들을 채우는 것이 더 좋을 때가
있다. 이 때 사용할 수 있는 선택인자로 method=
가 있다.
method='ffill'
은 앞에 있는 자료로 뒤에 있는 소실값들을 채울 수
있다.
In [58]:
시1 = pd.Series(['빨강', '파랑', '초록'], index=[0, 2, 4])
시1
Out[58]:
0 빨강
2 파랑
4 초록
dtype: object
In [59]:
시1.reindex(range(6), method='ffill')
Out[59]:
0 빨강
1 빨강
2 파랑
3 파랑
4 초록
5 초록
dtype: object
데이터프레임의 경우, reindex
는 행, 열 또는 둘다를 바꿀 수 있다.
하나의 인덱스만 주어지면 행에대해서 재 인덱싱을 한다.
In [60]:
import numpy as np
프 = pd.DataFrame(np.arange(6).reshape(3, 2), index=range(0, 5, 2), columns=['A', 'B'])
프
Out[60]:
A | B | |
---|---|---|
0 | 0 | 1 |
2 | 2 | 3 |
4 | 4 | 5 |
다음은 행에 대해서 재 인덱싱을 한 경우이다.
In [61]:
프.reindex(range(5))
Out[61]:
A | B | |
---|---|---|
0 | 0.0 | 1.0 |
1 | NaN | NaN |
2 | 2.0 | 3.0 |
3 | NaN | NaN |
4 | 4.0 | 5.0 |
다음은 열에 대해서만 재 인덱싱을 한 경우이다.
In [62]:
프.reindex(columns=['B', 'C', 'A'])
Out[62]:
B | C | A | |
---|---|---|---|
0 | 1 | NaN | 0 |
2 | 3 | NaN | 2 |
4 | 5 | NaN | 4 |
다음은 행, 열 모두에 재인덱싱을 한 것이다.
In [63]:
프.reindex(range(5), columns=['B', 'C', 'A'])
Out[63]:
B | C | A | |
---|---|---|---|
0 | 1.0 | NaN | 0.0 |
1 | NaN | NaN | NaN |
2 | 3.0 | NaN | 2.0 |
3 | NaN | NaN | NaN |
4 | 5.0 | NaN | 4.0 |
다음은 reindex
메소드의 인자들이다.
Argument | Description |
---|---|
index | New sequence to use as index. Can be Index instance or any other sequence-like Python data structure. An Index will be used exactly as is without any copying. |
method | Interpolation (fill) method; ‘ffill’ fills forward, while ‘bfill’ fills backward. |
fill_va lue | Substitute value to use when introducing missing data by reindexing. |
limit | When forward- or backfilling, maximum size gap (in number of elements) to fill. |
toleranc e | When forward- or backfilling, maximum size gap (in absolute numeric distance) to fill for inexact matches. |
level | Match simple Index on level of MultiIndex; otherwise select subset of. |
copy | If True, always copy underlying data even if new index is equivalent to old index; if False, do not copy the data when the indexes are equivalent. |
축으로부터 항목 제거¶
축으로부터 지정된 항목을 제거하는 것은 어렵지 않다. drop
메소드를
이용해서 지정된 축으로부터 항목들을 제거하고 새로운 객체를 반환한다.
In [64]:
시 = pd.Series(range(5), index=['a', 'b', 'c', 'd', 'e'])
시
Out[64]:
a 0
b 1
c 2
d 3
e 4
dtype: int64
In [65]:
새시 = 시.drop('c')
새시
Out[65]:
a 0
b 1
d 3
e 4
dtype: int64
In [66]:
시.drop(['a', 'c'])
Out[66]:
b 1
d 3
e 4
dtype: int64
데이터프레임의 경우는 두 개의 축으로부터 항목들을 제거할 수 있다.
In [67]:
프 = pd.DataFrame(np.arange(3 * 3).reshape(3, 3), index=['ㄱ', 'ㄴ', 'ㄷ'], columns=['A', 'B', 'C'])
프
Out[67]:
A | B | C | |
---|---|---|---|
ㄱ | 0 | 1 | 2 |
ㄴ | 3 | 4 | 5 |
ㄷ | 6 | 7 | 8 |
행으로부터 항목을 제거한다.
In [68]:
프.drop('ㄱ')
Out[68]:
A | B | C | |
---|---|---|---|
ㄴ | 3 | 4 | 5 |
ㄷ | 6 | 7 | 8 |
열로부터 항목을 제거한다.
In [69]:
프.drop(['A', 'B'], axis='columns')
Out[69]:
C | |
---|---|
ㄱ | 2 |
ㄴ | 5 |
ㄷ | 8 |
inplace=
인자를 통해서 원래 자료를 변경할 수 있다.
In [70]:
프.drop(['ㄱ'], inplace=True)
프
Out[70]:
A | B | C | |
---|---|---|---|
ㄴ | 3 | 4 | 5 |
ㄷ | 6 | 7 | 8 |
원래 자료가 변경되면 복구할 수 없기 때문에 inplace
인자는 주의해서
사용해야한다.
인덱싱, 선택, 여과¶
넘파이 인덱싱할 때와 마찬가지로 시리즈 인덱싱은 대괄호 []
를
사용하는 것은 같지만 시리즈 인덱싱에서는 인덱스 항목을 사용할 수 있다는
점이 다르다.
In [71]:
시 = pd.Series(range(5), index=['a', 'b', 'c', 'd', 'e'])
시
Out[71]:
a 0
b 1
c 2
d 3
e 4
dtype: int64
In [72]:
시[1]
Out[72]:
1
In [73]:
시['b']
Out[73]:
1
콜론을 이용해서도 선택할 수 있다.
In [74]:
시[1:3]
Out[74]:
b 1
c 2
dtype: int64
인덱스 항목을 통해서도 선택할 수 있다. 여기서 주의해야 할 것은 슬라이싱 끝 항목에 해당하는 자료가 포함된다는 것이다.
In [75]:
시['b':'c']
Out[75]:
b 1
c 2
dtype: int64
선택된 항목에 값을 할당함으로 기존 자료를 변경할 수 있다.
In [76]:
시['c':'d'] = 0
시
Out[76]:
a 0
b 1
c 0
d 0
e 4
dtype: int64
데이터프레임의 경우는 열이름을 이용해서 선택할 수 있다.
In [77]:
프 = pd.DataFrame(np.arange(3 * 4).reshape(3, 4), index=['A', 'B', 'C'], columns=['가', '나', '다', '라'])
프
Out[77]:
가 | 나 | 다 | 라 | |
---|---|---|---|---|
A | 0 | 1 | 2 | 3 |
B | 4 | 5 | 6 | 7 |
C | 8 | 9 | 10 | 11 |
하나의 열이름을 통해 접근하면 반환되는 유형은 시리즈이고 리스트로 접근하면 데이터프레임이 반환된다.
In [78]:
프['가']
Out[78]:
A 0
B 4
C 8
Name: 가, dtype: int32
In [79]:
type(프['가'])
Out[79]:
pandas.core.series.Series
In [80]:
프[['가', '다']]
Out[80]:
가 | 다 | |
---|---|---|
A | 0 | 2 |
B | 4 | 6 |
C | 8 | 10 |
데이터프레임의 인덱스를 콜론 :
을 이용해서 접근하면 행으로 접근하는
것이 된다.
In [81]:
프[:2]
Out[81]:
가 | 나 | 다 | 라 | |
---|---|---|---|---|
A | 0 | 1 | 2 | 3 |
B | 4 | 5 | 6 | 7 |
또는 행 인덱스의 항목으로도 접근할 수 있다.
In [82]:
프['A':'C']
Out[82]:
가 | 나 | 다 | 라 | |
---|---|---|---|---|
A | 0 | 1 | 2 | 3 |
B | 4 | 5 | 6 | 7 |
C | 8 | 9 | 10 | 11 |
여기서도 마찬가지로 마지막 항목이 포함되는 것을 볼 수 있다. 논리값으로도 인덱스를 사용할 수 있다. 논리 인덱스는 기본적으로 행에 대해서 접근한다.
In [83]:
프[프['가'] <= 4]
Out[83]:
가 | 나 | 다 | 라 | |
---|---|---|---|---|
A | 0 | 1 | 2 | 3 |
B | 4 | 5 | 6 | 7 |
또는 데이터프레임 전체에 대한 논리값을 이용해서도 접근할 수 있다.
In [84]:
프 < 6
Out[84]:
가 | 나 | 다 | 라 | |
---|---|---|---|---|
A | True | True | True | True |
B | True | True | False | False |
C | False | False | False | False |
이것을 이용하면 조건을 만족하는 데이터프레임의 성분을 한꺼번에 바꿀 수 있다.
In [85]:
프[프 < 6] = 0
프
Out[85]:
가 | 나 | 다 | 라 | |
---|---|---|---|---|
A | 0 | 0 | 0 | 0 |
B | 0 | 0 | 6 | 7 |
C | 8 | 9 | 10 | 11 |
loc
및 iloc
를 이용한 선택¶
넘파이 인덱스 경우와 비슷하게 행과 열을 이용해서 인덱싱을 할 수 있다.
행, 열 이름을 가지고 할 수 있는 것이 loc
이고 정수를 가지고
인덱싱을 하는 것이 iloc
이다.
In [86]:
프.loc['B', ['가', '다']]
Out[86]:
가 0
다 6
Name: B, dtype: int32
In [87]:
프.iloc[:2, [3, 0, 1]]
Out[87]:
라 | 가 | 나 | |
---|---|---|---|
A | 0 | 0 | 0 |
B | 7 | 0 | 0 |
인덱스의 인수가 하나만 나오면 행이 된다.
In [88]:
프.iloc[2]
Out[88]:
가 8
나 9
다 10
라 11
Name: C, dtype: int32
슬라이싱과 행, 열이름을 같이 사용할 수도 있다.
In [89]:
프.loc['A':'B', ['다', '가']]
Out[89]:
다 | 가 | |
---|---|---|
A | 0 | 0 |
B | 6 | 0 |
다음은 데이터프레임 인덱싱 선택 사항들이다.
Type | Notes |
---|---|
df[val] |
Select single column or sequence of columns from the DataFrame; special case conveniences: boolean array (filter rows), slice (slice rows), or boolean DataFrame (set values based on some criterion) |
df.loc[val] |
Selects single row or subset of rows from the DataFrame by label |
df.loc[:, val] |
Selects single column or subset of columns by label |
df.loc[val1, val2] |
Select both rows and columns by label |
df.iloc[where] |
Selects single row or subset of rows from the DataFrame by integer position |
df.iloc[:, where] |
Selects single column or subset of columns by integer position |
df.iloc[where_i, where_j] |
Select both rows and columns by integer position |
df.at[label_i, label_j] |
Select a single scalar value by row and column label |
df.iat[i, j] |
Select a single scalar value by row and column position (integers) |
reindex method |
Select either rows or columns by labels |
get_value, set_value methods |
Select single value by row and column label |
정수 인덱스(Integer Indexes)¶
정수 기반 인덱스를 사용할 때는 위치 인덱스로 접근하는 것을 허용하지 않는다. 예를 들어보자. 다음은 인덱스 이름이 정수로 되어있는 시리즈이다.
In [90]:
시1 = pd.Series(range(3))
시1
Out[90]:
0 0
1 1
2 2
dtype: int64
파이썬 인덱스에서 -1
은 가장 마지막 항목을 의미한다. 하지만
판다스는 정수형 인덱스를 이름으로 가지면 오류를 발생한다.
In [91]:
시1[-1]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-91-b4a177795d30> in <module>()
----> 1 시1[-1]
~\Anaconda3\lib\site-packages\pandas\core\series.py in __getitem__(self, key)
621 key = com._apply_if_callable(key, self)
622 try:
--> 623 result = self.index.get_value(self, key)
624
625 if not is_scalar(result):
~\Anaconda3\lib\site-packages\pandas\core\indexes\base.py in get_value(self, series, key)
2558 try:
2559 return self._engine.get_value(s, k,
-> 2560 tz=getattr(series.dtype, 'tz', None))
2561 except KeyError as e1:
2562 if len(self) > 0 and self.inferred_type in ['integer', 'boolean']:
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.Int64HashTable.get_item()
KeyError: -1
하지만 인덱스 이름이 정수형이 아닐 때는 문제없이 작동한다.
In [92]:
시2 = pd.Series(range(3), index=['a', 'b', 'c'])
시2
Out[92]:
a 0
b 1
c 2
dtype: int64
In [93]:
시2[-1]
Out[93]:
2
정확한 인덱싱을 하기위해서는 loc
또는 iloc
를 사용하는 것이
안전하다.
산술 연산¶
서로 다른 인덱스 객체들로 이루어진 객체들간의 산술 연산을 할 때 판다스는 두 인덱스 객체의 합집합에 대해서 연산을 한다. 예를 들어 보자.
In [94]:
시1 = pd.Series(range(5), index=['ㄱ', 'ㄴ', 'ㄷ', 'ㄹ', 'ㅂ'])
시1
Out[94]:
ㄱ 0
ㄴ 1
ㄷ 2
ㄹ 3
ㅂ 4
dtype: int64
In [95]:
시2 = pd.Series(range(1, 10, 2), index=['ㄱ', 'ㄴ', 'ㄷ', 'ㅁ', 'ㅅ'])
시2
Out[95]:
ㄱ 1
ㄴ 3
ㄷ 5
ㅁ 7
ㅅ 9
dtype: int64
두 객체를 더해보자.
In [96]:
시1 + 시2
Out[96]:
ㄱ 1.0
ㄴ 4.0
ㄷ 7.0
ㄹ NaN
ㅁ NaN
ㅂ NaN
ㅅ NaN
dtype: float64
인덱스 이름이 겹치지 않으면 각각의 객체는 소실값을 설정하고 연산을 할 때 소실값들에 대한 연산을 한다.
데이터프레임의 경우도 비슷하다.
In [97]:
데1 = pd.DataFrame(np.arange(3 * 3).reshape(3, 3), columns=list('ㄱㄴㄷ'), index=['가', '다', '라'])
데1
Out[97]:
ㄱ | ㄴ | ㄷ | |
---|---|---|---|
가 | 0 | 1 | 2 |
다 | 3 | 4 | 5 |
라 | 6 | 7 | 8 |
In [98]:
데2 = pd.DataFrame(np.arange(4 * 3).reshape(4, 3), columns=list('ㄱㄷㄹ'), index=['나', '다', '라', '마'])
데2
Out[98]:
ㄱ | ㄷ | ㄹ | |
---|---|---|---|
나 | 0 | 1 | 2 |
다 | 3 | 4 | 5 |
라 | 6 | 7 | 8 |
마 | 9 | 10 | 11 |
두 데이터프레임을 더하면 행, 열의 인덱스의 합집합에 대한 확장을 한 후 연산을 한다.
In [99]:
데1 + 데2
Out[99]:
ㄱ | ㄴ | ㄷ | ㄹ | |
---|---|---|---|---|
가 | NaN | NaN | NaN | NaN |
나 | NaN | NaN | NaN | NaN |
다 | 6.0 | NaN | 9.0 | NaN |
라 | 12.0 | NaN | 15.0 | NaN |
마 | NaN | NaN | NaN | NaN |
산술 연산 및 소실값 채우기¶
인덱스가 일치하지 않는 두 객체들간의 연산을 할 때 소실값을 0
과
같은 값으로 채우고 연산할 필요가 있다. 이 때 fill_value=
인자를
이용해서 소실값을 채운다. 단, 두 객체의 성분이 동시에 소실값을 가지면
fill_value
를 채우지 않고 np.nan
을 유지한다.
In [100]:
데1 = pd.DataFrame(np.arange(3 * 4).reshape(3, 4), columns=list('abcd'))
데1.loc[0, 'c'] = np.nan
데1
Out[100]:
a | b | c | d | |
---|---|---|---|---|
0 | 0 | 1 | NaN | 3 |
1 | 4 | 5 | 6.0 | 7 |
2 | 8 | 9 | 10.0 | 11 |
In [101]:
데2 = pd.DataFrame(np.arange(4 * 5).reshape(4, 5), columns=list('abcde'))
데2.loc[1, 'b'] = np.nan
데2.loc[0, 'c'] = np.nan
데2
Out[101]:
a | b | c | d | e | |
---|---|---|---|---|---|
0 | 0 | 1.0 | NaN | 3 | 4 |
1 | 5 | NaN | 7.0 | 8 | 9 |
2 | 10 | 11.0 | 12.0 | 13 | 14 |
3 | 15 | 16.0 | 17.0 | 18 | 19 |
두 객체가 겹쳐지지 않는 부분은 소실값(missing value)으로 대체되어 계산된다.
In [102]:
데1 + 데2
Out[102]:
a | b | c | d | e | |
---|---|---|---|---|---|
0 | 0.0 | 2.0 | NaN | 6.0 | NaN |
1 | 9.0 | NaN | 13.0 | 15.0 | NaN |
2 | 18.0 | 20.0 | 22.0 | 24.0 | NaN |
3 | NaN | NaN | NaN | NaN | NaN |
더하기 +
는 대응되는 메소드 add
를 사용하여 계산할 수 있다.
그리고 fill_value=
인자를 사용하여 둘 중의 하나가 NA
인 값을
fill_value
값으로 채우고 연산을 한다.
In [103]:
데1.add(데2, fill_value=100)
Out[103]:
a | b | c | d | e | |
---|---|---|---|---|---|
0 | 0.0 | 2.0 | NaN | 6.0 | 104.0 |
1 | 9.0 | 105.0 | 13.0 | 15.0 | 109.0 |
2 | 18.0 | 20.0 | 22.0 | 24.0 | 114.0 |
3 | 115.0 | 116.0 | 117.0 | 118.0 | 119.0 |
둘 다 NaN
인 (0, 'c')
성분은 fill_value
값으로 대체되지
않는 것을 알 수 있다. 스칼라 나눗셈에 대한 연산도 rdiv
메소드를
이용할 수 있다.
In [104]:
1 / 데1
Out[104]:
a | b | c | d | |
---|---|---|---|---|
0 | inf | 1.000000 | NaN | 0.333333 |
1 | 0.250000 | 0.200000 | 0.166667 | 0.142857 |
2 | 0.125000 | 0.111111 | 0.100000 | 0.090909 |
In [105]:
데1.rdiv(1)
Out[105]:
a | b | c | d | |
---|---|---|---|---|
0 | inf | 1.000000 | NaN | 0.333333 |
1 | 0.250000 | 0.200000 | 0.166667 | 0.142857 |
2 | 0.125000 | 0.111111 | 0.100000 | 0.090909 |
다음은 산술 연산 메소드들이다.
Method | Description |
---|---|
add, radd |
Methods for addition (+ ) |
sub, rsub |
Methods for subtraction (- ) |
div, rdiv |
Methods for division (/ ) |
floordiv, rfloordiv |
Methods for floor division (// ) |
mul, rmul |
Methods for multiplication (* ) |
pow, rpow |
Methods for exponentiation (** ) |
데이터프레임과 시리즈 연산¶
데이터프레임과 시리즈간의 연산이 가능하다. 기본적으로 데이터프레임의 열 이름과 시리즈의 인덱스 이름에 대해서 연산을 한다.
In [106]:
데 = pd.DataFrame(np.arange(3 * 4).reshape(3, 4), columns=list('abcd'), index=list('ㄱㄴㄷ'))
데
Out[106]:
a | b | c | d | |
---|---|---|---|---|
ㄱ | 0 | 1 | 2 | 3 |
ㄴ | 4 | 5 | 6 | 7 |
ㄷ | 8 | 9 | 10 | 11 |
주어진 데이터프레임의 첫번째 행을 시리즈 시1
으로 만들어 시리즈의
인덱스와 데이터프레임의 열 이름이 같아지도록 만들었다.
In [107]:
시1 = 데.iloc[0]
시1
Out[107]:
a 0
b 1
c 2
d 3
Name: ㄱ, dtype: int32
두 객체의 더하기는 데이터프레임의 각 행과 시리즈의 더하기가 된다. 여기서 사용되는 방법이 브로드캐스팅이다. 즉, 차원이 다른 두 객체를 일정한 규칙을 만족하도록 낮은 차원 객체를 높은 차원 객체와 같은 크기를 만든다. 브로드캐스팅은 부록을 참조하자.
In [108]:
데 + 시1
Out[108]:
a | b | c | d | |
---|---|---|---|---|
ㄱ | 0 | 2 | 4 | 6 |
ㄴ | 4 | 6 | 8 | 10 |
ㄷ | 8 | 10 | 12 | 14 |
만일 시리즈의 인덱스 이름과 데이터프레임의 행이름에 대해서 연산을 하고
싶으면 axis=
인자를 사용하면 된다. 다음은 데이터프레임의 첫번째 열을
시리즈 시2
로 만들었다. 따라서 시리즈의 인덱스 이름과
데이터프레임의 행이름이 같다.
In [109]:
시2 = 데['a']
시2
Out[109]:
ㄱ 0
ㄴ 4
ㄷ 8
Name: a, dtype: int32
데이터프레임의 열과 시리즈와 연산을 하려면
axis='index' 또는 axis=0
를 사용한다.
In [110]:
데.add(시2, axis='index')
Out[110]:
a | b | c | d | |
---|---|---|---|---|
ㄱ | 0 | 1 | 2 | 3 |
ㄴ | 8 | 9 | 10 | 11 |
ㄷ | 16 | 17 | 18 | 19 |
함수 사용 및 대응(Function Application and Mapping)¶
판다스는 넘파이 범용함수(ufunc; 성분끼리 연산)를 사용할 수 있다.
In [111]:
rng = np.random.RandomState(1234)
데 = pd.DataFrame(rng.randn(3 * 4).reshape(3, 4), columns=list('abcd'), index=list('ㄱㄴㄷ'))
데
Out[111]:
a | b | c | d | |
---|---|---|---|---|
ㄱ | 0.471435 | -1.190976 | 1.432707 | -0.312652 |
ㄴ | -0.720589 | 0.887163 | 0.859588 | -0.636524 |
ㄷ | 0.015696 | -2.242685 | 1.150036 | 0.991946 |
In [112]:
np.abs(데)
Out[112]:
a | b | c | d | |
---|---|---|---|---|
ㄱ | 0.471435 | 1.190976 | 1.432707 | 0.312652 |
ㄴ | 0.720589 | 0.887163 | 0.859588 | 0.636524 |
ㄷ | 0.015696 | 2.242685 | 1.150036 | 0.991946 |
데이터프레임의 행 또는 열벡터에 적용할 수 있는 apply
메소드가 있다.
In [113]:
f = lambda x : x.max() - x.min()
데.apply(f)
Out[113]:
a 1.192024
b 3.129848
c 0.573119
d 1.628470
dtype: float64
함수 f
는 배열 x
의 최대값에서 최소값을 뺀 값을 반환한다.
기본적으로 데이터프레임.apply(함수)
하면 데이터프레임의 열 벡터에
함수를 적용한다. axis='columns'
로하면 행에 대해서 함수를 적용할 수
있다.
In [114]:
데.apply(f, axis='columns')
Out[114]:
ㄱ 2.623683
ㄴ 1.607752
ㄷ 3.392721
dtype: float64
sum
, mean
과 같은 데이터프레임 메소드들은 apply
를 사용할
필요가 없다. 함수의 반환값으로 스칼라뿐 아니라 여러 값을 갖는 시리즈도
될 수 있다.
In [115]:
def g(x):
return pd.Series([x.min(), x.max()], index=['min', 'max'])
데.apply(g)
Out[115]:
a | b | c | d | |
---|---|---|---|---|
min | -0.720589 | -2.242685 | 0.859588 | -0.636524 |
max | 0.471435 | 0.887163 | 1.432707 | 0.991946 |
직접하기
4x5
데이터프레임을 하나 만들고 각 행과 열에 대해서 합과 평균을 반환하는 코드를 작성하시오.- 각 행과 열에 대해서 합을 구하고 각 합이 전체의 몇 퍼센트인지를 구하는 코드를 작성하시오.
applymap
메소드를 이용하면 성분에 대한 연산을 하는 함수를 사용할 수
있다. 다음과 같이 각 성분 숫자를 소수점 2자리만 표시되는 함수를 만들어
보자.
In [116]:
포맷 = lambda x : '{:.2f}'.format(x)
데.applymap(포맷)
Out[116]:
a | b | c | d | |
---|---|---|---|---|
ㄱ | 0.47 | -1.19 | 1.43 | -0.31 |
ㄴ | -0.72 | 0.89 | 0.86 | -0.64 |
ㄷ | 0.02 | -2.24 | 1.15 | 0.99 |
applymap
이라는 이름은 시리즈에 map
메소드가 있어서 각 성분에
대해서 함수를 적용할 수 있기 때문이다. 즉, 위의 함수는 다음과 같이 각
열에 대해서 map
을 적용한 것이다.
In [117]:
데['a'].map(포맷)
Out[117]:
ㄱ 0.47
ㄴ -0.72
ㄷ 0.02
Name: a, dtype: object
정렬 및 순위(Sorting and Ranking)¶
행 또는 열이름에 대해서 자료를 정렬하려면 sort_index
메소드를
사용하면 된다.
In [118]:
프 = pd.DataFrame(np.arange(3 * 4).reshape(3, 4), columns=list('ㄴㄱㄹㄷ'), index=list('dab'))
프
Out[118]:
ㄴ | ㄱ | ㄹ | ㄷ | |
---|---|---|---|---|
d | 0 | 1 | 2 | 3 |
a | 4 | 5 | 6 | 7 |
b | 8 | 9 | 10 | 11 |
sort_index
는 기본적으로 행 이름에 대해서 정렬을 한다.
In [119]:
프.sort_index()
Out[119]:
ㄴ | ㄱ | ㄹ | ㄷ | |
---|---|---|---|---|
a | 4 | 5 | 6 | 7 |
b | 8 | 9 | 10 | 11 |
d | 0 | 1 | 2 | 3 |
열이름에 대해서 정렬을 하고 싶으면 axis='columns'
를 사용한다.
In [120]:
프.sort_index(axis='columns')
Out[120]:
ㄱ | ㄴ | ㄷ | ㄹ | |
---|---|---|---|---|
d | 1 | 0 | 3 | 2 |
a | 5 | 4 | 7 | 6 |
b | 9 | 8 | 11 | 10 |
자료는 기본적으로 오름차순으로 정렬이 된다. 내림차순으로 정렬을 하고
싶으면 ascending=False
인자를 사용한다.
In [121]:
프.sort_index(axis=1, ascending=False)
Out[121]:
ㄹ | ㄷ | ㄴ | ㄱ | |
---|---|---|---|---|
d | 2 | 3 | 0 | 1 |
a | 6 | 7 | 4 | 5 |
b | 10 | 11 | 8 | 9 |
시리즈 값에 의한 정렬을 원하면 sort_values
메소드를 사용한다.
In [122]:
시 = pd.Series([12, -2, 0, -9])
시
Out[122]:
0 12
1 -2
2 0
3 -9
dtype: int64
In [123]:
시.sort_values()
Out[123]:
3 -9
1 -2
2 0
0 12
dtype: int64
소실값이 포함되어 있으면 기본적으로 소실값은 자료의 맨 나중에 정렬된다.
In [124]:
시1 = pd.Series([10, np.nan, -2, np.nan, -3])
시1.sort_values()
Out[124]:
4 -3.0
2 -2.0
0 10.0
1 NaN
3 NaN
dtype: float64
데이터프레임의 경우, 특정한 열에 대한 정렬을 원하면 by=열이름
인자를
사용하면 된다.
In [125]:
프1 = pd.DataFrame({'a': [-2, 3, -5, 1], 'b': [20, -29, 40, 20]})
프1
Out[125]:
a | b | |
---|---|---|
0 | -2 | 20 |
1 | 3 | -29 |
2 | -5 | 40 |
3 | 1 | 20 |
In [126]:
프1.sort_values(by='a')
Out[126]:
a | b | |
---|---|---|
2 | -5 | 40 |
0 | -2 | 20 |
3 | 1 | 20 |
1 | 3 | -29 |
두 개 이상의 열이름으로 정렬을 할 수 있다.
In [127]:
프1.sort_values(by=['b', 'a'])
Out[127]:
a | b | |
---|---|---|
1 | 3 | -29 |
0 | -2 | 20 |
3 | 1 | 20 |
2 | -5 | 40 |
순위(ranking)는 자료의 순서대로 순위를 표시한다. 같은 순위가 있을 경우
method=
인자에 적용되는 방법에 의해 순위를 정한다.
In [128]:
시 = pd.Series([7, -5, 7, 4, 2, 0, 4])
시.rank()
Out[128]:
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
method
기본값은 average
로 같은 숫자들의 순위의 평균을 구하는
것이다. 위에서 4는 순위가 4위와 5위이므로 평균은 4.5가 되는 것이다.
다음과 같이 method=first
값을 사용하면 자료의 위치가 앞에 있는 것이
앞 순위가 된다.
In [129]:
시.rank(method='first')
Out[129]:
0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
dtype: float64
데이터프레임도 열 또는 행에 대해서 순위를 구할 수 있다.
In [130]:
프 = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1], 'c': [-2, 5, 8, -2.5]})
프
Out[130]:
a | b | c | |
---|---|---|---|
0 | 0 | 4.3 | -2.0 |
1 | 1 | 7.0 | 5.0 |
2 | 0 | -3.0 | 8.0 |
3 | 1 | 2.0 | -2.5 |
In [131]:
프.rank(axis='columns')
Out[131]:
a | b | c | |
---|---|---|---|
0 | 2.0 | 3.0 | 1.0 |
1 | 1.0 | 3.0 | 2.0 |
2 | 2.0 | 1.0 | 3.0 |
3 | 2.0 | 3.0 | 1.0 |
Method | Description |
---|---|
'average
' |
Default: assign the average rank to each entry in the equal group |
'min' |
Use the minimum rank for the whole group |
'max' |
Use the maximum rank for the whole group |
``’first’` ` | Assign ranks in the order the values appear in the data |
``’dense’` ` | Like method=’min’, but ranks always increase by 1 in between groups rather than the number of equal elements in a group |
중복 이름을 가진 인덱스¶
이제까지 축 이름은 모두 유일한 예를 다루었다. reindex
와 같이
대부분의 메소드들은 열, 행이름들이 유일해야하지만 필수는 아니다. 다음과
같이 시리즈의 인덱스이름으로 중복된 이름 a
를 가질 수 있다.
In [132]:
시 = pd.Series(np.arange(5), index=list('abcda'))
시
Out[132]:
a 0
b 1
c 2
d 3
a 4
dtype: int32
중복된 이름에 대한 반환값은 다음과 같이 시리즈형이고 중복되지 않은 인덱스 이름의 반환값은 스칼라임을 알 수 있다.
In [133]:
시['a']
Out[133]:
a 0
a 4
dtype: int32
In [134]:
시['b']
Out[134]:
1
따라서 프로그래밍할 때 주의를 해야 하는 부분이다. 인덱스가 유일한지
아닌지를 판단할 수 있는 방법 중의 하나로 인덱스의 속성인
is_unique
를 이용할 수 있다.
In [135]:
시.index.is_unique
Out[135]:
False
데이터프레임의 경우도 마찬가지이다.
In [136]:
데 = pd.DataFrame(np.arange(3 * 4).reshape(3, 4), index=list('aba'))
데
Out[136]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
a | 0 | 1 | 2 | 3 |
b | 4 | 5 | 6 | 7 |
a | 8 | 9 | 10 | 11 |
In [137]:
데.loc['a']
Out[137]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
a | 0 | 1 | 2 | 3 |
a | 8 | 9 | 10 | 11 |
기술 통계량 계산 및 요약¶
판다스 객체는 공통적으로 사용되는 수학 또는 통계 메소드들을 갖추고 있다. 이것들 대부분은 시리즈 또는 데이터프레임의 행, 열로부터 평균 또는 합같은 스칼라 값을 뽑아 내는 요약 통계의 범주에 해당한다.
In [138]:
데 = pd.DataFrame([[1.5, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=list('abcd'), columns=['하나','둘'])
데
Out[138]:
하나 | 둘 | |
---|---|---|
a | 1.50 | NaN |
b | 7.10 | -4.5 |
c | NaN | NaN |
d | 0.75 | -1.3 |
데이터프레임의 sum
메소드는 열에 대한 합을 시리즈로 반환한다.
In [139]:
데.sum()
Out[139]:
하나 9.35
둘 -5.80
dtype: float64
axis='columns'
또는 axis=1
인자를 추가하면 행에 대한 합을
반환한다.
In [140]:
데.sum(axis='columns')
Out[140]:
a 1.50
b 2.60
c 0.00
d -0.55
dtype: float64
NA
값들은 계산할 때 기본적으로 제외된다. skipna=
인자를
추가함으로 계산시 적용할 수 있다.
In [141]:
데.sum(axis='columns', skipna=False)
Out[141]:
a NaN
b 2.60
c NaN
d -0.55
dtype: float64
요약 통계 메소드에 사용되는 선택 인자들이다.
메소드 | 설명 |
---|---|
axis |
Axis to reduce over; 0 for DataFrame’s rows and 1 for columns |
skipna |
Exclude missing values; True by default |
level |
Reduce grouped by level if the axis is hierarchically indexed (MultiIndex) |
idxmin
또는 idxmax
메소드들은 주어진 축의 값중 가장 큰 값 또는
가장 작은 값을 포함하는 인덱스 이름을 반환한다.
In [142]:
데.idxmax()
Out[142]:
하나 b
둘 d
dtype: object
각각의 열 중에서 가장 큰 값을 포함하고 있는 행 이름을 각각 반환한 것을 볼 수 있다. 누적 통계량 메소드로는 다음과 같은 것이 있다.
In [143]:
데.cumsum()
Out[143]:
하나 | 둘 | |
---|---|---|
a | 1.50 | NaN |
b | 8.60 | -4.5 |
c | NaN | NaN |
d | 9.35 | -5.8 |
각 열에 대해서 각 행까지 누적해서 더한 값들을 적고 있다. 또다른
메소드로는 한번에 여러 개의 통계량을 보여주는 describe
메소드가
있다.
In [144]:
데.describe()
Out[144]:
하나 | 둘 | |
---|---|---|
count | 3.000000 | 2.000000 |
mean | 3.116667 | -2.900000 |
std | 3.469990 | 2.262742 |
min | 0.750000 | -4.500000 |
25% | 1.125000 | -3.700000 |
50% | 1.500000 | -2.900000 |
75% | 4.300000 | -2.100000 |
max | 7.100000 | -1.300000 |
다음은 요약 통계 및 관련된 메소드들이다.
메소드 | 설명 |
---|---|
count | Number of non-NA values |
describe | Compute set of summary statistics for Series or each DataFrame column |
min, max | Compute minimum and maximum values |
argmin, argmax | Compute index locations (integers) at which minimum or maximum value obtained, respectively |
idxmin, idxmax | Compute index labels at which minimum or maximum value obtained, respectively |
quantile | Compute sample quantile ranging from 0 to 1 |
sum | Sum of values |
mean | Mean of values |
median | Arithmetic median (50% quantile) of values |
mad | Mean absolute deviation from mean value |
prod | Product of all values |
var | Sample variance of values |
std | Sample standard deviation of values |
skew | Sample skewness (third moment) of values |
kurt | Sample kurtosis (fourth moment) of values |
cumsum | Cumulative sum of values |
cummin, cummax | Cumulative minimum or maximum of values, respectively |
cumprod | Cumulative product of values |
diff | Compute first arithmetic difference (useful for time series) |
pct_change | Compute percent changes |
상관계수 및 공분산¶
증권 자료를 pandas-datareader
패키지를 이용해서 내려받을 수 있다.
패키지가 설치되어 있지 않으면 다음과 같이 설치를 한다.
conda install pandas-datareader
pandas_datareader
모듈을 이용해서 4개 회사 거래 자료를 내려받는다.
import pandas_datareader as web
import pickle
all_data = {ticker: web.get_data_quandl(ticker) for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}
미리 저장된 파일을 다음 사이트에서 내려받아 사용하도록 하자.
In [169]:
import pickle
from urllib import request
url = "http://compmath.korea.ac.kr/appmath/data/stock_example.dat"
with request.urlopen(url) as f:
all_data = pickle.loads(f.read())
내려받은 자료 구조를 살펴보자. all_data
의 자료형은 사전임을 알 수
있다.
In [170]:
type(all_data)
Out[170]:
dict
all_data
의 열쇠(key)를 살펴보면 다음과 같다.
In [171]:
all_data.keys()
Out[171]:
dict_keys(['AAPL', 'IBM', 'MSFT', 'GOOG'])
첫번째 열쇠에 대응되는 값을 살펴보자. 다음에서 보는 바와 같이 판다스 데이터프레임임을 알 수 있다.
In [172]:
aapl = all_data['AAPL']
type(aapl)
Out[172]:
pandas.core.frame.DataFrame
데이터프레임의 구조를 보자.
In [173]:
pd.set_option('display.max_rows', 10)
aapl
Out[173]:
Open | High | Low | Close | Volume | ExDividend | SplitRatio | AdjOpen | AdjHigh | AdjLow | AdjClose | AdjVolume | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Date | ||||||||||||
2018-03-27 | 173.68 | 175.15 | 166.92 | 168.340 | 38962839.0 | 0.0 | 1.0 | 173.680000 | 175.150000 | 166.920000 | 168.340000 | 38962839.0 |
2018-03-26 | 168.07 | 173.10 | 166.44 | 172.770 | 36272617.0 | 0.0 | 1.0 | 168.070000 | 173.100000 | 166.440000 | 172.770000 | 36272617.0 |
2018-03-23 | 168.39 | 169.92 | 164.94 | 164.940 | 40248954.0 | 0.0 | 1.0 | 168.390000 | 169.920000 | 164.940000 | 164.940000 | 40248954.0 |
2018-03-22 | 170.00 | 172.68 | 168.60 | 168.845 | 41051076.0 | 0.0 | 1.0 | 170.000000 | 172.680000 | 168.600000 | 168.845000 | 41051076.0 |
2018-03-21 | 175.04 | 175.09 | 171.26 | 171.270 | 35247358.0 | 0.0 | 1.0 | 175.040000 | 175.090000 | 171.260000 | 171.270000 | 35247358.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
2010-01-08 | 210.30 | 212.00 | 209.06 | 211.980 | 15986100.0 | 0.0 | 1.0 | 27.026481 | 27.244955 | 26.867124 | 27.242385 | 111902700.0 |
2010-01-07 | 211.75 | 212.00 | 209.05 | 210.580 | 17040400.0 | 0.0 | 1.0 | 27.212826 | 27.244955 | 26.865839 | 27.062465 | 119282800.0 |
2010-01-06 | 214.38 | 215.23 | 210.75 | 210.970 | 19720000.0 | 0.0 | 1.0 | 27.550818 | 27.660055 | 27.084312 | 27.112585 | 138040000.0 |
2010-01-05 | 214.60 | 215.59 | 213.25 | 214.380 | 21496600.0 | 0.0 | 1.0 | 27.579091 | 27.706320 | 27.405597 | 27.550818 | 150476200.0 |
2010-01-04 | 213.43 | 214.50 | 212.38 | 214.010 | 17633200.0 | 0.0 | 1.0 | 27.428730 | 27.566240 | 27.293790 | 27.503268 | 123432400.0 |
2070 rows × 12 columns
수정 종가(AdjClose
)와 거래량(Volume
) 자료를 추출하자.
In [174]:
import pandas as pd
adj_close = pd.DataFrame({ticker: data['AdjClose'] for ticker, data in all_data.items()})
volume = pd.DataFrame({ticker: data['Volume'] for ticker, data in all_data.items()})
In [175]:
adj_close
Out[175]:
AAPL | GOOG | IBM | MSFT | |
---|---|---|---|---|
Date | ||||
2010-01-04 | 27.503268 | NaN | 108.070985 | 25.153472 |
2010-01-05 | 27.550818 | NaN | 106.765484 | 25.161599 |
2010-01-06 | 27.112585 | NaN | 106.071937 | 25.007183 |
2010-01-07 | 27.062465 | NaN | 105.704765 | 24.748741 |
2010-01-08 | 27.242385 | NaN | 106.765484 | 24.917785 |
... | ... | ... | ... | ... |
2018-03-21 | 171.270000 | 1090.88 | 156.690000 | 92.480000 |
2018-03-22 | 168.845000 | 1049.08 | 152.090000 | 89.790000 |
2018-03-23 | 164.940000 | 1021.57 | 148.890000 | 87.180000 |
2018-03-26 | 172.770000 | 1053.21 | 153.370000 | 93.780000 |
2018-03-27 | 168.340000 | 1005.10 | 151.910000 | 89.470000 |
2071 rows × 4 columns
In [176]:
volume
Out[176]:
AAPL | GOOG | IBM | MSFT | |
---|---|---|---|---|
Date | ||||
2010-01-04 | 17633200.0 | NaN | 6155300.0 | 38409100.0 |
2010-01-05 | 21496600.0 | NaN | 6841400.0 | 49749600.0 |
2010-01-06 | 19720000.0 | NaN | 5605300.0 | 58182400.0 |
2010-01-07 | 17040400.0 | NaN | 5840600.0 | 50559700.0 |
2010-01-08 | 15986100.0 | NaN | 4197200.0 | 51197400.0 |
... | ... | ... | ... | ... |
2018-03-21 | 35247358.0 | 1640709.0 | 3240695.0 | 23753263.0 |
2018-03-22 | 41051076.0 | 2580374.0 | 4617371.0 | 37578166.0 |
2018-03-23 | 40248954.0 | 2113497.0 | 4389015.0 | 42159397.0 |
2018-03-26 | 36272617.0 | 2558385.0 | 4038586.0 | 55031149.0 |
2018-03-27 | 38962839.0 | 3029471.0 | 3810994.0 | 53704562.0 |
2071 rows × 4 columns
직접하기
- General Motors(GM), Coca Cola(KO), AT & T(T), Intel(INTC) 주식 자료를
다운받아
data
변수에 저장하시오.(만일 다운이 안되면 ‘http://compmath.korea.ac.kr/appmath/data/stock_GM_KO_T_INTC.dat’를 이용하시오.) data
로부터Open
과Close
값을 각각st_open
,st_close
변수에 저장하시오.
시계열 부분에서 자세히 살펴볼 차의 백분율(percent change)을 계산하면 다음과 같다.
In [177]:
pct_ch = adj_close.pct_change()
pct_ch.tail()
Out[177]:
AAPL | GOOG | IBM | MSFT | |
---|---|---|---|---|
Date | ||||
2018-03-21 | -0.022655 | -0.006222 | 0.003137 | -0.006979 |
2018-03-22 | -0.014159 | -0.038318 | -0.029357 | -0.029087 |
2018-03-23 | -0.023128 | -0.026223 | -0.021040 | -0.029068 |
2018-03-26 | 0.047472 | 0.030972 | 0.030089 | 0.075705 |
2018-03-27 | -0.025641 | -0.045679 | -0.009519 | -0.045959 |
시리즈 메소드 corr
을 통하여 NA
가 아닌 자료들에 대한
상관계수를 구할 수 있다.
In [178]:
pct_ch['MSFT'].corr(pct_ch['IBM'])
Out[178]:
0.47906169687685296
마찬가지로 cov
메소드를 이용해서 공분산을 구한다.
In [179]:
pct_ch['MSFT'].cov(pct_ch['IBM'])
Out[179]:
8.141169232110642e-05
데이터프레임의 corr
, cov
메소드는 각 열에 대한 상관계수 전체를
계산한다.
In [180]:
pct_ch.corr()
Out[180]:
AAPL | GOOG | IBM | MSFT | |
---|---|---|---|---|
AAPL | 1.000000 | 0.433070 | 0.365634 | 0.410778 |
GOOG | 0.433070 | 1.000000 | 0.374787 | 0.599392 |
IBM | 0.365634 | 0.374787 | 1.000000 | 0.479062 |
MSFT | 0.410778 | 0.599392 | 0.479062 | 1.000000 |
In [157]:
pct_ch.cov()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-157-de587e26bcb4> in <module>()
----> 1 pct_ch.cov()
NameError: name 'pct_ch' is not defined
데이터프레임의 corrwith
메소드를 이용하면 데이터프레임의 열 또는
행을 다른 데이터프레임(또는 시리즈)의 짝을 찾아 상관관계를 계산할 수
있다. 인자로 시리즈를 건네면 데이터프레임의 각 열과 상관관계를 계산하여
시리즈 형으로 반환한다.
In [181]:
pct_ch.corrwith(pct_ch.IBM)
Out[181]:
AAPL 0.365634
GOOG 0.374787
IBM 1.000000
MSFT 0.479062
dtype: float64
인자로 데이터프레임을 건네면 같은 열 이름에 대해서 상관관계를 계산한다.
In [182]:
pct_ch.corrwith(volume)
Out[182]:
AAPL -0.075738
GOOG 0.000481
IBM -0.156549
MSFT -0.084004
dtype: float64
선택인자 axis='columns'
를 사용하여 행에 대해서 상관관계를
계산한다.
In [183]:
pct_ch.corrwith(volume, axis='columns')
Out[183]:
Date
2010-01-04 NaN
2010-01-05 0.698721
2010-01-06 0.289461
2010-01-07 -0.911877
2010-01-08 -0.658798
...
2018-03-21 -0.843804
2018-03-22 0.758479
2018-03-23 -0.403013
2018-03-26 0.963553
2018-03-27 -0.362487
Length: 2071, dtype: float64
유일한 값, 빈도수, 포함관계¶
시리즈에서 값에 대한 정보를 추출하는 메소드들에 대해서 살펴보자. 다음과 같은 시리즈를 만들자.
In [184]:
시 = pd.Series(['ㄱ', 'ㄴ', 'ㄹ', 'ㄱ', 'ㄴ', 'ㄹ', 'ㄴ', 'ㄷ'])
시리즈 값들 중 유일한 값들을 unique
메소드를 이용해서 찾아내자.
In [162]:
유일 = 시.unique()
유일
Out[162]:
array(['ㄱ', 'ㄴ', 'ㄹ', 'ㄷ'], dtype=object)
value_counts
메소드를 이용해 값들에 대한 빈도수를 찾을 수 있다.
In [163]:
시.value_counts()
Out[163]:
ㄴ 3
ㄹ 2
ㄱ 2
ㄷ 1
dtype: int64
판다스 함수를 이용해서도 빈도수를 구할 수 있다.
In [164]:
pd.value_counts(시, sort=False)
Out[164]:
ㄱ 2
ㄹ 2
ㄷ 1
ㄴ 3
dtype: int64
isin
메소드를 이용해 값들이 시리즈 또는 데이터프레임의 열에
속했는지를 판단해 논리형 배열로 반환한다.
In [165]:
거름 = 시.isin(['ㄱ', 'ㄴ'])
거름
Out[165]:
0 True
1 True
2 False
3 True
4 True
5 False
6 True
7 False
dtype: bool
In [166]:
시[거름]
Out[166]:
0 ㄱ
1 ㄴ
3 ㄱ
4 ㄴ
6 ㄴ
dtype: object
다음은 포함관계, 유일값, 빈도수 메소드들이다.
Method | Description |
---|---|
isin | Compute boolean array indicating whether each Series value is contained in the passed sequence of values |
match | Compute integer indices for each value in an array into another array of distinct values; helpful for data alignment and join-type operations |
unique | Compute array of unique values in a Series, returned in the order observed |
value_cou nts | Return a Series containing unique values as its index and frequencies as its values, ordered count in descending order |
데이터프레임 열에 대한 빈도수를 다음과 같이 계산한다.
In [167]:
데 = pd.DataFrame({'가': ['a', 'c', 'd', 'c', 'd'],
'나': ['b', 'c', 'a', 'b', 'c'],
'다': ['a', 'e', 'b', 'd', 'e']})
데
Out[167]:
가 | 나 | 다 | |
---|---|---|---|
0 | a | b | a |
1 | c | c | e |
2 | d | a | b |
3 | c | b | d |
4 | d | c | e |
In [168]:
데.apply(pd.value_counts).fillna(0)
Out[168]:
가 | 나 | 다 | |
---|---|---|---|
a | 1.0 | 1.0 | 1.0 |
b | 0.0 | 2.0 | 1.0 |
c | 2.0 | 2.0 | 0.0 |
d | 2.0 | 0.0 | 1.0 |
e | 0.0 | 0.0 | 2.0 |
데이터프레임 가
열에 a
가 1개, b
는 0개 c
는 2개,
…등을 의미한다.