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()
함수를 사용해 생성
# 숫자형 벡터
<- c(2, 0, 2, 0, 0, 3, 2, 4)
x x
[1] 2 0 2 0 0 3 2 4
# 문자형 벡터
<- c("Boncho Ku", "R programming", "Male", "sophomore", "2020-03-24")
y y
[1] "Boncho Ku" "R programming" "Male" "sophomore"
[5] "2020-03-24"
- 두 개 이상의 벡터는
c()
함수를 통해 결합 가능- 함수 내
,
구분자를 통해 결합
- 함수 내
# 두 벡터의 결합 (1)
<- 1:5
x <- 10:6
y <- c(x, y)
z 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
<- 5:10
x <- x[1:3] # x 벡터에서 1에서 4번째 원소 추출
x1 <- c(x1, 15, x[4])
x2 x2
[1] 5 6 7 15 8
- 서로 다른 자료형으로 벡터를 구성한 경우 표현력이 높은 자료형으로
변환한 값 반환
- 예: 문자열 + 숫자로 구성된 벡터 \(\rightarrow\) 문자형 벡터
- 변환규칙:
NULL < raw < logical < integer < double < complex < character < list < expression
# 숫자형 벡터와 문자열 벡터 혼용
<- c(1, 2, "3", "4")
k k
[1] "1" "2" "3" "4"
is.numeric(k) # 벡터가 숫자형인지 판단하는 함수
[1] FALSE
is.character(k) # 벡터가 문자열인지 판단하는 함수
[1] TRUE
# 숫자형 벡터와 문자열 벡터 결합
<- 1:3
x <- c("a", "b", "c")
y <- c(x, y)
z z
[1] "1" "2" "3" "a" "b" "c"
is.numeric(z)
[1] FALSE
is.character(z)
[1] TRUE
# 숫자형 벡터와 논리형 벡터 결합
<- 9:4
x <- c(TRUE, TRUE, FALSE)
y <- c(x, y)
z
# TRUE/FALSE 가 1과 0으로 변환 z
[1] 9 8 7 6 5 4 1 1 0
is.numeric(z)
[1] TRUE
is.logical(z)
[1] FALSE
- 두 벡터는 중첩이 불가능 \(\rightarrow\) 동일한 벡터 2개를 결합 시 단일 차원 벡터 생성
<- y <- 1:3 # x와 y 동시에 [1, 2, 3] 할당
x x
[1] 1 2 3
y
[1] 1 2 3
<- c(x, y)
z z
[1] 1 2 3 1 2 3
- 벡터 각 원소에 이름 부여 가능
names()
함수를 이용해 원소 이름 지정- 사용 프로토타입:
names(x) <- 문자열 벡터
, 단x
와 이름에 입력할 문자열 벡터의 길이는 같아야 함. c()
함수에서 직접 이름 지정 \(\rightarrow\)c(atom_name1 = value, atom_name2 = value, ...)
<- c("Boncho Ku", "R programming", "Male", "sophomore", "2020-03-24")
x
# 벡터 원소 이름 지정
names(x) <- c("name", "course", "gender", "grade", "date")
x
name course gender grade date
"Boncho Ku" "R programming" "Male" "sophomore" "2020-03-24"
<- c(a = 10, b = 6, c = 9)
y names(y)
[1] "a" "b" "c"
- 벡터의 길이(차원) 확인
length()
또는NROW()
사용
<- 1:50
x # 객체의 길이 반환
# 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} \]
<- 1:3; y <- 2:4
x length(x); length(y)
[1] 3
[1] 3
x; y
[1] 1 2 3
[1] 2 3 4
# 사칙연산(+, -, *, /)
# 백터 vs. 백터
+ y x
[1] 3 5 7
- y x
[1] -1 -1 -1
* y x
[1] 2 6 12
/ y x
[1] 0.5000000 0.6666667 0.7500000
# 그외 연산
# 나머지(remainder)
%% x y
[1] 0 1 1
# 몫(quotient)
%/% x y
[1] 2 1 1
# 멱승(exponent)
^ x y
[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)
* 5 # 5을 x의 길이 만큼 재사용(반복) 후 곱 연산 수행 x
[1] 5 10 15
<- c(2, 1, 3, 5, 4); y <- c(2, 3, 4)
x 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 번째 원소를 재사용
+ y x
Warning in x + y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] 4 4 7 7 7
/ y x
Warning in x/y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] 1.0000000 0.3333333 0.7500000 2.5000000 1.3333333
- 연산 순서는 일반적인 사칙연산의 순서를 준용
- 단 1단위 수열을 생성하는
:
연산자가 사칙연산을 우선함
- 단 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
- 논리형 값으로 구성된 벡터의 기본 연산 시 수치형으로 변환된 연산 결과를 반환
# 논리형 벡터
<- c(TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE)
b1 <- c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE)
b2
is.numeric(b1); is.numeric(b2)
[1] FALSE
[1] FALSE
is.logical(b1); is.logical(b2)
[1] TRUE
[1] TRUE
# 논리형 벡터 연산
<- b1 + b2
b3 is.numeric(b3)
[1] TRUE
b3
[1] 1 2 1 2 2 2 0 1
- b2 b1
[1] 1 0 -1 0 0 0 0 -1
* b2 b1
[1] 0 1 0 1 1 1 0 0
/b2 b1
[1] Inf 1 0 1 1 1 NaN 0
- 두 벡터 간 비교 연산은 사칙연산과 마찬가지로 각 원소단위 연산을
수행하고 논리형 벡터 반환
- 재사용 규칙은 그대로 적용됨
# 두 벡터의 비교 연산
<- c(2, 4, 3, 10, 5, 9)
x <- c(3, 4, 6, 2, 10, 7)
y
== y x
[1] FALSE TRUE FALSE FALSE FALSE FALSE
!= y x
[1] TRUE FALSE TRUE TRUE TRUE TRUE
> y x
[1] FALSE FALSE FALSE TRUE FALSE TRUE
< y x
[1] TRUE FALSE TRUE FALSE TRUE FALSE
>= y x
[1] FALSE TRUE FALSE TRUE FALSE TRUE
<= y x
[1] TRUE TRUE TRUE FALSE TRUE FALSE
# 비교 연산 시 두 벡터의 길이가 다른 경우
<- 1:5; y <- 2:4
x
== y x
Warning in x == y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] FALSE FALSE FALSE FALSE FALSE
!= y x
Warning in x != y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] TRUE TRUE TRUE TRUE TRUE
> y x
Warning in x > y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] FALSE FALSE FALSE TRUE TRUE
< y x
Warning in x < y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] TRUE TRUE TRUE FALSE FALSE
>= y x
Warning in x >= y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] FALSE FALSE FALSE TRUE TRUE
<= y x
Warning in x <= y: 두 객체의 길이가 서로 배수관계에 있지 않습니다
[1] TRUE TRUE TRUE FALSE FALSE
- 문자열 벡터의 연산은
==
또는!=
만 가능(사칙연산 불가능)
# 문자열 벡터 연산 (==, !=)
<- letters[1:5]
c1 # a-z로 구성된 벡터에서 1-2, 6-8 번째 원소 추출
<- letters[c(1:2, 6:8)]
c2 c1
[1] "a" "b" "c" "d" "e"
c2
[1] "a" "b" "f" "g" "h"
== c2 c1
[1] TRUE TRUE FALSE FALSE FALSE
!= c2 c1
[1] FALSE FALSE TRUE TRUE TRUE
NA
를 포함한 두 벡터 연산 시 동일 위치에NA
가 존재하면 어떤 연산이든NA
값을 반환
# 결측을 포함한 벡터
<- c(1:10, c(NA, NA))
x <- c(NA, NA, 1:10)
y 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
# 결측을 포함한 벡터의 연산
+ y x
[1] NA NA 4 6 8 10 12 14 16 18 NA NA
/ y x
[1] NA NA 3.000000 2.000000 1.666667 1.500000 1.400000 1.333333
[9] 1.285714 1.250000 NA NA
< y x
[1] NA NA FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE NA NA
> y x
[1] NA NA TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE NA NA
NULL
이 벡터에 포함되더라도 벡터의 길이에는 변동이 없음
# NULL을 포함한 벡터
<- c(1, 2, 3, NULL, NULL, NULL) # 길이가 6?
x length(x)
[1] 3
x
[1] 1 2 3
2.3.3 벡터의 색인(indexing)
- 벡터의 특정 위치에 있는 원소를 추출
- 색인(indexing)을 통해 벡터의 원소에 접근 가능
- 타 언어는 대체로 첫 번째 색인이 0에서 시작하지만, R은 1부터 시작
x[i]
: 벡터x
의i
번 째 요소x[start:end]
:x
의start
부터end
까지 값 반환
<- c(1.2, 3.1, 4.2, 2.8, 3.3)
x 3] # x 원소 중 3 번째 원소 추출 x[
[1] 4.2
# x 원소 중 2-3번째 원소 추출
2:3] x[
[1] 3.1 4.2
x[-i]
: 벡터x
에서i
번 째 요소를 제외한 나머지 값 반환
# x의 3 번째 원소 제거
-3] x[
[1] 1.2 3.1 2.8 3.3
# 맨 마지막 원소(5 번째) 제거
# 아래 script는 동일한 결과 출력
1:(length(x) - 1)] x[
[1] 1.2 3.1 4.2 2.8
-length(x)] x[
[1] 1.2 3.1 4.2 2.8
x[idx_vec]
:idx_vec
가 인덱싱 벡터라고 할 때idx_vec
에 지정된 요소를 얻어옴. 일반적으로idx_vec
는 백터의 행 순서 번호 또는 각 벡터 원소의 이름에 대응하는 문자열 벡터를 인덱싱 벡터로 사용할 수 있음.
# 벡터를 이용한 인덱싱
# x 원소 중 1, 5번째 원소 추출
c(1, 5)] # c(1,5)는 벡터 x[
[1] 1.2 3.3
<- c(1, 4)
v x[v]
[1] 1.2 2.8
# 인덱스 번호 중복 가능
c(1, 2, 2, 4)] x[
[1] 1.2 3.1 3.1 2.8
# 원소 이름으로 인덱싱
# 원소 이름 지정
names(x) <- paste0("x", 1:length(x)) # 문자열 "x"와 숫자 1:5(벡터 길이)를 결합한 문자열 반환
"x3"] x[
x3
4.2
c("x2", "x4")] x[
x2 x4
3.1 2.8
- 필터링(filtering): 특정한 조건을 만족하는 원소 추출
- 비교 연산자를 이용한 조건 생성 \(\rightarrow\) 논리값을 이용한 원소 추출
<- c(5, 2, -3, 8)
z # z의 원소 중 z의 제곱이 8보다 큰 원소 추출
<- z[z^2 > 8]
w w
[1] 5 -3 8
- 작동 원리
z^2 > 8
은 벡터z
의 모든 원소 제곱값이 8 보다 큰 케이스를 논리형 값으로 반환
^2 z
[1] 25 4 9 64
<- z^2 > 8
idx idx
[1] TRUE FALSE TRUE TRUE
z[idx]
[1] 5 -3 8
- 특정 조건을 만족하는 벡터의 위치에 임의의 값을 치환할 수 있음
# 위 벡터 z 의 원소 중 z^2 > 8 인 원소의 값을 0으로 치환
<- 0 z[idx]
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 까지 길이를 갖는 수열 생성
- 사용 예시
<- seq(from = 2, to = 30, by = 2)
x x
[1] 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
# 간격이 꼭 정수가 아니어도 사용 가능
<- seq(from = 0, to = 3, by = 0.2)
x
# by 대신 length.out 으로 생성된 수열의 길이 조정
<- seq(from = -3, to = 3, length.out = 10)
x 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 )
- 사용 예시
<- rep(4, 5) # 4를 5번 반복
x x
[1] 4 4 4 4 4
# x <- c(1:3) 전체를 3번 반복한 벡터 반환
<- c(1:3)
x <- rep(x, times = 3)
xr1 xr1
[1] 1 2 3 1 2 3 1 2 3
# 벡터 x 의 각 원소를 4번씩 반복한 벡터 반환
<- rep(x, each = 4)
xr2 xr2
[1] 1 1 1 1 2 2 2 2 3 3 3 3
# 벡터 x 의 각 원소를 3번 반복하고 해당 벡터를 2회 반복
<- rep(x, each = 3, times = 2)
xr3 xr3
[1] 1 1 1 2 2 2 3 3 3 1 1 1 2 2 2 3 3 3
# 문자형 벡터의 반복
# 아래 sex 벡터의 각 원소를 2 번 반복하고 해당 벡터를 4회 반복
<- c("Male", "Female")
sex <- rep(sex, each = 2, times = 4)
sexr 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
를 처리하는 방식에서
차이를 보임
- 벡터 뿐 아니라 앞으로 배울 행렬 및 데이터프레임 객체에도 적용 가능
<- c(6, 1:3, NA, NA, 12)
x x
[1] 6 1 2 3 NA NA 12
# 일반적 필터링 적용
> 5] x[x
[1] 6 NA NA 12
# subset() 함수 적용
subset(x, x > 5)
[1] 6 12
which()
: 한 벡터에서 특정 조건에 맞는 위치(인덱스)를 반환
# which(): 논리형 벡터를 인수로 받고 해당 논리형 벡터가 참인 index 반환
which(
# 논리형 벡터
logical_vec )
- 사용 예시
<- c(3, 8, 3, 1, 7)
x
# 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
# 조건을 만족하는 원소가 존재하지 않는다면?
<- which(x > 9)
x 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)
:X
와Y
가 동일한지 판단 (\(X = Y\)) \(\rightarrow\) 논리값TRUE
또는FALSE
반환
<- y <- c(1, 9, 7, 3, 6)
x setequal(x, y)
[1] TRUE
union(X, Y)
:X
와Y
의 합집합 (\(X \cup Y\))
<- c(1, 9, 8, 2, 0, 3)
y union(x, y)
[1] 1 9 7 3 6 8 2 0
intersect(X, Y)
:X
와Y
의 교집합 (\(X \cap Y\))
intersect(x, y)
[1] 1 9 3
setdiff(X, Y)
:X
와Y
의 차집합 (\(X - Y\))
setdiff(x, y)
[1] 7 6
setdiff(y, x)
[1] 8 2 0
X %in% Y
:X
(기준)가 집합Y
의 원소인지 논리값 반환
<- c("apple", "banana", "strawberry", "mango", "peach", "orange")
x <- c("strawberry", "orange", "mango")
y
%in% y x
[1] FALSE FALSE TRUE TRUE FALSE TRUE
%in% x y
[1] TRUE TRUE TRUE
두 벡터의 동일성 테스트
- 두 벡터가 동일한지 테스트 하기 위해
x == y
연산의 반환 값은 위의 예제에서 확인한 것 처럼 각 원소에 대한 논리값을 반환(아래 예제 확인)
<- 1:3
x <- c(1, 3, 4)
y == y x
[1] TRUE FALSE FALSE
- 단지 두 벡터가 동일한지 아닌지를 확인하기 위해서는 하나의 논리값만
필요한 경우
all()
사용
all(x == y)
[1] FALSE
- 보다 나은 방법으로
identical()
함수 적용
# 두 객체의 동일성 여부 테스트
identical(x, y)
[1] FALSE
identical()
함수는 벡터가 갖는 데이터 타입의 동일성 까지 체크함
<- 1:5; y <- c(1, 2, 3, 4, 5)
x 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"