18. 데이터프레임 인덱싱#

주요 내용

SeriesDataFrame 객체를 다루는 다양한 도구를 살펴본다.

  • 인덱싱

  • 슬라이싱

  • 결측치 처리

기본 설정

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))

SeriesDataFrame을 표로 보여줄 때 사용되는 행의 수 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으로 지정한다. 참고로 ~mask1mask1의 부정을 나타내는 마스크 시리즈다.

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. 연습문제#

참고: (실습) 데이터프레임 인덱싱