Rfriend 2018. 7. 8. 20:32

이번 포스팅에서는 lambda 를 사용한 이름이 없는 익명 함수(the anonymous functions), 한 줄로 간단하게 다음 함수 안에 넣어서 사용할 수 있는 인 라인 함수 (inline functions)에 대해서 알아보겠습니다. 


R 사용하다가 Python 넘어올 때 처음으로 lambda 익명함수를 봤을 때 '이게 뭔가?' 했었는데요, 아래의 예제들을 같이 살펴보시면 어렵지 않게 이해할 수 있을 겁니다. 





(1) 익명 함수 (the anonymous functions) lambda 


이전 포스팅에서 소개했던 def function_name(argments): expression return result; 형태의 함수 정의와는 다르게, 함수의 이름 부여 없이도 lambda 를 사용하면 함수를 정의할 수 있습니다. 



[ lambda 함수 문법 ]

lambda arg1, agr2, ... arg n: expression

 


아래는 input으로 받은 숫자를 제곱해주는 함수를 def 와 lambda 로 각각 정의해 본 예제입니다. 


lambda 익명함수 정의는 한줄 짜리 간단한 함수를 작성해서 편하게 쓴다든지, 메모리를 아끼고 가독성을 높이는데 유용하게 쓸 수 있습니다. (lambda는 이름 없는 익명함수이기 때문에 한번 실행되고 다음줄로 넘어가면 heap 메모리 영역에서 증발되므로 메모리를 아낄 수 있습니다.)


def 를 사용한 이름을 가진 함수 정의

lambda 를 사용한 이름 없는(익명) 함수 정의

 

In [1]: def my_pow(x):

   ...:     result = x**2

   ...:     return result;


In [2]: my_pow(3)

Out[2]: 9


[3]: my_pow2 = lambda x: x**2


In [4]: my_pow2(3)

Out[4]: 9




다음으로 List, Tuple, String과 같은 순서열 데이터에 대해 lambda 함수와 함께 map(), filter(), reduce(), apply() 함수의 안에 익명 함수로 INLINE으로 사용하는 예제, 리스트 축약(list comprehension) 도 더불어 소개하겠습니다.  



 (2) map(lambda arg:expression, list) : lambda 함수를 리스트 원소에 적용하기


리스트의 여러개의 각 원소에 lambda 함수를 적용하고 싶으면 map() 함수를 lambda 와 같이 사용하면 됩니다.  (리스트에 문자열이나 튜플과 같은 순서열 자료형이 들어가도 동일함)


만약 리스트에 lambda 함수를 그냥 적용하면 아래처럼 TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int' 처럼 TypeError 가 납니다. 



In [5]: my_list = [1, 2, 3, 4]


In [6]: my_pow2(my_list) # TypeError

Traceback (most recent call last):


File "<ipython-input-6-0c500bc490e4>", line 1, in <module>

my_pow2(my_list) # TypeError


File "<ipython-input-3-6e25a06562c4>", line 1, in <lambda>

my_pow2 = lambda x: x**2


TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

 



아래는 숫자를 제곱해주는 lambda 함수를 map() 함수를 사용하여 리스트의 각 원소에 적용하고, 이 결과값을 리스트(list)로 반환하도록 한 예제입니다. Python3 에서는 map(lambda ) 를 실행하면 리스트가 아니라 map 객체로 반환이 되며, 리스트로 반환을 받고 싶으면 list() 로 명시적으로 변환을 해주어야 합니다. 



In [5]: my_list = [1, 2, 3, 4]


In [7]: list(map(lambda i: i**2, my_list))

Out[7]: [1, 4, 9, 16]

 




  (3) filter(lambda arg: expression, list) : lambda 함수로 리스트의 원소 필터링 하기


다음 예제는 filter(lambda ) 를 사용하여 여러개의 숫자를 원소로 가지는 리스트에서 '짝수(even number)'만을 선별(filter) 하여 리스트로 반환하는 lambda 익명함수 예제입니다. 



In [8]: num_list = range(10)


In [9]: even_list = list(filter(lambda x: (x%2 == 0), num_list))


In [10]: even_list

Out[10]: [0, 2, 4, 6, 8]

 



위의 예제를 리스트 축약(List Comprehension) 으로도 똑같은 결과 값을 구현할 수 있습니다. 메모리 절약 측면에서는 lambda 익명함수가 더 낫고, 코드 가독성 면에서는 리스트 축약이 좀더 나아보입니다. 



In [11]: new_list_2 = [x for x in num_list if x%2 == 0] # list comprehension


In [12]: new_list_2

Out[12]: [0, 2, 4, 6, 8]

 




  (4) reduce(lambda arg: expression, list): 리스트의 원소에 누적으로 함수 적용


reduce 는 lambda 함수를 리스트의 각 원소에 누적으로 계속 적용할 때 사용합니니다. 아래 예제는 reduce(lambda ) 를 사용하여 숫자 1 ~ 4까지 계속 곱하여 1*2*3*4 의 값을 구한 것입니다(4! 을 구한 것임). 



In [13]: from functools import reduce # python3


In [14]: my_list = [1, 2, 3, 4]


In [15]: reduce(lambda x, y: x*y, my_list)

 




  (5) apply(lambda x: pd.Series({'key': function(x)}) 

     : lambda 함수 적용한 칼럼들로 Pandas DataFrame 만들기


마지막으로, 아래는 DataFrame에 카테고리 변수를 기준으로 groupby() 를 하여 apply(lambda ) 함수로 DataFrame 내 각 칼럼별 요약 집계를 한 결과를 가지고 새로운 DataFrame을 만들어보는 예제입니다. 



데이터 전처리, 집계할 때 알아두면 유용합니다. 



In [16]: import pandas as pd


In [17]: aa = pd.DataFrame({'id': ['a', 'a', 'a', 'b', 'b'],

                                     'val': [1, 2, 3, 4, 5]})

In [18]: aa

Out[18]:

  id  val

0  a    1

1  a    2

2  a    3

3  b    4

4  b    5


In [19]: df = aa.groupby('id').apply(lambda x:

                 pd.Series({'clst_obs_cnt': len(x)

                               , 'clst_val_sum': sum(x.val)

                               , 'id_list': x.id.tolist()})).reset_index()


In [20]: df

Out[20]:

  id  clst_obs_cnt  clst_val_sum    id_list

0  a             3             6        [a, a, a]

1  b             2             9        [b, b]




많은 도움이 되었기를 바랍니다. 

728x90
반응형