R복습_사용자 정의 함수(apply,sapply,함수 적용 활용)

2024. 10. 1. 00:40R분석

사용자 정의함수

사용자 정의함수란 사용자가 직접 이름을 붙여 만드는 함수를 의미한다. 사용자 정의함수를 만드는 이유는 업무에서 지속적으로 반복되는코드나 작업을 함수로 정의한 후 필요할때마다 호출하여 쉽게 사용하기 위함이다.

함수의 구성요소

  • 기능(수행할 식 또는 내용)
  • 주어진 값(인수)
  • 반환값(return)

 

사용자 정의 함수 만들기

#사용자 정의함수
#함수명 <- function(매개변수,..){실행할 기능}


#인사말을 출력하는 greet()함수 정의
greet <- function(){
  print('hi!')
}


#함수 호출
greet()
greet()
#함수 호출은 횟수에 상관없이 계속 가능

#2개의 숫자를 전달받아 그 합계를 구하는 함수
addNum <- function(num1,num2){
  return(num1+num2)
}

#함수 호출
addNum(10) #매개변수의 개수에 맞춰 인수 전달해야함
addNum(10,10)

#숫자4개를 전달받아 합계를 구하는 함수
addNum <- function(num1,num2,num3,num4){
  result <- num1+num2+num3+num4
  return(result)
}

addNum(10,20,10,10)

사용자 정의함수를 선언할때는 function()함수를 사용하여 정의하는데,  함수명 <- function(매개변수){실행할 기능}의

형식으로 적는다.


첫번째 함수의 경우에는 매개변수와 리턴값없이 이뤄진 함수인데,  이처럼 함수는 매개변수와 리턴값이 필수가 아니기때문에 이와같은 함수도 정의할수있다.


두번째 함수의 경우에는  매개변수와 그 리턴값이 존재하는경우이다.  매개변수는 사용자가 함수를 호출할때 인수를 입력하면 그 인수를 저장하는공간이며,  리턴값은 매개변수에 저장된 값들을가지고 기능을 수행한다.


세번째의 경우도 마찬가지로 매개변수와 리턴값이 존재하는 함수인데,  세번째의경우는 함수내부에서 사용할수있는 지역변수안에 기능을 정의했고,  리턴값으로 그 지역변수를 반환받는 방식의 쿼리문이다.



함수호출

함수를 호출할때는 함수의 이름을적어 호출하면되는데,  첫번째 쿼리문처럼 함수안에 전달해줄 인수가없더라도 함수명 옆에 ()괄호를 적어야 한다.

 

 

 


두번째,세번째 처럼 매개변수가 존재하는 함수는 그 매개변수의 개수에 맞게 인수를 함수의 ()괄호에 입력해 전달해주는 형식으로 호출한다.



사용자 정의함수의 디폴트값

#숫자4개를 전달받아 합계를 구하는 함수
addNum <- function(num1,num2,num3,num4){
  result <- num1+num2+num3+num4
  return(result)
}

addNum(10,20,10,10)


#디폴트값 정의
addNum <- function(num1,num2,num3=100,num4=100){
          #디폴트 값은 중간부터 정의할 수 없음. 끝부터 가능
  result <- num1+num2+num3+num4
  return(result)
}

addNum(10,10)
#디폴트값이 있기때문에 인수를 2개만 전달해도 가능함

addNum(10,10,10)
addNum(10,10,10,10)
#디폴값이 설정되어있어도 다른 인수를 넣을 수 있음

사용자 정의함수에서 매개변수가있는경우,  매개변수에 디폴트값을 지정할 수 있다.  위의 쿼리문에서 일반적으로 숫자4개를 전달받아 합계를구하는 함수에서는 디폴트값이 정의되지 않았기때문에 매개변수의 개수에 맞게 인수를 전부 전달해주어야 오류가발생하지않는다.


하지만 그 밑에 쿼리문처럼 디폴트값을 선언하게되면 디폴트값을 선언하지않은 매개변수에만 인수값을 전달해주어도 계산이 가능하다.  디폴트값은 함수를 선언할때 매개변수=디폴트값의 형태로 지정하게되는데, 여기서 주의할점은 디폴트값은 중간부터 정의할수없고 맨끝부터 순서대로 지정해주어야한다는것이다.


이처럼 디폴트값을 설정해두었다면 디폴트값이 아닌 매개변수에만 값을 전달해주어도 되고,  디폴트값이 지정되어있는 매개변수에도 그냥 원하는값을 전달해 줄 수 있다.



종류에 따른 함수 호출방법

매개변수×  리턴값×

#함수호출
#1)매개변수, 리턴 둘다 없는 경우

welcome <- function(){
  print('안녕! :-)')
}

#함수 호출
welcome

매개변수와 리턴이 둘다없는 함수는,  보통 그 안에 리턴값대신 print나 cat을 이용한 출력문이 출력되는 경우가 대부분이다.  이와같은 함수는 함수명()의 형태로 출력하며,  이때 출력되는값은 리턴값이 아니라 함수를 호출하면 출력되는 출력문이기때문에 리턴값과 헷갈리면 안된다..!


매개변수o  리턴값×

#2)매개변수O, 리턴값X
printName <- function(name){
  print(name)
}

#함수 호출
printName('둘리')
printName('짱구')
printName('유리')

매개변수는 있는데 리턴값이 없는경우도 함수안에 출력문이 있는경우가 대부분이며,  위의 쿼리문의 경우도 인수로 입력된 매개변수를 출력문에서 전달받아 그대로 다시 출력하는 함수이다.  이 경우에는 전달해줄 매개변수가 존재하기 때문에 반드시 그 매개변수의 수에 맞게 함수명()괄호안에 인수를 전달해야한다


매개변수×  리턴값o

#3)매개변수X 리턴값O
getNum <- function(){
  num <- 10
  return(num)
}

getNum()
getNum()
#함수안에 지역변수 10이 저장되어있기때문에
#함수를 호출하게 되면 함수안에 저장되어있는 10이 호출됨

#리턴값이 있는 함수는 하나의 값처럼 사용이 가능함
print(10+getNum())
print(100*getNum())

매개변수는 없고 리턴값만 있는경우는 함수안에 전역변수를 선언한 후,  그 전역변수에 해당하는값을 호출하는경우가 많다.  대부분 이런함수들은 하나의 값의 형태로 출력되는  경우가 대부분이기 때문에 함수 자체를 하나의 값으로도 사용할수있다.


매개변수o  리턴값o

#4)매개변수O , 리턴O
addNum <- function(num1,num2){
  return(num1+num2)
}

getMax <- function(vec){
  return(max(vec))
}

addNum(10,20)
getMax(c(1,2,3))
getMax(seq(10,50,10)) #매개변수가 벡터이기때문에 인수 벡터를 전달

사실상 가장 많이 사용되는 함수의 형태이다.  매개변수를 입력받아 그 안에서 입력받은 매개변수를 가지고 해당 기능을 수행한 후, 그 결과를 리턴값으로 반환하는 함수형태인데,  함수의 매개변수로는 단일값이 들어갈수도있고 두번째 쿼리처럼 벡터로도 전달이 가능하다.  만약 매개변수를 벡터로 지정했을경우 당연히 인수도 벡터로 전달해주어야하기때문에  c()함수를사용하여 함수(c())의 형태로 인수를 전달해야한다

 

 

 

리턴값의 또 다른 의미

#함수 종료 return
#1)함수의 결과로 반환
#2)함수 종료
exit <- function(){
  print('return문은')
  return() #NULL(빈값의미), 함수를 종료시키고 밑에문장 실행되지 않음
  print('함수 종료를 의미하기도 합니다')
}

exit()
#밑에 문장은 출력되지 않음

 

리턴값은 함수안에서 리턴값 자체를반환하는 기능을하지만,
그 외에도 함수내부에서 함수종료를 의미하기도 한다.
보통 return(리턴값)의 형태로 리턴값을 정의하지만 함수가 진행되고,  그 중간부분에서 함수를 종료시키고싶다면 return()의 형태로 리턴의 괄호안에 아무것도 적지않은 상태로 만들면된다.


이처럼 return()의 밑에있던 출력문은 출력되지않고 함수가 종료된것을 확인할 수 있다.

 


함수의 반환값이 여러개인 경우

#반환해야하는 값이 여러개일 경우
my.func <- function(num1,num2){
  #숫자 2개를 입력받아, 더한값과 곱한값을 반환
  #return(num1+num2,num1*num2) #함수는 정의되더라도 실행 시 에러가 남
  num.sum <- num1+num2
  num.mul <- num1*num2
  return(list(sum=num.sum,
              mul=num.mul))
}

my.func(10,10)
#return(num1+num2,num1*num2)로 작성했을 경우
#다중인자 반환 허용하지 않음

my.func(20,10)

사용자 정의함수에서 리턴값은 여러개일 수 있는데,  이런경우 리턴값이 각각의 기능에맞춰 출력될수 있도록 지정해주어야한다.

만약 리턴값이 여러개라고 해서 returen(반환값1,반환값2)의 형태로적게되면 함수정의 자체에서는 에러가 나진않지만  이 상태에서 인수를 전달하게되면 다중인자 반환을 허용하지않는다는 메세지와함께 에러가 발생한다.


이때는 list()함수를 이용하여 반환값의 형태가 리스트가 될수있도록 해주어야하는데 함수내부에서 지역변수를 설정하여 그 지역변수에 각각의 기능을 수행할수있도록 지정한 후,  리턴값에서 리스트를 정의하여 속성명=속성값(지역변수)의 형태로 정의해주어야 한다.

 


반환값이 여러개인 함수 변수에 저장

#함수 자체를 변수에 저장하여 출력하기
result <- my.func(10,20)
print(result)

#변수에 저장하면 개별적으로 값을 출력할 수 있음
print(result$sum)
print(result$mul)

cat(result$sum+10)
#개별로 출력한값은 개별값으로 사용가능함

반환값이 여러개인 함수를 호출할때는 함수를 바로 호출하는것이 아니라 변수에 저장하여 사용할수도있다.  기본적인 방법으로 함수를 호출한 다음,  그대로 변수안에 저장하는방식인데,  이렇게 변수안에 저장된 함수는 리스트에서 속성명으로 값에 접근하듯 사용하면 여러개인 반환값을 각각 하나씩 따로 출력할수도있으며,  이렇게 단일값이 된 함수의 리턴값은 하나의값처럼 이용도 가능하다

 


사용자 정의함수 활용 실습

실습코드1

#========사용자 정의함수 실습========#

#<실습>
#함수명 : sum_num
#2개의 정수를 전달받아 그 합을 반환하는 함수

#함수정의
sum_num <- function(num1,num2){
  add <- num1+num2
  return(add)
}

#함수 호출
sum_num(10,100)
sum_num(20,10)



실습코드2

#<실습2>
#함수명 : fac_clac
#양의 정수를 전달받아 그 수의 팩토리얼을 계산하는 함수를 정의
#5!=1X2X3X4X5=>120

fac_clac <- function(num1){
  result <- 1
  for(i in 1:num1){
    result <- result*i
  }
  return(result)
}

fac_clac(5)

 


실습코드3

#<실습3>
#함수명: even_odd

#숫자형 벡터를 전달받으면 짝수와 홀수를
#각각의 리스트에 분리하여 반환하는 함수 정의
#결과 list(even=짝수들, odd=홀수들,,)

even_odd <- function(vec){
  even <- c()
  odd <- c()
  for(i in 1:length(vec)){
    if(vec[i]%%2==0){
      even[i] <- vec[i]}
    else{
        odd[i] <- vec[i]
      }
  }
  return(list(even=even,odd=odd))
}

even_odd(c(1,2,3,4,5,6,7,8,9,10))
#차례대로 값이 출력되긴 하나, na값이 함께 생성됨
#인덱스번호로 나뉘어져서 빈값이 생기면서 na가 발생함


even_odd <- function(vec){
  even_nums <- vec[vec%%2==0]
  odd_nums <- vec[vec%%2==1]
  result <- list(even <- even_nums, odd=odd_nums)
  return(result)
}

even_odd(c(1,2,3,4,5,6,7,8,9,10))

#벡터[조건식]방법을 이용하여 na값 발생없이 참인 값만 맞춰서 들어갈 수 있도록 함

 

 

 

사용자 정의함수 apply사용

apply함수 복습

#--막간 복습--#
#apply
ma1 <- matrix(seq(10,100,10),nrow=2,ncol=5)
print(ma1)
print(dim(ma1)) #2행 5열

#apply함수적용
apply(ma1,1,sum) #행별 함수 적용
apply(ma1,2,sum) #열별 함수 적용

+10더하는 사용자 정의 함수 생성

#+10더하는 사용자 정의 함수 생성
addTen <- function(num1){
  return(num1+10)
}

print(ma1)
apply(ma1,1,addTen)
#행렬에 사용자 정의함수 적용가능
#1행에 적용시 행의 결과가 열로 들어감

apply(ma1,2,addTen)
#열의 결과는 행으로 출력됨

apply에서 함수를 이용하면 행별로, 열별로 함수를 적용할 수 있었는데 r의 내장 기본함수(sum,mean,...)이외에도 사용자 정의함수를 적용할 수 있다. 사용방법은 동일한데, 일반 apply와 다른점은 사용자 정의함수를 사용하게 되면 행의 값은 열로, 열의 값은 행으로 출력된다는 것이다. 

 

 

사용자 정의함수 sapply사용

데이터프레임 복습

#--막간 복습--#

#데이터 프레임
pr.data <- data.frame(
  name=c('짱구','철수','유리'),
  score=c(70,90,80),
  age=c(5,6,7)
)

print(pr.data)

#데이터 프레임[행,열]
pr.data[,c('score','age')]
#열만 가져오는데 열의 이름으로 접근하며 벡터로 값을 전달

pr.data[1:2,c('score','age')]
pr.data['age']
pr.data[,'age']#이렇게 가져오면 데이터프레임이 아니라 벡터로 가져옴
class(pr.data['age']) #data.frame
class(pr.data[,'age']) #numeric

 

sapply(데이터셋, 함수)

#sapply(데이터셋, 함수)
result <- sapply(pr.data['age'],mean)
#sapply를 사용할때는 데이터프레임에서 열만 가져온 형태로 적어야함
#대괄호안에 콤마없이
print(result) #age열의 평균

print(sapply(pr.data[,'age'],mean)) 
#,콤마를 사용하여 벡터형태로 값을 전달하면 벡터요소값 하나하에 개별적으로
#평균을 구하는 함수를 적용한 것이여서 벡터의 요소 값이 그대로 출력됨 
print(sapply(pr.data['age'],addTen)) 
#데이터프레임에 함수 작용

sapply는 apply와 똑같은 기능을 하지만 데이터프레임에서 열만 추출하여 1차원 벡터형태가 된 데이터들 처럼 1차원에 함수를 적용할 수 있는 함수이다. 주로 데이터셋에서 열만 추출한 데이터와 함께 주로사용되며 사용방식은 행,열번호를 생략한 apply와 방식이 동일하다. 데이터프레임에서 열만 추출하여 사용할 경우 주의할점은 보통 열만 추출할때 데이터프레임명['열이름']의 형식으로 작성하는데 이때 ,콤마를 생략하게 되면 열만 가져온 데이터프레임 형태이지만 ,콤마를 붙여 데이터프레임명[,'열이름']의 형태로 적게되면 추출된 데이터의 형태는 벡터가 된다. 이렇게 추출된 완벽한 벡터의 형태에서 sapply에 적용하게 되면 백테 개별요소 하나하나에 함수를 적용할 수 있는 형태가 된다.

 

사용자 정의 함수활용 실습2

#<실습>
user.df <- data.frame(
  id=1:4,
  name=c('해리포터','론위즐리','헤르미온느','덤블도어'),
  age=c(17,16,17,60)
)
print(user.df)

#1) 나이를 인수로 받아서 나이가 20살이상이면 '성인' 아니면 '미성년자'
#문자열을 반환하는 함수 정의 한 후 함수 호출하기
#함수명 : is_adult()

is_adult <- function(num1){
  if(num1>=20){
    print('성인')}
  else {
    print('미성년자')
  }
}


#개별출력
is_adult(user.df$age[1])
is_adult(user.df$age[2])
is_adult(user.df$age[3])
is_adult(user.df$age[4])


#반복문으로 출력
for(i in 1:length(user.df$name)){
  is_adult(user.df$age[i])
}


#2)user.df에서 age열의 데이터 is_adult() 함수 적용 후
#user.df에 is_adult라는 새로운 열을 추가 
#추가한 열의 데이터는 is adult(함수의 반환값)


result_a <- sapply(user.df['age'],is_adult)
print(result_a)
#여기서는 괄호안에 콤마를 붙여 벡터의 형태로 값을 전달해주어야함.
#함수 안에 if문이 들어있기때문에 값이 if문을 만나 그 안에서 조건에따라
#출력되는 값이 새로운 변수에 저장되어야하는것인데 if문의 경우
#데이터프레임과 같이 비교대상이 하나 이상의 값이되면 에러가 발생하기때문


user.df$is_adult <- c(result_a)
user.df

 

 


 

 

학습일기

오늘은 사용자 정의함수에 대해서 학습했는데, 사용자 정의함수의 경우 파이썬에서 먼저 접했기때문에 개념학습 자체는 그리 어렵진 않았지만 파이썬과 또 사용법이나 정의, 사용자 정의함수에서 사용할 수 있는 함수 등등이 약간 달라서 헷갈리는 부분들이 조금 있었다. 개인적인 느낌으로는 r의 경우에는 사용자 정의 함수를 사용해서 기존에 있었던 데이터프레임이나 행렬등등에도 접근하는게 파이썬보다는 약간 더 간편하고 쉬운듯한 느낌이 있었다. 계속 언어 공부를 하다보면 사실 내장함수나, 기본개념등이 비슷한 부분들이 있어서 헷갈리는 경우가 있는데 이런 부분들을 단단하게 잡아놓아야 겠다...!