Python 공부 기록-13

김시헌·2025년 4월 9일

축 기준 (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처럼 축에대해 계산할 수 없는 형식이라면 아래와 같이 각 요소에 적용됩니다.
코드

print(df.apply(np.sqrt))

실행 결과

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

코드

print(df.apply(np.sum))

실행 결과

axis에 따른 차이

axis가 0인경우 Index(행)에 대해 연산을 수행하고, 1인경우는 columns(열)에 대해 연산을 수행합니다.
코드

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

실행 결과

코드

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

실행 결과

result_type에 따른 차이

먼저 lamba를 사용하여 기존 DataFrame에 [1,2,3]객체를 apply해보겠습니다.
코드

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

실행 결과

result_type = 'expand'인 경우

func를 기준으로 확장하여 columns를 지정하게 되는것을 확인할 수 있습니다.
코드

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

실행 결과

result_type = 'reduce'인 경우

func를 기준으로 축소하여 columns없이 Series 객체로 반환하는것을 확인할 수 있습니다.
코드

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

실행 결과

result_type = 'broadcast'인 경우

func를 기준으로 확장하되, columns는 기존 DataFrame의 것을 사용하는것을 확인할 수 있습니다.
코드

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메서드를 사용하지 않는다면 아래와같이 함수를 사용해야 합니다.
코드

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

실행 결과

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

코드

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):
코드

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을 출력합니다.

코드

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

실행 결과

코드

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이기 때문에 원본은 변경되지 않은것을 알 수 있습니다.

inplace = True인 경우

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

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

실행 결과
None

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

print(df)

실행 결과

레이블기반_스칼라 (at)

행/열 한쌍에 대한 단일 값에 엑세스합니다.

DataFrame.at

at 함수는 loc 함수와 같이 레이블 기반으로 인덱싱을 하지만,
DataFrame과 Series에 상관없이 하나의 스칼라값에 접근한다는 차이가 존재합니다.

사용법

값 가져오기 : result = df.at['행', '열']
값 설정하기 : df.at['행', '열'] = value

아래와 같이 기본적인 2x2 행렬을 만듭니다.

코드

df = pd.DataFrame([[1,2], [3,4]], index=['row1', 'row2'], columns=['col1', 'col2'])
print(df)

실행 결과

값 가져오기

행, 열 값을 인수로 입력하여 변수에 할당함으로써 값을 가져올 수 있습니다.
코드

result = df.at['row1', 'col2']
print(result)

실행 결과
2

값 설정하기

행, 열 값을 인수로 지정 후 값을 할당하여 값을 설정할 수 있습니다.
코드

df.at['row2', 'col1'] = '변경'
print(df)

실행 결과

기타

loc 메서드를 이용해 Series로 추출한 뒤 at메서드를 이용해 스칼라값을 얻는 방식으로 활용이 가능합니다.
코드

 df.loc['row2'].at['col2']

실행 결과
np.int64(4)

레이블기반_데이터 (loc)

행/열 설정에 따라 자유로운 인덱싱이 가능합니다.
loc는 bool 배열과 함께 사용이 가능합니다.

DataFrame.loc

loc 함수는 at 함수와 같이 레이블 기반으로 인덱싱을 합니다. DataFrame이나 Series형식으로의 반환이 필요하면 loc를 사용합니다.

사용법

기본 사용법
값 가져오기 : result = df.loc['행', '열']
값 설정하기 : df.loc['행', '열'] = value

입력 가능한 Input
1. 레이블 (만약 3을 입력할 경우 정수위치가 아닌 index의 레이블로 해석
2. list 객체 (예 : ['a', 'b', 'c'])
3. 레이블의 슬라이스 객체 (예 : 'b' : 'f')
4. 슬라이싱되는 축과 길이가 같은 bool 배열 (예 : [True, True, False, True])

아래와 같이 기본적인 3x3 행렬을 만듭니다.

코드

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

실행 결과

값 가져오기

단일 레이블을 지정할경우 Series 형태로 반환됩니다.
코드

result = df.loc['row1']
print(result)

실행 결과

레이블로 구성된 리스트. [ [ ] ]를 사용하면 DataFrame형태로 반환됩니다.

코드

result = df.loc[ ['row1','row3'] ]
print(result)

실행 결과

행과 열을 설정하여 단일 레이블의 값을 입력가능합니다.

코드

result = df.loc['row2', 'col2']
print(result)

실행 결과
5

슬라이스를 이용하여 인덱싱을 할 수 있습니다.

코드

result = df.loc['row1' : 'row3', 'col2']
print(result)

실행 결과

bool로 구성된 Series를 반환하는 조건문의 사용도 가능합니다. 아래의 경우 col3열에 대해서 5보다 큰 경우인 row2, row3행만 반환됩니다.

코드

result = df.loc[ df['col3'] > 5 ]
print(result)

실행 결과

여기에 해당조건을 만족하는 특정 열을 반환할 수도 있습니다. 아래의 경우 col3에서 5보다 큰 값을 만족하는 행에 대해서 col2의 값만 반환합니다.

코드

result = df.loc[ df['col3'] > 5, ['col2'] ] 
print(result)

실행 결과

람다함수를 이용하여 인덱싱이 가능합니다. 아래의 경우 col2의 값중 5인 값을 만족하는 행을 반환합니다.

코드

result = df.loc[lambda df : df['col2'] == 5]
print(result)

실행 결과

값 설정하기

기본적으로 조건을 만족하는 모든 항목의 값이 변경됩니다.
레이블을 지정하여 값 설정하기.
코드

df.loc[ ['row1', 'row3'], ['col3'] ] = 'A'
print(df)

실행 결과

하나의 레이블만 지정할 경우 해당 행/열 전체의 값 설정이 가능합니다.

코드

df.loc[ ['row1'] ] = 'A' # 행을 변경할 경우
print(df)

실행 결과

코드

df.loc[ : , ['col3'] ] = 'B' # 열을 변경할 경우 행을 전체선택 ( : ) 해줍니다.
print(df)

실행 결과

조건을 설정하여 만족하는 값의 변경이 가능합니다.

아래의 경우 col2에서 3보다 큰 열은 row2, row3이기 때문에 해당 열의 값이
전부 변경되었습니다.
코드

df.loc[df['col2'] > 3] = 'A'
print(df)

실행 결과

정수기반_스칼라 (iat)

정수기반 조회 메서드입니다. 행/열 한쌍에 대한 단일 값에 엑세스합니다.

DataFrame.iat

iat 함수는 iloc 함수와 같이 정수 기반으로 인덱싱을 합니다.
단일 스칼라값으로 반환하길 원한다면 iat함수를 사용합니다.

사용법

기본 사용법
값 가져오기 : result = df.iat['행', '열']
값 설정하기 : df.iat['행', '열'] = value

아래와 같이 기본적인 3x3 행렬을 만듭니다.

코드

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

실행 결과

값 가져오기

정수기반 조회 메서드이기 때문에, 행/열 쌍을 정수로 입력해야합니다.
아래의 경우 [rows=1,columns=2] 를 출력하는 것으로, 0부터 시작하기 때문에 [row2, col3]의 값인 6을 출력하게 됩니다.
코드

result = df.iat[1,2]
print(result)

실행 결과
6

값 설정하기

마찬가지로 정수값을 지정하여 해당 행/열의 값을 바꿀 수 있습니다.
코드

df.iat[1,2] = 'A'
print(df)

실행 결과

앞에서 n행 인덱싱 (head)

head함수는 Dataframe 객체를 위에서부터 n열 반환하는 함수입니다.
기본값은 5입니다.

사용법

df.head(n=5)

head 함수의 사용을 위해 np.random함수로 10x10 난수 DataFrame를 만들어 보겠습니다.

코드

data = np.random.randint(10,size=(10,10))
df = pd.DataFrame(data=data)
print(df)

실행 결과

n=양수 n이 0보다 크면 위에서부터 n까지 열을 반환합니다.

코드

print(df.head(3))

실행 결과

n=음수 n이 0보다 작으면 DataFrame의 끝에서부터 n개열을 제외하고 반환합니다.

코드

print(df.head(-3))

실행 결과

주의사항 print로 출력시에 보이는 출력물만 n열까지 보여주는것이 아니라 실제 값이 바뀌는 것임을 염두해야 합니다.

아래를 확인해보세요
코드

check = df.head(3)
print(check.index)

실행 결과
RangeIndex(start=0, stop=3, step=1)

뒤에서 n행 인덱싱 (tail)

tail함수는 Dataframe 객체를 아래에서부터 n열 반환하는 함수이다.
기본값은 5이다.

사용법

df.tail(n=5)

tail 함수의 사용을 위해 np.random함수로 10x10 난수 DataFrame를 만들어 보겠습니다.

코드

data = np.random.randint(10,size=(10,10))
df = pd.DataFrame(data=data)
print(df)

실행 결과

n=양수 n이 0보다 크면 아래에서부터 n까지 열을 반환한다.

코드

print(df.tail(3))

실행 결과

n=음수 n이 0보다 작으면 DataFrame의 위에서부터 n개열을 제외하고 반환합니다.

코드

print(df.tail(-3))

실행 결과

주의사항 print로 출력시에 보이는 출력물만 n열까지 보여주는것이 아니라 실제 값이 바뀌는 것임을 염두해야 합니다.

아래를 확인해보세요
코드

check = df.tail(3)
print(check.index)

실행 결과
RangeIndex(start=7, stop=10, step=1)

Multi Index의 경우

아래와 같이 Multi Index를 가지는 DataFrame 객체를 만듭니다.

코드

index_tuples = [('row1', 'val1'), ('row1', 'val2'), ('row2', 'val1'), ('row2', 'val2'), ('row2', 'val3'), ('row3', 'val2'),('row3', 'val3')]
values = [ [1,2,3], [4,5,6], [7,8,9], [10,11,12], [13,14,15], [16,17,18], [19,20,21]]
index = pd.MultiIndex.from_tuples(index_tuples) # 인덱스 설정
df = pd.DataFrame(values, columns=['col1', 'col2', 'col3'], index = index)
print(df)

실행 결과

하나의 행을 지정할 경우 단일 index로 반환된다.

코드

result = df.loc['row2']
print(result)

실행 결과

multi index에서 특정 index를 튜플로 지정할 경우 Series로 반환됩니다.

또한, 아래의 경우에서 튜플이 아니라 단일 행/열 레이블 형태로 입력하여도
같은 결과가 도출됩니다. (예 : result = df.loc[ ' row2', 'val2' ] )
코드

result = df.loc[('row2','val2')]
print(result)

실행 결과

만약 DataFrame 형태로 반환하고 싶다면 [ [ ] ] 형태로 인덱싱하면됩니다.

코드

result = df.loc[[('row2','val2')]]
print(result)

실행 결과

여기에 열 값까지 설정해줄 경우 해당 값을 반환할 수 있습니다.

코드

result = df.loc[('row2','val2'), 'col3']
print(result)

실행 결과
12

Multi Index의 튜플을 통해 슬라이스하여 인덱싱이 가능합니다.

코드

result = df.loc[('row1','val2') : ('row3','val2')] # row1의 val2부터 row3의 val2까지
print(result)

실행 결과

0개의 댓글