@@@ 데이터분석/데이터 분석의 모든 것

Chapter 2. R 프로그래밍 - 2.4 R 기초 프로그래밍

HTG 2021. 10. 4. 19:07
728x90

키워드 : if ~ else 문, for 문, 함수 정의, 함수 호출, 매개변수, NULL, NA, Inf, NaN

더보기

if 문 : 소괄호 안에 조건을 쓰고, 그 조건이 TRUE이면 if 문 뒤 { } 안의 문장이 수행된다. 조건이 FALSE이면 else 문 뒤 { } 안의 문장이 수행.
for 문 : 문장이 여러 번 수행할 수 있도록 제어하는 반복문.

함수 정의 : 함수에 프로그램 코드를 저장하는 것

함수 호출 : 함수 이름으로 저장된 프로그램 코드를 불러(호출하여) 실행하는 것

매개변수 : 함수 외부에서 데이터를 받아 저장할 변수

NULL : 변수에 값이 아직 정해지지 않았다는 의미로 변수를 초기화할 때 사용하는 상수.

NA : 데이터 분석에서 중요한 용어인 결측값을 의미하는 상수.

Inf : 무한대 실수를 의미하는 상수

NaN : 'Not a Number'를 의미하는 상수.

 

2.4.1 연산

수치 연산자
연산자 기호 설명 예제
+ - * / 사칙연산 10+2, 10-2, 10*2, 10/2
%% 나눈 나머지 10 %% 3  # 1
%/% 나눈 몫 10 %/% 3  # 3
^ 자승 2^3  # 8

 

논리 연산자
연산자 기호 설명 예제
== 같으면 참 10 == 10  # TRUE
!= 틀리면 참 10 != 10  # FALSE
< 작으면 참 10 < 5  # FALSE
<= 작거나 같으면 참 10 <= 10  # TRUE
> 크면 참 10 >5  # TRUE
>= 크거나 같으면 참 10 >=5  # TRUE
%in% 연산자 뒤 나열한 값들 중 하나와 일치하면 참 n <- 20 
n %in% (c(10,20,30))  # TRUE
& AND 연산자
양쪽 모두 참이면 참
n <- 10
n>=0 & n<= 100  # TRUE
| OR 연산자
둘 중 하나만 참이면 참
n <- 1000
n >=0 | n <= 100   # TRUE
! NOT 연산자
참이면 거짓, 거짓이면 참
! (10==10)  # FALSE

&, | 대신 &&, || 연산자를 사용하는 경우가 있는데, &&, || 는 하나의 진리값만 필요할 경우 사용.

즉, 벡터 연산이나 행렬 연산에서는 사용할 수 없는 경우가 많음.

if 문 등에서 많이 사용.

 

2.4.1.1 벡터 연산

벡터와 스칼라의 사칙연산을 수행하면 벡터 안의 모든 데이터에 각각 연산이 적용.
스칼라 : 0차원배열, 즉, 하나의 값

다음은 벡터와 스칼라의 연산.

> score <- c(10,20)
> score + 2             # 벡터의 모든 데이터에 각각 2를 더하여 반환
[1] 12 22

> score
[1] 10 20               # 벡터 자체는 변경되지 않아서 이전 값을 유지

# 연산 결과를 score 변수에 반영하려면 다음과 같이 score에 연산 결과를 저장함.
> score <- score + 2    # score 벡터의 모든 데이터에 각각 2를 더하고, 연산 결과를 score 에 저장
> score                 # score가 변경된 것을 확인할 수 있음.
[1] 12 22

다음과 같은 벡터와 벡터의 연산은 벡터 안의 각 데이터 간에 연산이 발생.

> first_score <- c(100,200)
> second_score <- c(90,91)
> 
> sum_score <- first_score + second_score # 벡터와 벡터의 더하기 연산
> sum_score
[1] 190 291
> 
> diff <- first_score - second_score
> diff
[1]  10 109

+, - 뿐만아니라  다른 연산(*, /, %%, %/% 등)도 동일하게 적용

길이가 다른 벡터 간 연산을 하면 개수가 부족한 쪽 벡터의 처음 데이터부터 반복 되면서 연산이 수행

> first_score <- c(100,200,300)            # 3개의 데이터
> second_score <- c(90,91)                 # 2개의 데이터
> sum_score <- first_score + second_score  # 벡터 간 데이터 개수가 맞지 않음

Warning message:
In first_score + second_score :
  longer object length is not a multiple of shorter object length

# 길이가 다른 벡터 간 연산의 경우 아래와 같은 경고 메시지가 나오지만 연산은 수행.
> sum_score
[1] 190 291 390

다음은 벡터의 논리 연산.

> first_score <- c(100,200,300)
> second_score <- c(90,91,92)
> 
> # 1, 2 데이터가 각각 서로 같은지 비교하는 연산
> first_score == second_score
[1] FALSE FALSE FALSE
> 
> # 1 데이터가 0에서 100사이의 값인지 하나씩 비교하는 연산
> first_score >= 0 & first_score <= 100
[1]  TRUE FALSE FALSE
> 
> # 1 데이터가 1 또는2 또는 100 인지 비교하는 연산
> first_score %in% c(1,2,100)
[1]  TRUE FALSE FALSE
> 
> # 첫번째 데이터만 참
> # 나머지는 1,2,100이 아니므로 거짓

 

2.4.1.2 행렬 연산

행렬과 스칼라 연산

> m1 <- matrix(c(1,2,3,4,5,6), nrow = 2)
> m1 <- m1*10
> 
> m1
     [,1] [,2] [,3]
[1,]   10   30   50
[2,]   20   40   60

행렬과 행렬의 연산

> m1 <- matrix(c(1,2,3,4,5,6,7,8,9), nrow = 3)
> m2 <- matrix(c(2,2,2,2,2,2,2,2,2), nrow = 3)
> 
> m1
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> 
> m2
     [,1] [,2] [,3]
[1,]    2    2    2
[2,]    2    2    2
[3,]    2    2    2
> 
> m1+m2             # - 도 가능
     [,1] [,2] [,3]
[1,]    3    6    9
[2,]    4    7   10
[3,]    5    8   11
> 
> m1*m2             # / 도 가능
     [,1] [,2] [,3]
[1,]    2    8   14
[2,]    4   10   16
[3,]    6   12   18

* 연산자와 다른 행렬곱(%*%) 연산자가 있음.

행렬곱 연산은 행렬의 각 요소들 간에 단순 곱을 하는 곱하기 연산과는 다른데, 다음 식이 행렬곱(%*%)의 계산 방법을 보여줌.

$ \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} $ %*% $ \begin{pmatrix} 5 & 6 & 7 \\ 8 & 9 & 10 \end{pmatrix} $ = $ \begin{pmatrix} 1\times5+2\times8 & 1\times6+2\times9 & 1\times7+2\times10 \\ 3\times5+4\times8 & 3\times6+4\times9 & 3\times7+4\times10 \end{pmatrix} $

 

다음은 행렬곱

> jumsu <- matrix(c(1,2,3,4), ncol = 2, byrow = T)
> jumsu
     [,1] [,2]
[1,]    1    2
[2,]    3    4
> 
> weight <- matrix(c(5,6,7,8,9,10), ncol = 3, byrow = T)
> weight
     [,1] [,2] [,3]
[1,]    5    6    7
[2,]    8    9   10
> 
> jumsu %*% weight
     [,1] [,2] [,3]
[1,]   21   24   27
[2,]   47   54   61

 

다음은 전치 행렬과 역행렬. t()로 전치 행렬 생성. solve()로 역행렬을 구함.

> jumsu
     [,1] [,2]
[1,]    1    2
[2,]    3    4
> 
> t(jumsu)     # 전치행렬
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> 
> solve(jumsu) # 역행렬
     [,1] [,2]
[1,] -2.0  1.0
[2,]  1.5 -0.5

 

2.4.2 흐름 제어문

컴퓨터 프로그램의 수행 순서는 기본적으로 왼쪽에서 오른쪽, 위에서 아래로 문장이 하나씩 수행.

이 기본 흐름을 변경할 수 있는 문장이 흐름 제어문.

흐름 제어문은 크게 조건문과 반복문으로 나뉨. 

조건문으로는 if ~ else 문 등이 있고, 반복문으로는 for, while, repeat 문 등이 있음.

 

2.4.2.1 if ~ else 문

조건문은 어떤 문장을 실행할지 하지 않을지를 판단하는 문장.

if 문 소괄호 뒤에 조건을 쓰고, 그 조건이 TRUE이면 if 문 뒤 { } 안의 문장이 수행.

 

다음은 if(조건) { } 문장의 예.

if 뒤 소괄호 () 안에 TURE 또는 FALSE가 결과로 나오는 조건을 씀.

조건이 TRUE이면 { }안의 문장이 수행.

> score <- 95
> if(score <= 100 & score >= 80) { # 조건이 TURE이므로 아래 문장이 실행
+   print("조건이 TRUE인 경우만 수행할 문자")
+ }
[1] "조건이 TRUE인 경우만 수행할 문자"

다음은 if(조건) { } else { } 문장

> score <- 86
> if(score >= 91) {      # 조건이 FALSE
+   print("A")           # TRUE일 때 수행할 문장
+ } else {
+   print("B or C or D") # FALSE일 때 수행할 문장
+ }
[1] "B or C or D"

다음은 if(조건) { } else if (조건) { } else 문을 연속해서 사용.

> score <- 86
> if(score >= 91) {print("A")
+ } else if(score >= 81) {print("B")
+ } else if(score >= 71) {print("C")
+ } else if(score >= 61) {print("D")
+ } else {print("F")}
[1] "B"

 

2.4.2.2 ifelse() 함수

ifelse() 함수는 if ~ else 문과 동일한 기능.

ifelse() 함수의 사용 방법.

ifelse(조건, "조건이 TRUE일 때 수행할 문장", "조건이 FALSE일 때 수행할 문장")

다음은 score가 91 이상일 때 "A"를 출력, 그렇지 않으면 "FALSE일 때 수행"을 출력하는 프로그램.

> score <- 85
> ifelse(score >= 91, "A", "FALSE일 때 수행")
[1] "FALSE일 때 수행"

ifelse() 함수를 중첩하여 사용할 수 있음.

ifelse(조건1, "조건1이 TRUE일 때 수행할 문장", ifelse(조건2, " ", " "))

조건1이 FALSE이면 ifelse(조건2, " ", " ") 문장을 수행.

다음은 ifelse() 함수를 중첩하여 사용한 예.

> score <- 85
> ifelse(score >= 91, "A", ifelse(score >= 81, "B", "C or D"))
[1] "B"
> 
> score <- 95
> ifelse(score >= 91, "A", ifelse(score >= 81, "B", "C or D"))
[1] "A"
> 
> score <- 65
> ifelse(score >= 91, "A", ifelse(score >= 81, "B",ifelse(score >= 71,"C","D")))
[1] "D"

조건문으로 switch 문도 있지만 R에서는 많이 사용되지 않으므로 설명을 생략.

 

2.4.2.3 for 문

for 문은 문장이 여러 번 수행할 수 있도록 제어하는 반복문 중 하나.

> for(num in(1:3)) {
+   print(num)
+ }
[1] 1
[1] 2
[1] 3

# 반복 수행할 문장이 단 하나이면 중괄호는 생략해도 됨.
> for(num in(1:3))
+   print(num)
[1] 1
[1] 2
[1] 3

for 문 안에 if 문이나 다른 제어문을 중첩해서 사용.

> for(num in(1:5)) {
+   if(num %% 2 == 0)
+     print(paste(num,"짝"))
+   else
+     print(paste(num,"홀"))
+ }
[1] "1 홀"
[1] "2 짝"
[1] "3 홀"
[1] "4 짝"
[1] "5 홀"

for 문을 이용해서 하나씩 수행하는 것보다 벡터나 행렬 연산을 통해서 수행하는 것이 속도면에서 휠씬유리.

위와 동일한 프로그램을 벡터 연산으로 수행.

> num <- c(1:5)
> ifelse(num %% 2 == 0, paste(num,"짝"),paste(num,"홀"))
[1] "1 홀" "2 짝" "3 홀" "4 짝" "5 홀"

 

2.4.2.4 while 문

for 문과 같은 반복문중에 하나.

무한루프에 빠지지 않도록 조건이 언젠가 FALSE가 될 수 있게 주의.

while(조건) {조건이 TRUE일 때 반복해서 수행할 문장}
> num <- 1
> while(num <= 5) {
+   print(num)
+   num <- num + 1
+ }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

while 문도 for 문과 같이 다른 제어문과 중첩해서 사용할 수 있음.

대처할 수 있는 벡터 연산이 있는 경우 벡터 연산이 수행 속도가 더 빠르므로 벡터 연산을 사용.

 

2.4.2.5 break 문

반복문 안에서 break 문을 만나면 반복문이 끝남.

> s <- 0
> 
> for(x in(1:1000)) {
+   s <- s + x
+   if(s >= 100)
+     break
+ }
> print(s)
[1] 105

 

2.4.2.6 next 문

반복문 안에서 next 문을 만나면 수행해야 할 반복문의 일부를 건너 띔.

> cnt = 0
> for(x in c(1, 2, NA, 3, NA, 4, 5)) {
+   if(is.na(x))
+     next
+   print(x)
+ }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

 

2.4.2.7 repeat 문

다른 프로그램 언어의 do ~ while 문과 유사.

while 문은 조건문이 처음부터 FALSE이면 반복문이 한 번도 수행되지 않을 수 있지만, repeat 문은 문장이 일단 수행된 후 다시 반복할지를 결정하는 조건 검사를 할 수 있어 적어도 한 번은 반드시 수행되도록 프로그래밍할 수 있음.

while 문장과 마찬가지로 무한 루프에 빠지지 않도록 주의.

> num <- 1
> repeat {
+   print(num)
+   num <- num + 2
+   if(num > 10)
+     break;
+ }
[1] 1
[1] 3
[1] 5
[1] 7
[1] 9

 

2.4.3 함수

변수는 데이터를 저장하고 있는 반면 함수는 프로그램 코드를 저장.

함수에 프로그램 코드를 저장하는 것을 함수 정의라고 부르고, 함수이름으로 저장된 프로그램 코드를 불러 실행하는 것을 함수 호출이라고 함.

함수를 호출할 때는 함수 이름 뒤에 소괄호를 붙임.

내장함수(bulit in)란 이미 만들어져 있는, 즉,이미 정의되어 있는 함수를 말함. (print(), str(), head() 등)

사용자 정의 함수란 사용자가 새롭게 함수를 정의한 함수.

 

2.4.3.1 함수 생성과 호출

변수에 데이터를 저장하는 대신 프로그램 코드를 저장하면 변수가 아니라 함수가 생성.

함수를 생성하려면 function이라는 키워드가 필요.

함수에 저장할 프로그램 코드를 function() 뒤 중괄호 { } 안에 작성.

사용자 정의 함수를 생성.

> a <- function() {
+   print(paste("testa"))
+   print(paste("testa"))
+ }
> # 함수 생성이 함수의 실행을 의미하지는 않음.
> # a()라는 함수가 생성되었을 뿐임.

함수를 생성하는 것과 실행하는 것은 다름.

함수에 저장된 프로그램 코드를 실행하는 것을 함수 호출.

함수 호출의 방법은 함수 이름 뒤에 소괄호 ()를 붙이면 됨.

> a()
[1] "testa"
[1] "testa"

 

2.4.3.2 매개변수가 있는 함수

함수를 생성할 때 함수 외부에서 데이터를 전달 받아 이용하도록 만들 수 있음.

함수 외부에서 데이터를 받아 저장할 변수를 매개변수라고 부름.

매개변수 생성은 function 뒤 소괄호 () 에 변수 이름을 작성하면 됨.

> a <- function(num){
+   print(num)
+ }
> 
> a(20)
[1] 20
> 
> a(10)
[1] 10

# 매개변수가 있어야하는 함수에 매개변수가 없다면 오류 발생
> a()
Error in print(num) : argument "num" is missing, with no default

# 이미 같은 이름의 함수가 존재하면 코드가 바뀌어 저장.

 

2.4.3.3 두 개 이상의 매개변수가 있는 함수

함수의 매개변수가 다음 예와 같이 두 개 이상 있을 수 있음.

a <- function(num1, num2) {
  print(paste(num1," ", num2))
}

두 개 이상의 매개변수에 값을 전달하며 함수를 호출하는 방법은 여러 가지 존재.

1) 순서대로 매핑. 매개변수가 여러 개인 경우 순서대로 매핑

> a(10,20)
[1] "10   20"

2) 매개변수 이름을 직접 기재해서 매개인자 전달하는 방법.

순서는 상관이 없음.

> a(num1 = 10, num2 = 20)
[1] "10   20"
> 
> a(num2 = 10, num1 = 20)
[1] "20   10"

매개변수 이름을 작성하여 데이터를 전달하는 경우 = 연산자나 <- 연산자를 사용할 수 있음.

둘의 차이는 = 로 할당했을 때는 함수 실행이 끝난 후 그 변수는 존재하지 않으나 <- 로 할당했을 때는 변수가 존재.

> a(num1 <- 10, num2 = 20)
[1] "10   20"
> 
> print(num1)
[1] 10
> 
> print(num2)
Error in print(num2) : object 'num2' not found

 

2.4.3.4 디폴트값이 있는 매개변수

매개변수에 디폴트(default) 값을 지정.

> a <- function(num1, num2 = 10) {
+   print(paste(num1," ",num2))
+ }
> 
> a(num1 = 10)          # 디폴트값이 있는 num2는 값을 전달하지 않아도 됨.
[1] "10   10"
> 
> a(10)
[1] "10   10"
> 
> a(10,30)              # 다른 값을 전달하면 그 값이 적용
[1] "10   30"
> 
> a(num2 = 20)          # 디폴트값이 없기 때문에 오류 발생
Error in paste(num1, " ", num2) : 
  argument "num1" is missing, with no default

디폴트값이 있는 매개변수의 다른 예.

> calculator <- function(num1, num2, op = "+") {
+   result <- 0
+   if(op == "+"){
+     result <- num1 + num2
+   } else if(op == "-"){
+     result <- num1 - num2
+   } else if(op == "*"){
+     result <- num1 * num2
+   } else if(op == "/"){
+     result <- num1 / num2
+   }
+   print(result)
+ }
> 
> calculator(10,20,"-")
[1] -10
> 
> calculator(10,20)
[1] 30

 

2.4.3.5 가변길이 매개변수

매개변수로 넘어오는 데이터의 개수를 제한하지 않을 수 없음.

이것을 가변길이 매개변수라고 부름.

매개변수 이름을 작성하는 자리에 (...)으로 작성.

> a <- function(...) {
+   for(n in c(...)) {
+     print(n)
+   }
+ }
> 
> a(1,2)
[1] 1
[1] 2
> 
> a(1,2,3,4)
[1] 1
[1] 2
[1] 3
[1] 4
> 
> a(10)
[1] 10

 

2.4.3.6 리턴 데이터가 있는 함수

함수의 실행이 끝난 후 실행 결과의 데이터를 함수를 호출한 쪽으로 돌려줌.

함수를 호출한 곳에 데이터를 돌려준다는 의미로 리턴 데이터 또는 반환 데이터라고 부름.

함수에서 데이터를 리턴하려면 return 뒤 소괄호 () 안에 리턴할 데이터나 변수를 작성.

> calculator <- function(num1, op = "+", num2) {
+   result <- 0
+   if(op == "+"){
+     result <- num1 + num2
+   } else if(op == "-"){
+     result <- num1 - num2
+   } else if(op == "*"){
+     result <- num1 * num2
+   } else if(op == "/"){
+     result <- num1 / num2
+   }
+   return(result)
+ }
> 
> n <- calculator(1, "+", 2)
> print(n)
[1] 3
> 
> n <- calculator(1, "+", 2)
> n <- calculator(n, "*", 2)
> n <- calculator(n, "-", 2)
> print(n)
[1] 4
> 
> # 함수를 중첩하여 호출하면 가장 안쪽의 함수부터 실행.
> print(calculator(calculator(calculator(1, "+", 2), "*", 2), "-", 2))
[1] 4
> # ((1+2)*2)-2

 

2.4.4 유용한 함수와 상수

결측값(결측치) - 어떤 자료의 항목이 비워져 있는 경우

데이터 분석에서는 결측값 처리가 매우 중요.

 

2.4.4.1 NULL과 NA

NULL은 변수에 값이 아직 정해지지 않았다는 의미로 변수를 초기화할 때 사용하는 상수.

NA는 데이터 분석에서 중요한 용어인 결측값을 의미하는 상수.

> a <- NULL
> is.null(a)               # is.null() 로 NULL여부 확인
[1] TRUE
> 
> b <- NA
> jumsu <- c(NA, 90, 100)
> 
> is.na(b)                 # is.na()로 NA 여부 확인
[1] TRUE
> 
> is.na(jumsu)
[1]  TRUE FALSE FALSE

 

2.4.4.2 Inf 와 NaN

Inf는 무한대 실수를 의미하는 상수, NaN은 'Not a Number'를 의미하는 상수

> num <- 10/0; num  # 연산 결과가 양수 무한대이므로 INF 저장
[1] Inf
> 
> num <- -10/0; num # 연산 결과가 음수 무한대이므로 -INF 저장
[1] -Inf
> 
> num <- 0/0; num   # 연산이 불가능하여 NaN 저장
[1] NaN

 

2.4.4.3 데이터 타입 변환과 타입 확인

데이터의 변환은 암시적 즉, 자동으로 종종 일어남.

자동이 아닌 명시적으로 데이터를 변환해야 할 때.

> data <- c(1,2,3)
> d1 <- as.character(data)  # data의 데이터를 불러와 character 타입으로 변환하여 반환
> d2 <- as.numeric(data)    # data의 데이터를 불러와 numeric 타입으로 변환하여 반환
> d3 <- as.factor(data)     # data의 데이터를 불러와 factor 타입으로 변환하여 반환
> d4 <- as.matrix(data)     # data의 데이터를 불러와 matrix 타입으로 변환하여 반환
> d5 <- as.array(data)      # data의 데이터를 불러와 array 타입으로 변환하여 반환
> d6 <- as.data.frame(data) # data의 데이터를 불러와 dataframe 타입으로 변환하여 반환

데이터 타입을 확인할 수 있는 함수

> is.character(d1)
[1] TRUE
> is.numeric(d2)
[1] TRUE
> is.factor(d3)
[1] TRUE
> is.matrix(d4)
[1] TRUE
> is.array(d5)
[1] TRUE
> is.data.frame(d6)
[1] TRUE
> 
> is.numeric(data)
[1] TRUE
> is.character(data)
[1] FALSE
> 
> class(data)
[1] "numeric"
> 
> str(data)
 num [1:3] 1 2 3

 

2.4.4.4 변수 삭제

remove() 또는 rm()를 이용하서 변수를 삭제

> score <- 100
> remove(score)
> score
Error: object 'score' not found