2.3 벡터(vector)

2.3.1 벡터의 특징

  • 타 프로그래밍 언어의 배열(array)의 개념으로 동일한 유형의 데이터 원소가 하나 이상(\(n \times 1\), \(n \geq 1\)) 으로 구성된 자료 형태
  • R 언어의 가장 기본적인 데이터 형태로 R에서 행해지는 모든 연산의 기본 단위임
  • 벡터 연산 시 반복구문(예:for loop)이 필요 없음. 이러한 형태의 연산을 벡터화 연산(vectorization)이라고 함.
  • 2.2 절에서 기술한 스칼라(scalar)는 사실 \(1 \times 1\) 벡터임
  • 수학적으로 벡터는 아래와 같이 정의할 수 있음.

\[\mathrm{\mathbf x} = [x_1, x_2, x_3, \ldots, x_n]^T \]

  • 벡터는 앞의 예시에서 본 바와 같이 c() 함수를 사용해 생성
# 숫자형 벡터 
x <- c(2, 0, 2, 0, 0, 3, 2, 4)
x
[1] 2 0 2 0 0 3 2 4
# 문자형 벡터
y <- c("Boncho Ku", "R programming", "Male", "sophomore", "2020-03-24")
y
[1] "Boncho Ku"     "R programming" "Male"          "sophomore"    
[5] "2020-03-24"   

  • 두 개 이상의 벡터는 c() 함수를 통해 결합 가능
    • 함수 내 , 구분자를 통해 결합
# 두 벡터의 결합 (1)
x <- 1:5
y <- 10:6
z <- c(x, y)
x
[1] 1 2 3 4 5
y
[1] 10  9  8  7  6
z
 [1]  1  2  3  4  5 10  9  8  7  6
x <- 5:10
x1 <- x[1:3] # x 벡터에서 1에서 4번째 원소 추출
x2 <- c(x1, 15, x[4])
x2
[1]  5  6  7 15  8

  • 서로 다른 자료형으로 벡터를 구성한 경우 표현력이 높은 자료형으로 변환한 값 반환
    • 예: 문자열 + 숫자로 구성된 벡터 \(\rightarrow\) 문자형 벡터
    • 변환규칙: NULL < raw < logical < integer < double < complex < character < list < expression
# 숫자형 벡터와 문자열 벡터 혼용
k <- c(1, 2, "3", "4")
k
[1] "1" "2" "3" "4"
is.numeric(k) # 벡터가 숫자형인지 판단하는 함수
[1] FALSE
is.character(k) # 벡터가 문자열인지 판단하는 함수
[1] TRUE
# 숫자형 벡터와 문자열 벡터 결합
x <- 1:3
y <- c("a", "b", "c")
z <- c(x, y)
z
[1] "1" "2" "3" "a" "b" "c"
is.numeric(z)
[1] FALSE
is.character(z)
[1] TRUE
# 숫자형 벡터와 논리형 벡터 결합
x <- 9:4
y <- c(TRUE, TRUE, FALSE)
z <- c(x, y)

z # TRUE/FALSE 가 1과 0으로 변환
[1] 9 8 7 6 5 4 1 1 0
is.numeric(z)
[1] TRUE
is.logical(z)
[1] FALSE

  • 두 벡터는 중첩이 불가능 \(\rightarrow\) 동일한 벡터 2개를 결합 시 단일 차원 벡터 생성
x <- y <- 1:3 # x와 y 동시에 [1, 2, 3] 할당
x 
[1] 1 2 3
y
[1] 1 2 3
z <- c(x, y)
z
[1] 1 2 3 1 2 3

  • 벡터 각 원소에 이름 부여 가능
    • names() 함수를 이용해 원소 이름 지정
    • 사용 프로토타입: names(x) <- 문자열 벡터, 단 x와 이름에 입력할 문자열 벡터의 길이는 같아야 함.
    • c() 함수에서 직접 이름 지정 \(\rightarrow\) c(atom_name1 = value, atom_name2 = value, ...)
x <- c("Boncho Ku", "R programming", "Male", "sophomore", "2020-03-24")

# 벡터 원소 이름 지정
names(x) <- c("name", "course", "gender", "grade", "date") 
x
           name          course          gender           grade            date 
    "Boncho Ku" "R programming"          "Male"     "sophomore"    "2020-03-24" 
y <- c(a = 10, b = 6, c = 9)
names(y)
[1] "a" "b" "c"

  • 벡터의 길이(차원) 확인
    • length() 또는 NROW() 사용
x <- 1:50
# 객체의 길이 반환
# length(): 벡터, 행렬인 경우 원소의 개수, 데이터프레임인 경우 열의 개수 반환
length(x) 
[1] 50
# NROW(): 벡터인 경우 원소의 개수, 행렬, 데이터 프레임인 경우 행의 개수 반환
NROW(x)
[1] 50

2.3.2 벡터의 연산

  • 원소 단위 사칙연산 및 비교연산 수행 \(\rightarrow\) 벡터화 연산(vectorized operation)
    • 예를 들어 \(\mathrm{\mathbf x} = [1, 2, 3]^T\) 이고, \(\mathrm{\mathbf y} = [2, 3, 4]^T\) 라고 할 때 \(\mathrm{\mathbf x} + \mathrm{\mathbf y}\)의 연산은 아래와 같음

\[\begin{bmatrix} 1 \\ 2\\ 3 \end{bmatrix} + \begin{bmatrix} 2 \\ 3\\ 4 \end{bmatrix} = \begin{bmatrix} 3 \\ 5 \\ 7 \end{bmatrix} \]

  • * 연산 시 행렬 대수학에서 벡터의 곱(product)과 다름을 주의

\[\begin{bmatrix} 1 \\ 2\\ 3 \end{bmatrix} * \begin{bmatrix} 2 \\ 3\\ 4 \end{bmatrix} = \begin{bmatrix} 2 \\ 6 \\ 12 \end{bmatrix} \]

x <- 1:3; y <- 2:4
length(x); length(y)
[1] 3
[1] 3
x; y
[1] 1 2 3
[1] 2 3 4
# 사칙연산(+, -, *, /)
# 백터 vs. 백터
x + y
[1] 3 5 7
x - y
[1] -1 -1 -1
x * y
[1]  2  6 12
x / y
[1] 0.5000000 0.6666667 0.7500000
# 그외 연산
# 나머지(remainder)
y %% x
[1] 0 1 1
# 몫(quotient)
y %/% x
[1] 2 1 1
# 멱승(exponent)
y ^ x
[1]  2  9 64

  • 차원이 서로 맞지 않는 경우 작은 차원(짧은 쪽)의 백터를 재사용함

\[\begin{bmatrix} 1 \\ 2\\ 3 \end{bmatrix} + [5] = \begin{bmatrix} 1 \\ 2\\ 3 \end{bmatrix} + \begin{bmatrix} 5 \\ 5\\ 5 \end{bmatrix} = \begin{bmatrix} 6 \\ 7 \\ 8 \end{bmatrix} \]

# 벡터(n by 1) vs. 스칼라(1 by 1)
x * 5 # 5을 x의 길이 만큼 재사용(반복) 후 곱 연산 수행
[1]  5 10 15
x <- c(2, 1, 3, 5, 4); y <- c(2, 3, 4)
x
[1] 2 1 3 5 4
y
[1] 2 3 4
length(x); length(y)
[1] 5
[1] 3
# x의 길이가 5이고 y의 길이가 3이기 때문에 5를 맞추기 위헤
# y의 원소 중 1-2 번째 원소를 재사용
x + y
Warning in x + y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] 4 4 7 7 7
x / y
Warning in x/y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] 1.0000000 0.3333333 0.7500000 2.5000000 1.3333333

  • 연산 순서는 일반적인 사칙연산의 순서를 준용
    • 단 1단위 수열을 생성하는 : 연산자가 사칙연산을 우선함
# 연산 우선 순위
1:5 * 3
[1]  3  6  9 12 15
1:(5 * 3)
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

  • 논리형 값으로 구성된 벡터의 기본 연산 시 수치형으로 변환된 연산 결과를 반환
# 논리형 벡터
b1 <- c(TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE)
b2 <- c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE)

is.numeric(b1); is.numeric(b2)
[1] FALSE
[1] FALSE
is.logical(b1); is.logical(b2)
[1] TRUE
[1] TRUE
# 논리형 벡터 연산
b3 <- b1 + b2
is.numeric(b3)
[1] TRUE
b3
[1] 1 2 1 2 2 2 0 1
b1 - b2
[1]  1  0 -1  0  0  0  0 -1
b1 * b2
[1] 0 1 0 1 1 1 0 0
b1/b2
[1] Inf   1   0   1   1   1 NaN   0

  • 두 벡터 간 비교 연산은 사칙연산과 마찬가지로 각 원소단위 연산을 수행하고 논리형 벡터 반환
    • 재사용 규칙은 그대로 적용됨
# 두 벡터의 비교 연산
x <- c(2, 4, 3, 10, 5, 9)
y <- c(3, 4, 6, 2, 10, 7)

x == y
[1] FALSE  TRUE FALSE FALSE FALSE FALSE
x != y
[1]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE
x > y
[1] FALSE FALSE FALSE  TRUE FALSE  TRUE
x < y
[1]  TRUE FALSE  TRUE FALSE  TRUE FALSE
x >= y
[1] FALSE  TRUE FALSE  TRUE FALSE  TRUE
x <= y
[1]  TRUE  TRUE  TRUE FALSE  TRUE FALSE
# 비교 연산 시 두 벡터의 길이가 다른 경우
x <- 1:5; y <- 2:4

x == y
Warning in x == y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] FALSE FALSE FALSE FALSE FALSE
x != y
Warning in x != y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] TRUE TRUE TRUE TRUE TRUE
x > y
Warning in x > y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] FALSE FALSE FALSE  TRUE  TRUE
x < y
Warning in x < y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1]  TRUE  TRUE  TRUE FALSE FALSE
x >= y
Warning in x >= y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] FALSE FALSE FALSE  TRUE  TRUE
x <= y
Warning in x <= y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1]  TRUE  TRUE  TRUE FALSE FALSE

  • 문자열 벡터의 연산은 == 또는 != 만 가능(사칙연산 불가능)
# 문자열 벡터 연산 (==, !=)
c1 <- letters[1:5]
# a-z로 구성된 벡터에서 1-2, 6-8 번째 원소 추출
c2 <- letters[c(1:2, 6:8)] 
c1
[1] "a" "b" "c" "d" "e"
c2
[1] "a" "b" "f" "g" "h"
c1 == c2
[1]  TRUE  TRUE FALSE FALSE FALSE
c1 != c2
[1] FALSE FALSE  TRUE  TRUE  TRUE

  • NA를 포함한 두 벡터 연산 시 동일 위치에 NA가 존재하면 어떤 연산이든 NA 값을 반환
# 결측을 포함한 벡터
x <- c(1:10, c(NA, NA))
y <- c(NA, NA, 1:10)
x
 [1]  1  2  3  4  5  6  7  8  9 10 NA NA
y
 [1] NA NA  1  2  3  4  5  6  7  8  9 10
is.na(x); is.na(y)
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE
 [1]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
# 결측을 포함한 벡터의 연산 
x + y
 [1] NA NA  4  6  8 10 12 14 16 18 NA NA
x / y
 [1]       NA       NA 3.000000 2.000000 1.666667 1.500000 1.400000 1.333333
 [9] 1.285714 1.250000       NA       NA
x < y
 [1]    NA    NA FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE    NA    NA
x > y
 [1]   NA   NA TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE   NA   NA

  • NULL이 벡터에 포함되더라도 벡터의 길이에는 변동이 없음
# NULL을 포함한 벡터 
x <- c(1, 2, 3, NULL, NULL, NULL) # 길이가 6?
length(x)
[1] 3
x
[1] 1 2 3

2.3.3 벡터의 색인(indexing)

  • 벡터의 특정 위치에 있는 원소를 추출
  • 색인(indexing)을 통해 벡터의 원소에 접근 가능
  • 타 언어는 대체로 첫 번째 색인이 0에서 시작하지만, R은 1부터 시작
  • x[i]: 벡터 xi번 째 요소
  • x[start:end]: xstart부터 end까지 값 반환
x <- c(1.2, 3.1, 4.2, 2.8, 3.3)
x[3] # x 원소 중 3 번째 원소 추출
[1] 4.2
# x 원소 중 2-3번째 원소 추출
x[2:3]
[1] 3.1 4.2

  • x[-i]: 벡터 x에서 i번 째 요소를 제외한 나머지 값 반환
# x의 3 번째 원소 제거
x[-3]
[1] 1.2 3.1 2.8 3.3
# 맨 마지막 원소(5 번째) 제거
# 아래 script는 동일한 결과 출력
x[1:(length(x) - 1)]
[1] 1.2 3.1 4.2 2.8
x[-length(x)]
[1] 1.2 3.1 4.2 2.8

  • x[idx_vec]: idx_vec가 인덱싱 벡터라고 할 때 idx_vec에 지정된 요소를 얻어옴. 일반적으로 idx_vec는 백터의 행 순서 번호 또는 각 벡터 원소의 이름에 대응하는 문자열 벡터를 인덱싱 벡터로 사용할 수 있음.
# 벡터를 이용한 인덱싱
# x 원소 중 1, 5번째 원소 추출
x[c(1, 5)] # c(1,5)는 벡터
[1] 1.2 3.3
v <- c(1, 4)
x[v]
[1] 1.2 2.8
# 인덱스 번호 중복 가능
x[c(1, 2, 2, 4)]
[1] 1.2 3.1 3.1 2.8
# 원소 이름으로 인덱싱
# 원소 이름 지정
names(x) <- paste0("x", 1:length(x)) # 문자열 "x"와 숫자 1:5(벡터 길이)를 결합한 문자열 반환
x["x3"]
 x3 
4.2 
x[c("x2", "x4")]
 x2  x4 
3.1 2.8 

  • 필터링(filtering): 특정한 조건을 만족하는 원소 추출
    • 비교 연산자를 이용한 조건 생성 \(\rightarrow\) 논리값을 이용한 원소 추출
z <- c(5, 2, -3, 8)
# z의 원소 중 z의 제곱이 8보다 큰 원소 추출
w <- z[z^2 > 8]
w
[1]  5 -3  8

  • 작동 원리
    • z^2 > 8은 벡터 z의 모든 원소 제곱값이 8 보다 큰 케이스를 논리형 값으로 반환
z^2
[1] 25  4  9 64
idx <- z^2 > 8
idx
[1]  TRUE FALSE  TRUE  TRUE
z[idx]
[1]  5 -3  8

  • 특정 조건을 만족하는 벡터의 위치에 임의의 값을 치환할 수 있음
# 위 벡터 z 의 원소 중 z^2 > 8 인 원소의 값을 0으로 치환
z[idx] <- 0

2.3.4 벡터 관련 함수

  • c() 함수 외에 R은 벡터 생성을 위해 몇 가지 유용한 함수를 제공함

seq 계열 함수

보다 자세한 사용 설명은 help(seq) 참고

seq(): 등차 수열 생성하는 함수로 from에서 end 까지 숫자 내에서 공차(간격)가 by 인 수열 생성

# seq(): 수열 생성 함수
seq(
  from, # 시작값
  to,   # 끝값
  by    # 공차(증가치)
)

# 기타 인수
# length.out = n
#   - 생성하고자 하는 벡터의 길이가 n인 수열 생성
# along.with = 1:n 
#   - index가 1에서 n 까지 길이를 갖는 수열 생성

  • 사용 예시
x <- seq(from = 2, to = 30, by = 2)
x 
 [1]  2  4  6  8 10 12 14 16 18 20 22 24 26 28 30
# 간격이 꼭 정수가 아니어도 사용 가능
x <- seq(from = 0, to = 3, by = 0.2)

# by 대신 length.out 으로 생성된 수열의 길이 조정
x <- seq(from = -3, to = 3, length.out = 10)
x
 [1] -3.0000000 -2.3333333 -1.6666667 -1.0000000 -0.3333333  0.3333333
 [7]  1.0000000  1.6666667  2.3333333  3.0000000
# from, to 인수 없이 length.out=10 인 경우
seq(length.out = 10)
 [1]  1  2  3  4  5  6  7  8  9 10
# by 대신 along.width 
seq(along.with=1:10)
 [1]  1  2  3  4  5  6  7  8  9 10
seq(1, 5, along.with=1:10)
 [1] 1.000000 1.444444 1.888889 2.333333 2.777778 3.222222 3.666667 4.111111
 [9] 4.555556 5.000000
# 벡터 x에 seq() 함수 적용 시 1:length(x) 값 반환
seq(x)
 [1]  1  2  3  4  5  6  7  8  9 10

seq_along(): 주어진 객체의 길이 만큼 1부터 1 간격의 수열 생성

  • seq() 함수와 매우 유사하나, 무조건 1부터 시작해서 인수로 seq()along.with 값을 이용한 함수
  • seq() 함수보다 조금 빠름
  • 사용 예시
# 1부터 x 벡터의 길이 까지 1 단위 수열 값 반환
seq_along(x)
 [1]  1  2  3  4  5  6  7  8  9 10

seq_len(): 인수로 받은 값 만큼 1부터 해당 값 까지 1 간격의 수열 생성

  • seq() 함수의 인수 중 length.out 값을 이용한 함수
  • 사용 예시
# 1부터 n 까지 1 단위 수열 값 반환
seq_len(10)
 [1]  1  2  3  4  5  6  7  8  9 10

rep 계열 함수

help(rep)을 통해 상세 내용 참고

rep(): 주어진 벡터의 원소를 반복

# rep(): 벡터 또는 벡터의 개별 원소를 반복한 값 반환
rep(
  x, # 반복할 값이 저장된 벡터
  times, # 전체 벡터의 반복 횟수
  each # 개별 원소의 반복 횟수
)

  • 사용 예시
x <- rep(4, 5) # 4를 5번 반복
x
[1] 4 4 4 4 4
# x <- c(1:3) 전체를 3번 반복한 벡터 반환
x <- c(1:3)
xr1 <- rep(x, times = 3)
xr1
[1] 1 2 3 1 2 3 1 2 3
# 벡터 x 의 각 원소를 4번씩 반복한 벡터 반환
xr2 <- rep(x, each = 4)
xr2
 [1] 1 1 1 1 2 2 2 2 3 3 3 3
# 벡터 x 의 각 원소를 3번 반복하고 해당 벡터를 2회 반복
xr3 <- rep(x, each = 3, times = 2)
xr3
 [1] 1 1 1 2 2 2 3 3 3 1 1 1 2 2 2 3 3 3
# 문자형 벡터의 반복
# 아래 sex 벡터의 각 원소를 2 번 반복하고 해당 벡터를 4회 반복
sex <- c("Male", "Female")
sexr <- rep(sex, each = 2, times = 4)
sexr
 [1] "Male"   "Male"   "Female" "Female" "Male"   "Male"   "Female" "Female"
 [9] "Male"   "Male"   "Female" "Female" "Male"   "Male"   "Female" "Female"

rep.int() & rep_len(): rep() 함수의 simple 버전으로 속도(performance)가 요구되는 프로그래밍 시 사용

  • 사용 예시
# 1:5 벡터를 3 번 반복
rep.int(1:5, 3)
 [1] 1 2 3 4 5 1 2 3 4 5 1 2 3 4 5
# 불완전한 사이클로 벡터 반복
rep_len(1:5, length.out = 7)
[1] 1 2 3 4 5 1 2

Filtering 관련 함수

help(subset) 참고

subset(): 기존 필터링 방식과 비교할 때 NA를 처리하는 방식에서 차이를 보임

  • 벡터 뿐 아니라 앞으로 배울 행렬 및 데이터프레임 객체에도 적용 가능
x <- c(6, 1:3, NA, NA, 12)
x
[1]  6  1  2  3 NA NA 12
# 일반적 필터링 적용 
x[x > 5]
[1]  6 NA NA 12
# subset() 함수 적용
subset(x, x > 5)
[1]  6 12

which(): 한 벡터에서 특정 조건에 맞는 위치(인덱스)를 반환

# which(): 논리형 벡터를 인수로 받고 해당 논리형 벡터가 참인 index 반환
which(
  logical_vec # 논리형 벡터
)

  • 사용 예시
x <- c(3, 8, 3, 1, 7)

# x의 원소값이 3인 index 반환
which(x == 3)
[1] 1 3
# x의 원소가 4보다 큰 원소의 index 반환
which(x > 4)
[1] 2 5
# 9월(Sep)과 12월(Dec)와 같은 원소 index
# month.abb: R 내장 벡터로 월 약어(Jan ~ Dec)를 저장한 문자열 벡터
which(month.abb == c("Sep", "Dec"))
[1]  9 12
# 조건을 만족하는 원소가 존재하지 않는다면?
x <- which(x > 9)
x
integer(0)
length(x) # 길이가 0인 벡터 반환 is.null(x) == TRUE ??
[1] 0
is.null(x)
[1] FALSE
# 특정 조건 만족 여부를 확인 
# any(condition) -> 하나라도 condition을 만족하는 원소가 존재하는지 판단
# TRUE 또는 FALSE 값 반환
any(x > 9)
[1] FALSE

집합 관련 함수

  • 벡터는 숫자, 문자열의 묶음, 즉 원소들의 집합(set)으로 볼 수 있기 때문에 집합 연산이 가능
  • 두 집합을 \(X\)\(Y\)로 정의 했을 때 아래와 같은 집합 연산 가능
  • setequal(X, Y): XY가 동일한지 판단 (\(X = Y\)) \(\rightarrow\) 논리값 TRUE 또는 FALSE 반환
x <- y <- c(1, 9, 7, 3, 6)
setequal(x, y)
[1] TRUE

  • union(X, Y): XY의 합집합 (\(X \cup Y\))
y <- c(1, 9, 8, 2, 0, 3)
union(x, y)
[1] 1 9 7 3 6 8 2 0

  • intersect(X, Y): XY의 교집합 (\(X \cap Y\))
intersect(x, y)
[1] 1 9 3

  • setdiff(X, Y): XY의 차집합 (\(X - Y\))
setdiff(x, y)
[1] 7 6
setdiff(y, x)
[1] 8 2 0

  • X %in% Y: X(기준)가 집합 Y의 원소인지 논리값 반환
x <- c("apple", "banana", "strawberry", "mango", "peach", "orange")
y <- c("strawberry", "orange", "mango")

x %in% y
[1] FALSE FALSE  TRUE  TRUE FALSE  TRUE
y %in% x
[1] TRUE TRUE TRUE

두 벡터의 동일성 테스트

  • 두 벡터가 동일한지 테스트 하기 위해 x == y 연산의 반환 값은 위의 예제에서 확인한 것 처럼 각 원소에 대한 논리값을 반환(아래 예제 확인)
x <- 1:3
y <- c(1, 3, 4)
x == y
[1]  TRUE FALSE FALSE

  • 단지 두 벡터가 동일한지 아닌지를 확인하기 위해서는 하나의 논리값만 필요한 경우 all() 사용
all(x == y)
[1] FALSE

  • 보다 나은 방법으로 identical() 함수 적용
# 두 객체의 동일성 여부 테스트
identical(x, y)
[1] FALSE

  • identical() 함수는 벡터가 갖는 데이터 타입의 동일성 까지 체크함
x <- 1:5; y <- c(1, 2, 3, 4, 5)
x
[1] 1 2 3 4 5
y
[1] 1 2 3 4 5
# all() 함수로 동일성 확인
all(x == y)
[1] TRUE
# identical 함수로 동일성 확인
identical(x, y)
[1] FALSE
# x, y 데이터 타입 확인
typeof(x)
[1] "integer"
typeof(y)
[1] "double"