이번 포스팅에서는 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

 



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

Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 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]




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

Posted by R Friend R_Friend

댓글을 달아 주세요