이번 포스팅에서는 Python 의 Pandas 에서 함수를 적용할 때 사용하는 map(), applymap(), apply() 메소드에 대해서 알아보겠습니다. 

 

(1) map(): Series 에 대해 element-wise 로 함수 적용

(2) applymap(): DataFrame에 대해 element-wise 로 함수 적용

(3) apply(): DataFrame에 대해 행/열 (row/column) 로 함수 적용

 

 

Pandas 함수 적용하기: map, applymap, apply

 

 

(1) map(): Series 에 대해 element-wise 로 함수 적용

 

예제로 사용할 DataFrame을 만들어보겠습니다. 

 

import numpy as np
import pandas as pd

## making a sample pandas DataFrame
np.random.seed(1004)

df = pd.DataFrame(
    np.random.randn(4, 3), 
    columns=['x1', 'x2', 'x3'], 
    index=['A', 'B', 'C', 'D'])

print(df)
#          x1        x2        x3
# A  0.594403  0.402609 -0.805162
# B  0.115126 -0.753065 -0.784118
# C  1.461576  1.576076 -0.171318
# D -0.914482  0.860139  0.358802

 

 

 

map() 메소드는 pandas Series 에 대해 함수를 적용할 때 사용합니다. 아래 예제는 익명 함수 lambda 로 정의한 s_formater 함수를 map() 메소드로 Series에 적용해 본 것입니다. 

 

## pandas Series
df['x1']
# A    0.594403
# B    0.115126
# C    1.461576
# D   -0.914482
# Name: x1, dtype: float64


## map: applying an element-wise function for Series
s_formater = lambda x: '%.2f'% x

df['x1'].map(s_formater)
# A     0.59
# B     0.12
# C     1.46
# D    -0.91
# Name: x1, dtype: object

 

 

 

아래 예제에서는 Series의 인덱스를 키로 해서 매핑(mapping by index)하여 Series의 값을 변환한 것입니다. 

-------------------------------------------------------

s_1 인덱스 --> s_1 값 = s_2 인덱스  -->  s_2 값

-------------------------------------------------------

'a'             -->             0                    -->     'A'

'b'             -->             1                     -->     'B'

'c'             -->             2                     -->     'C'

-------------------------------------------------------

 

 

## 예제 pandas Series 만들기
s_1 = pd.Series({'a': 0, 'b': 1, 'c': 2})
s_2 = pd.Series({0: 'A', 1: 'B', 2: 'C'})

print(s_1)
# a    0
# b    1
# c    2
# dtype: int64

print(s_2)
# 0    A
# 1    B
# 2    C
# dtype: object


##-- 인덱스 키를 기준으로 매핑하기 (mapping by index on Series)
print(s_1.map(s_2))
# a    A
# b    B
# c    C
# dtype: object

 

 

 

(2) applymap(): DataFrame에 대해 element-wise 로 함수 적용

 

다음 예제는 applymap() 메소드를 사용해서 익명함수 lambda 로 정의한 s_formater 함수를 DataFrame 의 각 원소에 대해 적용한 것입니다. (map() 은 Series 대상 vs. applymap()은 DataFrame 대상 element-wise 함수 적용)

 

## applymap: applying an element-wise for DataFrame
s_formater = lambda x: '%.2f'% x

df_2 = df.applymap(s_formater)

print(df_2)
#       x1     x2     x3
# A   0.59   0.40  -0.81
# B   0.12  -0.75  -0.78
# C   1.46   1.58  -0.17
# D  -0.91   0.86   0.36

 

 

 

(3) apply(): DataFrame에 대해 행/열 (row/column) 로 함수 적용

 

아래 예제는 column 기준 (axis=0),  row 기준 (axis=1) 으로 익명함수 lambda로 정의한 (열 또는 행 기준, 최대값 - 최소값) 을 계산하는 함수를 apply() 메소드로 DataFrame에 대해 적용해본 것입니다. 

 

## apply: applying a function on row/column basis of a DataFrame
f = lambda x: x.max() - x.min()

## on column basis
df.apply(f, axis=0)

# x1    2.376058
# x2    2.329141
# x3    1.163964
# dtype: float64



## on row basis
df.apply(f, axis=1)

# A    1.399565
# B    0.899243
# C    1.747393
# D    1.774621
# dtype: float64

 

 

 

아래 예는 apply() 메소드를 써서 DataFrame에 행(row) 기준으로 최대값을 계산하는 익명함수 lambda로 정의한 함수를 적용해서 'x_max'라는 새로운 칼럼을 추가한 것입니다. 

 

## 새로운 칼럼 추가하기
df['x_max'] = df.apply(lambda x: x.max(), axis=1)

print(df)
#          x1        x2        x3     x_max
# A  0.594403  0.402609 -0.805162  0.594403
# B  0.115126 -0.753065 -0.784118  0.115126
# C  1.461576  1.576076 -0.171318  1.576076
# D -0.914482  0.860139  0.358802  0.860139


## equivalent
df['x_max'] = df.max(axis=1)

 

 

* Pandas DataFrame에서 여러개의 칼럼에 대해 그룹을 집계를 할 때 다른 집계 함수를 적용하는 방법은 https://rfriend.tistory.com/393 를 참고하세요. 

 

* Python의 익명 함수 lambda 에 대해서는 https://rfriend.tistory.com/366 를 참고하세요. 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas의 DataFrame에서 문자열 변수들을 가지고 일부 포맷 변형을 한 후에 새로운 변수를 만드는 방법을 소개하겠습니다. 이게 얼핏 생각하면 쉬울 것 같은데요, 또 한번도 본적이 없으면 어렵습니다. ^^; lambda, apply() 함수와 문자열 처리 메소드 등에 대해서 알고 있으면 이해가 쉽습니다. 



(1) 'id' 변수가 전체 5개 자리가 되도록 왼쪽에 비어있는 부분에 '0'을 채워서 새로운 변수 'id_2' 만들기

    (Left padding with zeros so that make 5 positions)


(2) 새로 만든 'id_2' 변수와 'name' 변수를 각 원소별로 합쳐서 데이터프레임 안에 새로운 변수 'id_name' 만들기

    (element-wise string concatenation with multiple inputs array in pandas DataFrame)






먼저, 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 


 

In [1]: import pandas as pd


In [2]: df = pd.DataFrame({'id': [1, 2, 10, 20, 100, 200], 

   ...:                    'name': ['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'fff']})


In [3]: df

Out[3]: 

    id name

0    1  aaa

1    2  bbb

2   10  ccc

3   20  ddd

4  100  eee

5  200  fff





  (1) 'id' 변수가 전체 5개 자리가 되도록 왼쪽에 비어있는 부분에 '0'을 채워서 새로운 변수 'id_2' 만들기

     (Left padding with zeros so that make 5 positions)


lambda 로 format() 함수를 만들어서 apply() 로 적용을 하여 5자리 중에서 빈 자리를 '0'으로 채웠습니다.



In [4]: df['id_2'] = df['id'].apply(lambda x: "{:0>5d}".format(x))


In [5]: df

Out[5]: 

    id      name   id_2

0      1   aaa      00001

1      2   bbb     00002

2    10   ccc     00010

3    20  ddd    00020

4  100  eee     00100

5  200  fff      00200



다양한 숫자 포맷(number format) 함수는 https://mkaz.blog/code/python-string-format-cookbook/ 를 참고하세요. 




 (2) 새로 만든 'id_2' 변수와 'name' 변수를 각 원소별로 합쳐서 데이터프레임 안에

새로운 변수 'id_name' 만들기

    (element-wise string concatenation with multiple inputs array in pandas DataFrame)


그리고 역시 lambda 로 '_'를 중간 구분자로 해서 두 변수의 문자열을 결합('_'.join)하는 함수를 정의한 후에 apply() 로 적용하였습니다, 'axis = 1'을 설정해준 점 주의하시기 바랍니다. 


 

In [6]: df['id_name'] = df[['id_2', 'name']].apply(lambda x: '_'.join(x), axis=1)


In [7]: df

Out[7]: 

       id    name   id_2         id_name

0      1    aaa      00001    00001_aaa

1      2    bbb     00002    00002_bbb

2    10    ccc     00010     00010_ccc

3    20   ddd    00020    00020_ddd

4  100   eee    00100     00100_eee

5  200   fff     00200     00200_fff 





여기서 끝내면 좀 허전하고 아쉬우니 몇 가지 데이터 포맷 변경을 더 해보겠습니다.



(3) 'id' 변수의 값을 소숫점 두번째 자리까지 나타내는 새로운 변수 'id_3' 만들기


(4) 'name' 변수의 문자열을 전부 대문자로 바꾼 새로운 변수 'name_3' 만들기


(5) 데이터프레임 안의 'id_3'와 'name_3' 변수를 합쳐서 새로운 변수 'id_name_3' 만들기



(3) 'id' 변수의 값을 소숫점 두번째 자리까지 나타내는 새로운 변수 'id_3' 만들기


"{:.2f}".format() 함수를 사용하여 소숫점 두번째 자리까지 표현하였습니다. 



In [8]: df['id_3'] = df['id'].apply(lambda x: "{:.2f}".format(x))


In [9]: df





  (4) 'name' 변수의 문자열을 전부 대문자로 바꾼 새로운 변수 'name_3' 만들기


upper() 문자열 내장 메소드를 사용하여 소문자를 대문자로 변경하였습니다. 



In [10]: df['name_3'] = df['name'].apply(lambda x: x.upper())


In [11]: df

 




  (5) 데이터프레임 안의 'id_3'와 'name_3' 변수를 합쳐서 새로운 변수 'id_name_3' 만들기



In [14]: df['id_name_3'] = df[['id_3', 'name_3']].apply(lambda x: ':'.join(x), axis=1)


In [15]: df

 



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

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 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
반응형
Posted by Rfriend
,