Pandas DataFrame 함수 적용

상후·2025년 4월 9일
post-thumbnail

시작하기 전에 import 하기

import numpy as np
import pandas as pd
import numba

축 기준 (apply)

apply 메서드는 DataFrame에 함수를 적용하여 반환하는 메서드 입니다.
함수에 전달되는 객체는 Seires형식이며 DataFrame의 index(axis=0)이냐 columns(axis=1)이냐에 따라 다릅니다.
최종반환 유형은 적용된 함수에 따라 정해지지만 result_type을 지정하여 변경이 가능합니다.

사용법

기본 사용법
df.apply(func, axis=0, raw=False, result_type=None, args=(), kwargs)
function : 각 행이나 열에 적용할 함수 입니다.
axis : {0 : Index / 1 : columns} 함수를 적용할 축 입니다.
row : {True : ndarray / False : Series} 함수에 전달할 축의 형식입니다.
True면 ndarray형태로 전달하고 False면 Series형태로 전달합니다. 기본적으로 Series입니다.
result_type : {expand / reduce / broadcast} 반환값의 형태를 결정합니다. expand이면 배열 형태를
기준으로 열을 확장합니다.(기본 인덱스로), reduce인 경우는 그대로 Serise형태로 반환합니다.
broadcase인 경우 기존 열 형식대로 확장하여 반환합니다.(열의 수가 같아야합니다.)

예시

먼저, 간단한 3x3 객체를 하나 생성하겠습니다.

col = ['col1','col2','col3']
row = ['row1','row2','row3']
data = [[1,2,3],[4,5,6],[7,8,9]]
df = pd.DataFrame(data=data,index=row,columns=col)
print(df)

func의 성질에 따른 차이
func항목이 np.sqrt처럼 축에대해 계산할 수 없는 형식이라면 아래와 같이 각 요소에 적용됩니다.
(실행 예시1)

print(df.apply(np.sqrt))

np.sum처럼 축에대해 적용이 가능한경우라면 축 기준으로 연산을 수행합니다.
(실행 예시2)

print(df.apply(np.sum))

axis에 따른 차이
axis가 0인경우 Index(행)에 대해 연산을 수행하고, 1인경우는 columns(열)에 대해 연산을 수행합니다.
(실행 예시1)

print(df.apply(np.prod,axis=0))


(실행 예시2)

print(df.apply(np.prod,axis=1))

result_type에 따른 차이
먼저 lamba를 사용하여 기존 DataFrame에 [1,2,3]객체를 apply해보겠습니다.
(실행 예시1)

print(df.apply(lambda x : [1,2,3]))

result_type = 'expand'인 경우
func를 기준으로 확장하여 columns를 지정하게 되는것을 확인할 수 있습니다.
(실행 예시2)

print(df.apply(lambda x : [1,2,3], axis=1,result_type='expand'))

result_type = 'reduce'인 경우
func를 기준으로 축소하여 columns없이 Series 객체로 반환하는것을 확인할 수 있습니다.
(실행 예시3)

print(df.apply(lambda x : [1,2,3], axis=1,result_type='reduce'))

result_type = 'broadcast'인 경우
func를 기준으로 확장하되, columns는 기존 DataFrame의 것을 사용하는것을 확인할 수 있습니다.
(실행 예시4)

print(df.apply(lambda x : [1,2,3], axis=1,result_type='broadcast'))

요소별 (applymap)

applymap 메서드는 객체의 각 요소에 함수를 적용하는 메서드입니다.
즉, apply메서드와는 다르게 DataFrame의 각 요소 하나하나에 함수를 적용하여 스칼라 값을 반환합니다.

사용법

기본 사용법
df.apply(func, axis=0, raw=False, result_type=None, args=(), kwargs)
func : 단일 값을 반환하는 함수 입니다.
na_action : {None / 'ignore} NaN의 무시 여부입니다. 'ignore'이면 NaN을 함수로 전달하지 않습니다.

예시

먼저, pd.NA가 포함된 간단한 3x3 객체를 하나 생성하겠습니다.

col = ['col1','col2','col3']
row = ['row1','row2','row3']
data = [[1,2,3],[4,5,6],[7,pd.NA,9]]
df = pd.DataFrame(data=data,index=row,columns=col)
print(df)

applymap의 적용
함수 적용시 각 요소에 대해 함수의 연산이 되는것을 확인할 수 있습니다.

print(df.applymap(lambda x : x**2,na_action='ignore'))


함수내 함수 연속적용 (pipe)

pipe 메서드는 함수를 연속적으로 사용할 때 유용한 메서드입니다.
특히 함수가 인수를 사용할 때 pipe 메서드를 사용하면 보다 직관적으로 적용할 수 있습니다

사용법

기본 사용법
df.pipe(func, args, kwargs)
func : 함수입니다.
arg : 함수의 인수입니다.
kwargs : dict 형태의 함수의 인수입니다.
만약 함수 3개가 아래와 같이 있다고 해봅니다.
f1(data, arg1), f2(data, arg1, arg2, f3(data, arg3)
f1 > f2 > f3 순서로 포함되게 함수를 사용한다고 하면 아래와 같이 함수를 사용해야 합니다.
df=f1( f2( f3( data,arg3='c' ),arg2='b1',arg3='b2' ),arg1='a' )
이는 어떤 arg가 어떤함수인지 직관적으로 볼 수 없습니다. 이때, pipe함수를 사용할 수 있습니다.
df=data.pipe(f3, arg3='c').pipe(f2, arg2='b1', arg3='b2').pipe(f3, arg3='c')

예시

먼저 입력 데이터를 정의해보겠습니다

org_data = pd.DataFrame({'info':['삼성전자/3/70000','SK하이닉스/2/100000']})
print(org_data)

그리고 함수 두개를 만들어보겠습니다.
code_name(data) 는 (종목명/수량/가격)형태인 문자열 data를 입력받아서 각각으로 분리하고 수량과 가격의 dtype을 int로 변경하는 함수 입니다.

def code_name(data):
    result=pd.DataFrame(columns=['name','count','price']) 
    df = pd.DataFrame(list(data['info'].str.split('/'))) # '/ ' 로 구분하여 문자열을 나누어 리스트에 넣음
    result['name'] = df[0] # 첫번째 값인 이름이 입력
    result['count']= df[1] # 두번째 값인 수량이 입력
    result['price']= df[2] # 세번째 값인 가격이 입력
    result = result.astype({'count':int,'price':int}) # count와 price를 int로 바꿈(기존str)
    return result
print(code_name(org_data))

value_cal(data,unit=' ')은 가격과 수량을 곱한다음에 단위로 unit arg를 붙이는 함수입니다.

def value_cal(data,unit=''):
    result = pd.DataFrame(columns=['name','value']) 
    result['name'] =data['name'] # 이름은 기존거를 가져옴
    result['value']=data['count']*data['price'] # value는 count * price를 입력함
    result = result.astype({'value':str}) # value를 str로 변경(단위를 붙이기 위함)
    result['value']=result['value']+unit # 단위를 붙임
    return(result)
input=code_name(org_data)
print(value_cal(input,'원'))

pipe 메서드를 사용하지 않는경우
만약 pipe메서드를 사용하지 않는다면 아래와같이 함수를 사용해야 합니다.
(사용 예시1)

print(value_cal(code_name(org_data),'원'))

pipe메서드를 사용한다면 아래와같이 직관적으로 나타낼 수 있다.
(사용 예시2)

print(org_data.pipe(code_name).pipe(value_cal,'원'))


함수연속적용_축별 (aggregate, agg)

agg메서드는 apply와 비슷하게 함수를 적용하는 메서드이지만,
여러개의 함수를 동시에 적용할 수 있다는 장점이 있습니다.
그리고 name 를통해 사용자정의 함수명을 따로 설정할경우 그 이름을 사용한다는 점을 활용하여
함수를 사용한 DataFrame을 보다 깔끔하게 정리하는데도 용이하게 쓸 수 있습니다.

사용법

기본 사용법
df.agg(func=None, axis=0, args, kwargs)
func : 함수입니다.
axis :{0 : index(row) / 1 : columns} 축입니다 0은 행, 1은 열 입니다. arg : 함수의 인수 입니다..
kwargs : dict 형태의 함수의 인수입니다.

예시

먼저 간단한 3x3짜리 데이터를 만들어보겠습니다.

df = pd.DataFrame([[1,4,7],[2,5,8],[3,6,9]])
print(df)

입력되는 함수의 형태에 따라
입력함수로는 먼저 np.함수 형태나 그냥 문자열 형태로의 입력이 가능합니다.
np함수의 경우

ex1 = df.agg(np.prod)
print(ex1)

문자열일 경우

ex2 = df.agg('prod')
print(ex2)

lambda함수나 사용자 정의 함수를사용할 수도 있습니다.
lambda함수를 사용할 경우 열 명칭은 lambda가 됩니다.

ex3 = df.agg([lambda x : min(x) * max(x)])
print(ex3)

사용자정의 함수를 사용하게되면 기본적으로 함수명 열 이름으로 설정이 됩니다.

def func_sub(input):
    return max(input)-min(input)
ex4 = df.agg([func_sub,'sum'])
print(ex4)

만약 함수명을 name 메서드를 통해 따로 설정해주면 그 이름이 쓰입니다.

def func_sub(input):
    return max(input)-min(input)
func_sub.__name__='내함수'
ex5 = df.agg([func_sub,'sum'])
print(ex5)

여러 함수를 동시에 적용하는 경우
list나 dict형태로 func값을 입력하는 경우 여러 함수를 동시에 적용할 수 있습니다.
list로 입력하는 경우

ex6 = df.agg(['min','max','sum','prod'])
print(ex6)

dict를 이용하는 경우 순서를 변경하는것도 가능합니다.

ex7 = df.agg({2:'sum',0:'max',1:'min'})
print(ex7)

dict함수내에 다중함수를 적용할 수도 있습니다. 이 경우 해당되지 않는 index는 NaN을 출력합니다.

ex8 = df.agg({0:['sum','prod'],1:['max','min'],2:'mean'}) 
print(ex8)

axis인수를 변경할 경우

axis의경우 0이면 index, 1이면 columns 기준으로 연산을 진행합니다.
(실행 결과1)

ex2 = df.agg('prod', axis=0)
print(ex2)


(실행 결과2)

ex3 = df.agg('prod', axis=1)
print(ex3)


함수연속적용_요소별 (transform)

transform메서드는 agg와 비슷하게 함수를 적용하는 메서드이지만,
단일 요소별로 함수를 동시에 적용할 수 있다는 장점이 있습니다. 마치 apply와 applymap의 차이와 비슷합니다.

사용법

기본 사용법
df.transform(func, axis=0, args, kwargs)
func : 함수입니다.
axis :{0 : index(row) / 1 : columns} 축입니다 0은 행, 1은 열 입니다.
arg : 함수의 인수 입니다.
kwargs : dict 형태의 함수의 인수입니다.

예시

먼저 간단한 3x3짜리 데이터를 만들어보겠습니다.

col = ['col1','col2','col3']
row = ['row1','row2','row3']
df = pd.DataFrame(data=[[10,40,70],[20,50,80],[30,60,90]],index=row,columns=col)
print(df)

입력되는 함수의 형태에 따라
입력함수로는 먼저 np.함수 형태나 그냥 문자열 형태로의 입력이 가능합니다.
np함수의 경우

ex1 = df.transform(np.sqrt)
print(ex1)

문자열일 경우

ex2 = df.transform('sqrt')
print(ex2)

lambda함수나 사용자 정의 함수를사용할 수도 있습니다.
agg와 다르게 기존 레이블이 표시됩니다.

ex3 = df.transform(lambda x : np.sqrt(x))
print(ex3)

여러 함수를 동시에 적용하는 경우
list나 dict형태로 func값을 입력하는 경우 여러 함수를 동시에 적용할 수 있습니다.
list로 입력하는 경우 마치 multi index처럼 multi columns가 생성됩니다.

ex4 = df.transform(['exp','sqrt'])
print(ex4)

dict를 이용하는 경우 순서를 변경하는것도 가능합니다.

ex5 = df.transform({'col2':'exp','col1':'sqrt'})
print(ex5)


문자열 형식의 계산식 적용 (eval)

eval메서드는 파이썬의 eval 메서드와 사용목적이 동일합니다. 문자열로 된 계산식을 적용합니다.

사용법

기본 사용법
df.eval(expr, inplace=False, kwargs)
expr : 문자열 형태의 계산식입니다.
inplace : {True / False} 계산된 값이 원본을 변경할지의 여부입니다. 기본적으로 원본은 변경되지 않습니다.

예시

먼저 기본적인 3x3 DataFrame을 만들어보겠습니다.

data = [[1,2,3],[4,5,6],[7,8,9]]
col = ['col1','col2','col3']
row = ['row1','row2','row3']
df = pd.DataFrame(data = data, index = row, columns= col)
print(df)

기본적인 사용법
col1*col2-col3의 값을 갖는 col4를 만들어보겠습니다. 'col4'='col1'+'col2'-'col3'를 문자열 그대로 사용합니다.
계산이 적용된 col4열이 생성된 것을 확인할 수 있습니다.

print(df.eval('col4=col1*col2-col3'))

하지만 inplace인수가 기본값인 False이기 때문에 원본은 변경되지 않은것을 알 수 있습니다.

print(df)

inplace = True인 경우
Inplace = True로 할 경우 원본이 변경되는것을 확인할 수 있습니다.
inplace = True로 할경우 사본이 생성되지 않기 때문에 print할 경우 None이 출력됩니다.

print(df.eval('col4=col1*col2-col3',inplace=True))

하지만 원본은 변경되어있는것을 확인할 수 있습니다.

print(df)


profile
개발자를 꿈꾸는 학생

0개의 댓글