흔한말 (collection)/Python

파이썬 filter, map, reduce는 마치 공장같다(커피를 뽑는)

hnanmal 2022. 3. 23. 22:59
300x250

지난번 reduce 포스팅에서 말했듯이, filter, map, reduce 함수는 함께 조합되어 많이 쓰입니다.

 

  • 여러 가지 원소들 중 특정 조건을 만족하는 것들만 골라서 => filter
  • 골라진 원소들을 어떤 규칙을 통해 변환한 뒤 => map
  • 변환된 원소들의 특성을 하나로 추출하는 => reduce

 

이러한 방식으로 자주 쓰입니다.

 

지난번 filter함수 포스팅에서는 커피가루를 예로 들어서 여러 가지 필터링 코드를 짜 봤지요?
이번에도 커피를 가지고 골라서, 변환한 뒤, 추출하는 예시 코드를 통해서 이해해 보도록 하겠습니다.

과테말라_커피가루 = [
    {'이름': '카페인', '용해성': '수용성', '로스팅후변화': False, '함량': 10.00},
    {'이름': '광물질', '용해성': '불용성', '로스팅후변화': False, '함량': 0.10},
    {'이름': '지방', '용해성': '지용성', '로스팅후변화': False, '함량': 1.00},
    {'이름': '탄닌산', '용해성': '수용성', '로스팅후변화': False, '함량': 7.00},
    {'이름': '클로로겐산', '용해성': '수용성', '로스팅후변화': False, '함량': 3.00},
    {'이름': '트리고넬린', '용해성': '수용성', '로스팅후변화': False, '함량': 2.50},
    {'이름': '탄산가스', '용해성': '불용성', '로스팅후변화': True, '함량': 0.01},
    {'이름': '당분', '용해성': '수용성', '로스팅후변화': False, '함량': 1.50},
    {'이름': '섬유소', '용해성': '불용성', '로스팅후변화': False, '함량': 0.70},
        ]

케냐AA_커피가루 = [
    {'이름': '카페인', '용해성': '수용성', '로스팅후변화': False, '함량': 10.00},
    {'이름': '광물질', '용해성': '불용성', '로스팅후변화': False, '함량': 0.07},
    {'이름': '지방', '용해성': '지용성', '로스팅후변화': False, '함량': 1.01},
    {'이름': '탄닌산', '용해성': '수용성', '로스팅후변화': False, '함량': 7.21},
    {'이름': '클로로겐산', '용해성': '수용성', '로스팅후변화': False, '함량': 3.29},
    {'이름': '트리고넬린', '용해성': '수용성', '로스팅후변화': False, '함량': 2.32},
    {'이름': '탄산가스', '용해성': '불용성', '로스팅후변화': True, '함량': 0.01},
    {'이름': '당분', '용해성': '수용성', '로스팅후변화': False, '함량': 1.78},
    {'이름': '섬유소', '용해성': '불용성', '로스팅후변화': False, '함량': 0.98},
        ]

예가체프_커피가루 = [
    {'이름': '카페인', '용해성': '수용성', '로스팅후변화': False, '함량': 10.00},
    {'이름': '광물질', '용해성': '불용성', '로스팅후변화': False, '함량': 0.11},
    {'이름': '지방', '용해성': '지용성', '로스팅후변화': False, '함량': 1.33},
    {'이름': '탄닌산', '용해성': '수용성', '로스팅후변화': False, '함량': 6.56},
    {'이름': '클로로겐산', '용해성': '수용성', '로스팅후변화': False, '함량': 2.86},
    {'이름': '트리고넬린', '용해성': '수용성', '로스팅후변화': False, '함량': 2.76},
    {'이름': '탄산가스', '용해성': '불용성', '로스팅후변화': True, '함량': 0.01},
    {'이름': '당분', '용해성': '수용성', '로스팅후변화': False, '함량': 1.62},
    {'이름': '섬유소', '용해성': '불용성', '로스팅후변화': False, '함량': 0.88},
        ]

 

자 위와 같이 원두 종류별로 커피가루를 준비했습니다.

 

예시로 진행해볼 과정은 이렇습니다.

 

  1. map으로 로스팅하고
  2. filter를 통해 건더기를 제거한 뒤
  3. reduce에 초기값으로 물을 집어넣어 차례로 수용액을 만들고 최종적으로는 커피 용액을 추출하는 과정

 

입니다.

 

1. map으로 로스팅하고

먼저 로스팅을 통해서 커피 원두 속의 탄산가스를 날려버립시다.
일단 과테말라 원두로 시작해볼까요?

 

일단 로스팅 함수를 정의해봅시다.

def 로스팅하기(성분):
    if 성분['로스팅후변화']:
        성분['함량'] = 0
        return 성분
    else:
        변화함량 = 성분['함량'] - 0.01
        성분['함량'] = 변화함량
        return 성분

이 함수는 성분들을 로스팅해서, "로스팅 후 변화" 속성이 True 이면 함량을 0으로 만들고,
속성이 False 이면 원래 가지고 있던 함량에서 0.01g을 빼서 돌려줍니다.

 

과테말라 원두를 로스팅하려면 아래처럼 코딩하면 됩니다.

로스팅한_과테말라_커피가루 = list(map(로스팅하기, 과테말라_커피가루))

로스팅한_과테말라_커피가루
[{'이름': '카페인', '용해성': '수용성', '로스팅후변화': False, '함량': 9.99},
 {'이름': '광물질', '용해성': '불용성', '로스팅후변화': False, '함량': 0.09000000000000001},
 {'이름': '지방', '용해성': '지용성', '로스팅후변화': False, '함량': 0.99},
 {'이름': '탄닌산', '용해성': '수용성', '로스팅후변화': False, '함량': 6.99},
 {'이름': '클로로겐산', '용해성': '수용성', '로스팅후변화': False, '함량': 2.99},
 {'이름': '트리고넬린', '용해성': '수용성', '로스팅후변화': False, '함량': 2.49},
 {'이름': '탄산가스', '용해성': '불용성', '로스팅후변화': True, '함량': 0},
 {'이름': '당분', '용해성': '수용성', '로스팅후변화': False, '함량': 1.49},
 {'이름': '섬유소', '용해성': '불용성', '로스팅후변화': False, '함량': 0.69}]

모든 성분들이 로스팅되어 각자가 할 수 있는 변화를 일으킨 채 "로스팅한_과테말라_커피가루" 리스트에 잘 저장되어 있습니다.

 

2. filter를 통해 건더기를 제거한 뒤

이제는 로스팅을 했으니, 필터로 필요 없는 것들을 제거해야겠죠?
섬유소와 광물질처럼 물에 녹지 않는 것들과 이미 쭉정이만 남은 탄산가스를 제거해주면 되겠습니다.

 

그러면 필터링의 조건이 되는 함수를 먼저 선언해야겠죠?

def 성분필터링하기(성분):
    if 성분['함량'] == 0 or 성분['용해성'] == '불용성':
        return False
    else:
        return True

이 조건 함수는 인자로 들어온 함수를 살펴서 함량이 0 이거나,
용해성이 '불용성'인 성분들만 False로 반환해서 탈락시키는 함수입니다.

 

이 조건 함수를 사용해서 필터링하는 코드를 짜 보겠습니다.

필터링한_과테말라_커피가루 = list(filter(성분필터링하기, 로스팅한_과테말라_커피가루))

필터링한_과테말라_커피가루
[{'이름': '카페인', '용해성': '수용성', '로스팅후변화': False, '함량': 9.99},
 {'이름': '지방', '용해성': '지용성', '로스팅후변화': False, '함량': 0.99},
 {'이름': '탄닌산', '용해성': '수용성', '로스팅후변화': False, '함량': 6.99},
 {'이름': '클로로겐산', '용해성': '수용성', '로스팅후변화': False, '함량': 2.99},
 {'이름': '트리고넬린', '용해성': '수용성', '로스팅후변화': False, '함량': 2.49},
 {'이름': '당분', '용해성': '수용성', '로스팅후변화': False, '함량': 1.49}]

필터가 제 역할을 잘해줘서 마지막으로 카페인을 비롯한 6가지 성분들만 남았습니다!

 

이제 이 성분들을 커피 용액으로 만드는 일만 남았네요.

 

3. reduce에 초기값으로 물을 집어넣어 차례로 커피 수용액 추출하기

이제 리스트에서 원소를 두 개씩 꺼내서 짝짜꿍 시키는 reduce 함수 차례입니다.

짝짜궁 규칙 함수를 먼저 작성해야겠죠?

일단 먼저 물을 만들고요.

물 = {'이름': '물', '함량': 100}
def 용액만들기(액체, 성분):
    용액 = {'이름': '미정', '함량': 0}
    용액['이름'] = '+'.join([액체['이름'], 성분['이름']])
    용액['함량'] = 액체['함량'] + 성분['함량']
    return 용액

이 함수는 용액과 성분을 인자로 받아서,
용액의 이름을 기존 용액의 이름 뒤에 성분 이름을 더하기 표시로 결합해주고,
함량 수치도 합산해 주는 함수입니다.

 

예를 들어서 물과 카페인을 한번 "용액 만들기" 함수로 합쳐볼까요?

용액만들기(물, 필터링한_과테말라_커피가루[0])
{'이름': '물+카페인', '함량': 109.99}

이름은 '물+카페인'으로 바뀌고, 함량도 물의 중량인 100g과 카페인의 중량인 9.99g 이 합쳐져서 109.99g이 되었군요.
이제 이 짓을 모든 성분에 대해 반복해 주면 됩니다!!!

 

from functools import reduce

김이나는커피한잔 = reduce(용액만들기, 필터링한_과테말라_커피가루, 물)

김이나는커피한잔
{'이름': '물+카페인+지방+탄닌산+클로로겐산+트리고넬린+당분', '함량': 124.93999999999997}

 

와! 대단하네요! 커피 한잔 뚝딱입니다!!!

 

후후 불어가면서 한잔 하시고, 나머지 원두들에 대해서는 직접 한 번 실습해보세요!!
해보면 익숙해지니까요.

 

※잘 이해가 안 가시는 분들은 map 함수와 filter 함수 그리고 reduce 함수에 대한 개별 포스팅들이 있으니 그걸 보고 오시면 좋습니다.

반응형