달나라 노트

Python Pandas : rolling (DataFrame window function) 본문

Python/Python Pandas

Python Pandas : rolling (DataFrame window function)

CosmosProject 2021. 1. 22. 16:46
728x90
반응형

 

 

 

Pandas에서 사용할 수 있는 window function 기능을 알아봅시다.

 

import pandas as pd

dict_test = {
    'col1': [1, 1, 2, 2, 3, 3, 3, 4],
    'col2': [1000, 1100, 2100, 2050, 3000, 3100, 3200, 4200],
    'col3': ['a', 'b', 'a', 'c', 'a', 'a', 'd', 'e']
}

df_test = pd.DataFrame(dict_test)
# print(df_test)
# print(type(df_test))


- Output
   col1  col2 col3
0     1  1000    a
1     1  1100    b
2     2  2100    a
3     2  2050    c
4     3  3000    a
5     3  3100    a
6     3  3200    d
7     4  4200    e
<class 'pandas.core.frame.DataFrame'>

d먼저 위처럼 Test용 DataFrame을 만듭시다.

 

 

 

 

 

 

df_test = pd.DataFrame(dict_test)
df_test['rolling_col'] = df_test.loc[:, ['col2']].rolling(3).sum()
print(df_test)

- Output
   col1  col2 col3  rolling_col
0     1  1000    a          NaN
1     1  1100    b          NaN
2     2  2100    a       4200.0
3     2  2050    c       5250.0
4     3  3000    a       7150.0
5     3  3100    a       8150.0
6     3  3200    d       9300.0
7     4  4200    e      10500.0

위 코드는 col2를 기준으로 rolling sum을 하고있습니다.

코드를 부분부분 나눠서 파악해봅시다.

 

df_test.loc[:, ['col2']] -> rolling sum을 적용시킬 컬럼을 loc를 이용하여 추출합니다.

rolling(3) -> rolling을 적용시킵니다. rolling은 sum, mean 등의 함수를 함께 적용하는데 이때 어떤 데이터를 참조할지를 알려줘야 합니다. 예를들어서 rolling(3)이면 현재 행과 이전 2개 행을 참조합니다. 즉 총 3개의 행을 참조하는것이죠. 이건 예시를 보면서 이해해봅시다.

sum() -> rolling의 결과로 sum을 적용합니다.(mean은 평균입니다.)

 

위 예시의 Output을 봅시다.

rolling sum이 적용되는 컬럼은 col2입니다. col2의 숫자들을 참조해서 sum을 적용한 후 결과값이 rolling_col에 추가되었습니다.

 

 

 

 

row_index = 0인 rolling_col 값을 봅시다.

NaN으로 나와있죠. 그 사유는 rolling의 인자를 3(rolling(3))으로 지정했기 때문입니다.

  col2 rolling_col
-2 None  
-1 None  
0 1000 NaN
1 1100  

위 표를 봐봅시다.

rolling_col의 row_index = 0인 위치에 들어갈 값은 위 표에서 색칠된 3가지의 데이터를 참조하여 더합니다. 왜냐면 rolling(3).sum()으로 sum함수가 사용되었기 때문입니다.

즉, 현재 행(row_index = 0)과 그 이전 2개 행을 참조하여 총 3개의 행의 col2 컬럼 값을 참조해서 더하는거죠.

그러나 row_index = -1 또는 row_index = -2인 데이터는 없죠. 따라서 None + None + 1000 = NaN이라는 결과가 나오는겁니다.

 

 

 

 

row_index = 1인 rolling_col 값을 봅시다.

NaN으로 나와있죠. 그 사유는 마찬가지로 rolling의 인자를 3(rolling(3))으로 지정했기 때문입니다.

  col2 rolling_col
-1 None  
0 1000 NaN
1 1100 NaN

위 표를 봐봅시다.

rolling_col의 row_index = 1인 위치에 들어갈 값은 위 표에서 색칠된 3가지의 데이터를 참조합니다.

즉, 현재 행(row_index = 1)과 그 이전 2개 행을 참조하여 총 3개의 행을 참조하는거죠.

그러나 row_index = -1인 데이터는 없죠. 따라서 None + 1000 + 1100 = NaN이라는 결과가 나오는겁니다.

 

 

 

 

 

row_index = 2인 rolling_col 값을 봅시다.

  col2 rolling_col
0 1000 NaN
1 1100 NaN
2 2100 4200

rolling_col의 row_index = 2인 위치에 들어갈 값은 위 표에서 색칠된 3가지의 데이터를 참조합니다.

즉, 현재 행(row_index = 2)과 그 이전 2개 행을 참조하여 총 3개의 행을 참조하는거죠.

따라서 1000 + 1100 + 2100 = 4200이라는 결과가 나오는겁니다.

 

 

 

 

 

row_index = 3인 rolling_col 값을 봅시다.

  col2 rolling_col
0 1000 NaN
1 1100 NaN
2 2100 4200
3 2050 5250

rolling_col의 row_index = 3인 위치에 들어갈 값은 위 표에서 색칠된 3가지의 데이터를 참조합니다.

즉, 현재 행(row_index = 3)과 그 이전 2개 행을 참조하여 총 3개의 행을 참조하는거죠.

따라서 1100 + 2100 + 2050 = 5250이라는 결과가 나오는겁니다.

 

그 다음 행들도 다 이런 방식입니다.

 

 

 

 

 

 

 

 

 

 

이제 코드를 약간 바꿔봅시다.

df_test = pd.DataFrame(dict_test)
df_test['rolling_col'] = df_test.loc[:, ['col2']].rolling(3, min_periods=1).sum()
print(df_test)

- Output
   col1  col2 col3  rolling_col
0     1  1000    a       1000.0
1     1  1100    b       2100.0
2     2  2100    a       4200.0
3     2  2050    c       5250.0
4     3  3000    a       7150.0
5     3  3100    a       8150.0
6     3  3200    d       9300.0
7     4  4200    e      10500.0

코드는 전체적으로 비슷한데 min_periods라는 인자가 rolling함수에 추가되었습니다.

그리고 Output을 보면 이전 예시에 있었던 NaN값이 모두 사라졌죠?

이것은 min_periods = 1이라는 인자를 넣었기 때문입니다.

 

 

row_index = 0인 rolling_col 값을 봅시다.

  col2 rolling_col
-2 None  
-1 None  
0 1000 1000
1 1100  

rolling_col의 row_index = 0인 위치에 들어갈 값은 위 표에서 색칠된 3가지의 데이터를 참조하는건 위에서 봤던것과 동일합니다.

따라서 원래대로라면 None + None + 1000 = NaN이 되어야하지만, min_periods를 1로 지정했습니다.

즉, rolling(3)에 의해 rolling 시 참조할 행은 3개지만, 위처럼 존재하지 않는 행을 참조해야할 땐 최소 1개의 행만을 참조하여 결과를 반환합니다.

따라서 존재하지 않는 row_index = -1, -2인 행은 무시하고 row_index = 0인 데이터만 참조하게 되는것이죠.

 

 

 

 

 

 

 

df_test = pd.DataFrame(dict_test)
df_test['rolling_col'] = df_test.loc[:, ['col2']].rolling(3, min_periods=2).sum()
print(df_test)

- Output
   col1  col2 col3  rolling_col
0     1  1000    a          NaN
1     1  1100    b       2100.0
2     2  2100    a       4200.0
3     2  2050    c       5250.0
4     3  3000    a       7150.0
5     3  3100    a       8150.0
6     3  3200    d       9300.0
7     4  4200    e      10500.0

 

min_periods=2로 설정하였습니다.

 

row_index = 0인 rolling_col 값을 봅시다.

  col2 rolling_col
-2 None  
-1 None  
0 1000 1000
1 1100  

row_index=0의 rolling은 위처럼 3개의 행을 참조해야합니다.

그러나 2개의 행이 존재하지 않는 행이죠.

따라서 min_periods=2로 설정되어있으니 3개를 모두 참조할 필요가 없고, 참조할 행이 없으면 2개까지만 참조해도 됩니다.

그러면 위 예시에서 row_index=0의 rolling은 row_index=0, -1 의 행을 참조할 것입니다.

그러면 None + 1000 = NaN이 되죠.

 

 

 

반면에 row_index = 1인 rolling_col 값을 봅시다.

  col2 rolling_col
-1 None  
0 1000 NaN
1 1100 2100

row_index=1의 rolling은 위처럼 3개의 행을 참조해야합니다.

그러나 참조해야 할 3개의 행 중 1개의 행(row_index = -1)이 존재하지 않는 행이죠.

min_periods=2로 설정되어있으니 3개를 모두 참조할 필요가 없고, 참조할 행이 없으면 2개까지만 참조해도 됩니다.

그러면 위 예시에서 row_index=1의 rolling은 row_index=1, 0 의 행을 참조할 것입니다.

그러면 1000 + 1100 = 2100이 되죠.

 

 

 

 

 

 

 

 

 

또 한 가지 옵션을 추가해봅시다.

df_test = pd.DataFrame(dict_test)
df_test['rolling_col'] = df_test.loc[:, ['col2']].rolling(3, min_periods=2).sum().shift(-2)
print(df_test)

- Output
   col1  col2 col3  rolling_col
0     1  1000    a       4200.0
1     1  1100    b       5250.0
2     2  2100    a       7150.0
3     2  2050    c       8150.0
4     3  3000    a       9300.0
5     3  3100    a      10500.0
6     3  3200    d          NaN
7     4  4200    e          NaN

이전에 봤던 예시와 동일합니다.

그러나 맨 끝에 shift(-2)라는 옵션이 추가되었습니다.

이 말은 rolling sum의 결과를 위로 2개씩 당기라는 것입니다.

이전 예시와 비교해보면 rolling_col에 10500이라는 숫자는 분명히 row_index=7인 곳에 존재했었습니다.

그러나 위 예시에서는 shift(-2)에 의해 10500이라는 숫자가 row_index=5인 곳으로 이동되었습니다.

또한 기존에 row_index = 8, 9인 데이터가 없어 shift에 의해 row_index = 6, 7로 이동될 데이터가 없습니다.

따라서 row_index = 6, 7의 rolling_sum 데이터는 NaN으로 나타내어집니다.

 

 

 

 

shift에 양수를 넣을 수도 있습니다.

df_test = pd.DataFrame(dict_test)
df_test['rolling_col'] = df_test.loc[:, ['col2']].rolling(3, min_periods=2).sum().shift(2)
print(df_test)

- Output
   col1  col2 col3  rolling_col
0     1  1000    a          NaN
1     1  1100    b          NaN
2     2  2100    a       1000.0
3     2  2050    c       2100.0
4     3  3000    a       4200.0
5     3  3100    a       5250.0
6     3  3200    d       7150.0
7     4  4200    e       8150.0

shift에 양수를 입력하면 기존의 데이터들을 기존의 index에 +2를 한 위치로 이동시킵니다.

아래로 모두 두칸씩 이동되는거죠.

 

 

 

 

 


 

 

 

 

 

이번에는 숫자의 합이나 평균이 아니라 문자를 합쳐봅시다.

import pandas as pd

dict_test = {
    'col1': [1, 1, 2, 2, 3, 3, 3, 4],
    'col2': [1000, 1100, 2100, 2050, 3000, 3100, 3200, 4200],
    'col3': ['a', 'b', 'a', 'c', 'a', 'a', 'd', 'e']
}

df_test = pd.DataFrame(dict_test)
# print(df_test)
# print(type(df_test))


- Output
   col1  col2 col3
0     1  1000    a
1     1  1100    b
2     2  2100    a
3     2  2050    c
4     3  3000    a
5     3  3100    a
6     3  3200    d
7     4  4200    e
<class 'pandas.core.frame.DataFrame'>

동일한 예시 DataFrame을 사용해봅시다.

 

위 DataFrame을 보면 col1에는 중복된 숫자들이 있습니다.

그리고 col3에는 알파벳이 적혀있구요.

여기서 만약 col1에 있는 동일한 값끼리 묶은 다음 col3에 있는 문자들을 list의 형태로 연결하고싶으면 어떻게해야할까요?

 

아래 예시를 봅시다.

df_test = pd.DataFrame(dict_test)
df_test = df_test.groupby(by=['col1'])['col3'].apply(list)
print(df_test)
print(type(df_test))

- Output
col1
1       [a, b]
2       [a, c]
3    [a, a, d]
4          [e]
Name: col3, dtype: object
<class 'pandas.core.series.Series'>

df_test에서 col1 기준으로 groupby를 한 후,

groupby된 상태의 col3에다가 list를 적용하였습니다.

 

따라서 결과는 col1별로 col3의 데이터가 list의 형태로 묶여진 Series가 반환되었습니다.

 

 

 

이것을 DataFrame으로 다시 변경하고싶으면 아래 예시처럼 reset_index를 사용하면 됩니다.

df_test = pd.DataFrame(df_test)
df_test.reset_index(drop=False, inplace=True)
print(df_test)
print(type(df_test))

- Output
   col1       col3
0     1     [a, b]
1     2     [a, c]
2     3  [a, a, d]
3     4        [e]
<class 'pandas.core.frame.DataFrame'>

 

 

 

 

 

728x90
반응형
Comments