본문 바로가기
Python/Pandas

인덱스 정렬(순서 변경)을 위한 reindex() 메서드- pandas(28)

by 콩돌 2020. 9. 8.
반응형

파이썬 버전 3.8 기준

pandas 버전 1.1.1 기준


 인덱스 정렬(순서 변경)을 위한 reindex() 메서드

 

본 포스팅에서는 reindex()의 사용법에 다루도록 한다. 

reindex()를 사용하여 데이터를 정렬하는 방법부터 누락데이터 처리까지 그리고 상세 옵션을 설명하는 내용도 다루도록 한다.

 

 reindex()메서드

 

pandas에서 reindex() 메서드는 기본적인 데이터 정렬 메서드이다. 

  ○ reindex()는 특정 축을 따라 입력된 레이블 배열에 따라 데이터 순서를 조정한다.

  ○ 이 메서드는 상당히 자유도가 높은 방법으로 데이터를 정렬한다.

 

reindex()의 기능을 이용하면 다음과 같은 작업을 수행할 수 있다.

  ○ 기존에 존재하는 데이터에 레이블의 새로운 세트에 매치시켜 순서를 재조정한다.

  ○ 레이블은 존재하지만 데이터가 존재하지 않는 레이블 위치에 누락값을 발생시켜 마커 할 수 있다.

  ○ 만약 필요하다면, 로직을 사용해 레이블이 없는 데이터들을 채울 수 있다.

 
reindex()메서드 사용 형식
아래 형식은 reindex() 메서드의 사용 형식을 보여준다.

 

메서드 사용 형식)

# Series의 경우

result=object1.reindex(index, method=None, fill_value=np.nan, limit=None)


# DataFrame의 경우

result=object1.reindex(index, columns, axis=0, method=None, fill_value=np.nan, limit=None)

 

각 입력 객체별 설명은 다음과 같다.

  ○ object1: 데이터를 재정렬할 객체(DataFrame, Series)이다.

  ○ index: 정렬하는 순서로 구성된 인덱스의 배열(혹은 배열과 유사한array-like)을 입력받는다.

  ○ method: 위의 index가 object1에 없는 경우 해당 값을 채우는 방식을 입력받는다.

    ▷ 상세 내용은 아래 섹션에 정리하였다.

  ○ fill_value: 위의 index가 object1에 없는 경우 해당 값을 채우는 값을 입력받는다.

  ○ limit: 누락값이 연속적으로 여러 개 존재할 때 method방식에 따라 누락 값을 채우는 개수를 제한한다.

  ○ axis: 정렬을 수행할 축을 입력받는다. 기본값으로는 행을 기준으로 정렬을 수행한다.

    ▷ 'index' / 0 : 행에 대해서만 정렬을 수행시킨다.

    ▷ 'columns' / 1 : 열에 대해서만 정렬을 수행시킨다. 


추가적으로 DataFrame에서는 index와 columns 입력변수를 사용하여 각 방향에 대해 인덱스 정렬을 수행할 수 있으며, axis입력변수를 사용하여 한 방향에 대해서만 데이터 정렬을 수행할 수 있다. 

 

 reindex()메서드의 기본적인 사용 방식에 대한 예제

본 포스팅에서는 다음과 같이 pandas와 numpy를 import시킨 후 예제를 수행하였다.

  ○ 본 포스팅에서는 numpy는 딱히 쓰지는 않지만 습관적으로 그랬다.

 

모듈의 임폴트)

# pandas와 numpy의 import

In[2]: import pandas as pd

In[3]: import numpy as np

 

다음은 reindex() 메서드의 가장 단순한 사용 예제를 보여준다.

  ○ 다음 예제를 보면 입력된 레이블 배열 중에 'f' 레이블은 기존 Series에는 없었다. 

  ○ 따라서 'f' 레이블 결과에서 NaN이 나타난다.


Series에서의 reindex() 사용)

# 예제용 Series 데이터의 정의

In[4]: sample = pd.Series([1.1, 2.2, 3.3, 4.4, 5.5], index=['a', 'b', 'c', 'd', 'e'])

In[5]: sample

Out[5]: 

a    1.1

b    2.2

c    3.3

d    4.4

e    5.5

dtype: float64


# reindex() 사용 예제

In[6]: sample.reindex(['e','a','f','d'])

Out[6]: 

e    5.5

a    1.1

f    NaN

d    4.4

dtype: float64

 

사용자는 DataFrame에 reindex() 메서드를 사용해 행(row, index)과 열(columns)의 순서를 변경할 수 있다.

이를 수행하기 위해 크게 2가지 방식이 있는데 다음과 같이 설명할 수 있다.

  ○ index 및 columns 입력변수를 이용하여 두 방향을 동시에 정렬하는 방식

  ○ 하나의 입력 배열과 axis 입력변수를 사용하여 하나의 방향에 대해 정렬하는 방식(축 스타일 방식)

다음 예제는 위의 두 방식을 어떻게 적용하는지 보여주는 예제이다.


DataFrame에서의 reindex() 메서드의 사용)

# 예제용 DataFrame의 정의

In[7]: sample_df=pd.DataFrame([[1.1, 1.2, 1.3, 1.4], [2.1, 2.2, 2.3, 2.4], [3.1, 3.2, 3.3, 3.4]], index=['a', 'b', 'c'], columns=['A','B','C','D'])

In[8]: sample_df

Out[8]: 

     A    B    C    D

a  1.1  1.2  1.3  1.4

b  2.1  2.2  2.3  2.4

c  3.1  3.2  3.3  3.4


# reindex() 사용 예제(두방향 동시 정렬)

In[9]: sample_df.reindex(index=['b','a','d'], columns=['B','D','E'])

Out[9]: 

     B    D   E

b  2.2  2.4 NaN

a  1.2  1.4 NaN

d  NaN  NaN NaN


# reindex() 사용 예제(한방향 정렬)

In[10]: sample_df.reindex(['b','a','d'], axis='index')

Out[10]: 

     A    B    C    D

b  2.1  2.2  2.3  2.4

a  1.1  1.2  1.3  1.4

d  NaN  NaN  NaN  NaN


In[11]: sample_df.reindex(['B','A','D'], axis='columns')

Out[11]: 

     B    A    D

a  1.2  1.1  1.4

b  2.2  2.1  2.4

c  3.2  3.1  3.4

 

실제의 축 레이블의 정보를 담고있는 Index 객체는 객체간의 공유가 가능하다.

  ○ 즉, 다른 객체의 Index 객체를 사용하여 정렬을 수행하는 것이 가능하다.


따라서 사용자가 Series와 DataFrame을 둘 다 정의한 상황에서는 다음 예제와 같이 실행되어질 수 있다.

  ○ 아래 예제의 Series와 DataFrame은 위의 예제에서 사용한 객체와 동일하다.

  ○ 아래 예제의 결과는 정렬된 Series의 인덱스가 DataFrame의 인덱스에 따라 같은 파이썬 객체라는 것을 보여준다.


타 객체의 Index 객체를 사용한 데이터 정렬)

# 타 객체의 Index 객체를 이용한 데이터 정렬 

In[12]: copy_sample= sample.reindex(sample_df.index)

In[13]: copy_sample

Out[13]: 

a    1.1

b    2.2

c    3.3

dtype: float64


# 객체간의 Index 객체 공유

In[14]: copy_sample.index is sample_df.index

Out[14]: True

 

※ 참고사항

성능에 민감한 코드를 작성할 때, 재인덱싱을 통해 성능(혹은 연산속도)를 개선할 수 있다.

많은 연산들에서 미리 정렬된 데이터에서 작업을 수행하는 것이 더 빠르므로 미리 정렬을 수행해놓으면 좋다.

잦은 reindex로 인해 성능이 저하되는 메커니즘은 다음과 같다.

  → 두 개의 정렬되지 않은 DataFrame을 더하는 것은 내부적으로 reindex 과정을 야기한다.

  → 위 과정에서 사용자는 reindex 과정을 인지하지 못할 수도 있다. 

     (이는 pandas가 여러 연산에서 reindex를 자동으로 수행하도록 시켜놓았기 때문이다.)

  → 그러나 위의 과정을 반복해야할 필요성이 있을 때에는 재인덱싱을 반복해서 하므로 계산성능의 저하를 일으킬 수 있다.

 

 reindex_like() 메서드를 활용한 정렬

특정 객체를 만들고나서 그 객체의 축에 다른 객체와 같은 인덱스를 붙여야할 경우가 있다.

  ○ reindex() 메서드를 사용하면 위의 요구조건을 충분히 충족시킬 수 있다.

  ○ 하지만 이러한 상황에서 reindex_like() 메서드를 사용하면 매우 쉽게 인덱스를 변경할 수 있다.

  ○ 다음 예제는 reindex_like()의 사용 예제를 보여준다.


reindex_like() 메서드의 사용)

# 예제용 DataFrame의 정의

In[15]: sample_df=pd.DataFrame([[1.1, 1.2, 1.3, 1.4], [2.1, 2.2, 2.3, 2.4], [3.1, 3.2, 3.3, 3.4]], index=['a', 'b', 'c'], columns=['A','B','C','D'])

In[16]: sample_df2=pd.DataFrame([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]], index=['a', 'c', 'b'], columns=['B','C','A','D'])


In[17]: sample_df

Out[17]: 

     A    B    C    D

a  1.1  1.2  1.3  1.4

b  2.1  2.2  2.3  2.4

c  3.1  3.2  3.3  3.4

In[18]: sample_df2

Out[18]: 

   B  C   A   D

a  0  1   2   3

c  4  5   6   7

b  8  9  10  11


# reindex_like() 메서드의 사용 예제

In[19]: sample_df.reindex_like(sample_df2)

Out[19]: 

     B    C    A    D

a  1.2  1.3  1.1  1.4

c  3.2  3.3  3.1  3.4

b  2.2  2.3  2.1  2.4

 

 reindex() 메서드 사용 시 누락값 채우기

reindex() 메서드는 필요에 따라 활용할 수 있는 method 입력변수를 가진다. 
  ○ 이는 채우는 방법을 선택하는 메서드이다.
  ○ method='pad'/'ffill': 앞쪽방향으로 채운다.
  ○ method='bfill'/'backfill': 뒤쪽방향으로 채운다.
  ○ method='nearest': 가장 근처에 있는 인덱스 값으로 채운다. 
    ▷ 'nearest' 옵션은 단순 문자열 인덱스에는 적용이 불가능하다.


다음 예제들은 reindex() 메서드를 사용하여 누락값을 채우는 예제이다.

  ○ 예제가 내용은 크게 없지만 길으므로 예제를 쪼개서 포스팅한다. 


다음은 이 섹션에서 활용하는 예제용 DataFrame을 보여준다. 


예제용 DataFrame의 정의)

# 예제용 DataFrame의 정의

In[20]: sample1=pd.DataFrame([1, 2, 3, 4, 5, 6, 7, 8, 9], index=['a', 'b', 'c', 'd', 'e', 'f','g','h','i'])

In[21]: sample2=pd.DataFrame([1, 4, 7], index=['a', 'd', 'g'])


In[22]: sample1

Out[22]: 

   0

a  1

b  2

c  3

d  4

e  5

f  6

g  7

h  8

i  9

In[23]: sample2

Out[23]: 

   0

a  1

d  4

g  7


다음은 reindex() 메서드를 활용하여 누락 데이터가 발생했을 시 해당 값을 채우는 예제를 보여준다.


reindex() 메서드를 사용한 누락값 채움)

# reindex() 메서드의 사용(누락값 채우지 않을 시)

In[24]: sample2.reindex(sample1.index)

Out[24]: 

     0

a  1.0

b  NaN

c  NaN

d  4.0

e  NaN

f  NaN

g  7.0

h  NaN

i  NaN


# reindex() 메서드의 사용('ffill' 옵션)

In[25]: sample2.reindex(sample1.index, method='ffill')

Out[25]: 

   0

a  1

b  1

c  1

d  4

e  4

f  4

g  7

h  7

i  7


# reindex() 메서드의 사용('bfill' 옵션)

In[26]: sample2.reindex(sample1.index, method='bfill')

Out[26]: 

     0

a  1.0

b  4.0

c  4.0

d  4.0

e  7.0

f  7.0

g  7.0

h  NaN

i  NaN


# 'nearest'옵션은 index를 문자열로 사용할 경우 에러 발생

# 따라서 숫자형 인덱스로 변경하여 'nearest' 옵션 적용

In[27]: sample1.index=[0, 1, 2, 3, 4, 5, 6, 7, 8]

In[28]: sample2.index=[0, 4, 6]

In[29]: sample2.reindex(sample1.index, method='nearest')

Out[29]: 

   0

0  1

1  1

2  4

3  4

4  4

5  7

6  7

7  7

8  7

 

다음 예제와 같이 fillna()나 interpolate() 메서드를 사용하여 같은 방법으로도 같은 결과가 얻어질 수 있다.

  ○ index가 점진적으로 증가하거나 감소하지 하지 않는다면, reindex() 메서드는 ValueError를 발생시킨다.

  ○ fillna()나 interpolate() 메서드는 인덱스의 순서를 확인하는 절차를 수행하지 않는다.


fillna() 메서드의 사용)

# fillna() 메서드를 사용한 누락값 채우기

In[30]: sample2.reindex(sample1.index).fillna(method='ffill')

Out[30]: 

     0

a  1.0

b  1.0

c  1.0

d  4.0

e  4.0

f  4.0

g  7.0

h  7.0

i  7.0

 


 reindex() 메서드 사용 시 누락값을 채우는 것에 대한 제한 

limit나 tolerance 입력변수는 reindex를 수행할 때 값을 채우는 방법에 대한 추가적인 로직을 제공한다.

  ○ limit는 누락값이 연속적으로 있을때, 누락값을 채우기 위한 로직을 적용할 최대 수를 명시한다.

  ○ tolerance는 누락값이 연속적으로 있을 때, 누락값을 채울 인덱스와 값이 있는 인덱스의 간의 최대 거리를 명시한다.

    ▷ tolerance는 배열로 입력하여 각 인덱스마다 다르게 최대 거리를 명시할 수 있다. 


다음은 limit 옵션을 사용하는 예제를 보여준다.


limit 옵션의 사용)

# limit 옵션의 사용

In[31]: sample2.reindex(sample1.index, method='ffill', limit=1)

Out[31]: 

     0

a  1.0

b  1.0

c  NaN

d  4.0

e  4.0

f  NaN

g  7.0

h  7.0

i  NaN

 

다음은 tolerance 옵션을 사용하는 예제를 보여준다.

  ○ tolerance는 각 인덱스 값별로 거리를 명시하는 것이 가능하다.

  ○ DataFrameIndex, TimedeltaIndex, PeriodIndex가 사용될때, tolerance는 가능하다면 Timedelta을 사용할 것이 강제된다.

  ○ 이는 사용자에게 적절한 문자열을 사용하여 tolerance를 명시하도록 한다.

 

tolerance 옵션의 사용)

# tolerance 옵션의 사용

# index를 문자열로 사용할 경우 에러 발생

In[32]: sample1.index=[0, 1, 2, 3, 4, 5, 6, 7, 8]

In[33]: sample2.index=[0, 4, 6]
In[34]: sample2.reindex(sample1.index, method='ffill', tolerance=[0, 0, 0, 0, 0, 0, 0, 0, 0])
Out[34]: 
     0
0  1.0
1  NaN
2  NaN
3  NaN
4  4.0
5  NaN
6  7.0
7  NaN
8  NaN

In[35]: sample2.reindex(sample1.index, method='ffill', tolerance=[0, 0, 2, 0, 0, 0, 0, 0, 3])
Out[35]: 
     0
0  1.0
1  NaN
2  1.0
3  NaN
4  4.0
5  NaN
6  7.0
7  NaN
8  7.0

 

 drop() 메서드를 이용한 특정 레이블 데이터 제거

reindex와 상당히 관련있는 메서드는 drop() 메서드이다. 

이 함수는 축으로부터 레이블의 집합을 제거한다.


drop() 메서드의 사용)

# 예제 DataFrame의 정의 

In[36]: sample_df=pd.DataFrame([[1.1, 1.2, 1.3, 1.4], [2.1, 2.2, 2.3, 2.4], [3.1, 3.2, 3.3, 3.4]], index=['a', 'b', 'c'], columns=['A','B','C','D'])

In[36]: sample_df

Out[36]: 

     A    B    C    D

a  1.1  1.2  1.3  1.4

b  2.1  2.2  2.3  2.4

c  3.1  3.2  3.3  3.4


# drop() 메서드의 사용

In[37]: sample_df.drop(['a', 'c'], axis=0)

Out[37]: 

     A    B    C    D

b  2.1  2.2  2.3  2.4


In[38]: sample_df.drop(['B', 'C'], axis=1)

Out[38]: 

     A    D

a  1.1  1.4

b  2.1  2.4

c  3.1  3.4


아래의 예제는 작동하지만 명확하지 않은 방법이다.


예제)

In[39]: sample_df.reindex(sample_df.index.difference(['a', 'd']))

Out[39]: 

     A    B    C    D

b  2.1  2.2  2.3  2.4

c  3.1  3.2  3.3  3.4

 


 .reindex()메서드 세부사항

 

Series 혹은 DataFrame가 새롭게 입력된 인덱스(index)로 변경되게 한다.

이전 인덱스에서 값을 가지지 않는 위치에는 NA/NaN을 입력한다.

copy입력변수에 False가 입력되면 새로운 객체가 생성된다.

 

 DataFrame.reindex(**kwargs)

 

keywords for axes: array-like

선택적으로 입력이 가능하다.

기존의 레이블/인덱스를 대체할 새로운 레이블/인덱스를 키워드를 사용하여 입력받는다.

데이터를 복사하는 것을 방지하기 위해 가급적 인덱스 객체를 입력하는 것이 좋다.


method: {None, ‘backfill’/’bfill’, ‘pad’/’ffill’, ‘nearest’}

이 입력변수는 인덱스가 다시 적용되는 DataFrame의 빈 자리를 채우기 위해 사용하는 변수이다.

이는 단순하게 증가하거나 감소하는 인덱스를 사용하여 DataFrame과 Series에 적용이 가능하다.

  ○ None: 기본값이다. 빈 공간을 채우지 않는다.

  ○ pad/ffill: 마지막 유효한 관측치를 다음 유효한 관측치로 전파한다.

  ○ backfill/ bfill: 빈 데이터를 채우기 위해, 다음 관측치를 사용한다.

  ○ nearest: 빈 데이터를 채우기 위해, 가장 근처에 있는 유효한 관측치를 사용한다.


copy: bool

기본값은 True이다.

만약 입력된 인덱스가 동일하더라도, 새로운 객체를 반환한다.


level: int or name

입력된 MultiIndex level의 Index 값과 일치하는 level에 걸쳐 브로드캐스팅을 수행한다.


fill_value: scalar

기본값은 np.NaN로 설정되어 있다.

누락값에 채우고자 하는 값을 입력받는다.


limit: int, default None

앞으로 또는 뒤로 채울 연속적인 요소의 최대 수를 입력받는다.


tolerance: optional

정확히 매치하지 않는 경우에 기존 레이블과 새로운 레이블 간의 최대 거리를 입력받는다.  

매칭되는 위치에서의 인덱스의 값들은 abs(index[indexer] - target) <= tolerance 을 충족한다.

스칼라 값을 입력받는 경우에는 이는 모든 값들에 같은 Tolerance를 가진다.

리스트와 같은 자료형을 입력받는 경우에는 요소별로 다양하게 Tolerance를 적용할 수 있다.

  ○ 리스트와 같은 자료형은 list, tuple, Series, array 등을 포함한다.

  ○ 이 경우 반드시 인덱스와 같은 크기를 가져야한다.

  ○ 뿐만 아니라 dtype역시 기존인덱스와 정확히 매칭이 되어야한다.



 

 

 

 

 참고자료

  https://pandas.pydata.org/docs/user_guide/basics.html

  https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.reindex.html

 

 

 

 


반응형

댓글