18. 데이터프레임 인덱싱#
주요 내용
Series
와 DataFrame
객체를 다루는 다양한 도구를 살펴본다.
인덱싱
슬라이싱
결측치 처리
기본 설정
pandas
라이브러리는 보통 pd
라는 별칭으로 사용된다.
import pandas as pd
import numpy as np
랜덤 시드, 어레이 내부에 사용되는 부동소수점 정확도, 도표 크기 지정 옵션 등은 이전과 동일하다.
np.random.seed(12345)
np.set_printoptions(precision=4, suppress=True)
import matplotlib.pyplot as plt
plt.rc('figure', figsize=(10, 6))
Series
와 DataFrame
을 표로 보여줄 때 사용되는 행의 수 60이다.
pd.options.display.max_rows
60
보여지는 행의 수를 20으로 변경한다.
pd.set_option("display.max_rows", 20)
18.1. 시리즈 인덱싱/슬라이싱#
시리즈 인덱싱
1차원 넘파이 어레이와 거의 동일하게 작동한다. 설명을 위해 아래 데이터프레이을 이용한다.
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
obj
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
시리즈 인덱싱은 인덱스 라벨 또는 정수를 사용할 수 있다. 인덱스 라벨을 이용하는 경우엔 사전 인덱싱처럼 활용한다.
obj['b']
1.0
iloc[]
객체를 행의 정수 인덱스와 함께 이용한다.
obj.iloc[1]
1.0
시리즈 슬라이싱
행의 정수 인덱스를 이용한 슬라이싱 또한 iloc[]
객체를 이용한다.
사용법은 1차원 어레이의 경우와 유사하다.
obj.iloc[1:3]
b 1.0
c 2.0
dtype: float64
반면에 정수가 아닌 다른 인덱스 라벨을 이용하는 슬라이싱은 리스트의 슬라이싱과 유사한 방식으로 진행한다. 다만, 양쪽 구간의 끝을 모두 포함하는 점이 다르다.
obj['b':'c']
b 1.0
c 2.0
dtype: float64
obj['b':'c'] = 5
obj
a 0.0
b 5.0
c 5.0
d 3.0
dtype: float64
인덱스 라벨 슬라이싱은 기본적으로 알파벳 순서를 따르며
시리즈에 사용된 순서와 상관 없음에 주의한다.
예를 들어, 아래 코드는 'b'
부터 'd'
까지, 즉, 'b'
, 'c'
, 'd'
를 대상으로
슬라이싱을 진행한다.
obj.reindex(['b', 'd', 'c', 'a'])
b 5.0
d 3.0
c 5.0
a 0.0
dtype: float64
obj['b':'d']
b 5.0
c 5.0
d 3.0
dtype: float64
시리즈 팬시 인덱싱
여러 개의 인덱스를 리스트로 지정하여 팬시 인덱싱을 진행할 수 있다.
인덱스 라벨 활용
obj[['b', 'a', 'd']]
b 5.0
a 0.0
d 3.0
dtype: float64
정수 인덱스 활용:
iloc[]
객체 활용
obj.iloc[[1, 3]]
b 5.0
d 3.0
dtype: float64
시리즈 부울 인덱싱(필터링)
부울 인덱싱은 넘파이 어레이의 경우와 동일하게 작동한다.
obj < 2
a True
b False
c False
d False
dtype: bool
obj[obj < 2]
a 0.0
dtype: float64
18.2. 데이터프레임 인덱싱/슬라이싱#
2차원 넘파이 어레이와 거의 유사하게 작동한다. 설명을 위해 아래 데이터프레이을 이용한다.
state_dict = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada', 'NY', 'NY', 'NY'],
'year': [2000, 2001, 2002, 2001, 2002, 2003, 2002, 2003, 2004],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2, 8.3, 8.4, 8.5]}
df = pd.DataFrame(state_dict, columns=['year', 'state', 'pop', 'debt'],
index=['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine'])
df
year | state | pop | debt | |
---|---|---|---|---|
one | 2000 | Ohio | 1.5 | NaN |
two | 2001 | Ohio | 1.7 | NaN |
three | 2002 | Ohio | 3.6 | NaN |
four | 2001 | Nevada | 2.4 | NaN |
five | 2002 | Nevada | 2.9 | NaN |
six | 2003 | Nevada | 3.2 | NaN |
seven | 2002 | NY | 8.3 | NaN |
eight | 2003 | NY | 8.4 | NaN |
nine | 2004 | NY | 8.5 | NaN |
18.2.1. 열 라벨 인덱싱#
열 라벨 인덱싱은 시리즈, 사전 등과 동일한 방식을 사용한다.
예를 들어, state
열을 확인하면 시리즈를 생성한다.
df['state']
one Ohio
two Ohio
three Ohio
four Nevada
five Nevada
six Nevada
seven NY
eight NY
nine NY
Name: state, dtype: object
대괄호 대신 속성 형식을 사용할 수도 있다.
아래 코드는 year
열을 시리즈로 보여준다.
df.year
one 2000
two 2001
three 2002
four 2001
five 2002
six 2003
seven 2002
eight 2003
nine 2004
Name: year, dtype: int64
주의사항
대괄호를 사용하는 인덱싱은 임의의 문자열을 사용한다. 반면에 속성 형식은 변수를 사용하듯이 처리한다. 따라서 속성 형식에 사용될 수 있는 열의 이름은 일반 변수의 이름을 짓는 형식을 따라야 한다.
예를 들어, Ohio
주(state)인지 여부를 판정하는 'Ohio state'
라는 열을 추가해보자.
아래 코드는 새로운 열을 추가하기 위해 사전의 경우처럼 대괄호를 이용하여 새로운 열의 이름과 값을 지정한다.
df['Ohio state'] = df.state == 'Ohio'
df
year | state | pop | debt | Ohio state | |
---|---|---|---|---|---|
one | 2000 | Ohio | 1.5 | NaN | True |
two | 2001 | Ohio | 1.7 | NaN | True |
three | 2002 | Ohio | 3.6 | NaN | True |
four | 2001 | Nevada | 2.4 | NaN | False |
five | 2002 | Nevada | 2.9 | NaN | False |
six | 2003 | Nevada | 3.2 | NaN | False |
seven | 2002 | NY | 8.3 | NaN | False |
eight | 2003 | NY | 8.4 | NaN | False |
nine | 2004 | NY | 8.5 | NaN | False |
그러면 'Ohio state'
의 열을 확인하는 방법은 대괄호만 이용할 수 있으며 속성 형식은 불가능하다.
df['Ohio state']
one True
two True
three True
four False
five False
six False
seven False
eight False
nine False
Name: Ohio state, dtype: bool
아래와 같이 실행하면 문법 오류(SyntaxError
)가 발생한다.
이유는 Ohio state
가 변수 이름으로 허용되지 않기 때문이다.
df.Ohio state
Cell In[22], line 1
df.Ohio state
^
SyntaxError: invalid syntax
열 팬시 인덱싱
열 라벨의 리스트를 사용하면 어레이 팬시 인덱싱 방식으로 데이터프레임을 생성한다.
df[['state', 'state', 'year', 'year', 'year']]
state | state | year | year | year | |
---|---|---|---|---|---|
one | Ohio | Ohio | 2000 | 2000 | 2000 |
two | Ohio | Ohio | 2001 | 2001 | 2001 |
three | Ohio | Ohio | 2002 | 2002 | 2002 |
four | Nevada | Nevada | 2001 | 2001 | 2001 |
five | Nevada | Nevada | 2002 | 2002 | 2002 |
six | Nevada | Nevada | 2003 | 2003 | 2003 |
seven | NY | NY | 2002 | 2002 | 2002 |
eight | NY | NY | 2003 | 2003 | 2003 |
nine | NY | NY | 2004 | 2004 | 2004 |
길이가 1인 리스트일 때도 데이터프레임을 생성한다.
df[['state']]
state | |
---|---|
one | Ohio |
two | Ohio |
three | Ohio |
four | Nevada |
five | Nevada |
six | Nevada |
seven | NY |
eight | NY |
nine | NY |
반면에 열 라벨 인덱싱은 시리즈를 생성한다.
df['state']
one Ohio
two Ohio
three Ohio
four Nevada
five Nevada
six Nevada
seven NY
eight NY
nine NY
Name: state, dtype: object
열 업데이트
열 라벨 인덱싱을 이용하여 항목의 값을 지정할 수 있다.
아래 코드는 'debt'
열의 값을 16.5로 일정하게 지정한다.
참고로 브로드캐스팅이 작동한다.
df['debt'] = 16.5
df
year | state | pop | debt | Ohio state | |
---|---|---|---|---|---|
one | 2000 | Ohio | 1.5 | 16.5 | True |
two | 2001 | Ohio | 1.7 | 16.5 | True |
three | 2002 | Ohio | 3.6 | 16.5 | True |
four | 2001 | Nevada | 2.4 | 16.5 | False |
five | 2002 | Nevada | 2.9 | 16.5 | False |
six | 2003 | Nevada | 3.2 | 16.5 | False |
seven | 2002 | NY | 8.3 | 16.5 | False |
eight | 2003 | NY | 8.4 | 16.5 | False |
nine | 2004 | NY | 8.5 | 16.5 | False |
행의 길이와 동일한 리스트, 어레이 등을 이용하여 지정된 열의 항목을 업데이트할 수 있다. 단, 리스트, 어레이의 길이가 행의 개수와 동일해야 함에 주의해야 한다.
df['debt'] = np.arange(9.)
df
year | state | pop | debt | Ohio state | |
---|---|---|---|---|---|
one | 2000 | Ohio | 1.5 | 0.0 | True |
two | 2001 | Ohio | 1.7 | 1.0 | True |
three | 2002 | Ohio | 3.6 | 2.0 | True |
four | 2001 | Nevada | 2.4 | 3.0 | False |
five | 2002 | Nevada | 2.9 | 4.0 | False |
six | 2003 | Nevada | 3.2 | 5.0 | False |
seven | 2002 | NY | 8.3 | 6.0 | False |
eight | 2003 | NY | 8.4 | 7.0 | False |
nine | 2004 | NY | 8.5 | 8.0 | False |
반면에 시리즈를 이용하여 특정 열의 값을 지정할 수 있으며, 이 때는 항목의 길이가 행의 개수와 동일할 필요가 없다. 다만, 지정된 행의 인덱스 값만 삽입되며 나머지는 결측치로 처리된다.
val = pd.Series([-1.2, -1.5, -1.7, 2.2], index=['two', 'four', 'five', 'eleven'])
val
two -1.2
four -1.5
five -1.7
eleven 2.2
dtype: float64
위 시리즈를 이용하여 'debt'
열의 값을 업데이트하면 다음과 같다.
'two'
,'four'
,'five'
행은 지정된 값으로 업데이트나머지 인덱스의 값은 결측치로 처리됨.
'eleven'
에 해당하는 값은 무시됨. 이유는df
의 인덱스로 포함되지 않기 때문임.
df['debt'] = val
df
year | state | pop | debt | Ohio state | |
---|---|---|---|---|---|
one | 2000 | Ohio | 1.5 | NaN | True |
two | 2001 | Ohio | 1.7 | -1.2 | True |
three | 2002 | Ohio | 3.6 | NaN | True |
four | 2001 | Nevada | 2.4 | -1.5 | False |
five | 2002 | Nevada | 2.9 | -1.7 | False |
six | 2003 | Nevada | 3.2 | NaN | False |
seven | 2002 | NY | 8.3 | NaN | False |
eight | 2003 | NY | 8.4 | NaN | False |
nine | 2004 | NY | 8.5 | NaN | False |
18.2.2. 부울 인덱싱#
부울 인덱싱 또한 넘파이 2차원 어레이와 동일하게 작동한다. 설명을 위해 아래 데이터프레임을 이용한다.
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
data
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 |
아래 코드는 'three'
라벨 열에 포함된 항목이 5보다 큰 경우에만 True
로 갖는 마스크 시리즈를 생성한다.
mask1 = data['three'] > 5
mask1
Ohio False
Colorado True
Utah True
New York True
Name: three, dtype: bool
아래 코드는 True
인 행만 추출한다.
즉, 'three'
라벨 열의 항목 중에서 5보다 큰 값이 포함된 행만 추출한다.
data[mask1]
one | two | three | four | |
---|---|---|---|---|
Colorado | 4 | 5 | 6 | 7 |
Utah | 8 | 9 | 10 | 11 |
New York | 12 | 13 | 14 | 15 |
항목 업데이트
부울 인덱싱을 이용하여 특정 항목의 값을 업데이트할 수도 있다.
예를 들어 아래 코드는 'three'
라벨 열의 항목이 5보다 작은 행의 모든 항목을 0으로 지정한다.
참고로 ~mask1
은 mask1
의 부정을 나타내는 마스크 시리즈다.
data[~mask1] = 0
data
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 0 | 0 | 0 |
Colorado | 4 | 5 | 6 | 7 |
Utah | 8 | 9 | 10 | 11 |
New York | 12 | 13 | 14 | 15 |
데이터프레임에 포함된 각각의 항목에 대한 부울 인덱싱도 비슷하게 작동한다. 예를 들어 아래 코드는 6보다 작은 항목을 모두 0으로 지정한다.
mask2 = data < 6
mask2
one | two | three | four | |
---|---|---|---|---|
Ohio | True | True | True | True |
Colorado | True | True | False | False |
Utah | False | False | False | False |
New York | False | False | False | False |
data[mask2] = 0
data
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 0 | 0 | 0 |
Colorado | 0 | 0 | 6 | 7 |
Utah | 8 | 9 | 10 | 11 |
New York | 12 | 13 | 14 | 15 |
18.2.3. 행 인덱싱#
행 인덱싱은 loc()
또는 iloc()
함수를 이용한다.
loc()
함수: 인덱스 라벨을 이용할 경우iloc()
함수: 정수 인덱스를 이용할 경우
설명을 위해 계속해서 data
가 가리키는 데이터프레임을 이용한다.
data
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 0 | 0 | 0 |
Colorado | 0 | 0 | 6 | 7 |
Utah | 8 | 9 | 10 | 11 |
New York | 12 | 13 | 14 | 15 |
아래 코드는 'Colorado'
행을 시리즈로 추출한다.
data.loc['Colorado']
one 0
two 0
three 6
four 7
Name: Colorado, dtype: int64
'Colorado'
행이 2번 인덱스의 행기이에 다음과 같이 행 인덱싱을 진행해도 된다.
data.iloc[2]
one 8
two 9
three 10
four 11
Name: Utah, dtype: int64
행 팬시 인덱싱
여러 행을 대상으로 인덱싱 하려면 아래와 같이 행 라벨의 리스트 또는 정수 인덱스의 리스트를 활용한다. 결과로 데이터프레임이 생성된다.
행 라벨 리스트 활용
data.loc[['New York', 'Colorado', 'Colorado', 'Utah']]
one | two | three | four | |
---|---|---|---|---|
New York | 12 | 13 | 14 | 15 |
Colorado | 0 | 0 | 6 | 7 |
Colorado | 0 | 0 | 6 | 7 |
Utah | 8 | 9 | 10 | 11 |
정수 인덱스 리스트 활용
data.iloc[[3, 1, 1, 2]]
one | two | three | four | |
---|---|---|---|---|
New York | 12 | 13 | 14 | 15 |
Colorado | 0 | 0 | 6 | 7 |
Colorado | 0 | 0 | 6 | 7 |
Utah | 8 | 9 | 10 | 11 |
18.2.4. 데이터프레임 슬라이싱#
loc()
과 iloc()
함수를 행과 열에 동시에 적용할 수 있으며,
2차원 넘파이 어레이에 대한 인덱싱/슬라이싱이 작동하는 방식과 거의 동일하다.
설명을 위해 계속해서 data
가 가리키는 데이터프레임을 이용한다.
data
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 0 | 0 | 0 |
Colorado | 0 | 0 | 6 | 7 |
Utah | 8 | 9 | 10 | 11 |
New York | 12 | 13 | 14 | 15 |
열 슬라이싱
열에 대해서만 슬라이싱을 적용하려면 행을 전체로 지정한다. 아래 코드는 전체 행을 대상으로 3번 인덱스열 이전까지 추출한다.
data.iloc[:, :3]
one | two | three | |
---|---|---|---|
Ohio | 0 | 0 | 0 |
Colorado | 0 | 0 | 6 |
Utah | 8 | 9 | 10 |
New York | 12 | 13 | 14 |
행 슬라이싱
행에 대해서만 슬라이싱을 적용하려면 열을 전체로 지정한다.
data.iloc[2:, :]
one | two | three | four | |
---|---|---|---|---|
Utah | 8 | 9 | 10 | 11 |
New York | 12 | 13 | 14 | 15 |
열 슬라이싱은 지정하지 않아도 된다.
data.iloc[2:]
one | two | three | four | |
---|---|---|---|---|
Utah | 8 | 9 | 10 | 11 |
New York | 12 | 13 | 14 | 15 |
행/열 슬라이싱
행과 열 슬라이싱을 동시에 진행할 수도 있다. 아래 코드는 2번 인덱스 행부터의 모든 행을 대상으로 3번 인덱스 열 이전까지의 항목만 추출한다.
data.iloc[2:, :3]
one | two | three | |
---|---|---|---|
Utah | 8 | 9 | 10 |
New York | 12 | 13 | 14 |
스텝 활용
스텝step도 사용할 수 있다. 아래 코드는 짝수 인덱스 행에 속한 항목 중에서 홀수 인덱스 열의 항목만 추출한다.
data.iloc[::2, 1::2]
two | four | |
---|---|---|
Ohio | 0 | 0 |
Utah | 9 | 11 |
인덱싱과 슬라이싱 조합
인덱싱과 슬라이싱의 어떤 조합도 행과 열에 대해 적용할 수 있다.
아래 코드는 'Colorado'
행의 'two'
와 'three'
열의 항목을 시리즈로 추출한다.
data.loc['Colorado', ['two', 'three']]
two 0
three 6
Name: Colorado, dtype: int64
아래 코드는 2번 인덱스 행의 3번, 0번, 1번 인덱스 열의 항목을 시리즈로 추출한다.
data.iloc[2, [3, 0, 1]]
four 11
one 8
two 9
Name: Utah, dtype: int64
아래 코드는 1번, 2번 인덱스 행의 3번, 0번, 1번 인덱스 열의 항목을 데이터프레임으로 추출한다.
data.iloc[[1, 2], [3, 0, 1]]
four | one | two | |
---|---|---|---|
Colorado | 7 | 0 | 0 |
Utah | 11 | 8 | 9 |
아래 코드는 'Colorado'
라벨 행까지의 모든 행에서 'two'
열의 항목을 시리즈로 추출한다.
data.loc[:'Colorado', 'two']
Ohio 0
Colorado 0
Name: two, dtype: int64
아래 코드는 'Colorado'
라벨 행까지의 모든 행에서 'two'
열 이후의 모든 열의 항목을 데이터프레임으로 추출한다.
data.loc[:'Colorado', 'two':]
two | three | four | |
---|---|---|---|
Ohio | 0 | 0 | 0 |
Colorado | 0 | 6 | 7 |
부울 인덱싱 연속 적용
인덱싱/슬라이싱은 시리즈 또는 데이터프레임을 생성하기에 바로 이어 부울 인덱싱을 연달아 적용하면 보다 간편하게 원하는 시리즈 또는 데이터프레임을 생성할 수 있다.
아래 코드는 전체 행의 3번 인덱스 열 이전까지의 항목들 중에서
'three'
열의 항목이 5보다 큰 행의 값들만 항목으로 포함하는 데이터프레임을 생성한다.
data.iloc[:, :3][data.three > 5]
one | two | three | |
---|---|---|---|
Colorado | 0 | 0 | 6 |
Utah | 8 | 9 | 10 |
New York | 12 | 13 | 14 |
18.3. 데이터프레임 수정#
인덱싱/슬라이싱을 이용하여 항목 변경, 행/열 추가 등을 실행한다. 설명을 위해 새로운 데이터프레임을 생성한다.
아래 데이터프레임을 생성하기 위해 pd.date_range()
함수는 시간으로 구성된 인덱스 자료형을 생성한다.
함수 호출에 사용된 키워드 인자의 의미는 다음과 같다.
start="20130101
: 2013년 1월 1일부터 시작periods=6
: 첫째 인자로 지정된 시간부터 6 개의 시간 데이터 샘플 생성freq="D"
: 시간 데이터 샘플을 일(day) 단위로 생성
dates = pd.date_range(start="20130101", periods=6, freq="D")
dates
DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
'2013-01-05', '2013-01-06'],
dtype='datetime64[ns]', freq='D')
np.random.seed(0)
df1 = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list("ABCD"))
df1
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | 1.764052 | 0.400157 | 0.978738 | 2.240893 |
2013-01-02 | 1.867558 | -0.977278 | 0.950088 | -0.151357 |
2013-01-03 | -0.103219 | 0.410599 | 0.144044 | 1.454274 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 0.333674 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | -0.854096 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | -0.742165 |
열 추가
아래 시리즈를 df1
의 새로운 열로 추가하려 한다.
s1 = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range("20130102", periods=6))
s1
2013-01-02 1
2013-01-03 2
2013-01-04 3
2013-01-05 4
2013-01-06 5
2013-01-07 6
Freq: D, dtype: int64
열 라벨은 F
로 지정한다.
그러면 2013-01-01
의 데이터가 없기에 결측치로 처리된다.
df1.loc[:, "F"] = s1
df1
A | B | C | D | F | |
---|---|---|---|---|---|
2013-01-01 | 1.764052 | 0.400157 | 0.978738 | 2.240893 | NaN |
2013-01-02 | 1.867558 | -0.977278 | 0.950088 | -0.151357 | 1.0 |
2013-01-03 | -0.103219 | 0.410599 | 0.144044 | 1.454274 | 2.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 0.333674 | 3.0 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | -0.854096 | 4.0 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | -0.742165 | 5.0 |
항목 지정
아래 코드는 2013-01-01
행의 'A'
열의 값을 0으로 지정한다.
df1.loc[dates[0], "A"] = 0
df1
A | B | C | D | F | |
---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.400157 | 0.978738 | 2.240893 | NaN |
2013-01-02 | 1.867558 | -0.977278 | 0.950088 | -0.151357 | 1.0 |
2013-01-03 | -0.103219 | 0.410599 | 0.144044 | 1.454274 | 2.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 0.333674 | 3.0 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | -0.854096 | 4.0 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | -0.742165 | 5.0 |
iloc[]
도 활용할 수 있다.
아래 코드는 2013-01-01
행의 'B'
열의 값도 0으로 지정한다.
df1.iloc[0, 1] = 0
df1
A | B | C | D | F | |
---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 2.240893 | NaN |
2013-01-02 | 1.867558 | -0.977278 | 0.950088 | -0.151357 | 1.0 |
2013-01-03 | -0.103219 | 0.410599 | 0.144044 | 1.454274 | 2.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 0.333674 | 3.0 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | -0.854096 | 4.0 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | -0.742165 | 5.0 |
행/열 지정
어레이를 이용하여 열 또는 행을 지정할 수 있다.
아래 코드는 D
열을 새로운 어레이로 지정한다.
df1.loc[:, "D"] = np.array([5] * len(df1))
df1
A | B | C | D | F | |
---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN |
2013-01-02 | 1.867558 | -0.977278 | 0.950088 | 5.0 | 1.0 |
2013-01-03 | -0.103219 | 0.410599 | 0.144044 | 5.0 | 2.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | 5.0 | 4.0 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | 5.0 | 5.0 |
아래 코드는 2013-01-02
행을 새롭게 지정한다.
df1.loc[dates[1], :] = np.array([3] * df1.shape[1])
df1
A | B | C | D | F | |
---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 |
2013-01-03 | -0.103219 | 0.410599 | 0.144044 | 5.0 | 2.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | 5.0 | 4.0 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | 5.0 | 5.0 |
iloc[]
객체를 이용할 수도 있다.
아래 코드는 2번 행을 새로 지정한다.
df1.iloc[2, :] = np.array([4] * df1.shape[1])
df1
A | B | C | D | F | |
---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | 5.0 | 4.0 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | 5.0 | 5.0 |
18.4. 결측치 처리#
시리즈와 데이터프레임의 모든 결측치는 내부적으로 np.nan
로 처리된다.
다만 자료형에 따라 NaN
(부동소수점), NA
(정수), NaT
(시간) 등으로 표기된다.
18.4.1. 시리즈의 결측치#
연습을 위해 결측치를 하나 포함한 시리즈를 생성한다.
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Ohio', 'Oregon', 'Texas']
obj2 = pd.Series(sdata, index=states)
obj2
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64
isna()
와 notna()
pd.isna()
함수는 누락된 항목은 True
, 아니면 False
로 지정하여 단번에 결측치가 포함되었는지
여부를 확인해준다.
pd.isna(obj2)
California True
Ohio False
Oregon False
Texas False
dtype: bool
pd.notna()
함수는 누락된 항목은 False
, 아니면 True
로 지정하여 단번에 결측치가 포함되었는지
여부를 확인해준다.
pd.notna(obj2)
California False
Ohio True
Oregon True
Texas True
dtype: bool
두 함수를 호출하면 실제로는 시리즈 객체의 메서드인 isna()
또는 notna()
이 내부에서 호출된다.
obj2.isna()
California True
Ohio False
Oregon False
Texas False
dtype: bool
obj2.notna()
California False
Ohio True
Oregon True
Texas True
dtype: bool
isnull()
과 notnull()
isnull()
과 notnull()
는 각각 isna()
와 notna()
의 별칭이다.
pd.isnull(obj2)
California True
Ohio False
Oregon False
Texas False
dtype: bool
pd.notnull(obj2)
California False
Ohio True
Oregon True
Texas True
dtype: bool
obj2.isnull()
California True
Ohio False
Oregon False
Texas False
dtype: bool
obj2.notnull()
California False
Ohio True
Oregon True
Texas True
dtype: bool
any()
와 all()
any()
또는 all()
메서드를 활용하면 결측치 사용 여부를 단번에 알 수 있다.
예를 들어, pd.isna()
과 any()
메서드의 활용 경과가 True
이면 결측치가 있다는 의미이다.
obj2.isna().any()
True
반면에 pd.notna()
과 all()
메서드의 활용 경과가 False
이면 역시 결측치가 있다는 의미이다.
obj2.notna().all()
False
넘파이의 np.any()
, np.all()
를 활용해도 동일한 결과를 얻는다.
np.any(obj2.isna())
True
np.all(obj2.notna())
False
18.4.2. 데이터프레임의 결측치#
연습을 위해 df1
를 수정하여 결측치를 일부 포함한 데이터프레임을 생성한다.
df1
A | B | C | D | F | |
---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 |
2013-01-05 | 1.494079 | -0.205158 | 0.313068 | 5.0 | 4.0 |
2013-01-06 | -2.552990 | 0.653619 | 0.864436 | 5.0 | 5.0 |
리인덱싱으로 다음 데이터프레임을 생성한다.
'E'
열이 추가되는데 df1
에 없던 열이기에 모든 항목이 결측치로 지정된다.
df2 = df1.reindex(index = dates[0:4], columns = list(df1.columns) + ['E'])
df2
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN | NaN |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | NaN |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | NaN |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | NaN |
결측치 일부도 특정 값으로 채운다.
아래 코드는 'E'
열의 일부를 1로 지정한다.
df2.loc[dates[0] : dates[1], 'E'] = 1
df2
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | NaN |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | NaN |
isna()
와 notna()
pd.isna()
함수는 결측치가 위치한 곳만 True
항목을 갖는 부울 마스크를 생성한다.
pd.isna(df2)
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | False | False | False | False | True | False |
2013-01-02 | False | False | False | False | False | False |
2013-01-03 | False | False | False | False | False | True |
2013-01-04 | False | False | False | False | False | True |
데이터프레임의 isna()
메서드가 동일한 일을 수행한다.
df2.isna()
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | False | False | False | False | True | False |
2013-01-02 | False | False | False | False | False | False |
2013-01-03 | False | False | False | False | False | True |
2013-01-04 | False | False | False | False | False | True |
pd.notna()
함수는 결측치가 아닌 곳만 True
항목을 갖는 부울 마스크를 생성한다.
pd.notna(df2)
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | True | True | True | True | False | True |
2013-01-02 | True | True | True | True | True | True |
2013-01-03 | True | True | True | True | True | False |
2013-01-04 | True | True | True | True | True | False |
데이터프레임의 notna()
메서드가 동일한 일을 수행한다.
df2.notna()
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | True | True | True | True | False | True |
2013-01-02 | True | True | True | True | True | True |
2013-01-03 | True | True | True | True | True | False |
2013-01-04 | True | True | True | True | True | False |
참고
isnull()
과 notnull()
은 각각 isna()
와 notna()
별칭이다.
any()
와 all()
any()
또는 all()
메서드를 활용하면 결측치 사용 여부를 단번에 알 수 있다.
두 메서드는 기본적으로 열별로 적어도 하나가 또는 모두 참인지 여부를 확인한다. 결과는 시리즈다.
df2.isna().any()
A False
B False
C False
D False
F True
E True
dtype: bool
축 키워드 인자를 axis=0
로 지정한 것과 동일하다.
df2.isna().any(axis=0)
A False
B False
C False
D False
F True
E True
dtype: bool
행 별로 결측치 존재 여부를 확인하려면 축 키워드 인자를 axis=1
로 지정한다.
결과는 시리즈다.
df2.isna().any(axis=1)
2013-01-01 True
2013-01-02 False
2013-01-03 True
2013-01-04 True
Freq: D, dtype: bool
반면에 all()
메서드는 열별 또는 행별로 모두 참인지 확인한다.
df2.isna().all()
A False
B False
C False
D False
F False
E False
dtype: bool
축 키워드 인자를 axis=0
으로 지정한 것과 동일하다.
df2.isna().all(axis=0)
A False
B False
C False
D False
F False
E False
dtype: bool
행 별로 결측치 존재 여부를 확인하려면 축 키워드 인자를 axis=1
로 지정한다.
결과는 시리즈다.
df2.isna().all(axis=1)
2013-01-01 False
2013-01-02 False
2013-01-03 False
2013-01-04 False
Freq: D, dtype: bool
넘파이의 np.any()
, np.all()
는 전체 항목을 대상으로만 작동한다.
np.any(df2.isna())
True
np.all(df2.isna())
False
데이터프레임의 any()
메서드와 all()
메서드를 이용하여 전체 항목을 확인하려면 해당 메서드를 두 번 적용해야 한다.
df2.isna().any().any()
True
df2.isna().any(axis=1).any()
True
df2.isna().all().all()
False
df2.isna().all(axis=1).all()
False
dropna()
메서드
결측치를 포함한 행 또는 열이 삭제된 데이터프레임을 반환한다.
행 기준:
axis=0
이 기본값임. 아래 코드는 결측치를 하나 이상 포함한 모든 행 삭제.
df2.dropna(how='any')
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-02 | 3.0 | 3.0 | 3.0 | 3.0 | 3.0 | 1.0 |
행 기준:
axis=0
이 기본값임. 아래 코드는 결측치로만 구성된 모든 행 삭제.
df2.dropna(how='all')
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | NaN |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | NaN |
열 기준:
axis=1
지정. 아래 코드는 하나 이상의 결측치를 포함한 모든 열 삭제.
df2.dropna(axis=1, how='any')
A | B | C | D | |
---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 |
열 기준:
axis=1
지정. 아래 코드는 하나 결측치로만 구성된 모든 열 삭제.
df2.dropna(axis=1, how='all')
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | NaN |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | NaN |
fillna()
메서드
모든 결측치를 지정된 값으로 채운 데이터프레임을 생성한다.
df2.fillna(value=5)
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | 5.0 | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | 5.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | 5.0 |
ffill()
메서드
열 별로 모든 결측치를 위쪽에 위치한 가장 가까운 값으로 채운 데이터프레임을 생성한다.
df2.ffill()
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | NaN | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | 1.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | 1.0 |
axis=1
을 지정하면 행 기준으로 채워진다.
df2.ffill(axis=1)
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | 5.0 | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | 4.0 |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | 3.0 |
bfill()
메서드
열 별로 모든 결측치를 아래쪽에 위치한 가장 가까운 값으로 채운 데이터프레임을 생성한다.
df2.bfill()
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | 3.0 | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | NaN |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | NaN |
axis=1
을 지정하면 행 기준으로 채워진다.
df2.bfill(axis=1)
A | B | C | D | F | E | |
---|---|---|---|---|---|---|
2013-01-01 | 0.000000 | 0.000000 | 0.978738 | 5.0 | 1.0 | 1.0 |
2013-01-02 | 3.000000 | 3.000000 | 3.000000 | 3.0 | 3.0 | 1.0 |
2013-01-03 | 4.000000 | 4.000000 | 4.000000 | 4.0 | 4.0 | NaN |
2013-01-04 | 0.761038 | 0.121675 | 0.443863 | 5.0 | 3.0 | NaN |
18.5. 연습문제#
참고: (실습) 데이터프레임 인덱싱