데이터 전처리, 조작을 위한 dplyr 패키지의 단일 테이블에 대한 함수(one-table verbs)로서 지난번 포스티에서는
- 행(row) 선별 : filter(), slice()
- 행(row) 정렬 : arrange()
- 열(column) 선별 : select()
- 열(column) 조건부 선별 : select(df, starts_with()), select(df, ends_with()), select(df, contains()),
select(df, matchs()), select(df, one_of()), select(df, num_range())
- 변수 이름 변경 : rename()
함수에 대해서 알아보았습니다. (☞ 바로 가기 http://rfriend.tistory.com/234)
[ R dplyr package : a grammar of data manipulation ]
이번 포스팅에서는 dplyr 패키지에 대한 두번째 소개 포스팅으로서, distinct(), sample_n(), sample_frac(), mutate(), transmute(), summarise() 함수에 대해서 알아보겠습니다.
dplyr verbs |
description |
similar {package} function |
filter() |
Filter rows with condition |
{base} subset |
slice() |
Filter rows with position |
{base} subset |
arrange() |
Re-order or arrange rows |
{base} order |
select() |
Select columns |
{base} subset |
> select(df, starts_with()) |
Select columns that start with a prefix |
|
> select(df, ends_with()) |
Select columns that end with a prefix |
|
> select(df, contains()) |
Select columns that contain a character string |
|
> select(df, matchs()) |
Select columns that match a regular expression |
|
> select(df, one_of()) |
Select columns that are from a group of names |
|
> select(df, num_range()) |
Select columns from num_range a to n with a prefix |
|
rename() |
Rename column name |
{reshape} rename |
distinct() |
Extract distinct(unique) rows |
{base} unique |
sample_n() |
Random sample rows for a fixed number |
{base} sample |
sample_frac() |
Random sample rows for a fixed fraction |
{base} sample |
mutate() |
Create(add) new columns. mutate() allows you to refer to columns that you’ve just created |
{base} transform |
transmute() |
Create(add) new columns.
transmute() only keep the new columns. |
{base} transform |
summarise() |
Summarise values |
{base} summary |
(1) 중복없는 유일한 값 추출 : distinct() |
distinct(dataframe, 기준 var1, 기준 var2, ...) 의 형식으로 중복없는 유일한(distinct, unique) 값을 추출하고자 하는 기준 변수를 기입해주면 됩니다.
{base} 패키지의 unique() 함수와 매우 기능을 수행하며, dplyr 패키지의 distinct() 가 C언어로 짜여져서 속도는 훨씬 빠릅니다.
[문제] Cars93 데이터 프레임에서 '차종(Type)'과 '생산국-미국여부(Origin)' 변수를 기준으로 중복없는 유일한 값을 추출하시오.
library(dplyr) library(MASS) # to use Cars93 dataframe
> names(Cars93)
[1] "Manufacturer" "Model" "Type" "Min.Price"
[5] "Price" "Max.Price" "MPG.city" "MPG.highway"
[9] "AirBags" "DriveTrain" "Cylinders" "EngineSize"
[13] "Horsepower" "RPM" "Rev.per.mile" "Man.trans.avail"
[17] "Fuel.tank.capacity" "Passengers" "Length" "Wheelbase"
[21] "Width" "Turn.circle" "Rear.seat.room" "Luggage.room"
[25] "Weight" "Origin" "Make"
> # distinct(dataframe, var1, var2) : find unique values in a table
> > distinct(Cars93, Origin)
Origin
1 non-USA
2 USA
> > distinct(Cars93, Type)
Type
1 Small
2 Midsize
3 Compact
4 Large
5 Sporty
6 Van
>
> distinct(Cars93, Origin, Type)
Origin Type
1 non-USA Small
2 non-USA Midsize
3 non-USA Compact
4 USA Midsize
5 USA Large
6 USA Compact
7 USA Sporty
8 USA Van
9 USA Small
10 non-USA Sporty
11 non-USA Van |
참고로, {base} 패키지의 unique() 함수로는 unique(Cars93[, c("Origin", "Type")]) 이렇게 입력해주면 되며, 위의 distinct() 함수와는 달리 rownames 가 dataset의 row 번호로 반환되는 차이점이 있습니다. dplyr 패키지가 전반적으로 문법이 깔끔하다는 생각이 드실겁니다.
({base} 패키지의 unique() 함수, duplicated() 함수 포스팅 보러가기
: ☞ http://rfriend.tistory.com/165 )
(2) 무작위 표본 데이터 추출 : sample_n(), sample_frac() |
(2-1) sample_n(dataframe, a fixed number) : 특정 개수만큼 무작위 추출
[문제] Cars93 데이터 프레임(1~5번째 변수만 사용)에서 10개의 관측치를 무작위로 추출하시오.
> # sample_n() : randomly sample rows for a fixed number
> sample_n(Cars93[, 1:5], 10)
Manufacturer Model Type Min.Price Price
68 Oldsmobile Achieva Compact 13.0 13.5
38 Ford Crown_Victoria Large 20.1 20.9
75 Pontiac Firebird Sporty 14.0 17.7
59 Mercedes-Benz 300E Midsize 43.8 61.9
70 Oldsmobile Silhouette Van 19.5 19.5
49 Lexus ES300 Midsize 27.5 28.0
47 Hyundai Sonata Midsize 12.4 13.9
93 Volvo 850 Midsize 24.8 26.7
2 Acura Legend Midsize 29.2 33.9
41 Honda Prelude Sporty 17.0 19.8
> > # random sampling one more time > sample_n(Cars93[, 1:5], 10)
Manufacturer Model Type Min.Price Price
69 Oldsmobile Cutlass_Ciera Midsize 14.2 16.3
64 Nissan Sentra Small 8.7 11.8
44 Hyundai Excel Small 6.8 8.0
78 Saab 900 Compact 20.3 28.7
39 Geo Metro Small 6.7 8.4
22 Chrysler Imperial Large 29.5 29.5
7 Buick LeSabre Large 19.9 20.8
9 Buick Riviera Midsize 26.3 26.3
27 Dodge Dynasty Midsize 14.8 15.6
91 Volkswagen Corrado Sporty 22.9 23.3
|
(2-2) sample_frac(dataframe, a fixed fraction) : 특정 비율만큼 무작위 추출
[문제] Cars93 데이터 프레임(1~5번째 변수만 사용)에서 10%의 관측치를 무작위로 추출하시오.
> # sample_frac() : randomly sample rows for a fixed fraction
> nrow(Cars93)
[1] 93
> > nrow(Cars93)*0.1
[1] 9.3
> > sample_frac(Cars93[ , 1:5], 0.1)
Manufacturer Model Type Min.Price Price
72 Plymouth Laser Sporty 11.4 14.4
8 Buick Roadmaster Large 22.6 23.7
80 Subaru Justy Small 7.3 8.4
31 Ford Festiva Small 6.9 7.4
75 Pontiac Firebird Sporty 14.0 17.7
90 Volkswagen Passat Compact 17.6 20.0
30 Eagle Vision Large 17.5 19.3
41 Honda Prelude Sporty 17.0 19.8
76 Pontiac Grand_Prix Midsize 15.4 18.5
|
Cars93 데이터 프레임은 관측치가 93개 이며, 10%는 9.3개에 해당하므로, 위 예제에서 sample_frac(Cars93, 0.1)은 총 9개의 무작위 샘플을 추출했습니다.
(2-3) smaple_n(dataframe, n, replace = TRUE) : 복원 추출
위의 두개의 예제는 한번 추출한 표본은 다시 추출하지 않는 '비복원 추출(sampling with replacement)'이었습니다. (눈을 감고 주머니에서 한번 뽑았으면, 뽑힌 공은 다시 주머니에 넣지 않고 옆에 따로 빼어놓고, 다시 눈을 감고 주머니에서 공을 뽑음)
dplyr 패키지의 sample_n(), sample_frac() 함수의 default 는 비복원추출이며, 만약 '복원추출(sampling with replacement, bootstrap sampling)'을 하고 싶다면 'replace = TRUE' 옵션을 설정해주면 됩니다. (눈을 감고 주머니에서 공을 뽑고, 뽑힌 공을 다시 주머니에 넣은 후에, 눈을 감고 다시 주머니에서 공을 뽑음)
[문제] Cars93 데이터 프레임(1~5번까지 변수만 사용)에서 20개의 관측치를 무작위 복원추출 하시오.
> # sample_n(dataframe, n, replace = TRUE) : random sampling with replacement
> sample_n(Cars93[, 1:5], 20, replace = TRUE) # a bootstrap sample of 20 records
Manufacturer Model Type Min.Price Price
53 Mazda 323 Small 7.4 8.3
49 Lexus ES300 Midsize 27.5 28.0
92 Volvo 240 Compact 21.8 22.7
48 Infiniti Q45 Midsize 45.4 47.9
64 Nissan Sentra Small 8.7 11.8
56 Mazda MPV Van 16.6 19.1
23 Dodge Colt Small 7.9 9.2
25 Dodge Spirit Compact 11.9 13.3
68 Oldsmobile Achieva Compact 13.0 13.5
43 Honda Accord Compact 13.8 17.5
17 Chevrolet Astro Van 14.7 16.6
70 Oldsmobile Silhouette Van 19.5 19.5
10 Cadillac DeVille Large 33.0 34.7
81 Subaru Loyale Small 10.5 10.9
58 Mercedes-Benz 190E Compact 29.0 31.9
52 Lincoln Town_Car Large 34.4 36.1
31 Ford Festiva Small 6.9 7.4
43.1 Honda Accord Compact 13.8 17.5
47 Hyundai Sonata Midsize 12.4 13.9
17.1 Chevrolet Astro Van 14.7 16.6
|
위 무작위 표본 추출 결과를 보면 17번 Cehvrolet Astro와 43번 Honda Accord가 중복으로 추출되었음을 알 수 있습니다.
(2-4) dataframe %>% group_by(factor_var) %>% sample_n(size) : 집단별 층화 표본 추출
분석을 하다 보면 집단, 그룹별로 동일한 수의 표본을 무작위 추출해서 분석해야 하는 경우가 있습니다. 특히 분석 주제 혹은 분석에 큰 영향을 미치는 요인 변수에 대한 집단 분포(distribution)가 한쪽 그룹으로 심하게 편향된 모집단(biased, unbalanced population)의 경우 층화 무작위 표본 추출(stratified random sampling)이 필요합니다.
[예제] Cars93 데이터 프레임에서 '제조국가_미국여부(Origin)'의 'USA', 'non-USA' 요인 속성별로 각 10개씩의 표본을 무작위 비복원 추출하시오.
> # dataframe %>% group_by(factor_var) %>% sample_n(size) : random sampling by group
> Cars93[ , c("Manufacturer", "Model", "Origin")] %>% group_by(Origin) %>% sample_n(10)
Source: local data frame [20 x 3]
Groups: Origin [2]
Manufacturer Model Origin
<fctr> <fctr> <fctr>
1 Chrylser Concorde USA
2 Lincoln Town_Car USA
3 Ford Crown_Victoria USA
4 Oldsmobile Eighty-Eight USA
5 Oldsmobile Achieva USA
6 Lincoln Continental USA
7 Chrysler Imperial USA
8 Eagle Summit USA
9 Buick Riviera USA
10 Pontiac Bonneville USA
11 Volvo 240 non-USA
12 Mercedes-Benz 190E non-USA
13 Volkswagen Fox non-USA
14 Geo Metro non-USA
15 Honda Accord non-USA
16 Mazda 626 non-USA
17 Mazda Protege non-USA
18 Lexus SC300 non-USA
19 Acura Legend non-USA
20 Mercedes-Benz 300E non-USA
|
위의 '%>%' (단축키 : shift + ctrl + M)의 chaining 에 대해서는 다음번 포스팅에서 별도로 소개하겠으니 지금 궁금하시더라도 조금만 참아주세요. ^^;
(3) 새로운 변수 생성 : mutate(), transmute() |
(3-1) mutate(dataframe, 새로운변수 = 기존변수 조합한 수식, ...)
: 기존 변수 + 신규 변수 모두 keep
새로운 변수를 생성할 때 mutate() 함수를 사용하며, 이는 {base} 패키지의 transform() 함수와 유사합니다만, 조금 더 유용합니다.
[문제] Cars93 데이터프레임에서 최소가격(Min.Price)과 최대가격(Max.Price)의 범위(range), 최소가격 대비 최대가격의 비율(=Max.Price/Min.Price) 의 새로운 변수를 생성하시오.
> # mutate(dataframe, new_var = operation of old vars, ...) : Create(add) new columns
> Cars93_1 <- Cars93[c(1:10), c("Model", "Min.Price", "Max.Price")] # subset for better printing
> Cars93_1
Model Min.Price Max.Price
1 Integra 12.9 18.8
2 Legend 29.2 38.7
3 90 25.9 32.3
4 100 30.8 44.6
5 535i 23.7 36.2
6 Century 14.2 17.3
7 LeSabre 19.9 21.7
8 Roadmaster 22.6 24.9
9 Riviera 26.3 26.3
10 DeVille 33.0 36.3
>
>
> Cars93_1 <- mutate(Cars93_1,
+ Price_range = Max.Price - Min.Price,
+ Price_Min_Max_ratio = Max.Price / Min.Price)
>
>
> Cars93_1
Model Min.Price Max.Price Price_range Price_Min_Max_ratio
1 Integra 12.9 18.8 5.9 1.457364
2 Legend 29.2 38.7 9.5 1.325342
3 90 25.9 32.3 6.4 1.247104
4 100 30.8 44.6 13.8 1.448052
5 535i 23.7 36.2 12.5 1.527426
6 Century 14.2 17.3 3.1 1.218310
7 LeSabre 19.9 21.7 1.8 1.090452
8 Roadmaster 22.6 24.9 2.3 1.101770
9 Riviera 26.3 26.3 0.0 1.000000
10 DeVille 33.0 36.3 3.3 1.100000
|
{dplyr} 패키지의 mutate() 함수는 하나의 함수 명령문 안에서 신규로 생성한 변수를 바로 다른 신규 생성 변수의 input 변수로 사용할 수 있어서 편리합니다.
반면에, {base} 패키지의 transform() 함수는 하나의 명령문 안에 신규 변수를 다른 신규 변수의 input 변수로 사용할 수 없습니다. R 초급자가 transform() 함수 쓰다가 '객체가 없다고? 이게 왜 안되고 에러가 나지?'하면서 에러 메시지의 의미를 잘 이해를 못하고 어찌 조치를 취해야 할지 몰라 애를 먹는 부분이기도 합니다. 아래의 예제를 참고하시면 이해가 될 것입니다.
> # comparison with {dplyr} mutate() and {base} transform()
> # : mutate() allows you to refer to columns that you’ve just created
> Cars93_1 <- Cars93[c(1:10), c("Model", "Min.Price", "Max.Price")] # subset for better printing
> Cars93_1
Model Min.Price Max.Price
1 Integra 12.9 18.8
2 Legend 29.2 38.7
3 90 25.9 32.3
4 100 30.8 44.6
5 535i 23.7 36.2
6 Century 14.2 17.3
7 LeSabre 19.9 21.7
8 Roadmaster 22.6 24.9
9 Riviera 26.3 26.3
10 DeVille 33.0 36.3
>
> Cars93_2 <- mutate(Cars93_1,
+ Price_range = Max.Price - Min.Price,
+ Price_range_cd = ifelse(Price_range >= 5, 1, 0))
>
> Cars93_2
Model Min.Price Max.Price Price_range Price_range_cd
1 Integra 12.9 18.8 5.9 1
2 Legend 29.2 38.7 9.5 1
3 90 25.9 32.3 6.4 1
4 100 30.8 44.6 13.8 1
5 535i 23.7 36.2 12.5 1
6 Century 14.2 17.3 3.1 0
7 LeSabre 19.9 21.7 1.8 0
8 Roadmaster 22.6 24.9 2.3 0
9 Riviera 26.3 26.3 0.0 0
10 DeVille 33.0 36.3 3.3 0
> > # {base} transform()
> Cars93_3 <- transform(Cars93_1,
+ Price_range = Max.Price - Min.Price,
+ Price_range_cd = ifelse(Price_range >= 5, 1, 0))
Error in ifelse(Price_range >= 5, 1, 0) : object 'Price_range' not found
|
위의 예제에서 {base} 패키지의 transform() 함수로 Price_range_cd 신규 변수를 만들려면 (1) Price_range 신규 변수를 먼저 생성하고 -> (2) Price_range_cd 신규 변수를 생성하는 두단계 절차(multi steps)를 나누어서 밟아야 합니다. mutate() 함수 대비 조금 더 불편하지요?
(3-2) transmute(dataframe, 새로운 변수 = 기존 변수 조합한 수식, ...)
: 신규 변수만 keep
위의 mutate() 함수가 기존 변수와 신규 변수를 모두 가지고 있는데 반해서, transmute() 함수는 신규 변수만 저장을 하고 기존 변수는 날려버립니다.
최소가격(Min.Price)과 최대가격(Max.Price)의 범위(range), 최소가격 대비 최대가격의 비율(=Max.Price/Min.Price) 의 새로운 변수를 생성한 후에, 이들 2개의 신규변수만 남겨두시오.
> # transmute() : Create(add) new columns
> Cars93_1 <- Cars93[c(1:10), c("Model", "Min.Price", "Max.Price")] # subset for better printing
> Cars93_1
Model Min.Price Max.Price
1 Integra 12.9 18.8
2 Legend 29.2 38.7
3 90 25.9 32.3
4 100 30.8 44.6
5 535i 23.7 36.2
6 Century 14.2 17.3
7 LeSabre 19.9 21.7
8 Roadmaster 22.6 24.9
9 Riviera 26.3 26.3
10 DeVille 33.0 36.3
>
> Cars93_4 <- transmute(Cars93_1,
+ Price_range = Max.Price - Min.Price,
+ Price_Min_Max_ratio = Max.Price / Min.Price)
>
> Cars93_4
Price_range Price_Min_Max_ratio
1 5.9 1.457364
2 9.5 1.325342
3 6.4 1.247104
4 13.8 1.448052
5 12.5 1.527426
6 3.1 1.218310
7 1.8 1.090452
8 2.3 1.101770
9 0.0 1.000000
10 3.3 1.100000
|
(4-1) summarise(dataframe, mean, sd, ...) : 수치형 값에 대한 요약 통계량 계산
summarise() 함수이 제공하는 수치형 데이터에 대한 요약 통계량 옵션을 소개하자면요,
- mean(x, na.rm = TRUE) : 평균, 결측값을 제외하고 계산하려면 na.rm = TRUE 추가
- median(x, na.rm = TRUE) : 중앙값
- sd(x, na.rm = TRUE) : 표준편차
- min(x, na.rm = TRUE) : 최소값
- max(x, na.rm = TRUE) : 최대값
- IQR(x, na.rm = TRUE) : 사분위수 (Inter Quartile Range = Q3 - Q1)
- sum(x, na.rm = TRUE) : 합, 결측값을 제외하고 계산하려면 na.rm = TRUE 추가
[예제] Cars93 데이터 프레임에서 가격(Price)의 (a) 평균, (b) 중앙값, (c) 표준편차, (d) 최소값, (e) 최대값, (f) 사분위수(IQR), (g) 합계를 구하시오. (단, 결측값은 포함하지 않고 계산함)
> # summarise() : Summarise numeric values
> # > mean(), median(), sd(), min(), max(), IQR(), sum()
> # IQR : IQR(Inter quartile Range) = Upper Quartile - Lower Quartile
> summarise(Cars93,
+ Price_mean = mean(Price, na.rm = TRUE), # mean of Price
+ Price_median = median(Price, na.rm = TRUE), # median of Price
+ Price_sd = sd(Price, na.rm = TRUE), # standard deviation of Price
+ Price_min = min(Price, na.rm = T), # min of Price
+ Price_max = max(Price, na.rm = T), # max of Price
+ Price_IQR = IQR(Price), na.rm = T, # IQR of Price
+ Price_sum = sum(Price, na.rm = TRUE)) # sum of Price
Price_mean Price_median Price_sd Price_min Price_max Price_IQR na.rm Price_sum
1 19.50968 17.7 9.65943 7.4 61.9 11.1 TRUE 1814.4
|
(4-2) summarise(dataframe, n(), n_distinct(x), first(x), last(x), nth(x, n))
: 개수 계산, 관측값 indexing
- n() : 관측치 개수 계산, x 변수 입력하지 않음
- n_distinct(x) : 중복없는 유일한 관측치 개수 계산, 기준이 되는 x변수 입력함
- first(x) : 기준이 되는 x변수의 첫번째 관측치
- last(x) : 기준이 되는 x변수의 마지막 관측치
- nth(x, n) : 기준이 되는x변수의 n번째 관측치
[예제] Cars93_1 데이터 프레임에서 (a) 총 관측치의 개수, (b) 제조사(Manufacturer)의 개수(유일한 값), (c) 첫번째 관측치의 제조사 이름, (d) 마지막 관측치의 제조사 이름, (e) 5번째 관측치의 제조사 이름은?
> # summarise() : n(), n_distinct(), first(), last(), nth()
> Cars93_1 <- Cars93[c(1:10), c("Manufacturer", "Model", "Type")] # subset for better print
> Cars93_1
Manufacturer Model Type
1 Acura Integra Small
2 Acura Legend Midsize
3 Audi 90 Compact
4 Audi 100 Midsize
5 BMW 535i Midsize
6 Buick Century Midsize
7 Buick LeSabre Large
8 Buick Roadmaster Large
9 Buick Riviera Midsize
10 Cadillac DeVille Large
>
> summarise(Cars93_1,
+ tot_cnt = n(), # counting the number of all observations
+ Manufacturer_dist_cnt = n_distinct(Manufacturer), # distinct number of var
+ First_obs = first(Manufacturer), # first observation
+ Last_obs = last(Manufacturer), # last observation
+ Nth_5th_obs = nth(Manufacturer, 5)) # n'th observation
tot_cnt Manufacturer_dist_cnt First_obs Last_obs Nth_5th_obs
1 10 5 Acura Cadillac BMW
|
(4-3) summarise(group_by(dataframe, factor_var), mean, sd, ...) : 그룹별 요약 통계량 계산
그룹별 계산(Grouped operations)을 위해서 group_by() 함수를 이용합니다.
[예제] Cars93 데이터 프레임에서 '차종(Type)' 별로 구분해서 (a) 전체 관측치 개수, (b) (중복 없이 센) 제조사 개수, (c) 가격(Price)의 평균과 (d) 가격의 표준편차를 구하시오. (단, 결측값은 포함하지 않고 계산함)
> # summarise by group
> grouped <- group_by(Cars93, Type)
> summarise(grouped,
+ tot_conut = n(), # counting the number of cars
+ Manufacturer_dist_cnt = n_distinct(Manufacturer), # distinct number of var
+ Price_mean = mean(Price, na.rm = TRUE), # mean of Price
+ Price_sd = sd(Price, na.rm = TRUE) # standard deviation of Price
+ )
# A tibble: 6 x 5
Type tot_conut Manufacturer_dist_cnt Price_mean Price_sd
<fctr> <int> <int> <dbl> <dbl>
1 Compact 16 15 18.21250 6.686890
2 Large 11 10 24.30000 6.337507
3 Midsize 22 20 27.21818 12.264841
4 Small 21 16 10.16667 1.953288
5 Sporty 14 12 19.39286 7.974716
6 Van 9 8 19.10000 1.878164
|
(4) summarise_each() : 다수의 변수에 동일한 summarise 함수 적용
[문제] Cars93 데이터 프레임의 (i) 가격(Price) 변수와 (ii) 고속도로연비(MPG.highway) 의 두개의 변수에 대해 (a) 평균(mean), (b) 중앙값(median), (c) 표준편차(standard deviation) 의 3개의 함수를 동시에 적용하여 계산하시오.
> # summarize_each() : applies the same summary function(s) to multiple variables
> summarise_each(Cars93, funs(mean, median, sd), Price, MPG.highway)
Price_mean MPG.highway_mean Price_median MPG.highway_median Price_sd MPG.highway_sd
1 19.50968 29.08602 17.7 28 9.65943 5.331726
|
[Reference]
- Introduction to dplyr (http://127.0.0.1:21980/library/dplyr/doc/introduction.html)
- dplyr functions for single dataset (http://stat545.com/block010_dplyr-end-single-table.html)
- dplyr tutorial (http://genomicsclass.github.io/book/pages/dplyr_tutorial.html)
많은 도움이 되었기를 바랍니다.
이전 포스팅과 이번 포스팅으로서 dplyr 패키지의 데이터 전처리, 조작을 위한 기본기를 익혔습니다.
다음번 포스팅에서는 R dplyr 패키지의 심화 함수로서, chain operations (Operator %>%, shift+ctrl+M) 에 대해서 소개하겠습니다.
이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.