달나라 노트

Python Pandas : DataFrame.apply & DataFrame.applymap 본문

Python/Python Pandas

Python Pandas : DataFrame.apply & DataFrame.applymap

CosmosProject 2020. 11. 2. 16:11
728x90
반응형

 

 

DataFrame.apply

apply는 DataFrame에 존재하는 컬럼들의 데이터를 받아 특정 함수를 적용시켜서 해당 함수에 있는 로직대로 데이터를 반환한 데이터를 특정 컬럼에 넣어주는 기능을 가집니다.

 

마치 엑셀에서 내가 원하는대로 filter를 걸고 해당 행(row)에 대해 원하는 수식들을 적용시키는 것과 비슷합니다.



먼저 예시로 사용할 DataFrame을 만들어줍니다.item name과 각 item들에 대한 id, 그리고 판매가격 정보를 담은 DataFrame입니다.

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)
print(df_item)
print(type(df_item))
 
 - Output
   item_id item_name  price
0        1         a   1000
1        2         b   2000
2        3         c   3000
3        4         d   4000

<class 'pandas.core.frame.DataFrame'>



만약 4개의 상품 모두의 가격을 2배로 올려야 하는 상황이 발생했을 때,

기존 DataFrame의 price 컬럼에 존재하는 가격 정보들을 모두 2배로 변경하는 것을 apply를 이용하여 적용시켜보겠습니다.

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)


df_item.loc[:, 'price'] = df_item.apply(lambda x: x['price'] * 2, axis=1)
 
print(df_item)
 
 
 
- Output
   item_id item_name  price
0        1         a   2000
1        2         b   4000
2        3         c   6000
3        4         d   8000

위 결과를 보면 price 컬럼에 있는 가격들이 모두 2배가 된 것을 알 수 있습니다.

 

코드를 나눠서 해석해봅시다.

 

- df_item.apply : df_item이라는 DataFrame에 apply를 적용시키겠다는 뜻입니다.

 

- (lambda x : apply는 DataFame에 어떤 함수를 적용시켜주기 때문에 lambda도 사용할 수 있습니다. 여기서 x는 apply가 적용할 대상인 df_item을 의미합니다.

 

- x['price'] * 2 : 우리는 가격 컬럼에 있는 데이터들을 모두 2배로 늘릴 것이므로, x(df_item)에서 price컬럼만 가져와야합니다. 이것은 x['price'] 처럼 명시할 수 있고, 여기에 * 2를 하였습니다.

 

- axis=1 : 이것은 apply를 위한 인자 중 하나인데, price 컬럼에 있는 값들을 2배한다고하면 세로 방향(컬럼 방향)으로 위에서부터 차례로 한 개의 행씩 내려가면서 2배를 하게됩니다.

axis=1은 이렇게 함수를 적용할 때 세로 방향으로 내려오면서 적용하라는 뜻입니다.(=이 말은 함수를 price컬럼의 각 행마다 적용시키라는 뜻과 같습니다.)

 

 

 

 

 

 

 

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)

apply_result = df_item.apply(lambda x: x['price'] * 2, axis=1)
print(apply_result)
print(type(apply_result))
 
 
 
- Output
0  2000
1  4000
2  6000
3  8000
<class 'pandas.core.series.Series'>

 

위 예시를 보면 apply의 결과로 Series가 return되는걸 볼 수 있습니다.

apply가 Series를 return하기 때문에 loc를 이용하여 apply의 결과로 나온 Series를 DataFrame의 column으로 삽입시킬 수 있는 것이죠.

 

 

 

 

이제 apply가 어떤 방식으로 작동하는지 이해하기 위해 아래 코드를 봅시다.

 

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)

df_item = df_item.apply(lambda x: print(x), axis=1)
 
 
 
- Output
item_id         1
item_name       a
price        1000
Name: 0, dtype: object

item_id         2
item_name       b
price        2000
Name: 1, dtype: object

item_id         3
item_name       c
price        3000
Name: 2, dtype: object

item_id         4
item_name       d
price        4000
Name: 3, dtype: object

 

apply의 lambda x 식 내부에 print를 이용하여 x를 출력해봤습니다.

그 결과를 보니 apply가 적용된 DataFrame의 행(row) 하나하나가 출력되는걸 볼 수 있습니다.

즉, apply는 DataFrame에 적용되어 DataFrame에 존재하는 모든 행(row)에 하나씩 접근하여 연산을 수행합니다.

 

lambda x 에서 x는 DataFrame에 존재하는 행(row)이 하나씩 할당된다고 생각하면 됩니다.

 

 

 

 

 

 

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)

df_item = df_item.apply(lambda x: print(x['price']), axis=1)


-- Result
1000
2000
3000
4000

 

위처럼 lambda x 에서 x['price']를 출력해보면 price컬럼에 있는 값들 하나하나가 출력됩니다.

apply의 lambda x에서 x는 DataFrame의 행(row)을 받아온다고 했으니 그 행의 price 컬럼에 존재하는 값을 얻어온 것이죠.

 

이렇게 apply는 DataFrame의 행 하나하나를 가져오고 그 행에 있는 값들 중 특정 컬럼의 값에 접근할 수 있다는 특성 때문에 이제부터 설명할 다양한 연산이 가능합니다.

 

 

 



apply는 lambda 속에 직접 함수 내용을 적어주지 않고 별도로 함수를 생성하여 적용하는 것도 가능합니다.

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)

def my_func(price):
   return price * 2

df_item.loc[:, 'price'] = df_item.apply(lambda x: my_func(x['price']), axis=1) # 함수의 인자로 df_item의 price컬럼을 전달해야 합니다.

print(df_item)



- Output
   item_id item_name  price
0        1         a   2000
1        2         b   4000
2        3         c   6000
3        4         d   8000



 

 

 

 

 

또 다른 예시로 item_name이 a, c인 것에 대해서만 가격을 2배 해야한다면, 함수만 고쳐서 간단하게 적용할 수 있습니다.

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)

def my_func(item_name, price):
    if item_name in ['a', 'c']:
        return price * 2
    else:
        return price
 
df_item.loc[:, 'price'] = df_item.apply(lambda x: my_func(x['item_name'], x['price']), axis=1)
 
print(df_item)
 
 
 
- Output
   item_id item_name  price
0        1         a   2000
1        2         b   2000
2        3         c   6000
3        4         d   4000

위처럼 apply에 적용시킬 함수(my_func)에 필요한 인자가 2개(item_name, price)이므로,

실제 apply의 lambda에서 my_func을 사용할 때에도 my_func(x['item_name'], x['price']) 처럼 필요한 인자를 담고있는 컬럼을 전달해주어야 합니다.

 

 

 

 

 

 

loc의 filtering 조건을 이용하면 굳이 함수를 고치지 않아도 원하는 행에 대해서만 apply를 적용할 수 있습니다.

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)


con = (df_item['item_id'].isin([2, 4]))
print(df_item.loc[con, :].apply(lambda x: x['price'] * 2, axis=1))

df_item.loc[con, 'price'] = df_item.loc[con, :].apply(lambda x: x['price'] * 2, axis=1)
print(df_item)

df_item.loc[:, 'price'] = df_item.loc[con, :].apply(lambda x: x['price'] * 2, axis=1)
print(df_item)
 
 
 
- Output
1    4000
3    8000
dtype: int64

   item_id item_name  price
0        1         a   1000
1        2         b   4000
2        3         c   3000
3        4         d   8000

   item_id item_name    price
0        1         a      NaN
1        2         b   8000.0
2        3         c      NaN
3        4         d  16000.0

위 예시는 df_item에서 item_id가 2또는 4인 행에 대해서만 apply를 적용한 것입니다.

 

 

print(df_item.loc[con, :].apply(lambda x: x['price'] * 2, axis=1))

위처럼 apply의 결과는 apply가 적용된 최종 return값을 가진 Series입니다.

 

또한 item_id가 2, 4인 행의 index는 1, 3이기 때문에 apply가 적용된 결과 Series에서도 index가 1, 3인 것을 볼 수 있습니다.

즉, loc에 행 filtering(con)이 걸린 채로 apply를 적용하게 되면 결과 Series에서는 원본 DataFrame의 index를 그대로 가져갑니다.

 

 

 

df_item.loc[con, 'price'] = df_item.loc[con, :].apply(lambda x: x['price'] * 2, axis=1)

그래서 보면 apply를 적용한 Series를 그대로 df_item의 price column에 할당하고 있습니다.

이 경우 왼쪽 DataFrame인 df_item에도 loc에 con조건이 걸려있고,

오른쪽에서 apply가 적용되는 DataFrame인 df_item에도 동일하게 loc에 con 조건이 걸려있습니다.

 

즉, 할당을 동일한 행에 대해서만 진행하겠다는 의미입니다.

 

그래서 결과를 보면 index=1, 3인 행의 값만 2배로 overwrite되었으며 나머지 행은 원본 df_item의 값을 그대로 가지고있습니다.

 

 

 

 

df_item.loc[:, 'price'] = df_item.loc[con, :].apply(lambda x: x['price'] * 2, axis=1)

두 번째는 이 부분인데 여기서는 왼쪽 DataFrame인 df_item에는 loc에 con 조건이 없습니다.

반면에 오른쪽 DataFrame에는 loc에 con 조건이 있죠.

 

따라서 오른쪽 DataFrame에 적용된 apply의 결과에는 index=1, 3의 내용만 있을 것이고,

index=1, 3의 값만 가진 데이터를 df_item 전체 행을 대상으로 overwrite하다보니

df_item의 price에서 index=0, 2는 NaN으로 표시됩니다.

등호의 오른쪽에서 apply의 결과에는 index=0, 2에 대한 값은 없으니까요.

 

 

 

이렇게 loc의 특성과 apply를 이용하여 원하는대로 필터링을 하여 값을 조작하고 그 값을 다른 column에 넣어줄 수 있습니다.

 



 

 

 

 

apply의 lambda에서 적용할 컬럼을 x['item_name']의 형태가 아닌 x.item_name의 형태로 적어도 됩니다.

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)

def my_func(item_name, price):
    if item_name in ['a', 'c']:
        return price * 2
    else:
    	return price
 
df_item.loc[:, 'price'] = df_item.apply(lambda x: my_func(x.item_name, x.price), axis=1)

print(df_item)



- Output
   item_id item_name  price
0        1         a   2000
1        2         b   2000
2        3         c   6000
3        4         d   4000

위처럼 apply에 적용시킬 함수(my_func)에 필요한 인자가 2개(item_name, price)이므로,

실제 apply의 lambda에서 my_func을 사용할 때에도 my_func(x['item_name'], x['price']) 처럼 필요한 인자를 담고있는 컬럼을 전달해주어야 합니다.



 

 

 

 

 

이제까지의 예시는 모두 최종적으로 데이터를 받을 컬럼 이름이 price였습니다.(df_item['price'] = df_item.apply)

 

그러나 아래처럼 컬럼 이름을 기존의 DataFrame이 가지고 있는 컬럼 이름과 겹치지 않도록 명시해준다면 새로운 컬럼을 생성할 수도 있습니다.

import pandas as pd

dict_item = {
    'item_id': [1, 2, 3, 4],
    'item_name': ['a', 'b', 'c', 'd'],
    'price': [1000, 2000, 3000, 4000]
}
df_item = pd.DataFrame(dict_item)

def my_func(item_name, price):
    if item_name in ['a', 'c']:
        return price * 2
    else:
    	return x * 2
 
df_item.loc[:, 'new_price'] = df_item.apply(lambda x: my_func(x.item_name, x.price), axis=1)

print(df_item)



- Output
   item_id item_name  price  new_price
0        1         a   1000       2000
1        2         b   2000       2000
2        3         c   3000       6000
3        4         d   4000       4000




 

 

 

 


 

 

 

 

 

 

 

DataFrame.applymap

applymap은 어떤 함수를 DataFrame에 존재하는 모든 데이터에 적용시킬 때 사용합니다.



먼저 예시로 사용할 DataFrame을 만들어줍니다.

import pandas as pd

dict_1 = {
    'col1': [1, 2, 3, 4, 5],
    'col2': [2, 3, 4, 5, 6],
    'col3': [3, 4, 5, 6, 7]
}

df_1 = pd.DataFrame(dict_1)
print(df_1)
print(type(df_1))
 
 
 
 - Output
   col1  col2  col3
0     1     2     3
1     2     3     4
2     3     4     5
3     4     5     6
4     5     6     7

<class 'pandas.core.frame.DataFrame'>




applymap을 이용해서 DataFrame에 있는 모든 데이터에 곱하기 2를 해봅시다.

import pandas as pd

dict_1 = {
    'col1': [1, 2, 3, 4, 5],
    'col2': [2, 3, 4, 5, 6],
    'col3': [3, 4, 5, 6, 7]
}

df_1 = pd.DataFrame(dict_1)


df_test_1 = df_1.applymap(lambda x: x * 2)

print(df_test_1)
print(type(df_test_1))
 
 
 
 - Output
   col1  col2  col3
0     2     4     6
1     4     6     8
2     6     8    10
3     8    10    12
4    10    12    14
<class 'pandas.core.frame.DataFrame'>

보시면 lambda로 적용한 함수(x * 2)가 DataFrame의 모든 요소에 적용되어 모두 곱하기 2가 되었음을 알 수 있습니다.




별도로 위 예시처럼 일정한 사칙연산을 하는거면 아래처럼 진행할 수도 있습니다.

import pandas as pd

dict_1 = {
    'col1': [1, 2, 3, 4, 5],
    'col2': [2, 3, 4, 5, 6],
    'col3': [3, 4, 5, 6, 7]
}

df_1 = pd.DataFrame(dict_1)

df_1 = df_1 * 2

print(df_test_1)
print(type(df_test_1))
 
 
 
 - Output
   col1  col2  col3
0     2     4     6
1     4     6     8
2     6     8    10
3     8    10    12
4    10    12    14
<class 'pandas.core.frame.DataFrame'>



 

 

 

 

 

728x90
반응형
Comments