IT/AI\ML

[python] 데이터 사이언티스트(데이터 분석가)에 도움이 되는 Tip : yield, partial, map, filter, reduce, enumerate

개발자 두더지 2021. 8. 3. 23:20
728x90

일본 포스팅을 번역한 것입니다. 오역 및 직역이 있으며 내용의 오류가 있는 경우 댓글로 알려주시면 수정하겠습니다.  

 

 책 Data Science from Scratch 챕터 2에서 나온 일부의 내용을 내가 필요할 때 보기위해 정리한 메모이다. 

 

yield


 yield에 대한 이미지는 이 포스팅에서 잘 다루고 있다. 그 다음 이 포스팅을 읽는다면 더욱 이해가 깊어질 것이다. 여기서 yield에 대해 한 문장으로 간략하게 말하자면, "return을 사용하면 그 행에서 처리가 끝나 값을 모두 return하게 되므로, 거대한 데이터를 이용해서 반복 작업을 할 경우, 반복할 때마다 메모리를 쓸모없이 낭비하게 된다. 이때, yield을 사용하면 편리하다"라고 할 수 있다.

def func():
    a = 10
    b = 20
    c = a + b
    yield c      # 여기서 일단 정지

    a = 100
    b = 200
    c = a + b
    yield c      # 여기서 일단 정지

    a = 1000
    b = 2000
    c = a + b
    yield c      # 여기서 일단 정지


for x in func():
    print (x)

#---- 결과 -----
# 30
# 300
# 3000
#---------------

 

 

partial


 함수를 여러번 돌릴 때 함수의 일부를 사용하여 새로운 함수를 만들고 싶을 때 도움이 되는 것이 이 partial이다. 사용시에는 functools로 부터 import해야한다.

# 예를 들어, base를 power승(제곱)하는 함수를 만든다.
def exp(base, power):
    return base ** power

 이 함수를 사용하여 2의 x 제곱을 계산하는 함수를 만들고 싶은 경우, 아래와 같이 쓸 수 있다.

def two_to_the(power):
    return exp(2, power)

 그러나 partial를 사용하면 아래와 같이 더욱 간단하게 작성할 수 있다. 

from functools import partial
two_to_the = partial(exp, 2)   # 첫 번째 인수에는 사용하는 함수, 두 번째 인수에는 base를 지정
print two_to_the(3)            # 8

 참고하는 함수의 다른 인수를 지정하고 싶은 경우에는, 그 인수의 이름을 포함해서 지정한다.

square_of = partial(exp, power=2)   # 이번에는 power를 지정
print square_of(3)                  # 9

 이 partial는 매우 편리하므로 습관들이는 것이 좋을 것 같다.

 

 

Python의 기본적인 고차함수 (higher-order functions)


map, filterm reduce는 고차함수라고 불린다. 참고로 고차함수란 영어로는 "higher-order functions"로, "다른 함수를 인수로 받아들이는 함수"이다. 예를 들어 리스트의 각 요소에 공통 처리를 하도록 작성하고 싶은 경우 등, 처리를 알기 쉽게 간략히 기재하는 것이 가능하다.

map

map(x, y)는 x(i) for i in y이다.

def double(x):
    return 2 * x

xs = [1, 2, 3, 4]

twice_xs = [double(x) for x in xs]   # [2, 4, 6, 8]
twice_xs = map(double, xs)           # same as above

list_doubler = partial(map, double)  # *function* that doubles a list
twice_xs = list_doubler(xs)          # [2, 4, 6, 8]

 여러 개의 인수를 가진 리스트가 부여된 경우, 여러 개의 인수를 가진 함수에도 사용할 수 있게 된다.

def multiply(x, y):
    return x * y

products = map(multiply, [1, 2], [4, 5])   # [1 * 4, 2 * 5] = [4, 10]

 

filter

filter(x, y)는 i for i in y if x(i)이다.

def is_even(x):   # True if x is even, False if x is odd
    return x % 2 == 0

x_evens = [x for x in xs if is_even(x)]   # [2, 4]
x_evens = filter(is_even, xs)             # same as above

list_evener = partial(filter, is_even)    # *function* that filters a list
x_evens = list_evener(xs)                 # [2, 4]

 

reduce

여러 개의 요소를 하나로 정리하고 싶을 때 사용한다. 구체적으로는 배열의 맨 앞에서부터 2개의 요소를 뽑아내 처리를 하고, 다음은 그 결과와 다음의 요소와 함께 처리하는 것을 반복하는 경우가 있을 것이다.

 python3에서 부터는 functools로 부터 import해야만 reduce를 사용할 수 있다.

from functools import reduce

def multiply(x, y): return x * y

x_product = reduce(multiply, xs)           # = 1 * 2 * 3 * 4 = 24

list_product = partial(reduce, multiply)   # *function* that reduces a list
x_product = list_product(xs)               # 24

 

enumerate

 루프 처리를 할 때 enumerate를 사용하면 요소의 인덱스와 요소의 양쪽을 동시에 취득할 수 있다. 예를 들어 documents이라는 리스트의 요소와 인덱스 양쪽에 do_something이라는 처리를 반복 실행하는 경우 다음과 같이 작성할 수 있다.

 # not Pythonic
for i in range(len(documents)):
    document = documents[i]
    do_something(i, document)

 그러나 위 코드는 python답지 않은 코딩으로 enumerate를 사용하면 (index, element)의 튜플을 만들게 되어 아래와 같이 작성할 수 있게 된다.

for i, document in enumerate(documents):
    do_something(i, document)

 만약 인덱스만을 취득하고 싶은 경우는 다음과 같이 작성할 수 있다.

for i in range(len(documents)): do_something(i)     # not Pythonic
for i, _ in enumerate(documents): do_something(i)   # Pythonic

 참고로 eumerate와 zip은 비슷해보이지만 다음과 같이 출력 결과가 완전히 다르다.

a = [a1, a2, a3]
b = [b1, b2, b3]

# enumerate
for i, a_i in enumerate(a):
    print(i, a_i)

#---- 결과 -----
# 0 a1
# 1 a2
# 2 a3
#---------------


# zip
for a_i, b_i in zip(a,b):
    print(a_i, b_i)

#---- 결과 -----
# a1 b1
# a2 b2
# a3 b3
#---------------

참고자료

https://qiita.com/tk_01/items/e047d82be6397b494f28

728x90