데이터 프레임은 R 데이터 구조 중 실용적인 측면에서 가장 많이 사용하는 것이다. 엑셀 프로그램이나 기타 파일, 데이터베이스 등에서 데이터를 읽어오는 대부분의 함수들은 기본적으로 데이터 프레임으로 읽어 들인다. 또 데이터 프레임은 스프레드시트나 데이터 베이스의 테이블과 비슷하게 사람들이 가장 직관적으로 이해하기 편해서 자주 사용한다.그러므로 R에 능숙해지기 위해서라면 데이터 프레임을 자유자재로 활용할 수 있는 능력을 갖추어야 한다.
데이터 프레임 서브세팅은 앞에서 소개한 벡터 서브세팅의 개념을 2차원으로 연장한 것이다. 여기서는 R에 내장된 mtcars 라는 데이터 프레임을 가지고 설명할 것이다. 이 데이터는 자동차 관련 잡지에서 뽑은 것으로, 자세한 내용은 help(mtcars),?() 으로 통해 확인 할 수 있다.
먼저 어떤 구조를 가졌는지 str()함수로 확인해보자.
> str(mtcars)
'data.frame': 32 obs. of 11 variables:
$ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
$ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
$ disp: num 160 160 108 258 360 ...
$ hp : num 110 110 93 110 175 105 245 62 95 123 ...
$ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
$ wt : num 2.62 2.88 2.32 3.21 3.44 ...
$ qsec: num 16.5 17 18.6 19.4 17 ...
$ vs : num 0 0 1 1 0 1 0 1 1 1 ...
$ am : num 1 1 1 0 0 0 0 0 0 0 ...
$ gear: num 4 4 4 3 3 3 3 4 4 4 ...
$ carb: num 4 4 1 1 2 1 4 2 2 4 ...
> head(mtcars)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
모든 변수가 numeric 이어서 실린더 수 (cy1), 자동 / 수동 여부(am) 등은 팩터로 변환할 것이다. 그리고 mtcars를 mtcars_df로 변환하여 사용할 것이다.
> mtcars_df$am <- as.factor(mtcars_df$am)
> mtcars_df <- mtcars
> mtcars_df$cyl <- as.factor(mtcars_df$cyl)
> mtcars_df$am <- as.factor(mtcars_df$am)
> str(mtcars_df)
'data.frame': 32 obs. of 11 variables:
$ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
$ cyl : Factor w/ 3 levels "4","6","8": 2 2 1 2 3 2 3 1 1 2 ... //Factor 변환
$ disp: num 160 160 108 258 360 ...
$ hp : num 110 110 93 110 175 105 245 62 95 123 ...
$ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
$ wt : num 2.62 2.88 2.32 3.21 3.44 ...
$ qsec: num 16.5 17 18.6 19.4 17 ...
$ vs : num 0 0 1 1 0 1 0 1 1 1 ...
$ am : Factor w/ 2 levels "0","1": 2 2 2 1 1 1 1 1 1 1 변 //Factor 변환
$ gear: num 4 4 4 3 3 3 3 4 4 4 ...
$ carb: num 4 4 1 1 2 1 4 2 2 4 ...
13-1 특정 셀에 접근하기, 수정하기
데이터 프레임은 [행, 열] 의 문법을 가지고 서브세팅 한다. 열 단위, 행단위에 대해서는 뒤에서 설명한다. 특정 셀에 접근할 때는 그 인덱스를 주면 된다.
> head(mtcars_df)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
> mtcars_df[1,2]
[1] 6
Levels: 4 6 8
이 데이터 프레임은 1행 ,2열의 값을 가지고 있다. 좌변에 두면 값을 수정할 수도 있다.
> mtcars_df[1,2] <- 8
> head(mtcars_df)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 8 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
> mtcars_df[1,2] <- 6 //원복
> head(mtcars_Df)
> head(mtcars_df)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
참고로 데이터 프레임에 열을 추가하는 것도 간단하다. 다음과 같이 데이터 프레임에 새로운 열을 만들면서 좌변에 두면 된다.
여기서는 mtcars$이름 으로 열을 추가하였다.
> mtcars_df$plus <- mtcars$mpg*1000
> head(mtcars_df)
mpg cyl disp hp drat wt qsec vs am gear carb plus
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 21000
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 21000
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 22800
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 21400
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 18700
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 18100
열을 제거할 수도 있다. '없다'는 뜻의 NULL 값을 주면 된다.
> mtcars_df$plus <- NULL
> head(mtcars)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
9-2 열들의 선택
데이터 셋이 큰 경우 그 중에 꼭 필요한 열만 선택하는 일은 아주 흔한 일이다. 이것을 열을 '선택하다(select)' 라고 표현한다.
여기서는 mpg,cyl,disp,wt,am 이라는 열만 선택하려고 한다. 앞에서 설명했듯이 데이터 프레임에서의 서브세팅은 아토믹 벡터의 2차원적인 확장이다. 아토믹 벡터에서는 [인덱스]를 가지고 작업했는데, 데이터 프레임에서는 [행,열]의 구조를 가지고 작업한다. 여기서는 열이 관심 대상이므로 콤마(,) 뒤의 열에 집중한다.
우리가 원하는 열들만 추출하기 위해서는 다음과 같이 한다. 여기서는 열의 이름을 사용하고 순서를 약간 변형했다.
열의 순서도 사용할 순 있지만 이름을 사용한 것이 더욱 편리하다.
> selected <- mtcars_df[ ,c('mpg','wt','am','cyl','disp')]
> head(selected)
mpg wt am cyl disp
Mazda RX4 21.0 2.620 1 6 160
Mazda RX4 Wag 21.0 2.875 1 6 160
Datsun 710 22.8 2.320 1 4 108
Hornet 4 Drive 21.4 3.215 0 6 258
Hornet Sportabout 18.7 3.440 0 8 360
Valiant 18.1 3.460 0 6 225
코드를 잘 보면, 인덱싱할 때 [행,열] 부분에서 열 부분을 빈 공간으로 두었다. 빈 공간은 모든(all)을 의미한다. []안에 음의 정수로 된 벡터를 사용하면, 해당 위치의 것들은 제외한다는 의미가 여기서도 적용된다. 열에서도 마찬가지이다. 단, 주의할 부분은 -c("mpg","wt) 등을 써서 mpg,wt열을 제외할 수는 없다. 다음 코드를 봐도 알 수 있듯이 음수를 표시하는 단항연산자인 -를 문자열(character)에 대해서 적용할 수는 없기 때문이다.
> selected[,-(3:5)]
mpg wt
Mazda RX4 21.0 2.620
Mazda RX4 Wag 21.0 2.875
Datsun 710 22.8 2.320
Hornet 4 Drive 21.4 3.215
Hornet Sportabout 18.7 3.440
Valiant 18.1 3.460
Duster 360 14.3 3.570
Merc 240D 24.4 3.190
Merc 230 22.8 3.150
Merc 280 19.2 3.440
Merc 280C 17.8 3.440
Merc 450SE 16.4 4.070
Merc 450SL 17.3 3.730
Merc 450SLC 15.2 3.780
Cadillac Fleetwood 10.4 5.250
Lincoln Continental 10.4 5.424
Chrysler Imperial 14.7 5.345
Fiat 128 32.4 2.200
Honda Civic 30.4 1.615
Toyota Corolla 33.9 1.835
Toyota Corona 21.5 2.465
Dodge Challenger 15.5 3.520
AMC Javelin 15.2 3.435
Camaro Z28 13.3 3.840
Pontiac Firebird 19.2 3.845
Fiat X1-9 27.3 1.935
Porsche 914-2 26.0 2.140
Lotus Europa 30.4 1.513
Ford Pantera L 15.8 3.170
Ferrari Dino 19.7 2.770
Maserati Bora 15.0 3.570
Volvo 142E 21.4 2.780
9-3 행들을 필터
앞에서 열에 대해서는 선택(select)이라는 단어를 사용했는데, 행에 대해서는 필터(filter)라는 단어를 사용한다. 엑셀을 써본 독자들이라면 비슷한 기능이 있다는 것을 알 것이다. 이번에는 행이 주된 관심이기 때문에 [행,열] 에서 행에 주목한다.
필터를 위해서 행의 이름이나 인덱스를 사용할 수 있다. 이런 것을 슬라이싱(slicing)이라고 부른다.
> sliced <- selected[c(1:10),]
> sliced
mpg wt am cyl disp
Mazda RX4 21.0 2.620 1 6 160.0
Mazda RX4 Wag 21.0 2.875 1 6 160.0
Datsun 710 22.8 2.320 1 4 108.0
Hornet 4 Drive 21.4 3.215 0 6 258.0
Hornet Sportabout 18.7 3.440 0 8 360.0
Valiant 18.1 3.460 0 6 225.0
Duster 360 14.3 3.570 0 8 360.0
Merc 240D 24.4 3.190 0 4 146.7
Merc 230 22.8 3.150 0 4 140.8
Merc 280 19.2 3.440 0 6 167.6
필터는 대부분의 경우에 조건을 사용한다. 어떤 값 이상의 것만을 필터링 하거나, 어떤 조건에 맞는 행들만을 필터링 한다. 몇가지 사례를 볼 것인데, 먼저 카테고리형 변수인 실린더를 사용하여 6기통을 가진 행들만 필터링 해본다.
코드를 보면 [행,열] 에서 열이 비어 있다. 여기서도 비어 있는 것은 '모든(all)'을 의미한다.
> filtered_cyl_6 <- selected[selected$cyl==6,]
> filtered_cyl_6
mpg wt am cyl disp
Mazda RX4 21.0 2.620 1 6 160.0
Mazda RX4 Wag 21.0 2.875 1 6 160.0
Hornet 4 Drive 21.4 3.215 0 6 258.0
Valiant 18.1 3.460 0 6 225.0
Merc 280 19.2 3.440 0 6 167.6
Merc 280C 17.8 3.440 0 6 167.6
Ferrari Dino 19.7 2.770 1 6 145.0
다음은 무게(wt)가 평균 이상의 행들만 필터링 해본다.
> mean(selected$wt)
[1] 3.21725
> wt_over_mean <- selected[selected$wt >= mean(selected$wt),]
> wt_over_mean
mpg wt am cyl disp
Hornet Sportabout 18.7 3.440 0 8 360.0
Valiant 18.1 3.460 0 6 225.0
Duster 360 14.3 3.570 0 8 360.0
Merc 280 19.2 3.440 0 6 167.6
Merc 280C 17.8 3.440 0 6 167.6
Merc 450SE 16.4 4.070 0 8 275.8
Merc 450SL 17.3 3.730 0 8 275.8
Merc 450SLC 15.2 3.780 0 8 275.8
Cadillac Fleetwood 10.4 5.250 0 8 472.0
Lincoln Continental 10.4 5.424 0 8 460.0
Chrysler Imperial 14.7 5.345 0 8 440.0
Dodge Challenger 15.5 3.520 0 8 318.0
AMC Javelin 15.2 3.435 0 8 304.0
Camaro Z28 13.3 3.840 0 8 350.0
Pontiac Firebird 19.2 3.845 0 8 400.0
Maserati Bora 15.0 3.570 1 8 301.0
여기에서 행 필터와 직접 관련은 없을지 모르지만, 행 필터를 하다 보면 생기는 문제가 하나 있다. wt_over_mean 데이터 프레임의 cyl은 실제 값을 보면 레벨이 6과 8이 되어야 하는데 str() 함수를 적용해 보면 이전의 레벨이 그대로 유지되어 있다.
> str(wt_over_mean)
'data.frame': 16 obs. of 5 variables:
$ mpg : num 18.7 18.1 14.3 19.2 17.8 16.4 17.3 15.2 10.4 10.4 ...
$ wt : num 3.44 3.46 3.57 3.44 3.44 ...
$ am : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
$ cyl : Factor w/ 3 levels "4","6","8": 3 2 3 2 2 3 3 3 3 3 ...
$ disp: num 360 225 360 168 168 ...
사용되지 않는 레벨은 factor() 함수를 적용하면 사라진다.
> wt_over_mean$cyl <- factor(wt_over_mean$cyl)
> str(wt_over_mean)
'data.frame': 16 obs. of 5 variables:
$ mpg : num 18.7 18.1 14.3 19.2 17.8 16.4 17.3 15.2 10.4 10.4 ...
$ wt : num 3.44 3.46 3.57 3.44 3.44 ...
$ am : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
$ cyl : Factor w/ 2 levels "6","8": 2 1 2 1 1 2 2 2 2 2 ...
$ disp: num 360 225 360 168 168 ...
9-4 열의 선택과 행의 필터를 동시에
앞에서 소개한 두 가지 방법을 쓰면 열의 선택과 행의 필터를 동시에 할 수도 있다. 다음은 cy1이 "4" 이거나 "6"인 행들을 필터링 하고 "mpg","wt","cy1"열들만 선택한 것이다.
> selected_filtered <- mtcars_df[mtcars_df$cyl%in% c("4","6"), c("mpg","wt","cyl")]
> selected_filtered
mpg wt cyl
Mazda RX4 21.0 2.620 6
Mazda RX4 Wag 21.0 2.875 6
Datsun 710 22.8 2.320 4
Hornet 4 Drive 21.4 3.215 6
Valiant 18.1 3.460 6
Merc 240D 24.4 3.190 4
Merc 230 22.8 3.150 4
Merc 280 19.2 3.440 6
Merc 280C 17.8 3.440 6
Fiat 128 32.4 2.200 4
Honda Civic 30.4 1.615 4
Toyota Corolla 33.9 1.835 4
Toyota Corona 21.5 2.465 4
Fiat X1-9 27.3 1.935 4
Porsche 914-2 26.0 2.140 4
Lotus Europa 30.4 1.513 4
Ferrari Dino 19.7 2.770 6
Volvo 142E 21.4 2.780 4
행의 조건은 다음과 같다.
> mtcars_df$cyl %in% c("4","6")
mtcars_df 의 cy1 행에서 "4","6"이 들어간 값들 이라는 뜻이다.
%in% 연산자는 멤버십을 따지는 R연산자로 , 잘 쓰면 매우 유용하다.
> "a" %in% c("a","b","c") # "a"라는 요소가 c("a","b","c") 안에 있어서 TRUE
[1] TRUE
> "a" %in% c("b","c") #"a"라는 요소가 c("b","c")안에 없으니 FALSE
[1] FALSE
> 3%in%c(1,2,4)
[1] FALSE
> 3%in%c(1,3,5)
[1] TRUE
우리는 다음과 같은 표현식으로 서브세팅을 시도했다.
> mtcars_df[mtcars_df$cyl%in%c("4","6"),c("mpg","wt","cyl")]
사실 %in% 연산자를 쓰지 않으면 다음과 같이 코딩해야 한다.
ars_df[mtcars_df$cyl == "4" | mtcars_df$cy == "6", c("mpg","wt","cyl")]
mpg wt cyl
Mazda RX4 21.0 2.620 6
Mazda RX4 Wag 21.0 2.875 6
Datsun 710 22.8 2.320 4
Hornet 4 Drive 21.4 3.215 6
Valiant 18.1 3.460 6
Merc 240D 24.4 3.190 4
Merc 230 22.8 3.150 4
Merc 280 19.2 3.440 6
Merc 280C 17.8 3.440 6
Fiat 128 32.4 2.200 4
Honda Civic 30.4 1.615 4
Toyota Corolla 33.9 1.835 4
Toyota Corona 21.5 2.465 4
Fiat X1-9 27.3 1.935 4
Porsche 914-2 26.0 2.140 4
Lotus Europa 30.4 1.513 4
Ferrari Dino 19.7 2.770 6
Volvo 142E 21.4 2.780 4
자신이 코딩한 경우라면 간결하다고 자평할 수 있으나, 다른 사람이 작성한 코드라면 까다롭다고 느낄 수 있다. 코드는 보기 좋고 이해하기 편해야 하기 때문에 과정을 나누어 진행한다는 것이 좋다. 그런데 과정을 나누려고 보면 중간 객체들을 만들어야 하는 수고를 해야 하고, 이런 과정이 성능에도 영향을 줄 수 있다. 그런데 (뒤에서 설명하겠지만) 이렇게 하지 않고 체인(chain)으로 연결하는 방법도 있다. 이 방법을 익혀서 이해하기 쉽게 코딩하는 습관을 가지는 것이 좋다.
9-5 서브세팅 코드 이해하기
다음 서브세팅 코드를 보자.
> df1 <- mtcars[mtcars$cyl == 6 | mtcars$cyl ==4,]
> df1
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
처음 이런 코드를 보면 tmcars 라는 대상이 되는 데이터 프레임이 자주 나오기 때문에 [] 안에 있는 것과 밖에 있는 것이 서로 연관되어 있다고 생각할 수 있다. 그런데 사실 R의 입장에서는 관련이 없다. R이 하는 것은 표현식을 평가하는 것 이외에는 별 다른 것이 없다. 즉[] 안에 있는 표현식은 논리형 벡터를 만드는 과정에 불가하다. 실제로 보면 다음과 같은 논리형 벡터를 얻는 과정인 것이다.
mtcars$cyl==6 | mtcars$cyl ==4
[1] TRUE TRUE TRUE TRUE FALSE TRUE FALSE TRUE TRUE TRUE TRUE FALSE
[13] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE FALSE FALSE FALSE
[25] FALSE TRUE TRUE TRUE FALSE TRUE FALSE TRUE
이 논리형 벡터 [] 안에 사용되고 있는 것뿐이다. 따라서 다음과 같이 단계를 나눠서 해도 전혀 문제가 되지 않는다. 이 경우에는 논리형 벡터를 condition 이라는 벡터에 할당하고 [] 안에 넣었다.
> condition <- mtcars$cyl ==6 | mtcars$cyl ==4
> mtcars[condition,]
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
그러믈 다음과 같이 코딩해서는 안 된다. cyl이라는 독립적인 벡터가 존재하지 않기 때문이다.
> df1 <- mtcars[cyl==6 | cyl ==4,]
Error in `[.data.frame`(mtcars, cyl == 6 | cyl == 4, ) :
객체 'cyl'를 찾을 수
그런데 mtcars 를 반복하지 않아도 되는 방법이 있다. 대표적인 경우가 subset() 이다.
> df2 <- subset(mtcars,cyl==6 | cyl ==4)
> df2
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
subset() 함수 안에서 cyl이라는 열의 이름을 mtcars$cyl 이라 하지 않아도 되는 이유는 subset()함수가 특별하기 때문이다. 이런 함수들은 비표준 평가(non - standard evaluation)를 따른다고 말한다. 반면 앞에서 소개한 [] 의 방식을 표준 평가라고 한다. 이런 비표준 평가를 타이핑해야 코드를 줄일 수 있기 때문에 인터랙티브 환경 , 즉 R콘솔에서 한 줄 씩 진행하는 경우는 매우 도움이 된다. 그러나 프로그래밍 할 때는 이것을 사용할 순 없다.
다음을 보자.
> x<-"cyl"
> df3 <- subset(mtcars, x==6 | x ==4) #cyl 대신에 x라는 요소에 cyl을 대입하여 사용
> df3
[1] mpg cyl disp hp drat wt qsec vs am gear carb
<0 행> <또는 row.names의 길이가 0입니다>
> df3 <- subset(mtcars, cyl==6 | cyl ==4)
> df3
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
9-6 그룹으로 분리
데이터셋에 카테고리형 변수가 있다는 것은 데이터셋을 그 변수의 레벨에 따라 나누어 분석하고자 하는 의도가 기본적으로 담겨 있는 것이다. 치료군과 비치료군을 비교하는 것과 같다.
어떤 카테고리형 변수가 있을 때, 그 변수의 레벨에 따라서 그룹을 분리하는 함수로 split()이 있다. 첫 번째 인자에 나눌 데이터셋을 주고, 두 번째 인자로 팩터를 지정한다.
> split(mtcars_df, mtcars_df$cyl)
$`4`
mpg cyl disp hp drat wt qsec vs am gear carb
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
$`6`
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
$`8`
mpg cyl disp hp drat wt qsec vs am gear carb
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
Merc 450SLC 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
Dodge Challenger 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
Camaro Z28 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
결과를 보면 이 함수의 반환 값은 mtcars_df를 cyl의 레벨(levels)에 따라서 분리한 것들을 모은 하나의 R 리스트임을 알 수 있다. 조금 더 눈여겨볼 만한 것은 리스트의 구성요소에 백틱(`, 보통 자판기에서 숫자 1앞에 키가 위치한다.)을 사용한 부분이다. 백틱에 대해서는 뒤에서 설명한다.
이런 split() 함수는 데이터 프레임 뿐만이 아니라 일반적인 벡터에도 사용할 수 있다.
> split(mtcars$cyl,mtcars$am) #$cyl에서 am을 기준으로 나눈다
$`0`
[1] 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 8 8 8 8
$`1`
[1] 6 6 4 4 4 4 4 4 4 8 6 8 4
이 코드도 cyl 과 am 이 어떤 연관이 있어서 그런 것은 아니고, 그 개수만 맞으면 된다.
> mtcars$cyl
[1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4
> mtcars$am
[1] 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1
이와 같은 독립적인 벡터가 있었을 때 mtcars$cyl 의 요소의 값을 해당 인덱스의 위치에 존재하는 mtcars$am의 값에 따라서 구분한다.
이것은 나중에 apply 계열의 함수를 사용할 줄 알게 되면 아주 강력한 기능을 발휘한다. 또 dplyr 패키지에서 소개한 group_by() 함수를 이해하는데도 기초가 된다.
먼저 잠깐 보면 다음과 같은 코드를 사용해 각 그룹별로 데이터를 정리할 수 있다. 여기서는 그룹별 mpg 의 평균을 계산해 보았다.
> ex <- split(mtcars, mtcars$cyl)
> unlist(lapply(ex,function(df)mean(df$mpg)))
4 6 8
26.66364 19.74286 15.10000
9-7 문자열 서브세팅의 응용
서브 세팅을 응용해 간단하게 해결할 수 있는 문제를 살펴보자. 성별에 대해 다음과 같이 코딩된 벡터가 있다.
"u"는 결측치를 의미한다.
> gender <- c("f","m","m","u","f","f")
이것을 다음과 같이 바꾸는 문제이다.
> gender2 <- c("Female","Male","Male",NA,"Female","Female")
우선 바꿀 벡터(gender)의 값들을 이름(names)으로 하고, 그 이름에 대하여 바뀔 값이 부여된 벡터를 만든다.
> gender <- c("f","m","m","u","f","f")
> gender2 <- c("Female","Male","Male",NA,"Female","Female")
> lookup <- c("Male","Female",NA)
> names(lookup) <- c("m","f","u")
> lookup
m f u
"Male" "Female" NA
그런 다음 아래와 같이 한다.
> gender2 <- lookup[gender]
> names(gender2) <- NULL
> gender2
[1] "Female" "Male" "Male" NA "Female" "Female"
이 과정을 이해하는 핵심은 lookup[gender]라는 부분에 있다. gender가 문자열 벡터이기 때문에 []안에 들어간 문자열은 대상 객체의 이름을 대상으로 값을 찾는다.
> lookup[c("f","m","m","u","f","f")]
f m m u f f
"Female" "Male" "Male" NA "Female" "Female"
[]안의 벡터에서 첫 번째 요소 값 "f"에 대해서는 이것을 이름으로 갖는 lookup의 값이 "Female"이다. 두번째 요소의 값 "m"에 대해서는 이것을 이름으로 갖는 lookup의 값이 "Male" 이다. 세번째 요소의 값 "m"에 대해서는 그 값이 "Male" 이다.
이런식으로 마지막 "f"까지 진행된다. 이름과 값이 있는 표가 있어야 하며 이 표에 대해서 [] 안에 바꿀 벡터를 넣어야 한다.
'전공 > R프로그래밍' 카테고리의 다른 글
R프로그래밍 15. R 함수 (0) | 2019.07.31 |
---|---|
R프로그래밍 14. $,[[,[ 의 차이점 (0) | 2019.07.22 |
R 프로그래밍 12. R에 내장된 데이터셋 (0) | 2019.07.04 |
R프로그래밍 11.팩터(factor) : 카테고리형 데이터를 표현 , 행렬과 배열 (0) | 2019.07.03 |
R프로그래밍 10. R객체의 메타 데이터 : 속성 (0) | 2019.07.02 |