재현가능한 과학적 분석을 위한 R
데이터 부분집합
학습 목표
- 벡터, 요인, 행렬, 리스트, 데이터프레임 부분집합을 뽑아낼 수 있다.
- 개별, 다수 요소를 다음 기준으로 뽑아낼 수 있다:
- 색인
- 명칭
- 비교 연산을 사용
- 다양한 자료구조로부터 요소를 건너뛰거나 제거할 수 있다.
R에는 강력한 부분집합 연산자를 다수 구비되어 있다. 이를 완전히 익히게 되면 어떤 유형의 데이터셋에 대해서도 복잡한 연산을 수월하게 수행할 수 있게 된다.
어떤 유형의 객체에서 부분집합을 뽑아낼 수 있는 방식은 6가지가 있다. 다른 자료구조에 대한 부분집합을 뽑아내는 연산자는 3가지가 있다.
R의 핵심으로 가장 많은 일은 하는 것부터 시작해본다: 원자 벡터(atomic vector)
x <- c(5.4, 6.2, 7.1, 4.8, 7.5)
names(x) <- c('a', 'b', 'c', 'd', 'e')
x a b c d e
5.4 6.2 7.1 4.8 7.5
이제 작업할 준비가 마루타 벡터를 생성했다. 해당 벡터 내용물을 손에 넣는 방식은 무엇인가?
색인을 사용한 요소 접근
벡터 요소를 추출하는데, 대응되는 색인을 부여하는데, 1부터 시작된다:
x[1] a
5.4
x[4] d
4.8
꺾쇠 괄호 연산자는 다른 어떤 함수와 비슷한다. 원자 벡터(그리과 행렬)에 대해, “n번째 요소를 뽑아낸다”라는 의미다.
한번에 다수 요소를 뽑아낼 수도 있다:
x[c(1, 3)] a c
5.4 7.1
혹은, 벡터 슬라이스로 뽑아낼 수도 있다:
x[1:4] a b c d
5.4 6.2 7.1 4.8
: 연산자는 왼쪽 요소부터 우측 요소까지 연속된 숫자를 생성한다. 예를 들어, x[1:4] 은 x[c(1,2,3,4)]와 동등하다:
x[c(1,1,3)] a a c
5.4 5.4 7.1
벡터를 벗어난 숫자를 뽑아내려고 하면, R은 결측값을 반환한다:
x[6]<NA>
NA
길이 1을 갖는 벡터로 NA가 담겨있고, 명칭도 NA다.
0번째 요소를 뽑아내려고 하면, 공벡터가 반환된다:
x[0]named numeric(0)
요소 건너뛰고 제거하기
벡터 색인으로 음수를 사용하면, R은 명세된 숫자를 제외한 모든 요소를 반환한다:
x[-2] a c d e
5.4 7.1 4.8 7.5
다수 요소를 건너뛸 수도 있다:
x[c(-1, -5)] # 혹은 x[-c(1,5)] b c d
6.2 7.1 4.8
벡터에서 요소를 제거하려면, 결과를 다시 벡터에 대입할 필요가 있다:
x <- x[-4]
x a b c e
5.4 6.2 7.1 7.5
도전 과제 1
다음과 같이 코드가 주어졌다:
x <- c(5.4, 6.2, 7.1, 4.8, 7.5)
names(x) <- c('a', 'b', 'c', 'd', 'e')
print(x) a b c d e
5.4 6.2 7.1 4.8 7.5
- 다음 출력결과를 산출하는 적어도 서로 다른 명령어 3개 제시한다:
b c d
6.2 7.1 4.8
- 작업결과를 옆 사람과 비교한다. 서로 다른 전략을 취했나요?
명칭으로 부분집합 뽑아내기
색인 대신에 명칭을 사용해서, 요소를 뽑아낼 수 있다:
x[c("a", "c")] a c
5.4 7.1
명칭을 사용한 것이 객체에 대한 부분집합을 뽑아내는 훨씬 더 신뢰성 있는 방식이다: 다양한 요소 위치는 부분집합을 뽑아내는 연산자를 연결해서 적용할 때 종종 변경되지만, 명칭은 항상 동일하게 남게 마련이다!
불행하게도, 그다지 수월하게 요소를 건너뛰거나 제거할 수는 없다.
요소 하나를 건너뛰거나 제거하려면:
x[-which(names(x) == "a")] b c d e
6.2 7.1 4.8 7.5
which 함수는 함수 인자의 모든 TRUE 요소에 대한 색인을 반환한다. 함수에 전달되기 전에 표현식이 평가됨을 기억한다. 내부를 파고들어, 어떤 일이 진행되는지 명확히 알아보자.
다음이 가장 먼저 진행된다:
names(x) == "a"[1] TRUE FALSE FALSE FALSE FALSE
조건 연산자는 x 벡터에 대한 모든 명칭에 적용된다. 첫번째 명칭만 “a” 라서, 해당 요소만 참(TRUE)이 된다.
그리고 나면, which가 이를 색인으로 변환한다:
which(names(x) == "a")[1] 1
첫번째 요소만 참(TRUE)이라서, which는 1을 반환한다. 이제 색인을 갖게 되서, 건너뛰는 연산이 동작한다. 왜냐하면 음수 색인이기 때문이다!
명칭을 갖는 다수 색인을 건너뛰는 것도 유사하다. 하지만, 다른 비교 연산자를 사용한다:
x[-which(names(x) %in% c("a", "c"))] b d e
6.2 4.8 7.5
%in% 비교연산자는 좌측 인자(이번 경우에, x 명칭)에 대한 각 요소를 훑는다. 그리고 나서, “해당 요소가 두번째 인자에 나타나는가?”라고 질의한다.
도전과제 2
다음 코드를 실행해서 x 벡터를 정의한다.
x <- c(5.4, 6.2, 7.1, 4.8, 7.5)
names(x) <- c('a', 'b', 'c', 'd', 'e')
print(x) a b c d e
5.4 6.2 7.1 4.8 7.5
x 벡터가 주어지면, 다음 명령어는 어떤 작업을 수행할 것으로 예상되는가?
x[-which(names(x) == "g")]상기 명령어를 시도해보고, 실행결과를 살펴본다. 여러분의 예상과 일치하는가? 왜 이런 결과가 나왔을까? (Tip: 여러분이 직접 작성한 것처럼 한땀한땀 명령어 각각을 테스트한다 - 매우 유용한 디버깅 전략이다.)
다음 중 어떤 것이 사실인가:
which에 전달되는TRUE값이 없다면, 공벡터가 반환된다.
which에 전달되는TRUE값이 없다면, 오류 메시지가 나타난다.
integer()는 공벡터다.
- 공벡터를 부정하면 “모든” 벡터를 만들어낸다.
x[]은x[integer()]와 동일한 결과를 산출한다.
x <- 1:3 x[1] 1 2 3names(x) <- c('a', 'a', 'a') xa a a 1 2 3x['a'] # 첫번째 값만 반환한다.a 1x[which(names(x) == 'a')] # 세가지 값 모두 반환한다.a a a 1 2 3
그러면, 이전처럼 == 연산자는 왜 사용할 수 없을까? 매우 좋은 질문이다.
비교에 해당되는 항목만 살펴보자:
names(x) == c('a', 'c')Warning in names(x) == c("a", "c"): 두 객체의 길이가 서로 배수관계에 있지
않습니다
[1] TRUE FALSE TRUE
분명히, “c”는 x 요소명칭 중에 존재한다. 그런데, 왜 동작을 하지 않을까? ==은 %in% 과는 다소 다른방식으로 동작한다. ==은 좌측 인자 요소 각각을 대응되는 우측 요소 각각과 비교한다.
== 연산자를 모사한 것이 다음에 나와 있다:
c("a", "b", "c", "e") # x 명칭
| | | | # ==으로 요소들을 비교한다.
c("a", "c")한 벡터가 다른 벡터보다 작은 경우, 해당 벡터는 재사용된다:
c("a", "b", "c", "e") # x 명칭
| | | | # ==으로 요소들을 비교한다.
c("a", "c", "a", "c")이런 경우, R이 단순히 c("a", "c")을 두번 반복한다. 더 긴 벡터가 더 짧은 벡터의 배수가 아닌 경우, R은 경고 메시지도 출력한다:
names(x) == c('a', 'c', 'e')[1] TRUE FALSE FALSE
== 와 %in% 차이점을 숙지하는 것이 중요한데, 이유는 탐지가 어렵고 미묘한 버그가 스며들 수 있기 때문이다!
논리 연산자를 통한 부분집합 뽑아내기
더 단순하게는 논리 연산자로 부분집합을 뽑아낼 수도 있다:
x[c(TRUE, TRUE, FALSE, FALSE)]a a
1 2
이번 경우, 논리 벡터는 부분집합을 뽑아내는 벡터 길이만큼 재사용됨에 주목한다!
x[c(TRUE, FALSE)]a a
1 3
비교 연산자는 논리벡터로 평가되기 때문에, 간결하게 벡터 부분집합을 뽑아내는데 사용할 수도 있다:
x[x > 7]named integer(0)
도전과제 3
다음 코드가 주어졌다:
x <- c(5.4, 6.2, 7.1, 4.8, 7.5)
names(x) <- c('a', 'b', 'c', 'd', 'e')
print(x) a b c d e
5.4 6.2 7.1 4.8 7.5
x 벡터에서 4보다 크고 7보다 적은 값을 부분집합으로 뽑아내는 명령어를 작성한다.
특수값 처리하기
어느 지점에 다다르면, R 함수에 처리할 수 없는 결측값, 무한값, 정의되지 않는 값을 갖는 데이터와 마주하게 된다.
이런 유형의 데이터를 필터링하는데 사용되는 특수 함수가 있다:
is.na는 벡터, 행렬, 데이터프레임에 포함된NA위치를 반환한다.- 마찬가지로,
is.nan와is.infinite함수도NaN와Inf값에 대한 동일한 작업을 수행한다. is.finite함수는NA,NaN,Inf값을 포함하지 않는 벡터, 행렬, 데이터프레임에 대한 모든 위치정보를 반환한다.na.omit는 벡터에서 모든 결측값을 필터링해서 제외시키다.
요인 부분집합으로 뽑아내기
지금까지 벡터 부분집합을 뽑아내는 다양한 방식을 탐색했다. 다른 자료구조에 대한 부분집합은 어떻게 뽑아낼 수 있을까?
요인 부분집합 뽑아내기는 벡터 부분집합 뽑아내기와 동일한 방식으로 동작한다.
f <- factor(c("a", "a", "b", "c", "c", "d"))
f[f == "a"][1] a a
Levels: a b c d
f[f %in% c("b", "c")][1] b c c
Levels: a b c d
f[1:3][1] a a b
Levels: a b c d
중요한 주의점 하나는 건너뛰는 요소가 설사 해당 범주가 요인으로 존재하지 않더라도, 수준(level)을 제거하지 않는다는 점이다:
f[-3][1] a a c c d
Levels: a b c d
행렬 부분집합 뽑아내기
행렬의 경우도 [ 함수를 사용해서 부분집합을 뽑아낸다. 이번 경우에는 인자를 두개 사용한다: 첫번째 인자는 행에 적용되고, 두번째 인자는 칼럼에 적용된다:
set.seed(1)
m <- matrix(rnorm(6*4), ncol=4, nrow=6)
m[3:4, c(3,1)] [,1] [,2]
[1,] 1.12493092 -0.8356286
[2,] -0.04493361 1.5952808
첫번째 혹은 두번째 인자를 공백으로 남겨놓을 수도 있는데, 모든 행 혹은 칼럼을 각각 불러올 경우 사용한다:
m[, c(3,4)] [,1] [,2]
[1,] -0.62124058 0.82122120
[2,] -2.21469989 0.59390132
[3,] 1.12493092 0.91897737
[4,] -0.04493361 0.78213630
[5,] -0.01619026 0.07456498
[6,] 0.94383621 -1.98935170
행 혹은 칼럼 하나만 접근하고자 하면, R이 자동으로 결과값을 벡터로 전환시킨다:
m[3,][1] -0.8356286 0.5757814 1.1249309 0.9189774
결과값을 행렬로 그대로 유지하고자 한다면, 세번째 인자를 명세할 필요가 있다; drop = FALSE:
m[3, , drop=FALSE] [,1] [,2] [,3] [,4]
[1,] -0.8356286 0.5757814 1.124931 0.9189774
벡터와 달리, 행렬 외부 행과 칼럼을 접근하고자 하면, R이 오류를 던진다:
m[, c(3,6)]Error in m[, c(3, 6)]: 첨자의 허용 범위를 벗어났습니다
행렬을 까면 정말 자료형이 벡터라서, 단지 인자 하나로만 부분집합을 추출할 수도 있다:
m[5][1] 0.3295078
보통 유용하지는 않다. 하지만, 행렬이 열우선형식(column-major format)으로 기본디폴트 설정으로 되어있음에 주목한다. 즉, 벡터 요소가 칼럼방향으로 배열된다는 것을 의미한다:
matrix(1:6, nrow=2, ncol=3) [,1] [,2] [,3]
[1,] 1 3 5
[2,] 2 4 6
행렬을 행우선으로 쭉 펼치고자 한다면, byrow=TRUE를 사용한다:
matrix(1:6, nrow=2, ncol=3, byrow=TRUE) [,1] [,2] [,3]
[1,] 1 2 3
[2,] 4 5 6
행과 칼럼 색인 대신에 행명칭(rownames)과 열명칭(column names)을 사용해서 배열 부분집합을 뽑아낼 수 있다.
도전과제 4
다음과 같은 코드가 주어졌다:
m <- matrix(1:18, nrow=3, ncol=6)
print(m) [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 4 7 10 13 16
[2,] 2 5 8 11 14 17
[3,] 3 6 9 12 15 18
- 다음 중 어떤 명령어가 값 11과 14를 추출하는 하는가?
A. m[2,4,2,5]
B. m[2:5]
C. m[4:5,2]
D. m[2,c(4,5)]
리스트 부분집합 뽑아내기
이제 몇가지 새로운 부분집합을 뽑아내는 연산자를 소개한다. 리스트 부분집합을 뽑아내는데 사용되는 함수가 세가지 있다; 원자벡터와 행렬에서 살펴본 [, 그리고 [[, $이 있다.
[을 사용하면, 항상 리스트만 반환한다. 리스트 부분집합을 뽑아내고자 하지만, 요소는 뽑아내고 싶지 않다면, 아마도 [ 연산자를 사용할 것이다.
xlist <- list(a = "Software Carpentry", b = 1:10, data = head(iris))
xlist[1]$a
[1] "Software Carpentry"
상기 명령어는 요소 하나만 갖는 리스트를 반환한다.
[ 연산자를 사용해서 원자벡터에 적용한 그대로 리스트 요소를 부분집합으로 뽑아낼 수 있다. 하지만, 리스트가 재귀적으로 되어 있지 않다면, 비교 연산자는 동작하지 않는다. 이유는 비교 연산자가 데이터 구조 내부 개별 요소가 아닌, 리스트 각 요소에 내재한 자료구조로 되어있기 때문이다.
xlist[1:2]$a
[1] "Software Carpentry"
$b
[1] 1 2 3 4 5 6 7 8 9 10
리스트 개별 요소를 추출하려면, 이중 꺾쇠 함수를 사용한다: [[.
xlist[[1]][1] "Software Carpentry"
이제 결과값이 리스트가 아닌 벡터에 주목한다.
한번에 요소 하나이상을 추출할 수는 없다:
xlist[[1:2]]Error in xlist[[1:2]]: 첨자의 허용 범위를 벗어났습니다
요소를 건너뛰는 것도 사용할 수 없다:
xlist[[-1]]Error in xlist[[-1]]: 한 개 이상의 구성요소 선택을 시도합니다
하지만, 명칭을 사용해서 요소에 대한 부분집합으로 뽑아내거나, 요소를 추출할 때 사용할 수 있다:
xlist[["a"]][1] "Software Carpentry"
$ 함수는 명칭으로 요소를 뽑아내는데 사용되는 초간편 방법이다:
xlist$data Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
도전 과제 5
다음 리스트가 주어졌다:
xlist <- list(a = "Software Carpentry", b = 1:10, data = head(iris))리스트와 벡터 부분집합을 추출하는 지식을 활용해서, xlist에서 숫자 2를 추출한다. 힌트: 숫자 2는 리스트 “b” 항목 내부에 담겨있다.
도전 과제 6
선형 모형이 다음과 같이 주어졌다:
mod <- aov(pop ~ lifeExp, data=gapminder)잔차 자유도를 추출하라. 힌트: attributes() 함수가 도움을 줄 것이다.
데이터프레임
데이터프레임을 까면 내부는 리스트로 구성된 것을 기억한다. 그래서 유사한 규칙이 적용된다. 하지만, 데이터프레임도 2차원 객체다:
[함수에 인자를 하나만 넣으면 리스트와 동일하게 동작한다. 즉, 각 리스트 요소는 칼럼에 대응된다. 작업결과 나오는 객체는 데이터프레임이다:
head(gapminder[3]) pop
1 8425333
2 9240934
3 10267083
4 11537966
5 13079460
6 14880372
유사하게, [[ 함수는 칼럼 한개만 추출하는데 동작된다:
head(gapminder[["lifeExp"]])[1] 28.801 30.332 31.997 34.020 36.088 38.438
명칭으로 칼럼을 추출하는데 사용되는 편리한 단축어가 $이다:
head(gapminder$year)[1] 1952 1957 1962 1967 1972 1977
인자가 두개 있는 경우, [ 함수는 행렬에 대해서와 마찬가지로 동작한다:
gapminder[1:3,] country year pop continent lifeExp gdpPercap
1 Afghanistan 1952 8425333 Asia 28.801 779.4453
2 Afghanistan 1957 9240934 Asia 30.332 820.8530
3 Afghanistan 1962 10267083 Asia 31.997 853.1007
행 하나만 부분집합으로 뽑아내면, 결과는 데이터프레임이 되는데 이유는 각 요소가 혼합된 자료형으로 구성되었기 때문이다:
gapminder[3,] country year pop continent lifeExp gdpPercap
3 Afghanistan 1962 10267083 Asia 31.997 853.1007
하지만, 단일 칼럼에 대해서 결과는 벡터다. drop = FALSE를 세번째 인자로 넣으면 바꿀 수 있다.
도전과제 7
데이터프레임 부분집합을 뽑아내는 오류가 다음에 나와 있는데 이를 버그없이 수정하라:
- 1957년에 수집된 관측점을 뽑아내라.
gapminder[gapminder$year = 1957,]- 1에서 4를 제외한 모든 칼럼을 뽑아내라.
gapminder[,-1:4]- 기대수명이 80세 이상 되는 행을 추출하라.
gapminder[gapminder$lifeExp > 80]- 첫번째 행과 4번째 5번째 칼럼(
lifeExp,gdpPercap)을 뽑아내라. .
gapminder[1, 4, 5]- 고급: 2002년과 2007년에 대한 정보를 담고 있는 행을 추출하라.
gapminder[gapminder$year == 2002 | 2007,]도전과제 8
gapminder[1:20]명령어는 왜 오류를 반환하는가?gapminder[1:20,]와 어떻게 다른가?gapminder_small이라는 데이터프레임을 생성하는데 1에서 9까지 행과 19에서 23까지 행만 포함한다. 이 작업을 하나 혹은 두 단계로 작성한다.
도전과제 해답
도전과제 1에 대한 해답
다음과 같은 코드가 주어졌다:
x <- c(5.4, 6.2, 7.1, 4.8, 7.5)
names(x) <- c('a', 'b', 'c', 'd', 'e')
print(x) a b c d e
5.4 6.2 7.1 4.8 7.5
- 다음 출력결과를 산출하는 적어도 서로 다른 명령어 3개를 제시한다:
b c d
6.2 7.1 4.8
x[2:4]
x[-c(1,5)]
x[c("b", "c", "d")]
x[c(2,3,4)]도전과제 2에 대한 해답
다음 코드를 실행해서 x 벡터를 정의한다.
x <- c(5.4, 6.2, 7.1, 4.8, 7.5)
names(x) <- c('a', 'b', 'c', 'd', 'e')
print(x) a b c d e
5.4 6.2 7.1 4.8 7.5
x 벡터가 주어지면, 다음 명령어는 어떤 작업을 수행할 것으로 예상되는가?
x[-which(names(x) == "g")]상기 명령어를 시도해보고, 실행결과를 살펴본다. 여러분의 예상과 일치하는가? 왜 이런 결과가 나왔을까? (Tip: 여러분이 직접 작성한 것처럼 한땀한땀 명령어 각각을 테스트한다 - 매우 유용한 디버깅 전략이다.)
다음 중 어떤 것이 사실인가:
which에 전달되는TRUE값이 없다면, 공벡터가 반환된다.
which에 전달되는TRUE값이 없다면, 오류 메시지가 나타난다.
integer()는 공벡터다.
- 공벡터를 부정하면 “모든” 벡터를 만들어낸다.
x[]은x[integer()]와 동일한 결과를 산출한다.
정답: A 와 C 가 맞다.
which 명령어는 입력값에 대한 모든 TRUE 값에 대한 색인을 반환한다. names(x) == "g"은 어떤 TRUE값도 반환하지 않는다. which 명령어에 전달되는 어떤 TRUE 값도 없기 때문에, 공벡터를 반환했다. 음수 부호로 벡터를 부정한다고 의미가 변경되지는 않는다. x 벡터에서 값을 가져오는데 공벡터를 사용했기 때문에, 빈 숫자벡터를 만들어 낸다. 결과는 named numeric 공벡터다. 이유는 x 벡터 자료형이 “named numeric”인데, 명칭을 값에 대입했기 때문이다(str(x)을 시도해 보라).
도전과제 4에 대한 해답
다음과 같은 코드가 주어졌다:
m <- matrix(1:18, nrow=3, ncol=6)
print(m) [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 4 7 10 13 16
[2,] 2 5 8 11 14 17
[3,] 3 6 9 12 15 18
- 다음 중 어떤 명령어가 값 11과 14를 추출하는 하는가?
A. m[2,4,2,5]
B. m[2:5]
C. m[4:5,2]
D. m[2,c(4,5)]
정답: D
도전과제 5에 대한 해답
다음 리스트가 주어졌다:
xlist <- list(a = "Software Carpentry", b = 1:10, data = head(iris))리스트와 벡터 부분집합을 추출하는 지식을 활용해서, xlist에서 숫자 2를 추출한다. 힌트: 숫자 2는 리스트 “b” 항목 내부에 담겨있다.
xlist$b[2]
xlist[[2]][2]
xlist[["b"]][2]도전과제 6에 대한 해답
선형 모형이 다음과 같이 주어졌다:
mod <- aov(pop ~ lifeExp, data=gapminder)잔차 자유도를 추출하라. 힌트: attributes() 함수가 도움을 줄 것이다.
attributes(mod) ## `df.residual` is one of the names of `mod`
mod$df.residual도전과제 7에 대한 해답
데이터프레임 부분집합을 뽑아내는 오류가 다음에 나와 있는데 이를 버그없이 수정하라:
- 1957년에 수집된 관측점을 뽑아내라.
# gapminder[gapminder$year = 1957,]
gapminder[gapminder$year == 1957,]- 1에서 4를 제외한 모든 칼럼을 뽑아내라.
# gapminder[,-1:4]
gapminder[,-c(1:4)]- 기대수명이 80세 이상 되는 행을 추출하라.
# gapminder[gapminder$lifeExp > 80]
gapminder[gapminder$lifeExp > 80,]- 첫번째 행과 4번째 5번째 칼럼(
lifeExp,gdpPercap)을 뽑아내라. .
# gapminder[1, 4, 5]
gapminder[1, c(4, 5)]- 고급: 2002년과 2007년에 대한 정보를 담고 있는 행을 추출하라.
# gapminder[gapminder$year == 2002 | 2007,]
gapminder[gapminder$year == 2002 | gapminder$year == 2007,]
gapminder[gapminder$year %in% c(2002, 2007),]도전과제 8에 대한 해답
gapminder[1:20]명령어는 왜 오류를 반환하는가?gapminder[1:20,]와 어떻게 다른가?
정답: gapminder는 데이터프레임이다. 그래서 차원 2개로 부분집합을 뽑아낼 필요가 있다. gapminder[1:20, ]는 첫번째부터 20번째 모든 행과 모든 칼럼을 부분집합으로 뽑아낸다.
gapminder_small이라는 데이터프레임을 생성하는데 1에서 9까지 행과 19에서 23까지 행만 포함한다. 이 작업을 하나 혹은 두 단계로 작성한다.
gapminder_small <- gapminder[c(1:9, 19:23),]