'r'에 해당되는 글 210건

  1. 2016.09.18 [R] 데이터 전처리, 조작을 위한 dplyr 패키지 : distinct(), sample_n(), sample_frac(), mutate(), transmute(), summarise(), summarise_each(), group_by() 함수 (2)
  2. 2016.09.12 [R] 데이터전처리, 조작을 위한 dplyr 패키지 : filter() 함수, slice() 함수, arrange() 함수, select() 함수, rename() 함수 (25)
  3. 2016.09.04 [R] 다변량 정규분포 확률밀도함수(multivariate normal distribution probability density function) (2)
  4. 2016.09.03 [Hive] R로 Hive에 접속해서 데이터 추출하기, R DB connect (R, Hive dbConnect) : rJava, RJDBC, dbConnect(), dbGetQuery(), R로 PostgreSQL 사용하는 방법, R로 MySQL 접속해서 사용하는 방법 (4)
  5. 2016.08.30 [R] 누적 합(cumulative sums) : cumsum(), 누적 곱(cumulative products) : cumprod(), 누적 최소값(cumulative minima) : cummin(), 누적 최대값(cumulative maxima) : cummax()
  6. 2016.08.15 [R 군집분석 (Cluster Analysis)] (3) 퍼지 군집 (Fuzzy Clustering) : Fuzzy C-means Clustering Algorithm (FCM)
  7. 2016.08.06 [R 군집분석 (Clsuter Analysis) ] (2) K-중심 군집(K-Centroid Clustering) : K-means Clustering (2)
  8. 2016.07.16 [R 군집분석 (Cluster Analysis)] (1) 응집형 계층적 군집화 : (1-5) Ward 연결법 (Ward Linkage Method) (11)
  9. 2016.07.10 [R] 여러개의 데이터프레임을 한꺼번에 하나의 데이터프레임으로 묶기, data.table package : rbindlist(data) (2)
  10. 2016.07.09 [R] 지수표기을 숫자표기로 바꾸는 R 옵션, options(scipen=100), format(scientific=FALSE) (4)

데이터 전처리, 조작을 위한 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) 값 요약 : summarise()

 

(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) 에 대해서 소개하겠습니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 꾸리꾸리 2019.08.12 10:44  댓글주소  수정/삭제  댓글쓰기

    역으로 중복된것만 추출하려면 어떻게 해야하나요?

이번 포스팅에서는 데이터프레임(dataframe)을 위한 데이터 전처리, 조작(data pre-processing, data manipulation)을 쉽고! 빠르게! 할 수 있도록 해주는 dplyr 패키지에 대해서 알아보겠습니다.

 

R help에 검색해보면 dplyr 패키지를 아래와 같이 소개하고 있습니다.

 

dplyr은 유연한 데이터 조작의 문법을 제공합니다.  이것은 plyr의 차기작으로서, 데이터프레임을 집중적으로 다루는 툴입니다

 

dplyr provides a flexible grammar of data manipulation. It's the next iteration of plyr, focused on tools for working with data frames (hence the d in the name)

 

 

데이터 조작을 위한 문법으로 체계화를 해서 한번 배워놓으면 쉽다는 점과 더불어, C언어로 만들어서 매우 빠르다는 점도 dplyr 패키지의 크나큰 장점 중의 하나입니다.

 

 

 

아마 R을 좀 다룬 분이라면은 그래프/시각화를 문법으로 승화시켜서 체계를 잡아놓은 "ggplot2" package ( "ggplot is an implementation of the grammer of Graphics in R")를 알고 계실텐데요, 이 ggplot2를 만든 그 유명한 Hadley Wickham 이 dplyr 패키지도 만들었습니다.  

 

아래에 소개한 것처럼, r-bloggers.com에 소개되어 있는 Hadley 인 인터뷰를 보면, 기존 통계학에서는 데이터 전처리(Data munging and manipulation)를 "내 일이 아니야"라고 무시했었다고 합니다. 그런데 Hadley Wickham이 보기에는 모델링과 시각화로 부터 통찰을 끄집에 내는데 있어서 데이터 조작, 전처리가 매우 중요하고 또 어려운 영역이라고 보기에 Hadley가 직접 나서서 이를 도와줄 수 있는 R packages를 만들었다고 나오네요.  ^^b

"Hadley Wickham on why he created all those R packages"

July 27, 2015 By David Smith

 

“There are definitely some academic statisticians who just don’t understand why what I do is statistics, but basically I think they are all wrong . What I do is fundamentally statistics. The fact that data science exists as a field is a colossal failure of statistics.

 

To me, that is what statistics is all about. It is gaining insight from data using modelling and visualizationData munging and manipulation is hard and statistics has just said that’s not our domain.”

* source : https://www.r-bloggers.com/hadley-wickham-on-why-he-created-all-those-r-packages/

 

 

Hadley Wickham의 Github repository 주소는요 https://github.com/hadley?tab=repositories  입니다. 여기 가보시면 엄청나게 많은 R packages들에 입이 쩍 벌어질겁니다. 만약에 노벨상에 R community에 기여한 공로를 치하하는 상으로 '노벨 R 상'이 있다면 Hadley Wickham이 그 첫번째 수상자가 된다고 해도 전혀 이상할게 없을 정도로 정말 R의 확산에 지대한 공헌을 하신 분입니다.

 

서론이 길었습니다.  ggplot2가 시각화/그래프에 관한한 우리를 실망시키지 않았듯이, dplyr 또한 데이터 전처리에 관한한 coverage의 방대함과 문법의 명료함이 우리를 매료시킬 것이라고 생각하며, 하나씩 예를 들어 설명을 시작해 보겠습니다.

 

아래의 설명은 browseVignettes(package = "dplyr") 치면 팝업으로 나오는 "Introduction to dplyr" HTML 페이지를 참조하였습니다.

 

예제로 사용할 데이터는 MASS 패키지에 들어있는 Cars93 데이터프레임입니다.  원래는 93개의 자동차 관측치에 27개의 변수를 가지고 있는데요, 예시들기 편하도록 앞에서부터 변수 8개만 선택해서 사용하겠습니다.  (Cars93_1 dataframe) 

 

> library(MASS)
> # subset Cars93
> Cars93_1 <- Cars93[, 1:8]
> str(Cars93_1)
'data.frame':	93 obs. of  8 variables:
 $ Manufacturer: Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model       : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type        : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price   : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price       : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price   : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city    : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway : int  31 25 26 26 30 31 28 25 27 25 ...

 

 

 

 

단일 테이블을 대상으로 하는 dplyr 패키지의 함수들(Single table verbs)을 표로 정리해보면 아래와 같습니다.

 

 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 keeps the new columns.

 {base} transform

 summarise()

 Summarise values

 {base} summary

 

 

하나씩 설명을 이어가보면요,

 

(1) 데이터 프레임의 행 부분집합 선별 (select row subset from data frame) : filter(), slice()

 

 

(1-1) filter(dataframe, filter condition 1, filter condition 2, ...)

    : &(AND) 조건으로 row 데이터 부분집합 선별 (select a subset of rows in a data frame with 'AND' condition)

 

Cars93_1 데이터 프레임에 차종(Type)별로 보면 Compact 차종이 총 16개 있음을 알 수 있습니다. 

 

[문제] 차종(Type)이 "Compact"이면서 & 최대가격(Max.Price)이 20 백$ 이하이고 & 고속도로 연비(MPG.highway) 가 30 이상인 관측치를 선별하시오.

 

위 문제는 아래와 같이 dplyr의 filter() 함수를 사용하면 됩니다.  참고로, subset() 함수의 subset과 동일한 기능을 합니다.

 

> # number of cars by Type
> table(Cars93_1$Type)

Compact   Large Midsize   Small  Sporty     Van 
     16      11      22      21      14       9 
> 
> install.packages("dplyr")
> library(dplyr)
> # filter() : select a subset of rows in a data frame
> filter(Cars93_1, Type == c("Compact"), Max.Price <= 20, MPG.highway >= 30)
  Manufacturer    Model    Type Min.Price Price Max.Price MPG.city MPG.highway
1    Chevrolet Cavalier Compact       8.5  13.4      18.3       25          36
2    Chevrolet  Corsica Compact      11.4  11.4      11.4       25          34
3        Mazda      626 Compact      14.3  16.5      18.7       26          34
4       Nissan   Altima Compact      13.0  15.7      18.3       24          30
5   Oldsmobile  Achieva Compact      13.0  13.5      14.0       24          31
6      Pontiac  Sunbird Compact       9.4  11.1      12.8       23          31

 

 

 

 

(1-2) filter(dataframe, filter condition 1 | filter condition 2 | ...)

    : |(OR) 조건으로 row 데이터 부분집합 선별 (select a subset of rows in a data frame with 'OR' condition)

 

OR(또는) 조건으로 부분집합을 선별하려면 '|'를 사용하면 됩니다. (subset() 함수와 동일)

 

[문제] 차종(Type)이 "Compact"이거나 |(OR) 최대가격(Max.Price)이 20 백$ 이하이거나 |(OR) 고속도로 연비(MPG.highway) 가 30 이상인 관측치를 선별하시오.

 

위 문제는 래와 같이 dplyr의 filter() 함수를 사용하면 됩니다.  

 

> # filter(dataframe, condition1 | condition2) : or
> filter(Cars93_1, Type == c("Compact") | Max.Price <= 20 | MPG.highway >= 30)
    Manufacturer         Model    Type Min.Price Price Max.Price MPG.city MPG.highway
1          Acura       Integra   Small      12.9  15.9      18.8       25          31
2           Audi            90 Compact      25.9  29.1      32.3       20          26
3            BMW          535i Midsize      23.7  30.0      36.2       22          30
4          Buick       Century Midsize      14.2  15.7      17.3       22          31
5      Chevrolet      Cavalier Compact       8.5  13.4      18.3       25          36
6      Chevrolet       Corsica Compact      11.4  11.4      11.4       25          34
7      Chevrolet        Camaro  Sporty      13.4  15.1      16.8       19          28

     .....  이하 생략 (총 58개 관측치) ..... 

 

 

 

 

(1-3) slice(dataframe, from, to) : 위치를 지정해서 row 데이터 부분집합 선별하기 

 

filter()가 조건에 의한 선별이었다면, 위치(position)를 사용해서 부분집합 선별은 slice() 함수를 사용합니다.

 

[문제] Cars93_1 데이터프레임의 6번째에서 10번째 행(row)의 데이터를 선별하시오.

 

> # slice() : select rows by position
> slice(Cars93_1, 6:10)
  Manufacturer      Model    Type Min.Price Price Max.Price MPG.city MPG.highway
1        Buick    Century Midsize      14.2  15.7      17.3       22          31
2        Buick    LeSabre   Large      19.9  20.8      21.7       19          28
3        Buick Roadmaster   Large      22.6  23.7      24.9       16          25
4        Buick    Riviera Midsize      26.3  26.3      26.3       19          27
5     Cadillac    DeVille   Large      33.0  34.7      36.3       16          25

 

 

 

 

 (2) 데이터 프레임 행 정렬하기 (arrange rows of data frame) : arrange()

 

(2) arrange(dataframe, order criterion 1, order criterion 2, ...)

 

데이터 프레임을 정렬할 때 arrange() 함수를 쓰면 매우 편리합니다. 

여러개의 기준에 의해서 정렬을 하고 싶으면 기준 변수를 순서대로 나열하면 됩니다.

기본 정렬 옵셥은 오름차순(ascending)이며, 만약 내림차순(descending) 으로 정렬을 하고 싶다면 desc()를 입력해주면 됩니다.

 

[문제] 고속도로 연비(MPG.highway) 가 높은 순서대로 정렬을 하시오.  만약 고속도로 연비가 동일하다면 최고가격(Max.Price)가 낮은 순서대로 정렬하시오.  (난 연비가 높고 가격은 낮은 차가 좋아~)

 

> # arrange() : reorder rows of data frame
> arrange(Cars93_1, desc(MPG.highway), Max.Price)
    Manufacturer          Model    Type Min.Price Price Max.Price MPG.city MPG.highway
1            Geo          Metro   Small       6.7   8.4      10.0       46          50
2          Honda          Civic   Small       8.4  12.1      15.8       42          46
3         Suzuki          Swift   Small       7.3   8.6      10.0       39          43
4        Pontiac         LeMans   Small       8.2   9.0       9.9       31          41
5         Saturn             SL   Small       9.2  11.1      12.9       28          38
6          Mazda            323   Small       7.4   8.3       9.1       29          37
7         Subaru          Justy   Small       7.3   8.4       9.5       33          37
8         Toyota         Tercel   Small       7.8   9.8      11.8       32          37
9          Mazda        Protege   Small      10.9  11.6      12.3       28          36
10           Geo          Storm  Sporty      11.5  12.5      13.5       30          36

   .... 이하 생략 ....

 

 

 

 

참고로, arrange() 함수 말고도 아래처럼 order() 함수를 사용해서 indexing 하는 방법도 있습니다만, 아무래도 arrange() 함수가 더 깔끔하고 해석하기에 좋습니다.

 

> Cars93[order(-Cars93_1$MPG.highway, Cars93_1$Max.Price), ]
    Manufacturer          Model    Type Min.Price Price Max.Price MPG.city MPG.highway
39           Geo          Metro   Small       6.7   8.4      10.0       46          50
42         Honda          Civic   Small       8.4  12.1      15.8       42          46
83        Suzuki          Swift   Small       7.3   8.6      10.0       39          43
73       Pontiac         LeMans   Small       8.2   9.0       9.9       31          41
79        Saturn             SL   Small       9.2  11.1      12.9       28          38
53         Mazda            323   Small       7.4   8.3       9.1       29          37
80        Subaru          Justy   Small       7.3   8.4       9.5       33          37
84        Toyota         Tercel   Small       7.8   9.8      11.8       32          37
54         Mazda        Protege   Small      10.9  11.6      12.3       28          36
40           Geo          Storm  Sporty      11.5  12.5      13.5       30          36

 

 

 


 

(3) 데이터 프레임 변수 선별하기 : select()

 

(3-1) select(dataframe, VAR1, VAR2, ...) : 선별하고자 하는 변수 이름을 기입

 

[문제] Cars93_1 데이터 프레임으로부터 제조사명(Manufacturer), 최대가격(Max.Price), 고속도로연비(MPG.highway) 3개 변수(칼럼)를 선별하시오.

 

> # select() : Select columns by name
> select(Cars93_1, Manufacturer, Max.Price, MPG.highway)
    Manufacturer Max.Price MPG.highway
1          Acura      18.8          31
2          Acura      38.7          25
3           Audi      32.3          26
4           Audi      44.6          26
5            BMW      36.2          30

   .... 이하 생략 ....

 

 

 

 

(3-2) select(dataframe, VAR_a:VAR_n, ...) : a번째부터 n번째 변수 선별

 

서로 인접해서 줄지어서 있는 변수들을 선별하고자 할 때는 아래의 예시처럼 ':'를 사용하면 됩니다.

 

[문제] Cars93_1 데이터 프레임에서 1번째에 위치한 제조사(Manufacturer) ~ 5번째에 위치한 가격(Price)까지의 서로 이어저 있는 총 5개의 변수들을 선별하시오.

 

> select(Cars93_1, Manufacturer:Price)
    Manufacturer          Model    Type Min.Price Price
1          Acura        Integra   Small      12.9  15.9
2          Acura         Legend Midsize      29.2  33.9
3           Audi             90 Compact      25.9  29.1
4           Audi            100 Midsize      30.8  37.7
5            BMW           535i Midsize      23.7  30.0

     .... 이하 생략 ....

 

 

아래와 같이 서로 인접해서 줄지어서 있는 변수들의 위치를 알고 있으면 (가령 a부터 n번째 위치) 'a:n'처럼 숫자를 직접 입력해주면 바로 위의 결과와 동일한 결과를 얻을 수 있습니다.

 

> select(Cars93_1, 1:5)
    Manufacturer          Model    Type Min.Price Price
1          Acura        Integra   Small      12.9  15.9
2          Acura         Legend Midsize      29.2  33.9
3           Audi             90 Compact      25.9  29.1
4           Audi            100 Midsize      30.8  37.7
5            BMW           535i Midsize      23.7  30.0

     .... 이하 생략 ....

 

 

 

참고로, dplyr 패키지의 select() 함수는 base패키지에 내장되어 있는 subset(dataframe, select=...) 함수와 기능이 같습니다.  아래 subset() 함수 결과가 위와 동일합니다. (동일한 기능을 하는 함수가 여러개 있으니깐 헷갈리지요? ^^;)

 

> subset(Cars93_1, select = c(Manufacturer:Price))
    Manufacturer          Model    Type Min.Price Price
1          Acura        Integra   Small      12.9  15.9
2          Acura         Legend Midsize      29.2  33.9
3           Audi             90 Compact      25.9  29.1
4           Audi            100 Midsize      30.8  37.7
5            BMW           535i Midsize      23.7  30.0

   .... 이하 생략 .... 

 

 

 

 

(3-3) select(dataframe, -(VAR_a:VAR_n)) : a번째부터 n번째 변수는 쏙 빼고 선별

 

다시 dplyr 패키지로 돌아와서요, select() 함수에서 변수 앞에 '-'(minus) 부호를 사용하면 그 변수는 빼고(to drop variables) 선별하라는 뜻입니다.

 

> # select(dataframe, -var1, -var2, ...) : to drop variables
> select(Cars93_1, -(Manufacturer:Price)) Max.Price MPG.city MPG.highway 1 18.8 25 31 2 38.7 18 25 3 32.3 20 26 4 44.6 19 26 5 36.2 22 30

     .... 이하 생략 ....

 

 

 

 

(3-4) select(dataframe, starts_with("xx_name")) : "xx_name"으로 시작하는 모든 변수 선별

 

select() 함수 안에 starts_with() 를 사용해서 "xx_name"으로 시작하는 모든 변수를 선별할 수 있는 재미있는 기능도 가지고 있습니다.

 

[문제] Cars93_1 데이터 프레임에서 "MPG"로 시작하는 모든 변수를 선별하시오.

 

 

> # select(dataframe, starts_with("xx_name"))





> # : select all variables, starting with a "xx_name" prefix
> select(Cars93_1, starts_with("MPG"))
MPG.city MPG.highway 1 25 31 2 18 25 3 20 26 4 19 26 5 22 30
    .... 이하 생략 ....

 

"MPG"로 시작하는 변수가 "MPG.city"(도시 연비), "MPG.highway"(고속도로 연비) 두 개가 있군요.

 

 

 

 

(3-5) select(dataframe, ends_with("xx_name")) : "xx_name"으로 끝나는 모든 변수 선별

 

starts_with가() 있으면 ends_with()도 있지 않을까 싶지요?  네, 맞습니다.  "xx_name"으로 끝나는 모든 변수를 골라내고 싶다면 select() 함수 안에다가 ends_with() 를 추가해주면 됩니다.

 

[문제] Cars93_1 데이터 프레임에서 "Price"로 끝나는 모든 변수를 선별하시오.

 

> # select(dataframe, ends_with("xx_name"))
> #   : select all variables, ending with a "xx_name" prefix
> select(Cars93_1, ends_with("Price"))
   Min.Price Price Max.Price
1       12.9  15.9      18.8
2       29.2  33.9      38.7
3       25.9  29.1      32.3
4       30.8  37.7      44.6
5       23.7  30.0      36.2

    .... 이하 생략 .... 

 

"Price"로 끝나는 변수가 "Min.Price", "Price", "Max.Price" 총 3개가 있군요.

 

 

 

 

(3-6) select(dataframe, contains("xx_name")) : "xx_name"을 포함하는 모든 변수 선별

 

select() 함수에 contains() 를 사용하면 특정 이름을 포함하는 모든 변수를 선별할 수 있습니다. 이때 "xx_name"은 대소문자를 구분하지 않습니다.

 

[문제] Cars93_1 데이터 프레임에 있는 변수들 중에서 "P"를 포함하는 모든 변수를 선별하시오.

 

> # select(dataframe, contains("xx_string"))
> #   : select all variables which contains a "xx_string" literal string
> select(Cars93_1, contains("P"))
      Type Min.Price Price Max.Price MPG.city MPG.highway
1    Small      12.9  15.9      18.8       25          31
2  Midsize      29.2  33.9      38.7       18          25
3  Compact      25.9  29.1      32.3       20          26
4  Midsize      30.8  37.7      44.6       19          26
5  Midsize      23.7  30.0      36.2       22          30

   .... 이하 생략 ....

 

"P"를 포함하는 변수로는 "Type"(<- 이거는 소문자 'p'로서, 대소문자 구분 안함), "Min.Price", "Price", "Max.Price", "MPG.city", "MPG.highway"의 6개 변수가 있군요.

 

 

 

 

(3-7) select(dataframe, matches(".xx_string.")) : 정규 표현과 일치하는 문자열이 포함된 모든 변수 선별

 

역시 대소문자는 구분하지 않습니다.

 

[문제] 변수 문자열 중간에 "P"를 포함하는 변수를 모두 선별하시오

 

> # select(dataframe, matches(".xx_string."))
> #   : Select columns that match a regular expression
> head(select(Cars93_1, matches(".P.")))
     Type Min.Price Max.Price MPG.city MPG.highway
1   Small      12.9      18.8       25          31
2 Midsize      29.2      38.7       18          25
3 Compact      25.9      32.3       20          26
4 Midsize      30.8      44.6       19          26
5 Midsize      23.7      36.2       22          30
6 Midsize      14.2      17.3       22          31

 

> head(select(Cars93_1, matches("P"))) # exactly the same with contains("P")
     Type Min.Price Price Max.Price MPG.city MPG.highway
1   Small      12.9  15.9      18.8       25          31
2 Midsize      29.2  33.9      38.7       18          25
3 Compact      25.9  29.1      32.3       20          26
4 Midsize      30.8  37.7      44.6       19          26
5 Midsize      23.7  30.0      36.2       22          30
6 Midsize      14.2  15.7      17.3       22          31

 

위에 match() 옵션 안에다가 앞에 예제에는 (".P.")를, 뒤의 예제에는 점이 없이 ("P")를 사용했는데요, 그 결과를 보고 차이를 아시겠는지요?  앞뒤로 '.'(dot) 을 붙이면 시작과 끝 말고 변수명 중간에만 특정 문자열이 포함된 변수만 선별하라는 뜻입니다.  matches(".P.") 로 한 경우에는 "P"로 시작하는 "Price" 변수가 없는 반면에, 그냥 matches("P")로 한 경우는 "P"로 시작하는 "Price"변수가 포함되어 있습니다.

 

참고로, '.'(dot) 이 없이 matches()를 쓰면 contains() 와 동일한 결과를 반환합니다.

 

 

 

 

(3-8) select(dataframe, one_of(vars)) : 변수 이름 그룹에 포함된 모든 변수 선별

 

[문제] "Manufacturer", "MAX.Price", "MPG.highway" 의 3개 변수이름을 포함하는 변수 그룹이 있다고 할 때, Cars93 데이터 프레임에서 이 변수 그룹에 있는 변수가 있다면(<- 즉, 있을 수도 있지만 없을 수도 있다는 뜻임!) 모두 선별하시오.

 

> # select(dataframe, one_of(vars))
> #   : Select columns that are from a group of names
> vars <- c("Manufacturer", "MAX.Price", "MPG.highway")
> head(select(Cars93_1, one_of(vars)))
  Manufacturer MPG.highway
1        Acura          31
2        Acura          25
3         Audi          26
4         Audi          26
5          BMW          30
6        Buick          31
Warning message:
In one_of(vars) : Unknown variables: `MAX.Price`

 

 

위의 결과를 보니 "MAX.Price"라는 변수는 "Unknown variables"라고 해서 Warning mesage가 뜨는군요.  Cars93에 보면 "Max.Price"라는 변수는 있어도 "MAX.Price"라는 변수는 없거든요.  이처럼 변수 그룹 vars 에 나열된 이름 중에서 데이터 프레임에 포함된 변수는 선별해서 반환을 해주고, 만약 해당 이름의 변수가 없다면 그냥 Warning message를 제시해주는 것으로 잘 실행이 됩니다.

 

 

반면에 그냥 select() 함수로 위의 변수 그룹을 선별하라고 해보면요, 아래처럼  "object 'MAX.Price' not found" error 메시지와 함께 아예 실행이 안되요.  one_of() 함수가 어떤 때 쓰는건지 이제 이해하시겠지요?!

 

> select(Cars93_1, Manufacturer, MAX.Price, MPG.highway)
Error in eval(expr, envir, enclos) : object 'MAX.Price' not found

 

 

 

 

 

(3-9) select(dataframe, num_range("V", a:n)) : 접두사와 숫자 범위를 조합해서 변수 선별

 

변수 이름이 동일하게 특정 접두사로 시작하는 데이터 프레임의 경우 유용하게 사용할 수 있는 함수입니다.

 

[문제] "V1", "V2", "V3", "V4"의 4개 변수를 가진 df 데이터 프레임에서 "V2", "V3" 변수를 선별하시오. 단, 이때 접두사 "V"와 숫자 범위 2:3 을 조합해서 쓰는 num_range() 옵션을 사용하시오.

 

> # select(df, num_range("V", a:n))
> #   : Select columns from num_range a to n with a prefix
> V1 <- c(rep(1, 10))
> V2 <- c(rep(1:2, 5))
> V3 <- c(rep(1:5, 2))
> V4 <- c(rep(1:10))
> 
> df <- data.frame(V1, V2, V3, V4)
> df
   V1 V2 V3 V4
1   1  1  1  1
2   1  2  2  2
3   1  1  3  3
4   1  2  4  4
5   1  1  5  5
6   1  2  1  6
7   1  1  2  7
8   1  2  3  8
9   1  1  4  9
10  1  2  5 10
> 
> select(df, num_range("V", 2:3))
   V2 V3
1   1  1
2   2  2
3   1  3
4   2  4
5   1  5
6   2  1
7   1  2
8   2  3
9   1  4
10  2  5

 

 

 

 

 

 

(4) 데이터 프레임 변수 이름 변경하기 : rename()

 

dpylr 패키지의 rename() 함수는 rename(dataframe, new_var1 = old_var1, new_var2 = old_var2, ...) 의 형식으로 사용합니다. 

 

   - 새로운 변수 이름을 앞에, 이전 변수이름을 뒤에 위치시킵니다.

   - 큰 따옴표 안씁니다.  그냥 변수 이름만 쓰면 됩니다.

   - 이름을 변경하고자 하는 변수가 여러개 일 경우 ',' (comma)로 구분해서 연속해서 써줍니다.

 

 

[문제] Cars93_1 데이터 프레임의 8개 변수명 앞에 'New_' 라는 접두사(prefix)를 붙여서 변수 이름을 바꾸시오.

 

> # rename() : rename column name
> names(Cars93_1) 
[1] "Manufacturer" "Model"        "Type"         "Min.Price"    "Price"        "Max.Price"   
[7] "MPG.city"     "MPG.highway" 
> 

> # rename(dataframe, new_var1 = old_var1, new_var2 = old_var2, ...)
> Cars93_2 <- rename(Cars93_1,
+ New_Manufacturer = Manufacturer, + New_Model = Model, + New_Type = Type, + New_Min.Price = Min.Price, + New_Price = Price, + New_Max.Price = Max.Price, + New_MPG.city = MPG.city, + New_MPG.highway = MPG.highway) > > names(Cars93_2) [1] "New_Manufacturer" "New_Model" "New_Type" "New_Min.Price" [5] "New_Price" "New_Max.Price" "New_MPG.city" "New_MPG.highway"

 

 

 

이전에 plyr 패키지의 rename() 함수나 reshaple 패키지의 rename() 함수를 사용하던 분이라면 완전 헷갈리실 겁니다.  큰 따옴표("var_name")를 써야 하는건지 말아야 하는건지, 새로운 변수 이름(new_var)과 이전 변수 이름(old_var)의 위치가 앞인지 뒤인지, 변수가 여러개인 경우 c() 로 묶어주어야 하는지 아닌지가 패키지별로 조금씩 다르거든요. (참고  링크=> http://rfriend.tistory.com/41 ) 

 

저도 매번 헷갈립니다. -_-;  그래프는 ggplot2로 단일화해 나가듯이... 데이터 전처리는 dplyr 패키지로 단일화해 나가는 것도 헷갈림을 줄일 수 있는 좋은 전략이라고 생각합니다. (기존에 익혔던 함수가 아깝고 불편함을 못느낀다면 현상 유지도 물론 ok.)

 


dplyr 패키지의 distinct(), sample_n(), sample_frac(), mutate(), transmute(), summarise() 함수에 대해서는 다음번 포스팅에서 소개하겠습니다.

 

많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~ ♡'를 꾸욱 눌러주세요. ^^

 

 

 

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. RRR 2016.10.17 21:53  댓글주소  수정/삭제  댓글쓰기

    언제나 좋은 글 감사합니다.
    근데 혹시 32비트 윈도우에서 해당 dplyr 패키지 특정 함수가 에러나는 문제에 대해서는 해결방법을 알고 계시는 지 여쭤볼 수 있을까요? 예를 들어, arrange함수를 사용하면, 백이면 백 "This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information." 와 같은 오류메시지가 뜨면서 프로그램이 강제 종료되네요. ㅠㅜ

    • R Friend R_Friend 2016.10.17 21:59 신고  댓글주소  수정/삭제

      RRR님, 저는 64bit로 써서 그런지 몰라도 언급해 주신 에러 메시지는 저도 처음 보네요.

      arrange() 함수는 window function 이라서 만약 데이터 사이즈를 감당 못할 정도로 메모리가 부족하면 강제로 종료될 수도 있을거 같습니다.

  2. sokys 2017.04.05 15:07  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 블로그 잘 읽고 있습니다.

    start_with 같은 기능을 변수 말고 행에 적용할 수는 없을까요?
    예를 들면, 학생부 데이터 school에서 Names라는 변수에 학생들의 이름이 있다고 하면,
    J로 시작하는 이름의 학생들만을 선별하고 싶다고 할 때, 어떻게 하면 될까요??

    data.frame(t(school)) 해서 colnames을 저 변수로 해서 하면 되기는 하겠는데.. 너무 복잡해서요 ㅎㅎ

    • R Friend R_Friend 2017.07.16 00:12 신고  댓글주소  수정/삭제

      안녕하세요 sokys 님.
      답글이 너무 늦었습니다. 문의 댓글이 달린줄 모르고 있다가 최근에 아래의 다른분 문의 댓글을 보면서 알게 되었습니다. ^^;
      아마도 지금 댓글을 달면 못보실 것 같기는 한데요. ㅠ_ㅜ , 그래도 혹시 모르니 답변 달아놓습니다. substr() 함수를 사용하면 됩니다.

      > num <- c(1, 2, 3, 4, 5)
      > name <- c("Kim", "Jeung", "Lee", "Jang", "Choi")
      > school.df <- data.frame(num, name)
      > school.df
      num name
      1 1 Kim
      2 2 Jeung
      3 3 Lee
      4 4 Jang
      5 5 Choi
      >
      > school.df_J <- filter(school.df, substr(school.df[,'name'], 1, 1) == 'J')
      > school.df_J
      num name
      1 2 Jeung
      2 4 Jang


      문자열 처리 함수에 대해서 정리한 포스팅은 아래 참고하세요.

      http://rfriend.tistory.com/37

  3. practice 2017.07.11 13:53  댓글주소  수정/삭제  댓글쓰기

    Error in match.arg(method) : 'arg' must be NULL or a character vector

    처음 filter 구문과 arrange 구문을 사용하면 해당 에러가 계속 나오는데 뭐가 문제일까요?

    • R Friend R_Friend 2017.07.15 23:11 신고  댓글주소  수정/삭제

      안녕하세요 practice님, 답변이 늦어서 죄송합니다.

      'arg' must be NULL or a character vector 에러는 함수 안에 argument 쓸 때 순서 상 들어가야 하는데 없거나, 없어야 하는데 이상한게 들어가 있거나 하면 발생합니다. filter, arrange 함수 안에 들어가는 argument에 뭔가 잘못이 있는 것 같습니다.

      filter 구문과 arrange 구문을 함께 사용해본 예제 아래에 공유합니다.

      library(MASS)
      library(dplyr)
      Cars93_1 <- Cars93[, 1:8]
      filter(Cars93_1, Type == c("Compact") & Max.Price <= 20 & MPG.highway >= 30) %>%
      arrange(desc(MPG.highway), Max.Price)

      Manufacturer Model Type Min.Price Price Max.Price MPG.city MPG.highway
      1 Chevrolet Cavalier Compact 8.5 13.4 18.3 25 36
      2 Chevrolet Corsica Compact 11.4 11.4 11.4 25 34
      3 Mazda 626 Compact 14.3 16.5 18.7 26 34
      4 Pontiac Sunbird Compact 9.4 11.1 12.8 23 31
      5 Oldsmobile Achieva Compact 13.0 13.5 14.0 24 31
      6 Nissan Altima Compact 13.0 15.7 18.3 24 30

  4. eun 2017.11.09 10:58  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 ^^ Github 언급이 있으셔서 여쭤봅니다.
    Github에서 패키지(예시 파일 전체)를 다운받았는데
    저는 기존 RData 파일(ex_sizeMat.RData)을 csv로 불러와서 기존형식에서 데이터만 제것으로(값) 바꿨구요, 다른이름으로 저장(mydata.Rdata)해서 불러왔지만 파일에 제 RData 파일이 아닌 내장된(기존에 저장되있는 sizeMat.RData) 파일만 읽네요..

    RData 안에 함수들이 기존 파일 명(sizeMat)으로 되있어서 이름이 다른 제것을 안읽는건지 아니면 중간에 다른 과정이 필요한건지 궁금해서 문의드립니다.ㅎㅎ

    초보 R 사용자에게 너무 고마운 블로그입니다 ^^ 앞으로도 번창하세요!!!

  5. 무구묵구 2019.01.09 14:29 신고  댓글주소  수정/삭제  댓글쓰기

    제가 for문으로 다음과 같이 2007~2016년까지 3년씩 반복하여서 데이터를 추출하려고 코딩을 하였는데... filter로 하면 3개년씩 반복되지 않고 마지막 년도의 평균, 분산만 산출되더라고요... 아래 제가 짠 for문 좀 봐주십사 글 남깁니다.

    for (x in 2007:2016){
    + assign(paste("year_", x, "to",x+2,sep=""), F4_data %>% group_by(Location) %>% filter(year %in% c(x:x+2)) %>%
    + summarise(mean_BOD = mean(ln_BOD), var_BOD = var(ln_BOD), ave_BOD = exp(mean_BOD+var_BOD/2),
    + mean_TP = mean(ln_TP), var_TP = var(ln_TP), ave_TP = exp(mean_TP+var_TP/2)))
    + }

    • R Friend R_Friend 2019.01.09 15:26 신고  댓글주소  수정/삭제

      안녕하세요 무구묵구님,

      (1) filter 부분에서 c(x:x+2) 를 c(x, x+1, x+2) 로 바꾸어서 해보시기 바랍니다.
      (방법1) filter(year %in% c(x, x+1, x+2))

      (2) 혹은 x+2 부분에 괄호를 한번 더 쳐주어도 되구요.
      (방법2) filter(year %in% c(x:(x+2)))

    • 무구묵구 2019.01.09 16:09 신고  댓글주소  수정/삭제

      글 올리자마자 감사드립니다.
      쉽게 해결되었네요^^ 감사드립니다.

  6. 무구묵구 2019.03.04 18:14 신고  댓글주소  수정/삭제  댓글쓰기

    질문이 있어서 다시한번 글을 올립니다. 자료를 전처리 하는 과정에서 지점에 따른 연도별 일 자료가 있습니다. 지점별로 연도별 85%이상되는 값을 제외하고 코딩을 하려고 하는데... 어찌해야할지...궁금하여서 글 올립니다.

    • R Friend R_Friend 2019.03.04 18:16 신고  댓글주소  수정/삭제

      filter 를 이용하면 될 듯 합니다.

    • 무구묵구 2019.03.05 14:38 신고  댓글주소  수정/삭제

      xcx <- F3_data %>% group_by(Location, year) %>% filter(X1 < quantile(X1, probs = 0.85) & X2 < quantile(X2, probs = 0.85))
      말씀하신대로 85퍼센타일 이하의 값만 하려고 filter를 사용하였는데요...85퍼센타일값이 전체값의 퍼센타일 값으로 읽혀버리네요...저는 그룹핑한거처럼 Location, year별로 85퍼센타일을 구하고 그 이하의 값만 사용하려고 하는데 잘 안되네요. 한번 봐주십시오ㅠ

    • R Friend R_Friend 2019.03.05 15:23 신고  댓글주소  수정/삭제

      안녕하세요.
      한번에 chain으로 하기가 좀 애매해서요, 2개 step으로 나누어서 quantile 먼저 계산하고, merge 후에, filter 하는 방식으로 해보시지요.

      df2 <- df %>%
      group_by(grp, grp2) %>%
      summarise(x1_qt_85 = quantile(x1, probs=0.85),
      x2_qt_85 = quantile(x2, probs=0.85))

      df3 <- merge(df, df2, by=c('grp', 'grp2'))
      df4 <- df3 %>% filter(x1 < x1_qt_85 & x2 < x2_qt_85) %>% select(grp, grp2, x1, x2)

    • 무구묵구 2019.03.05 17:07 신고  댓글주소  수정/삭제

      댓글 감사드립니다. 많은 도움이 되었습니다.

      한가지만 더 여쭤보겠습니다. 보내주신 코드로 실행하면
      df4 <- df3 %>% filter(x1 < x1_qt_85 & x2 < x2_qt_85) %>% select(grp, grp2, x1, x2)

      이부분에서 "&"로 인하여 둘다 만족하는 경우의 수가 나오는거 같더라고요. 그래서 "&"대신 "|"를 사용하였는데 x1이 x1_qt_85보다 큰경우도 포함하게 되더라고요...

      x1, x2를 각각하면 되긴 하지만 한번에 하는 방법도 있지 않을까 해서 질문 드립니다.

    • R Friend R_Friend 2019.03.05 17:27 신고  댓글주소  수정/삭제

      두번에 나누어서 해야할거 같습니다.

    • 무구묵구 2019.03.05 17:45 신고  댓글주소  수정/삭제

      네^^감사합니다.

  7. redsky911 2019.07.11 17:31  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 R을 처음 공부하면서 님 블로그를 많이 참고 하고 있는데 아무리 찾아봐도 해결이 안되는 부분이 있어 질문 드립니다.

    a, b, c 세개의 열로 구성된 데이터가 있다고 가정 했을 때 a열의 각 행의 데이터가 동일한 행의 b, c열의 데이터 사이에 있는(b < a < c 인 경우) 행만 추출하고 싶은데요...

    처음에 filter 함수를 적용해서 DATA <- DATA %>% filter("a">"b" & "a"<"c") 이런식으로 작성을 해봤는데 역시 안되더군요;; 그래서 이래저래 구글링도 해보고 했지만 도저히 모르겠네요...
    아예 다른 함수로 접근을 해야할지... 해결 방법을 좀 알려주시면 감사하겠습니다.

    • R Friend R_Friend 2019.07.11 21:26 신고  댓글주소  수정/삭제

      안녕하세요.
      아래 코드 참고해서 실행해보시기 바랍니다.

      > # make a sample DataFrame
      > a <- c(1, 2, 1, 3, 3, 4)
      > b <- c(2, 1, 2, 1, 2, 5)
      > c <- c(1, 3, 3, 5, NA, 6)
      >
      > df <- data.frame(a, b, c)
      > df
      a b c
      1 1 2 1
      2 2 1 3
      3 1 2 3
      4 3 1 5
      5 3 2 NA
      6 4 5 6
      >
      > # (1) subset from base
      > df_2 <- subset(df, subset = (a > b & a < c))
      > df_2
      a b c
      2 2 1 3
      4 3 1 5
      >
      > # (2) or filter from dplyr
      > library(dplyr)
      > df_3 <- df %>% filter(a > b & a < c)
      > df_3
      a b c
      1 2 1 3
      2 3 1 5

    • redsky911 2019.07.16 10:07  댓글주소  수정/삭제

      답변 감사합니다.
      필터를 사용할 때 변수명에 따옴표를 해놔서 잘 안됐던거 같네요;;
      덕분에 쉽게 해결 했습니다.

  8. 무구묵구 2019.07.23 17:58 신고  댓글주소  수정/삭제  댓글쓰기

    질문이 있어서 또 찾아뵙니다.
    for 반복문을 사용해서 두개의 변수가 각각 증가하도록 하고 mutate하여서 변수를 추가하고자 다음과 같이 했는데... 뭐가 문제인지 구동이 되지 않네요...
    맥한번 집어 주시기 부탁드립니다.

    for (i in seq(0.5, 5, 0.1)){
    F3_data %>% mutate(BOD_1 = if(BOD >= i, 1, 0))}
    for (j in seq(0.01, 0.2, 0.001)){
    F3_data %>% mutate(TP_1 = if(TP_1 >= j, 1, 0))}

    • R Friend R_Friend 2019.07.23 18:11 신고  댓글주소  수정/삭제

      질문의 의도거 제가 명확하지가 않운데요, 추측컨데 칼럼을 loop 개수 만큼 계속 에름 바꿔가면서 생성하려고 하시는 듯 하니다. 그렇다면 아래 블로그의 하단 답변 참고하세요.

      https://codeday.me/ko/qa/20190316/79825.html

    • 2019.07.24 09:52  댓글주소  수정/삭제

      비밀댓글입니다

    • R Friend R_Friend 2019.07.24 10:52 신고  댓글주소  수정/삭제

      loop를 한번 돌때 마다 mutate로 만들어주는 변수도 한개씩 다른 이름으로 추가를 해주어야 에러가 안날거 같습니다. 위의 답변에 링크의 블로그 하단에 답변 코드 참고하세요.

    • R Friend R_Friend 2019.07.24 12:56 신고  댓글주소  수정/삭제

      혹시 원하시는게 for loop을 돌면서 i가 0.1씩 증가하면서 비교하는 것을 row 가 하나씩 내려가면서 비교하는 걸로 하려는 것인가요? (칼럼은 1개, for loop을 통해 생성된 i으ㅏ 개수는 row의 총 개수와 동일?)

혼합모델 군집화(mixture model clustering)의 혼합 모델에는 어떠한 분포도 가능하지만 일반적으로 정규분포(Gaussian normal distribution)을 가장 많이 사용하며, 다음번 포스팅에서는 가우시안 혼합 모델(Gaussian misture model, 줄여서 GMM) 군집화를 다루겠습니다. .

 

혼합모델 군집화(mixture model clustering)에 들어가기에 앞서, 이번 포스팅에서는 정규분포(Gaussian normal distribution)에 대해서 먼저 몸풀기 차원에서 짚어보고 넘아가겠습니다.  준비운동 안하고 혼합모델 군집화에 바로 투입됐다가는 자칫 머리에 쥐가 나거나 OTL 하게 되는 불상사가 예상되기 때문입니다. ^^; 

 

이번 포스팅은 쉬어 가는 코너, 한번에 다 몰라도 좌절하지 않기 코너, 생각날 때 찾아보고 복기하는 코너로 여기면 좋겠습니다.

 

기계학습, 다변량 통계분석 책을 보다 보면 수식, 표기법 때문에 많이 어려워했던 기억이 있을 겁니다.  가장 기본이 되고 많이 사용하는 개념, 수식, 표기법을 중심으로 소개해보겠습니다.

 

 

1) 모집단(Population) vs. 표본집단(Sample)

 

알고자 하는 '전체 객체, 성분, 관측치를 포함하는 집단'을 모집단이라고 하며, 보통은 시간, 비용 상의 이슈로 인해서 모수(parameter)가 알려지지 않은(unknown) 경우가 많습니다.

 

모집단의 특성을 알기 위해 시간과 비용을 감당할 수 있는 범위 내에서 모집단으로 부터 모집단을 잘 대표할 수 있도록 표본(sample)을 추출해서 분석을 하게 됩니다.

 

 

모집단과 표본집단을 알았으니, 각 집단의 기대값과 공분산, 상관행렬, 상관계수는 어떻게 표기하는지 비교해서 살펴보겠습니다.

 

 

2) 모집단(Population)의 기대값과 공분산, 상관행렬, 상관계수

 

성분들이 확률변수로 이루어진 p x 1 확률벡터 X에 대한 평균 벡터(mean vector), 공분산 행렬(covariance matrix), 상관행렬(correlation matrix), 상관계수(correlation coefficient) 는 아래와 같습니다. 

 

공분산 행렬(covariance matrix)을 'Σ' 로 표기한다는 점 유념하시기 바랍니다.  다변량 정규분포 확률분포를 표기할 때 'Σ'가 나옵니다.  (* 주의 : 모두 summation 하라는 뜻이 아님 -.-").

 

 

 

위의 모집단(population)에 대한 표기법을 아래의 표본집단(sample)에 대한 표기법과 비교를 해가면서 유심히 살펴보시고 기억해놓으면 좋겠습니다. 

(강의 듣다보면 혼동해서 잘못쓰는 강사나 교재도 가끔 있음 -_-;) 

 

 

 

(3) 표본집단의 기대값과 공분산, 상관행렬, 상관계수

 

모집단으로부터 n개의 확률표본 X1, X2, ..., Xn에 대해 각 n x p 확률벡터 Xi = (Xi1, Xi2, ..., Xip)' , i = 1, 2, ..., n 의 표본 평균 벡터(sample mean vector), 표본 공분산 행렬(sample covariance matrix), 표본 상관행렬(sample correlation matrix), 표본 상관계수(sample correlation coefficient)는 아래와 같습니다.

 

표본 상관행렬의 우상(rigth upper)과 좌하(left down)의 값은 상호 대칭으로 같습니다. (가령, r12r21은 같은 값을 가짐)

 

 

 


 

 

정규분포는 변수의 개수(1개, 2개, p개)에 따라서 '일변량 정규분포 (univariate normal distribution)', '이변량 정규분포 (bivariate normal distribution)', 다변량 정규분포 (multivariate normal distribution)'로 구분할 수 있습니다.

 

일변량/이변량/다변량 정규분포 확률밀도함수(probability density function)의 표기법과 수식, 그리고 그래프를 살펴보도록 하겠습니다.

 

 

(4) 일변량 정규분포 (univariate normal distribution) vs. 다변량 정규분포 (multivariate normal distribution)

 

일변량 정규분포의 확률변수 X에 대한 확률밀도함수(probability density function) 식은 기억하기가 좀 복잡하고 어려운데요, 자꾸 보다보면 어느샌가 익숙해지고, 더 자주 보다보면 자기도 모르게 기억될 날이 올겁니다. (기억을 했다가도... 인간은 위대한 망각의 동물인지라 두어달만 안보면 다시... ㅋㅋ)

 

 

위의 표기에서 일변량 정규분포 대비 다변량 정규분포의 확률밀도함수의 어디가 서로 다른지 유심히 살펴보시기 바랍니다.  특히 다변량 정규분포의 확률밀도함수 표기법이 통계를 전공하지 않았거나, 선형대수 기본을 모르면 매우 생소하고 어렵게만 느껴질 것 같습니다. (왜 저런거냐고 묻는 댓글 질문은 정중히 사양해염... -_-*)

 

 

 

(5) 이변량 정규분포 (bivariate normal distribution)

 

3차원 이상 넘어가면 사람 머리로 인식하기가 곤란하니, 시각화로 살펴보기에 그나마 가능한 2개의 변수를 가지는 2차원의 '이변량 정규분포(bivariate normal distribution)'의 확률밀도함수(probability density function)에 대해서 알아보겠습니다.  위의 다변량 정규분포의 확률밀도함수 식에서 p = 2 를 대입하면 됩니다.

 

[ 이변량 정규분포 확률밀도함수 - 식 1 ]

 

 

 

위의 이변량 정규분포의 확률밀도함수를 다시 정리해보면 아래와 같습니다.

 

[ 이변량 정규분포 확률밀도함수 - 식 2 ]

 

 

 

'이변량 정규분포 확률밀도함수   식 2'를 보면 이건 뭐... 외계어가 따로 없습니다.  도대체 이렇게 복잡하면서도 쓰임새가 어마무시 많은 요물단지 정규분포를 정리한 천재들이 누구인지 궁금하시지요?

 

위키피디아를 찾아보니, 위대한 수학자 가우스(carl Friedrich Gauss)가 정규분포를 발견했고(그래서 영어로 Gaussian Normal Distribution 이라고 함), 라플라스(Marquis de Laplace)가 그 유명하고 중요하며 시험문제로 반드시 나오는 '중심극한의 정리(Central Limit Theorem)'를 증명하였습니다. 피어슨(Karl Pearson)이 20세기 들어서 '정규분포(Normal Distribution)'이라고 명명하는 것을 확산시키는데 일조했다고 나오네요.  가우스, 라플라스, 피어슨... 역시 천재 거물들 맞군요.

(Normal distribution 이외의 분포는 그럼 비정상(Abnormal) 이라는 소리? -_-?)

 

 

 

 

(* source : https://en.wikipedia.org/wiki/Normal_distribution)

 

 

 

수식으로만 봐서는 저게 뭔가 싶으실텐데요, R의 base 패키지에 내장되어 있는 persp() 함수를 가지고 위의 '이변량 정규분포 - 식 2'를 사용하여 3D plot 으로 그려보겠습니다.

 

persp() 의 3D plot에 manipulate 패키지를 접목해서 동적(interactive plotting)으로 상하(phi), 좌우(theta) 로 돌려가면서 살펴보겠습니다. 

 

아래 첫번째 그래프의 노란색으로 표시해 놓은 단추를 누르면 interactive plot 의 slider 가 나타납니다.  theta(좌~우), phi(상~하)를 조절해가면서 3-D 그래프를 돌려서 보시면 '아, 이변량 정규분포 확률밀도함수가 이런거구나. 느낌 오네~' 하실겁니다.

 

 

##---------------------------------------
## (Gaussian) Mixture Model Clustering
##---------------------------------------

 

# bivariate nomral probability density function
mu1 <- 0   # setting the expected value of x1
mu2 <- 0   # setting the expected value of x2

 

s11 <- 3   # setting the variance of x1
s12 <- 4  # setting the covariance between x1 and x2
s22 <- 3   # setting the variance of x2

 

rho12 <- s12/(sqrt(s11)*sqrt(s22)) # setting the correlation coefficient between x1 and x2
rho12

 

x1 <- seq(-5, 5, length = 50) # generating the vector series x1
x2 <- seq(-5, 5, length = 50) # generating the vector series x2


# multivariate normal density function
gaussian_func <- function(x1, x2){
  term1 <- 1/(2*pi*sqrt(s11*s22*(1-rho12^2)))
  term2 <- -1/(2*(1-rho12^2))
  term3 <- (x1 - mu1)^2/s11
  term4 <- (x2 - mu2)^2/s22
  term5 <- 2*rho12*((x1 - mu1)*(x2 - mu2))/(sqrt(s11)*sqrt(s22))
  term1*exp(term2*(term3 + term4 - term5))
}


# calculating the density values
z_score <- outer(x1, x2, gaussian_func)
head(z_score)

 

 

##------------
# 3-D normal distribution density plot : persp() of {base} package
# with interactive plotting with Manipulate package in RStudio

 

install.packages("manipulate")
library(manipulate)

 

manipulate(persp(x1, x2, z_score,
                 theta = theta_x, # theta gives the azimuthal viewing direction
                 phi = phi_x, # phi gives the colatitude viewing direction
                 main = "Two dimensional Normal Distribution"), 
           theta_x = slider(10, 90, initial = 35), 
           phi_x = slider(10, 90, initial = 10))

 


 

# click the button at yellow circle position

 

[ theta : 35,   phi : 10 ]

 


 

[ theta : 60,   phi : 10 ]

 


[ theta : 80,   phi : 10 ]


[ theta : 90,   phi : 10 ]


 

[ theta : 35,   phi : 30 ]


[ theta : 35,   phi: 50 ]


 

[ theta : 35,   phi: 70 ]


 

[ theta : 35,   phi: 90 ]

 

 

 

 

마지막 그림이 phi = 90 으로서, 꼭대기 상단에서 비행기 타고 내려다본 그림인데요, 이걸 2-D 의 등고선 그림(contour plot)으로 그려보면 아래와 같습니다.

 

 

# 2-D contour plot
contour(x1, x2, z_score, xlab = "x1", ylab = "x2",
        main = "Bivariate(2 dimensional) Normal Distribution : contour plot")

 

 

 

 

 

참고로, 일변량 정규분포 그래프는 http://rfriend.tistory.com/102 의 이전 포스팅을 참고하세요.

 

[Reference]

1. 김재희, 'R 다변량 통계분석', 교우사, 2011

2. Wikipedia :  https://en.wikipedia.org/wiki/Normal_distribution

 

 

이번 포스팅에서 살펴본 '다변량 정규분포'에 대한 기본기를 바탕으로 해서, 다음번 포스팅에서는 '가우시언 혼합 분포 군집화 (Gaussian Mixture Model Clustering)'에 대해서 알아보겠습니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 심교훈 2019.04.01 11:10 신고  댓글주소  수정/삭제  댓글쓰기

    정말 유용한 글 감사합니다!

 

이번 포스팅에서는 R로 Hive에 바로 접속(dbConnect)해서 바로 Hive 내의 데이터를 추출해서 R의 데이터 프레임으로 만드는 방법에 대해서 소개하겠습니다.

 

dbConnect 를 사용하지 않을 때와 사용할 때를 비교해보면 아무래도 dbConnect를 하는 것이 편합니다. 

 

그리고 혹시 R로 Hive table의 데이터를 사용해서 자동화하는 프로그램을 짰다고 할 경우 관리 측면에서도 직접 Hive에 연동해서 batch job 실행시키는 것이 관리 측면에서도 용이합니다.

 

------------------------------------------- 

[ R 따로, Hive 따로 사용할 경우 Hive에서 데이터 추출 및 R에서 사용하기 절차]

 

  1) Hive 로 데이터 처리/추출하여 text file, 또는 csv file 로 내리기

    => 2) R 에서 read.table() 혹은 read.csv() 함수로 데이터 불러오기

 -------------------------------------------

 

vs.

 

------------------------------------------- 

[ R로 Hive에 dbConnect 할 경우 Hive에서 데이터 추출 및 R에서 사용하기 절차 ]

 

  1) R에서 Hive Query로 직접 데이터 처리하여 R로 바로 data.frame으로 내리기 (끝!)
     (별도로 불러오기 필요 없음!)

-------------------------------------------

 

 

 

대신에 사용환경 설정해주는게 처음에 좀 번거로운게 있습니다. ^^;

한번 고생하고 나중에 두고 두고 편할 거냐, 아니면 당장의 수고스러움을 피하고 조금 불편한거 감수할거냐의 선택인데요, 이왕 이번 글 읽기 시작하셨으니 아래 설명 참고해서 R로 Hive 접근할 수 있는 환경 세팅해보심이 어떨런지요. 

 

차근차근 설명해보겠습니다.

 

 

(1) Java 최신 버전 설치하기

 

R로 Hive 연동해서 쓸 때 Java를 사용하므로 먼저 Java를 설치해야 합니다.  이미 Java를 설치했다고 하더라도 최신 버전이 아니면 에러가 나더라구요.

 

Java 설치 확인하는 방법은 cmd 창에서 아래 처럼 'where /R C:\ java.exe' 라고 입력했는데 '정보 : 제공된 패턴에 해당되는 파일을 찾지 못했습니다'라는 메시지가 뜨면 설치가 안되어 있는 겁니다.

 

 

 

설치를 하려면 아래의 링크된 주소로 접속해서 Java SE download 를 합니다.

 

http://www.oracle.com/technetwork/java/javase/downloads/index.html

 

 

아래 그림이 화면에 나오면 Java DOWNLOAD 이미지 클릭하세요.

 

 

그러면 아래의 화면이 나오는데요, 각자의 OS, bit 체계 확인해서 본인의 것에 맞는거 다운로드 하시면 됩니다.   (참고로, 저는 window7 OS 64bix 운영체제에 R x64 3.3.1 version 사용하고 있습니다. )

 

중간에 ( ) Accept License Agreement 에 체크하고 다운로드 받으세요.

 

 

 

[참고 : Windows 운영체제 확인하는 방법]

 

(방법 1) 제어판 > 시스템  선택

(방법 20) 시작메뉴 오른쪽의) 컴퓨터 > (마우스 오른쪽 누르고) 속성 선택

 

하면 아래의 화면 나옵니다. 

 

 

 

 

jdk-8u101-windows-x64.exe 파일을 다운로드 완료하였다면 클릭해서 설치해주세요.

(계속'Next' 누르시면 됩니다)

 

 

 

C:\Program Files\Java\jre1.8.0_101  폴더에 Java가 설치되었을 겁니다.

cmd 창에서 확인해보니 잘 설치가 되었군요.

 

 

 

 

 

(2) 메모리 늘리기(increase heap size of rJava) : options(java.parameters = "-Xmx8g")

 

Java는 virtual machine을 사용하므로, 만약 메모리가 부족하면 'java.lang.OutOfMemoryError: Java heap space' 에러 메시지가 뜹니다. 

 

R에서는 default memory setting 값이 512 MB 입니다.  Hive에서 512 MB 넘는 파일 추출하기라도 하면 에러납니다.  메모리 문제를 피하기 위해서 아래처럼 options(java.parameters = "-Xmx4g" 라는 명령문으로 메모리를 4G 까지 쓸 수 있도록 설정해보겠습니다.

(만약 컴퓨터의 메모리가 빠방하다면 options(java.parameters = "-Xmx8g" 로 해서 8G 로 설정할 수도 있습니다)

 

> ##---------------------------- > ## R - Hive connect > ##---------------------------- > > # R: Increase heap size for rJava > # : assign 4 gigabytes of heap space to the Java environment > # (<-> default setting : 512 MB) > > options(java.parameters = "-Xmx4g" )

 

 

 

 

(3) rJava, RJDBC 패키지 설치 및 로딩 (rJava, RJDBC RJDBC package installation and loading)

 

RJDBC가 rJava에 의존적이므로 반드시 library(rJava)를 실행한 후에 library(RJDBC) 순서로 로딩을 해야 합니다.

 

> # rJava, RJDDB package installation and loading
> install.packages("rJava")
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.3/rJava_0.9-8.zip'
Content type 'application/zip' length 713501 bytes (696 KB)
downloaded 696 KB

package ‘rJava’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
	C:\Users\Administrator\AppData\Local\Temp\Rtmpc9SiL3\downloaded_packages

> install.packages("RJDBC") trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.3/RJDBC_0.2-5.zip' Content type 'application/zip' length 66405 bytes (64 KB) downloaded 64 KB package ‘RJDBC’ successfully unpacked and MD5 sums checked The downloaded binary packages are in C:\Users\Administrator\AppData\Local\Temp\Rtmpc9SiL3\downloaded_packages

> library(DBI)
> library(rJava) > library(RJDBC)

 

 

 

 

(4) R에 Hive Class Path, Dirver, dbConnect 설정

 

Hive/Hadoop Class Path, dbConnect 설정은 하둡 엔지니어에게 물어서 사용하시기 바랍니다.

(아래는 그냥 예시임...)

 

# setting class path

hive.class.path = list.files(path=c("/usr/lib/hive/lib"), pattern="jar", full.names=T);
hadoop.lib.path = list.files(path=c("/usr/lib/hadoop/lib"), pattern="jar", full.names=T);
hadoop.class.path = list.files(path=c("/usr/lib/hadoop"), pattern="jar", full.names=T); 
class.path = c(hive.class.path, hadoop.lib.path, hadoop.class.path);
.jinit(classpath = class.path)

 

# setting driver

drv <- JDBC("org.apache.hive.jdbc.HiveDriver")

 

# setting dbConnect : driver, ip, port, userID, password

conn <- dbConnect(drv, "jdbc:hive2://xxx/xxx", "userID", "password")

 

 

(* 주의 : dbConnect 할 때요, Hive 설치된 것이 jdbc:hive인지 jdbc:hive2 인지 확인해서 사용하세요.)

 

 

 

(5) R에서 Hive Query 날리기 : dbGetQuery()

 

 

# Hive Query

db_qry <- dbGetQuery(conn,
      "SELECT *
          FROM db.table
          WHERE ymd = '2016-09-03'
          LIMIT 100;")

 

db_qry # query result

 

 

 

이렇게 R에서 Hive로 Query를 날리면 R에 데이터 프레임(data.frame) 이 생성됩니다.

 

 

 

[참고] hive 2.0.1 설치 및 metastore 설정 =>  http://www.gooper.com/ss/bigdata/271507 

 

 


 

참고로, RPostgreSQL package를 사용해서 R로 PostgreSQL 사용하는 방법도 아래에 소개합니다.  

 

 

# installation and loading RPostgreSQL package

install.packages("RPostgreSQL")
library(RPostgreSQL)

 

# setting Driver : dbDriver()

drv <- dbDriver("PostgreSQL")

 

# setting dbConnect
con <- dbConnect(drv, dbname = "dbname",
                               host = "xx.xx.xxx.xx",
                               port = xxxx,
                               user = "userID",
                               password = "password")

 

# DB SQL Query

r_sql <- c("SELECT var1, sum(var2) as sum_var2
    FROM db.table
    WHERE var3 = 'xxx' AND var4 = 'xxx'
    GROUP BY var1
    ORDER BY var1;")

 

sql_result <- dbGetQuery(con, r_sql)

 

sql_result # query result

 

dbDisconnect(conn = con)

 

 

 


 

 

RMySQL package를 사용해서 R로 MySQL 접속해서 사용하는 방법은 아래와 같습니다. 

위의 RPostgreSQL과 비슷해요.

 

 

# install and loading RMySQL package

install.packages("RMySQL")
library(RMySQL)

 

# setting dbDriver, dbConnect()

drv <- dbDriver("MySQL")

con <- dbConnect(drv,
                 dbname = "dbname",
     user = "userID",
     host = "xx.xx.xxx.xx",
     port = xxxx,
     password = "password")

 

# MySQL query : dbGetQuery

r_sql <- c("SELECT var1, sum(var2) as sum_var2
    FROM db.table
    WHERE var3 = 'xxx' AND var4 = 'xxx'
    GROUP BY var1
    ORDER BY var1;")
 
test.table <- dbGetQuery(con, r_sql)

 

# disconnect MySQL DB : dbDisconnect()

dbDisconnect(conn = con)

 

 

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 궁금해 2016.10.31 14:30  댓글주소  수정/삭제  댓글쓰기

    위에서 r에서 hive연결시
    drv <- JDBC("org.apache.hive.jdbc.HiveDriver")

    이부분에서 Error in .jfindClass(as.character(driverClass)[1]) : class not found
    라고 나오는데 어떻게 처리해야하나요?

    • R Friend R_Friend 2016.10.31 15:47 신고  댓글주소  수정/삭제

      안녕하세요 궁금해님,

      저는 문의하신 에러를 겪어보지 못해서 정확한 해법은 잘 모르겠습니다.

      댓글로 남겨주신 에러메시지를 가지고 구글링을 해서 몇 개 찾아보니 "의존성이 있는 jar 파일들을 동일한 폴더에 압축해제해놔야 한다"가 해법인거 같습니다.

      아래에 구글링해서 찾은 글 하나 링크 걸어놓습니다. 아래 링크글의 댓글 답변 참고하세요.

      http://stackoverflow.com/questions/31109276/r-error-in-jfindclassas-characterdriverclass1-class-not-found

  2. marketer 2017.01.19 10:36  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 포스팅 잘 보고 있습니다.

    RMySQL패키지를 이용하여 쿼리를 조회하려하는데,
    MySQL데이터가 한글일 경우 R에서 깨지는 현상을 보이고 있습니다.

    이에 대한 해결책이 있을까요?

    감사합니다.

이번 포스팅에서는 누적(cumulative)과 관련된 함수로서 {base} package에 기본으로 내장되어 있는 함수들인

 

 - 누적 합 (cumulative sums) : cumsum()

 

 - 누적 곱 (cumulative products) : cumprod()

 

 - 누적 최소값 (cumulative minima) : cummin()

 

 - 누적 최대값 (cumulative maxima) : cummax()

 

함수들에 대해서 아주 간단한 벡터(vector) 예제를 들어서 살펴보도록 하겠습니다.

 

이 함수들을 알고 있으면 달랑 한줄이면 끝인데요, 이 함수들을 모르고 있으면 누적 합, 누적 곱, 누적 최소값, 누적 최대값 구하는 프로그래밍하기가 은근히 어렵습니다. 

 

 

 

 

 

 

(1) 누적 합 (cumulative sums) : cumsum()

 

(1-1) 벡터 누적 합 (cumulative sum of vector)

 

아래와 같이 1~10까지의 정수 벡터가 있다고 합시다. 그리고 이에 대해 오른쪽으로 하나씩 이동해가면서 누적 합을 구해보면 아래의 표와 같습니다.

 

vector

1

2

3

4

5

6

7

8

9

 10

cumulative sums

1

3

6

10

15

21

28

36

45

55 

 

 

 

이걸 R base package의 cumsum() 함수를 이용해서 계산하면 아래와 같습니다.

 

> ##---------------------------------------------------------- > ## cumulative Sums, Products, and Minima or Maxima (Extremes) > ##---------------------------------------------------------- > # {base} package > > # (1) cumulative sums : cumsum() > # (1-1) cumulative sums (vector) : cumsum() > c(1:10) [1] 1 2 3 4 5 6 7 8 9 10 > > cumsum(1:10) [1] 1 3 6 10 15 21 28 36 45 55

 

 

 

 

이를 cumsum() 함수를 쓰지 않고 while 반복문을 사용해서 프로그래밍할 수도 있습니다.

(그런데, 계산 결과 제시되는 포맷이 위의 cumsum()과는 다르군요. ^^; )

 

> # cf. cumulative sums : while loop programing
> x <- 0
> i <- 1
> while( i <= 10) {
+         x = x + i
+         print(x) 
+         i = i + 1
+       }
[1] 1
[1] 3
[1] 6
[1] 10
[1] 15
[1] 21
[1] 28
[1] 36
[1] 45
[1] 55

 

 

 

누적 합(cumulative sums)은 그래도 종종 사용하는 만큼 벡터와 더불어서 데이터 프레임(dataframe) 예제도 곁들어서 살펴보겠습니다.

 

(1-2) 칼럼 1개만의 누적합을 구하는 방법과, (1-3) (numeric) 칼럼 모두의 누적합을 구하는 방법으로 나누어서 예를 들어보겠습니다.

 

 

(1-2) 데이터 프레임 : 변수 1개의 누적 합 (dataframe - cumulative sum of a column)

 

1~10까지의 정수인 x1 에 대한 누적합이므로 위의 (1-1) 벡터 예제와 결과가 동일하게 나옵니다.

결과 제시 포맷만 데이터 프레임의 새로운 변수(cum_sum_x1)로 생성된게 다를 뿐입니다.

 

> # (1-2) cumulative sums (dataframe - only 1 variable)
> #  : mydata$cum_sum <- cumsum(madata$var1)
> x1 <- c(1:10)
> x2 <- c(rep(1:5,2))
> x3 <- c(rep(2, 10))
> 
> x123 <- data.frame(x1, x2, x3) # making dataframe
> 
> x123
   x1 x2 x3
1   1  1  2
2   2  2  2
3   3  3  2
4   4  4  2
5   5  5  2
6   6  1  2
7   7  2  2
8   8  3  2
9   9  4  2
10 10  5  2
> 
> 
> x123$cum_sum_x1 <- cumsum(x123$x1) # cumulative sum of x1
> 
> x123
   x1 x2 x3 cum_sum_x1
1   1  1  2          1
2   2  2  2          3
3   3  3  2          6
4   4  4  2         10
5   5  5  2         15
6   6  1  2         21
7   7  2  2         28
8   8  3  2         36
9   9  4  2         45
10 10  5  2         55

 

 

 

 

(1-3) 데이터 프레임 : 숫자형 모든 변수의 누적 합 (dataframe - cumulative sum of all variables)

 

cumsum() 함수에 apply() 함수를 응용하면 소기의 목적을 달성할 수 있습니다. 

 

아래 예제의 1번째 행(row), 2번째 행, 3번째 행이 어떻게 구해졌는지만 풀어서 설명을 해보면요,

 

1st across_cum_sum = 1 + 1 + 2 + 1 = 5

2nd across_cum_sum = 5(1st across_cum_sum) + (2 + 2 + 2 + 3)(2nd row) = 14

3rd across_cum_sum = 14(2nd across_cum_sum) + (3 + 3 + 2 + 6)(3rd row) = 28

 

 

> # (1-3) cumulative sums (dataframe - all variables)
> #  : mydata$across_cum_sum <- cumsum(apply(mydata, 1, sum))
> x123$across_cum_sum <- cumsum(apply(x123, 1, sum))
> 
> x123
   x1 x2 x3 cum_sum_x1 across_cum_sum
1   1  1  2          1              5
2   2  2  2          3             14
3   3  3  2          6             28
4   4  4  2         10             48
5   5  5  2         15             75
6   6  1  2         21            105
7   7  2  2         28            144
8   8  3  2         36            193
9   9  4  2         45            253
10 10  5  2         55            325

 

 

 

(2) 누적 곱 (cumulative products) : cumprod()

 

1~10까지의 정수 벡터를 가지고 누적 곱 예를 들어보겠습니다.

숫자 하나씩 오른쪽으로 이동하면서 곱해나가면 됩니다. 

(아직 실전에서 써먹어 본적은 없음 -,-;)

 

> # (2) cumulative products (vector) : cumprod()
> c(1:10)
 [1]  1  2  3  4  5  6  7  8  9 10
> 
> cumprod(1:10)
 [1]       1       2       6      24     120     720    5040   40320  362880 3628800

 

 

 

 

(3) 누적 최소값 (cumulative minima) : cummin()

 

정수 (3, 2, 1, 2, 1, 0, 4, 3, 2) 의 순서로 이루어진 벡터에 대해서 누적 최소값을 구해보겠습니다. 

오른쪽으로 하나씩 숫자를 이동하면서 과거 지나온 숫자를 기억하고 있다가, 매번 평가를 통해 가장 작은 값(cumulative minima)을 반환하게 됩니다.

 

> # (3) cumulative minima (vector) : cummin()
> c(3:1, 2:0, 4:2)
[1] 3 2 1 2 1 0 4 3 2
> 
> cummin(c(3:1, 2:0, 4:2))
[1] 3 2 1 1 1 0 0 0 0

 

 

 

 

(4) 누적 최대값 (cumulative maxima) : cummax()

 

정수 (3, 2, 1, 2, 1, 0, 4, 3, 2) 의 순서로 이루어진 벡터에 대해서 누적 최대값을 구해보겠습니다.

오른쪽으로 하나씩 숫자를 이동하면서 과거 지나온 숫자를 기억하고 있다가, 매번 평가를 통해 가장 큰 값(cumulative maxima)을 반환하게 됩니다.

 

> # (4) cumulative maxima (vector) : cummax()
> c(3:1, 2:0, 4:2)
[1] 3 2 1 2 1 0 4 3 2
> 
> cummax(c(3:1, 2:0, 4:2))
[1] 3 3 3 3 3 3 4 4 4

 

 

 

많은 도움 되었기를 바랍니다.

 

 

참고로, R dplyr 패키지의 Window funciton 중에서
 - Cumulative aggregates : cumall() 함수, cumany() 함수, cummean() 함수
에 대해서는 여기 ( ☞ http://rfriend.tistory.com/246 )를 참고하세요.

 

 

이번 포스팅이 도움이 되셨다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요. ^^

 

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

지난번 포스팅에서는 분할적 군집화(Partitional Clustering) 중에서 프로토타입 기반(Prototype-based)의 군집화 기법인 K-중심 군집(K-Centroid Clustering)에 대해서 알아보았습니다.

 

이번 포스팅에서는 분할적 군집화(Partitional Clustering) 중에서 프로토타입 기반(Prototype-based) 군집화 기법의 두번째로 퍼지 군집 (Fuzzy Clustering)에 대해서 알아보겠습니다.

 

퍼지 군집 (Fuzzy Clustering)은 혼합 분포 군집(Mixture Distribution Clustering)과 함께 "Soft Clustering"이라고도 하는데요, 각 관측치가 (단 하나의 군집에만 속하는 것이 아니라) 여러 군집에 속할 수 있으며, 이를 각 군집에 속할 가능성(possibility), 확률(probability)로 제시해줍니다.

(가령, 관측치 3번은 군집 1 속할 가능성이 0.7, 군집 2에 속할 가능성이 0.3)

 

 

[ Partitional Clustering > Prototype-based > Fuzzy Clustering ]

 

 

 

 

퍼지 군집에 대해 자세히 살펴보기 전에 먼저 퍼지 논리(Fuzzy Logic)에 대해서 간략히 살펴보겠습니다.

 

Fuzzy를 영어사전에서 찾아보면 '애매 모호함' 이라는 뜻입니다. 

 

(뜻이) (be) vague (idea), obscure, fuzzy, inexplicit, puzzling, ambiguous (meaning), hazy (notion), dim (memory), doubtful, evasive, equivocal, elusive

 

Fuzzy Set Theory, Fuzzy Logic은 '애매 모호한 대상을 다루는 논리'입니다. 

 

고전 논리 연산(Boolean Logic)에서는 1(참, Truth) 아니면 0(거짓, False), 모 아니면 도, 아군 아니면 적군으로 단순 명쾌하게 구분을 합니다.

 

반면에 Fuzzy 논리(Fuzzy Logic)에서는 진리값(Truth Value)이 0~1 사이의 실수값(real nummber between 0 and 1)으로 확장하여 소속도(degrees of membership)에 대한 애매모호한 정도, 가능성의 정도(degrees of possibility)를 표현합니다.

 

 

[ Boolean Logic vs. Fuzzy Logic ]

 

 

 

위의 '키(Height)'를 예를 들어보면요, 고전 논리(Boolean Logic)에서는 키 175 cm 를 기준으로 해서, 175cm 미만은 키가 작은 집단 ('0', short group), 175cm 이상은 '키가 큰 집단' ('1', tall group)으로 구분하고 있습니다.  따라서 고전 논리에 따르면 키 174.999cm인 사람은 '키가 작은 집단'('0', short group)에 속하게 되며, 키 175.001 cm인 사람 (키 174.999 cm인 사람보다 단지 0.002 cm 큼)은 '키가 큰 집단' ('1', tall group)에 속하게 됩니다. 0.002 cm 차이가 두 사람이 속하는 그룹을 나누었는데요, 뭔가 불합리하다는 생각이 들지요?

 

반면에, 퍼지 논리 (Fuzzy Logic)에서는 소속도(degrees of membership)의 가능성(Possibility)을 진리 값(Truth Value)이 0~1 사이의 연속된 실수값으로 확장해서 나타내준다고 했지요?! 위의 키 예를 살펴보면요, 174.999 cm 인 사람이 '키가 큰 집단'에 속할 가능성이 0.499 이고 & '키가 작은 집단'에 속할 가능성(possibilty)이 0.501 입니다.  키가 175.001 cm 인 사람이 '키가 큰 집단'에 속할 가능성이 0.501 이고 & '키가 작은 집단'에 속할 가능성은 0.499 입니다. 

 

키나 몸무게, 나이, 시력, 청력... 등 연속된 개념에 대해서는 아무래도 고전 논리(Boolean Logic)보다는 퍼지 논리(Fuzzy Logic)이 애매 모호함의 정도(degrees)를 나타내기에 더 적합해 보입니다.  사실, 인간이 사용하는 용어, 표현, 개념의 "상당 부분이 애매모호한것 같습니다" (<= 이 표현 자체가 바로 애매 모호함 그 자체이지요. "상당 부분"? "같습니다"?).  퍼지 논리, 퍼지 집합론(Fuzzy set theory)을 이용하면 컴퓨터가 인간이 생각하고 표현하는 애매 모호함을 인식하고 연산할 수 있습니다.  

 

 

군집분석으로 다시 돌아와서 생각해보면요, K-중심 군집(K-Centroid Clustering)에서는 각 관측치가 특정 군집에 속하거나('1') 혹은 아니거나('0')의 둘 중 하나였습니다(each data point can only belong to exactly one cluster)반면에, 퍼지 군집(Fuzzy Clustering)에서는 퍼지 이론(Fuzzy set theory)에 기반해서 각 관측치가 여러 군집에 동시에 속할 수 있으며(data points can potentially belong to multiple clusters), 각 군집별로 속할 가능성(degrees of possibility, probability)을 제시해줍니다.

 

 

[ Soft Clustering compared to Hard(Non-fuzzy) Clustering ]

 

 

 

관측치 중에서 각 군집과 군집의 중간 사이에 위치해서 특정 군집에 할당하기에 애매 모호한 관측치가 많이 있는 데이터셋이라면 '집단에 속할 가능성'을 나타내주는 퍼지 군집(Fuzzy Clustering)이 '단순 무식하게 여기 아니면 저기'로 배타적으로(exclusively) 나누어주는 K-중심군집(K-Centroid Clustering)보다 상대적으로 더 적합하다고 볼 수 있습니다.  (연산량이 많아지는 단점이 있긴 합니다만, 요즘엔 컴퓨팅 파워가 높으므로 예전 대비 문제가 덜 한편이죠)

 

 

개념에 대해 소개했으니, 이제 알고리즘으로 한단계 내려가 보겠습니다.

 

퍼지 군집 알고리즘으로 가장 많이 사용되는 것으로 Fuzzy C-means(FCM) Clustering Algorithm 입니다. FCM 알고리즘은 1973년 J.C.Dunn이 개발하였고, 1981년 J.C.Bezdek 이 발전시켰습니다. (* source : https://en.wikipedia.org/wiki/Fuzzy_clustering)  이번 포스팅에서는 Fuzzy C-means Clustering Algorithm을 가지고 설명하겠습니다.

 

Fuzzy C-means Clustering Algorithm은 K-means Algorithm 과 매우 유사합니다.  군집 내 관측치들 간의 유사성을 최대화하고 (즉, 군집의 중심과 관측치 간 거리의 합을 최소화), 군집 간 비유사성을 최대화 (즉, 군집의 중심 간 거리의 합을 최대화) 하는 최적 해(optimal solution)를 반복적인 연산을 통해 찾는 개념은 똑같습니다.  K 개의 군집을 분석가가 사전적으로 지정해주는 것도 같습니다.  유사성의 측도로서 거리(distance)를 사용하는 것도 같습니다 (정확히는 비유사성(dis-similarity)의 측도).

 

다만, 퍼지 군집에서는 각 관측치가 특정 군집에 속할 가능성, 가중치 w 를 계산하는 것이 다를 뿐입니다. (아래에는 군집 개수를 K로 통일해서 Fuzzy K-means Clustering 으로 사용하겠음)

 

 

[ Fuzzy K-means Clustering Algorithm ]

 

 

1. Choose a number of clusters, K.

2. Assign randomly to each point coefficients for being in the clusters.

3. Repeat

  - Compute the centroid for each cluster.
  - For each point, compute its coefficients of

  being in the clusters.

 

4. until

  the algorithm has converged

  (that is, the coefficients' change between

  two iterations is no more than the given

  sensitivity threshold)
 

 

1. 군집의 개수 K를 선택함

 

2. 각 관측치가 특정 군집에 속할 가중치
  (가능성) 값을 무작위로 할당함

 

3. Repeat

 - 각 군집의 중심을 계산함

 - 각 관측치에 대해 특정 군집에 속할
  가중치(가능성) 값을 다시 계산함

 

4. until

 알고리즘이 수렴할 때까지 반복함

 (즉, 3번의 2개 반복에서 더이상 가중치 값의
 변화가 주어진 민감도 기준치 미만일 때)

 

 

 

 

위의 알고리즘 내용을 수식으로 표현하기 전에 표기법부터 정리해보죠. 군집화를 하려고 하는 데이터셋 집합이 m개의 변수, n개의 관측치, K개의 군집이 있고, 각 군집에 속할 가능성을 가중치 값 w 로 표기한다고 하면 아래와 같습니다.  

 

[ 표기법 (Notation) ]

 

 

 

퍼지 군집 모형은 아래 두 개의 분할 조건을 만족합니다.

 

1) 데이터 가 각 군집 에 속할 가능성의 가중값 합은 1이다. (즉, 확률 합이 1)

  

    ---- (식 1)

 

2) 각 군집 는 하나 이상의 데이터가 0이 아닌 가중값을 가지며, 그 군집의 모든 가중값이 1이 될 수는 없다. (모두 0이거나 모두 1이면 군집분석 효용성 없음)

 

   ---- (식 2)

 

 

 

퍼지 군집을 하는 원리가 '군집 내 유사성 최대화 (즉, 관측치와 퍼지 군집 중심과의 거리 합 최소화)', '군집 간 비유사성 최대화 (즉, 군집의 중심 간 거리 합 최대화)' 하는 것이라고 했습니다 (K-means clustering 과 원리 동일). 

 

'군집 내 유사성 최대화 (즉, 관측치와 퍼지 군집 중심과의 거리[d(xi, ck)] 합 최소화)'를 달리 표현하면 '퍼지 군집 내 오차 제곱 합(SSE : Sum of Squared Error) 최소화'라고 할 수 있습니다.

 

  ---- (식 3)

 

위의 식에서 p는 1보다 큰 상수로서, 가중값의 정도를 조절하는 매개변수(parameter) 입니다. p 값이 1이면 K-means Clustering과 결과가 비슷해집니다. (K-means Clustering을 Fuzzy Clustering의 특수한 경우이며,  Fuzzy Clustering이 더 포괄적이라고 생각할 수 있습니다.)

 

위의 (식 3)에서 SSE를 최소로 하는 각 군집의 평균 를 구하기 위해서 (식 3)을 에 대해 편미분(partial derivative with respect to ) 한 후 '0'으로 놓고 연립방정식을 풀면 아래와 같은 해를 얻습니다.  (K-means clustering 에서 각 군집의 중심 평균을 구할 때는 해당 군집의 관측치 값만 사용하는데 반해서, Fuzzy K-means Clustering 에서는 전체 관측치에다가 각 군집에 속할 가능성인 가중치를 곱한 값을 사용함)

 

   ---- (식 4)

 

 

 

 

위의 (식 3)에서 SSE를 최소로 하는 가중값 를 구하기 위해서 (식 3)을 에 대해 편미분(partial derivative with respect to ) 한 후 '0'으로 놓고 연립방정식을 풀면 아래와 같은 해를 얻게 됩니다.

 

     ---- (식 5)

 

 

 

p 값이 커지면 커질수록 각 군집의 평균이 전체 평균에 가까워져서 한 군집으로 데이터를 분류하는 것이 점점 더 모호(fuzzier)해집니다. 일반적으로 위의 (식 5)의 가중값 의 재계산식을 간편히 하기 위해 p=2를 많이 사용합니다.

 

p=2 를 (식 5)에 대입하면 (식 5)가 아래와 같이 계산하기 용이하게 정리됩니다.

 

       ---- (식 6)

 

 

위의 (식 6)의 분자를 살펴보면, 관측치 가 군집 에 속할 가능성, 가중치 는 관측치 와 군집의 중심 의 거리(distance) 제곱에 반비례함을 알 수 있습니다. 즉 관측치와 군집의 중심 간 거리가 짧을 수록 (즉, 유사할 수록, 그 군집에 속할 가능성이 높을 수록) 는 커지게 됩니다. 

(* 출처 : 이정진)

 

이때 분모는 각 데이터와 군집 1 ~ K 까지의 거리 제곱의 역수의 합이며, 이것으로 분자(특정 군집 K와 관측치 간의 거리 제곱의 역수)를 나누어주게 되면 여러 군집에 속하는 가중값의 합이 1이 되도록 해주는 표준화 상수 역할을 하게 됩니다.

 


 

간단한 예제를 가지고 Fuzzy K-means Clustering을 반복 수행해보겠습니다. 

 

n = 4 (관측치 4개),

m = 2 (변수 2개),

K = 2 (군집 2개),

p = 2 (가중치 계산 상수 parameter),

군집 중심과 관측치간의 거리는 유클리드 제곱 거리(Squared euclidean distance) 인

 

간단한 예제이며, 엑셀로 수식 걸어서 반복 수행하였습니다. (아래 첨부한 엑셀 파일 참조하세용~)

Fuzzy_Clustering_example.xlsx

 

 

[ 데이터셋 ]

data x1 x2
obs 1 -1 -2
obs 2 -2 -1
obs 3 1 3
obs 4 3 2

[ x1, x2 축 기준 관측치 산점도 ]

 

 

무작위로 obs 2, obs 4에 군집 1 가중값(wi1)으로 0.8, 군집 2 가중값(wi2)으로 0.2를 할당하고, obs 1, obs 3에는 군집 1 가중값(wi1)으로 0.2, 군집 2 가중값(wi2)으로 0.8을 할당하였습니다. (각 관측치의 모든 군집이 가중값 합의 1이 되어야 하므로 군집 1 가중값이 정해지면 나머지 군집 2의 가중값은 1-wi1 으로 자동으로 정해짐)

 

관측치가 군집 에 속할 가능성인 가중값  는 (식 6)에 의해서, 군집 의 중심  는 (식 4)에 의해서 구했습니다.

 

 

1st iteration

data

cluster 1

cluster 2

weight
wi1

wi1^2*xi
(x1, x2)

1/d(xi, c1)
^2

weight
wi2

wi2^2*xi
(x1, x2)

1/d(xi, c2)
^2

obs 1

0.2

-0.04

-0.08

0.12

0.8

-0.64

-1.28

0.14

obs 2

0.8

-1.28

-0.64

0.12

0.2

-0.08

-0.04

0.16

obs 3

0.2

0.04

0.12

0.15

0.8

0.64

1.92

0.14

obs 4

0.8

1.92

1.28

0.12

0.2

0.12

0.08

0.09

new centroid
(new mean)

 

0.47

0.50

 

 

0.03

0.50

 

 

 

가중값 가 수렴할 때까지 위 계산을 반복을 합니다.

(위의 표 'Fuzzy K-means Clustering Algorithm' 참조)

 

 

2nd iteration

data

cluster 1

cluster 2

weight
wi1

wi1^2*xi
(x1, x2)

1/d(xi, c1)
^2

weight
wi2

wi2^2*xi
(x1, x2)

1/d(xi, c2)
^2

obs 1

0.46

-0.22

-0.43

0.09

0.54

-0.29

-0.57

0.18

obs 2

0.43

-0.37

-0.19

0.10

0.57

-0.64

-0.32

0.21

obs 3

0.52

0.27

0.82

0.21

0.48

0.23

0.68

0.11

obs 4

0.56

0.95

0.63

0.14

0.44

0.58

0.38

0.08

new centroid
(new mean)

 

0.63

0.84

 

 

-0.12

0.16

 

 

 

3rd iteration
data

cluster 1

cluster 2

weight
wi1

wi1^2*xi
(x1, x2)

1/d(xi, c1)
^2

weight
wi2

wi2^2*xi
(x1, x2)

1/d(xi, c2)
^2

obs 1

0.34

-0.11

-0.23

0.05

0.66

-0.44

-0.88

0.55

obs 2

0.32

-0.21

-0.10

0.06

0.68

-0.92

-0.46

0.63

obs 3

0.66

0.44

1.31

0.56

0.34

0.12

0.35

0.06

obs 4

0.65

1.28

0.86

0.33

0.35

0.36

0.24

0.05

new centroid
(new mean)

 

1.30

1.70

 

 

-0.78

-0.66

 

 

 

4th iteration
data

cluster 1

cluster 2

weight
wi1

wi1^2*xi
(x1, x2)

1/d(xi, c1)^2

weight
wi2

wi2^2*xi
(x1, x2)

1/d(xi, c2)
^2

obs 1

0.09

-0.01

-0.02

0.03

0.91

-0.83

-1.66

1.94

obs 2

0.08

-0.01

-0.01

0.04

0.92

-1.69

-0.84

2.02

obs 3

0.90

0.82

2.45

0.86

0.10

0.01

0.03

0.04

obs 4

0.88

2.31

1.54

0.74

0.12

0.05

0.03

0.05

new centroid
(new mean)

 

1.94

2.48

 

 

-1.45

-1.44

 

 

 

5th iteration
data

cluster 1

cluster 2

weight
wi1

wi1^2*xi
(x1, x2)

1/d(xi, c1)^2

weight
wi2

wi2^2*xi
(x1, x2)

1/d(xi, c2)
^2

obs 1

0.02

0.00

0.00

0.03

0.98

-0.96

-1.93

2.00

obs 2

0.02

0.00

0.00

0.04

0.98

-1.93

-0.97

2.00

obs 3

0.96

0.92

2.75

0.83

0.04

0.00

0.01

0.04

obs 4

0.94

2.65

1.77

0.77

0.06

0.01

0.01

0.05

new centroid
(new mean)

 

1.98

2.51

 

 

-1.49

-1.49

 

 

 

6th iteration

data

cluster 1

cluster 2

weight
wi1

wi1^2*xi
(x1, x2)

1/d(xi, c1)^2

weight
wi2

wi2^2*xi
(x1, x2)

1/d(xi, c2)
^2

obs 1

0.02

0.00

0.00

0.03

0.98

-0.97

-1.93

2.00

obs 2

0.02

0.00

0.00

0.04

0.98

-1.93

-0.97

2.00

obs 3

0.96

0.91

2.74

0.82

0.04

0.00

0.01

0.04

obs 4

0.94

2.67

1.78

0.78

0.06

0.01

0.01

0.05

new centroid
(new mean)

 

1.98

2.51

 

 

-1.49

-1.49

 

 

 

5번째 반복과 6번째 반복의 가중값의 변화가 거의 없으므로 (즉, 수렴하였으므로) 퍼지 군집 알고리즘을 종료합니다.

 

결과적으로 obs 1 과 obs 2 는 군집 2 (cluster 2)에 속하고, obs 3 과 obs4 는 군집 1로 분류가 되었습니다.

(obs 1의 w11 = 0.02, w12 = 0.98,

 obs 2의 w21 = 0.02, w22 = 0.98,

 obs 3의 w31 = 0.96, w32 = 0.04,

 obs 4이 w41 = 0.94, w42 = 0.06  이므로)

 

위에 제시한 예제의 산점도를 보면 obs 1과 obs 2가 서로 인접해 있으며, obs 3과 obs 4가 서로 인접해 있으므로 군집화가 제대로 된 셈이네요. 

 

비록 처음에 무작위로 가중값을 부여했을 때 obs 1과 obs 3을 군집2로 가중치를 0.8 할당, obs 2와 obs 4를 군집1로 가중치를 0.8 할당하였습니다만, 6차례의 반복을 거치면서 각 관측치별 가중치도 새로 계산하고, 군집 1과 군집 2의 중심(K-평균)도 새로 계산하면서 군집화(clustering)을 반복하다보니 인접한(유사한) 관측치끼리 군집으로 잘 묶였습니다.

 

처음에 가중값 부여할 때 무작위로 한다고 했는데요, 여기서 무작위로 부여하는 숫자가 바뀌면 (무작위 이므로 뭐가 될지 모름 -_-;) 물론 군집화의 결과가 바뀔 수 있다는 점은 알고 계시구요. (K-means clustering 도 초기 중심값이 무작위로 할당되다보니 분석 돌릴 때마다 군집화 결과가 바뀔 수 있다는 건 동일함)

 

아래에 R 예제에서는 초기 rational starting point를 행렬로 입력할 수 있는 기능이 있으므로, 관측값별 초기 가중값을 합리적으로 부여할 수 있는 상황이라면 이용할 만 하겠습니다.

 

 


 

R의 'fclust' Package를 가지고 위에서 소개했던 예제에 대해서 Fuzzy clustering을 수행해보겠습니다.

 

1) 먼저, fclust Package 를 설치하고 로딩해보겠습니다.

 

> ##---------------------------------------------
> ## Fuzzy K-means Clustering : R fclust Package
> ##---------------------------------------------
> install.packages("fclust")
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.3/fclust_1.1.2.zip'
Content type 'application/zip' length 197520 bytes (192 KB)
downloaded 192 KB

package ‘fclust’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
	C:\Users\Administrator\AppData\Local\Temp\RtmpkzHe81\downloaded_packages
> library(fclust)

 

 

 

 

2) Dataset 준비

 

  위의 이론 설명에서 사용했던 2개 변수(x1, x2), 4개 관측치 예제 데이터를 똑같이 사용하겠습니다.

 

> # dataset
> x1 <- c(-1, -2, 1, 3)
> x2 <- c(-2, -1, 3, 2)
> x1_x2_d.f <- data.frame(x1, x2)
> x1_x2_d.f
  x1 x2
1 -1 -2
2 -2 -1
3  1  3
4  3  2

 

 

 

 

3) rational starting point Matrix U

 

위의 이론 설명에서 사용했던 값을 그대로 사용해서요, 관측값 1번이 군집1에 속할 가중값은 0.2, 군집2에 속할 가중값은 0.8, ... , 관측값 4번이 군집1에 속할 가중값이 0.8, 군집2에 속할 가중값은 0.2 의 초기값을 행렬(Matrix)로 만들었습니다.  이걸 아래의 FKM() 함수의 startU 옵션의 할당값으로 지정해줄겁니다.

 

> # rational starting point Maxtix U
> rational_starting_point <- matrix(c(0.2, 0.8, 0.2, 0.8, 0.8, 0.2, 0.8, 0.2), 
+                                   nrow = 4, ncol = 2, byrow = F)
> rational_starting_point
     [,1] [,2]
[1,]  0.2  0.8
[2,]  0.8  0.2
[3,]  0.2  0.8
[4,]  0.8  0.2

 

 

 

 

4) Fuzzy K-means Clustering using FKM() function of fclust package

 

fclust Package이 FKM() 함수의 매개변수 및 옵션에 대해서 소개하자면 아래와 같으며, 데이터셋(행렬 또는 데이터 프레임) 할당해주는 X, 군집의 개수를 지정해주는 k, 퍼지 매개변수(parameter) m (위의 '식 5'번의 p) 에 대해서만 옵션을 설정해보겠으며, 나머지는 default 설정 그대로 사용하겠습니다.

(같은 척도의 데이터이므로 표준화 필요 없음. default 가 no standardization)

 

 Arguments

 Description

 X

 Matrix or data.frame 

 k

 Number of clusters (default: 2)

 m

 Parameter of fuzziness (default: 2) 

 (위 이론 부분의 '식 5'번의 p 이며, p=2 이면 '식 6'번처럼 계산이 간단해짐)

 RS

 Number of (random) starts (default: 1)

 stand

 Standardization: if stand=1, the clustering algorithm is run using standardized data (default: no standardization) 

 startU

 Rational starting point for the membership degree matrix U

 (default: no rational start) 

 conv

 Convergence criterion (default: 1e-9) 

 maxit

 Maximum number of iterations (default: 1e+6) 

( * source : Paolo Giordani, Maria Brigida Ferraro)

 

 

퍼지군집 분석 결과는 아래와 같습니다.

 

> # Fuzzy K-means clustering with FKM() fuctnion of fclust package > x1_x2_FKM <- FKM(X = x1_x2_d.f, # Matrix or data.frame + k = 2, # Number of clusters (default: 2) + m = 2, # Parameter of fuzziness (default: 2) + startU = rational_starting_point)
> # startU : Rational starting point for the membership degree matrix U
>
>


>
# Fuzzy K-means clustering results > x1_x2_FKM Fuzzy clustering object of class 'fclust' Number of objects: 4 Number of clusters: 2 Closest hard clustering partition: 1 2 3 4 2 2 1 1 Membership degree matrix (rounded): Clus 1 Clus 2 1 0.02 0.98 2 0.02 0.98 3 0.95 0.05 4 0.96 0.04 Available components: [1] "U" "H" "clus" "value" "cput" "iter" "k" "m" "stand" "Xca" [11] "X" "call"

 

 

 

 

 

R의 fclust Package 의 FKM() 함수로 퍼지 군집화를 한 결과와, 위에서 엑셀을 가지고 6번 반복해서 푼 결과가 일치함을 알 수 있습니다. (obs3, obs4의 degree of membership, ie, weight 가 소숫점 두째자리에서 약간 다르기는 하지만 무시할만 합니다. 엑셀로는 반복을 6번하고 멈추었는데요, R은 14번 반복을 했네요.  이는 R fclust Package의 수렴 기준(Convergence criterion)의 default 값이 '1e-9' 으로서 매우 매우 작기 때문에 반복을 좀더 많이 했습니다.

 

> x1_x2_FKM$iter # number of iteration Start 1 14

 

 

 

fclust Package FKM() 함수의 분석결과 객체에 대해 소개하자면 아래와 같으며, 필요한 정보는 indexing 해서 사용하면 유용합니다.

 

fclust Package

FKM() object value 

Description 

 U

 Membership degree matrix

 H

 Prototype matrix

 clus

 Matrix containing the indices of the clusters

 where the objects are assigned (column1) and

 the associated membership degrees (column 2)

 value

 Vector containing the loss function values for the RS starts 

 cput

 Vector containing the computational times (user times) for the RS starts 

 iter

 Vector containing the numbers of iterations for the RS starts 

 k

 Number of clusters (군집의 수)

 m

 Parameter of fuzziness (위 식5번의 p와 동일)

 stand

 Standardization (Yes if stand=1, No if stand=0) 

 Xca

 Data used in the clustering algorithm (standardized data if stand=1)

 X

 Raw data

 call  Matched call (함수 다시 호출하기)

( * source : Paolo Giordani, Maria Brigida Ferraro)

 

 

예를 들어, 소속 정도의 가중값을 알고 싶다면 'U' 객체를 indexing 해오면 됩니다.

 

> # Membership degree matrix
> x1_x2_FKM$U
      Clus 1     Clus 2
1 0.01684418 0.98315582
2 0.01734390 0.98265610
3 0.95397165 0.04602835
4 0.96353078 0.03646922

 

 

 

 

각 퍼지 군집의 중심 위치도 궁금하지요?  이때 쓰는게 'H' 객체입니다.  Cluster 1 은 x1 중심좌표가 '2.008850', x2 중심좌표가 '2.493750' 으로서 우상단에 위치하고 있으며, Cluster 2는 x1 중심좌표가 '-1.493918', x2 중심좌표는 '-1.492924' 로서 좌하단에 위치하고 있군요.

 

> # Prototype matrix : H
> x1_x2_FKM$H
              x1        x2
Clus 1  2.008850  2.493750
Clus 2 -1.493918 -1.492924

 

 

 

 

변수가 2개인 2차원 데이터이므로 산점도 그래프로 그려보면 좀더 명확하게 이해할 수 있겠네요.  아래의 그래프를 보면 2개의 군집별로 색깔이 다르게 나와있구요, '*' 표시는 각 군집의 중심을 의미합니다. 바로 위해서 x1_x2_FKM$H 로 indexing했던 바로 그 좌표값입니다.

 

> # Fuzzy K-means Clustering plot
> plot(x1_x2_FKM)

 

 

 

이상으로 퍼지 군집(Fuzzy K-means Clustering Algorithm)에 대한 소개를 마치겠습니다.

 

 

다음번 포스팅에서는 '혼합분포군집(Mixture Distribution Clustering)' 모형에 대해서 알아보겠습니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

[Reference]

- Michael Negnevitsky, "인공지능 개론(Artificial Intelligence)' (2nd Edition), 한빛아카데미, 2013

- 이정진, "R, SAS, MS-SQL을 활용한 데이터마이닝", 자유아카데미, 2011

- Fuzzy C-means Clustering Algorithm : https://en.wikipedia.org/wiki/Fuzzy_clustering

- R fuzzy clusterin package 'fclust' : Paolo Giordani, Maria Brigida Ferraro,  https://cran.r-project.org/web/packages/fclust/fclust.pdf

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

지난번 포스팅에서는 (1) 응집형 계층적 군집화(Agglomerative Hierarchical Clustering) 방법 5가지(단일연결법, 완전연결법, 평균연결법, 중심연결법, Ward연결법) 중에서, 오차제곱합의 증분으로 군집 간 (비)유사성을 측정해서 군집화를 하는 Ward 연결법에 대해서 알아보았습니다.

 

 

이번 포스팅부터는 (2) 분할적 군집화(Partitional Clustering) 기법 중에서 프로토타입 기반(Prototype-based)K-중심 군집화(K-centroid clustering)에 대해서 알아보도록 하겠습니다.

 

 

[ 분할적 군집 (Partitional Clustering, Non-hierarchical Clustering) ]

 

 

 

 

복습하는 차원에서 한번 더 복기를 하자면,

 

- 응집형 계층적 군집화(Agglomerative Hierarchical Clustering)은 각각의 객체에서 시작해서 유사성 척도(proximity measure)에 의거해 유사한 (거리가 짧은) 객체들을 Tree 형태의 계층적 군집으로 차근 차근 묶어가는 기법입니다. (a set of nested clusters organized as a hierarchical trees).  일단 한번 군집으로 묶이면 그 객체는 계속 그 군집에 속하게 되고 변동이 없으며, 계층 구조의 Tree에서 어느 수준을 기준으로 하느냐에 따라서 군집이 달라지게 됩니다.

 

- 분할적 군집화(Partitional Clustering)은 객체가 하나의 군집에 exclusive하게 속하도록 군집을 형성합니다. (A division data objects into non-overlapping subsets such that each data object is in exactly one subset). 분할 방법에는 프로토타입 기반(Prototype-based), 분포 기반(distribution-based), 밀도 기반(Density-based), 그래프 기반(Graph-based) 기법이 있습니다.

 

 

참고로,

- Hard clustering은 객체별로 어느 군집에 속할지를 명시적으로 할당하는 기법이며,

K-중심군집은 Hard clustering에 속합니다.

 

- Soft clustering은 각 객체가 어느 군집에 속할지를 가중치(weight)나 확률(probability)로서 가능성 정도를 나타내주는 기법으로서, Fuzzy Clustering과 혼합분포군집(Mixture Distribution Clustering)이 이에 속합니다.

 

 

 

이번 포스팅에서 소개할 분할적 군집화는 이중에서 프로토타입 기반(Prototype-based) 기법 중에서도 K-중심군집(K-centroid Clustering) 모형이 되겠습니다.

 

프로토타입 기반 군집화(Prototype-based Clustering)는 미리 정해놓은 각 군집의 프로토타입에 각 객체가 얼마나 유사한가 (혹은 가까운가)를 가지고 군집을 형성하는 기법입니다. 

 

K-중심군집에서는 연속형 데이터의 경우 평균(Mean)이나 중앙값(Median)을 그 군집의 프로토타입으로 하며, 이산형 데이터인 경우는 최빈값(Mode)이나 메도이드(Medoid)라고 해서 해당 군집을 가장 잘 표현할 수 있는 측도를 정해서 프로토타입으로 정하게 됩니다.

 

보통 군집분석을 공부한다고 했을 때 가장 많이 회자되고, 가장 처음에 배우는 기법이 아마도 'K-평균 군집화(K-means Clustering)이 아닐까 싶습니다.  그런데 앞서 소개드린 것처럼 군집분석 기법에는 정말 많은 알고리즘이 있습니다. K-평균 군집은 그 중에서도 일부에 해당하는 기법일 뿐이며, 프로토타잎도 데이터 형태에 따라서 '평균(Mean)'을 쓰는 K-means Clustering, '중앙값(Median)'을 쓰는 K-median Clustering, '메도이드(Medoid)'를 쓰는 K-medoid Clustering 등으로 세분화된다는 점은 알아두시면 좋겠습니다.  이들을 모두 묶어서 'K-중심군집(K-centroid Clustering)'이라고 합니다.

 

여기서 'K'는 '군집의 수(number of clusters)'를 나타내는 모수(parameter)로서, 분석가가 사전에 정해주어야 합니다.  참고로, 군집의 수 K를 미리 지정해주어야 하는 군집분석 기법으로는 이번 포스팅의 주제인 K-중심군집(K-centroid Clustering), 그리고 퍼지군집(Fuzzy Clustering), 혼합분포 군집(Mixture Distribution Clustering) 등이 있습니다.

 

군집의 수 K를 정하는 문제가 참 중요한데요, 좀 어렵고 애매한 부분이 있습니다.  저는 보통은 업에 대한 이해를 바탕으로 분석 목적을 감안하여 복수의 k를 지정해서 군집분석을 수행한 후에, 군집에 대한 profiling을 해보고, 가장 적합하다고 판단되는 k를 정하곤 했습니다.  다분히 분석가의 업에 대한 경험/이해도와 주관이 많이 들어가고, Biz 활용 목적과 현실적 제약조건도 고려해야 하며, 또 시행착오와 profiling을 통한 오랜 탐색이 필요한 접근법입니다. (Science가 아니라 Art?)^^;  

 

아래의 괄호안에 자료에 보니 계량적으로 최적의 k 를 찾아가는 기법이 소개되어 있습니다.  이 기법들을 여기서 소개하려면 얘기가 너무 길어지므로 pass하겠으며, 관심있는 분은 아래 링크한 위키글을 참고하시기 바랍니다. (군집 수 k 정하는 기법 참고 자료 ☞ https://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set)

 

 

[ 군집의 수 K 정하기의 애미모호함 ?  어려움?? ]

 

 

 

 

이번 포스팅에서는 일반적으로 가장 많이 사용하는 'K-평균 군집(K-means Clustering)'에 대해서만 대표로 설명을 하도록 하겠습니다.

 

 

K-평균 군집(K-means Clustering)의 원리를 알아보면요,

 

1) 군집 내 응집도 최대화(maximizing cohesion within cluster) : 군집 內 중심(centroid)과 해당 군집의 각 객체 간 거리의 합 최소화

 

2) 군집 간 분리도 최대화(maxizing separation between clusters) : 각 군집의 중심(centroid) 間 거리 합 최대화 

 

의 두 가지 목적함수를 만족하는 해(解, solution)를 찾는 것입니다.

 

 

[ K-평균 군집의 개념 및 원리 (Concept and Principle of K-means Clustering) ]

 

 

 

즉, 군집분석은 결국 위의 두 목적함수에 대한 최적화 (optimization of global objective function) 문제임을 알 수 있습니다.  복잡도(complexity)를 살펴보면, 군집의 수가 k, 차원의 수가 d, 객체의 수가 n 일 때 입니다. (* 출처 : https://en.wikipedia.org/wiki/K-means_clustering)  기본적으로 객체의 수(n)가 많을 수록 시간이 오래걸리며, 특히 변수의 수(d)와 군집의 수(k)가 늘어날 수록 지수적으로 계산 시간이 증가함을 알 수 있습니다.  따라서 허접한 변수들 몽땅 때려넣고 군집화하라고 컴퓨터한테 일 시킬 것이 아니라, 똘똘하고 핵심적인 변수를 선별해서 차원을 줄인 후에 군집분석을 실행하는 것이 연산시간을 줄이는 측면에서나, 또 Biz. 목적에 맞게 잘 군집화가 되도록 하는 측면에서나 중요합니다.

 

 

다음으로, 'K-중심군집 알고리즘(K-centroid Clustering Algorithm)'에 대해서 알아보겠습니다.  알고리즘은 알고나면 허무할 정도로 정말 쉽습니다. ^^' 컴퓨터야 중심과 객체 간 거리를 반복적으로 계산하느라 죽어나겠지만 말이지요.

 

 

[ 'K-중심군집 알고리즘(K-centroid Clustering Algorithm) ]

 

 

0: Select number of clusters, K

 

1: Select K points as the initial centroids


2: Repeat


3:     Form K clusters by assigning all points
       to the closest centroid


4:     Recompute the centroid of each cluster


5: Until The centroids don't change 

 

 

0: 군집의 수 K 결정

 

1: K개 군집의 초기 중심 선정

 

2: Repeat

 

3:    객체와 K군집의 중심과 거리가 가장 가까운

      군집으로 각 객체를 할당

 

4:    객체와 새로 바뀐 K군집의 중심과의

      거리를 재계산

 

5: Until K군집 중심이 바뀌지 않을 때까지 반복

 

 

 

 

위 알고리즘을 도식화해서 예를 들어보면 아래와 같습니다. 매번의 반복(iteration) 마다 군집의 중심이 새로 계산되고, 새로 바뀐 중심과 각 객체간 거리가 다시 계산되면서 군집이 계속 동적으로 바뀌다가 더 이상 변동이 없이 수렴될 때까지 반복을 하게 됩니다. 

 

 

[ K-평균군집화의 개념적인 예시 (Conceptual exmaple of K-means Clustering) ]

 

 

 

위의 개념적 예시 그림에서도 짐작할 수 있듯이, 초기 중심값(initial centroid)가 바뀌면 군집 묶이는게 바뀔 수 있습니다.  이런 점때문에 B2C에서 고객세분화(customer segmentation)에 K-means clustering을 적용하게 되면 매번 군집화를 시도할 때마다 군집에 묶이는 고객이 달라질 수 있어서 문제가 될 소지가 있습니다.  (참고로, R에서 set.seed = 1234 처럼 set.seed() 함수를 사용하면 무작위수가 매번 실행때마다 동일하게 할 수 있음)

 

 

이번 포스팅은 K-평균 군집(K-means Clustering)을 다룬다고 했으므로, 중심(centroid)는 '평균(Mean)'이 되겠지요.

 

그리고 유사도(proximity, similarity)는 보통은 유클리드 거리(Euclidean distance,

) 혹은 유클리드 제곱 거리(SSE, SUM of Squared Error, R에서 사용하는 거리)를 사용합니다.  데이터 특성에 따라서 유사도 측도로서 적합한 Measure를 선택해서 분석하시면 되겠습니다. (☞ 유사도 측정 Distance Measures 참고 : http://rfriend.tistory.com/199)

 

거리를 가지고 유사성을 측정한다고 했는데요, 이러다 보니 K-평균군집(K-means Clustering)은 노이즈나 이상치에 민감(Sensitive to Noise and Outlier)한 단점이 있습니다.  평균보다는 중앙값이 이상치에 덜 민감하므로 이상치로 인한 왜곡이 우려되면 K-중앙값군집(K-median Clustering)이 대안이 될 수 있겠네요.  아니면 탐색적 분석 단계에서 이상치를 제거하는 것도 방법이 될 수 있겠고요.

 

 

마지막으로, 만약 여러 변수들의 계측 단위(scale)이 서로 다르다면 사전에 표준화(standardization)를 해줄 필요가 있습니다.  안그러면 측정 단위가 큰 특정 한, 두개의 변수에 의해 군집화가 휘둘릴 수 있기 때문입니다.  보통 표준정규분포로 z 표준화를 많이 사용합니다. (R의 scale() 함수)

 

 


 

 

K-중심 군집(K-Centroid Clustering) 이론에 대해서는 왠만큼 소개를 한듯 하니, 이제 데이터셋을 가지고 R script 를 써가면서 실습을 해보겠습니다.  R script 가 무척 짧아서 당황하실수도 있다는 점 미리 안내드립니다. ㅋㅋ

 

실습 데이터셋은 iris 입니다. 

Sepal.Length, Sepal.Width, Petal.Length, Petal.Width 의 4개의 변수를 가지고 있고, 150개의 관측치를 가지고 있는 데이터프레임입니다.

 

결측값은 없이 깨끗하네요.

 

측정 척도의 단위(scale)가 4개 변수 모두 길이를 재는 동일 단위이므로 iris 데이터셋의 경우 별도로 표준화르 할 필요는 없겠네요. 

 

> ##-----------------------------------
> ## K-centroid clustering
> ##-----------------------------------
> 
> # dataset : iris
> str(iris)
'data.frame':	150 obs. of  5 variables:
 $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
 $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
 $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
 $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
> head(iris)
  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
> 
> # checking missing value
> colSums(is.na(iris))
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
           0            0            0            0            0

 

 

 

 

산포도(Scatter Plot)를 그려보면 아래와 같습니다.

 

> # scatter plot of iris
> panel.fun <- function(x, y, ...) {
+   horizontal <- (par("usr")[1] + par("usr")[2]) / 2; 
+   vertical <- (par("usr")[3] + par("usr")[4]) / 2; 
+   text(horizontal, vertical, format(abs(cor(x,y)), digits=2)) 
+ }
> 
> pairs(iris[1:4], 
+       pch = 21, bg = c("red","green3","blue")[unclass(iris$Species)], 
+       upper.panel=panel.fun, 
+       main = "Statter Plot of Iris Dataset")

 

 

 

 

K-means Clustering이 중심과의 거리를 가지고 군집을 묶는 방법이다보니, 위의 산포도를 보면 Petal.Length와 Petal.Width 의 두개의 변수를 가지고 군집화(Clustering)를 하는 것이 제일 좋을 것 같군요.(아마 Sepal.Width와 Petal.Length 의 두개 변수를 사용해서 K-means Clustering을 돌리면 좌측 상단의 두 Species가 반반씩 잘못 섞여서 군집화가 될겁니다. 왜 그럴지는 한번 생각해보시길...) 

 

Petal.Length와 Petal.Width 로 산점도를 그려보면 아래와 같습니다.

 

[plot 1 : original scatter plot]

 

 

> # Original Scatter Plot of Iris Petal.Length & Petal.Width 
> install.packages("ggplot2")
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.3/ggplot2_2.1.0.zip'
Content type 'application/zip' length 2001758 bytes (1.9 MB)
downloaded 1.9 MB

package ‘ggplot2’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
	C:\Users\Administrator\AppData\Local\Temp\Rtmpi20q05\downloaded_packages
> library(ggplot2)
> iris_plot <- ggplot(data=iris, aes(x=Petal.Length, y=Petal.Width, colour=Species)) + 
+   geom_point(shape=19, size=4) + 
+   ggtitle("Original Scatter Plot of Iris Petal.Length & Petal.Width")
>   
> iris_plot
> # text annotation with Species Name
> iris_plot_2 <- iris_plot +
+   annotate("text", x=1.5, y=0.7, label="Setosa", size=5) + # text annotation
+   annotate("text", x=3.5, y=1.5, label="Versicolor", size=5) + 
+   annotate("text", x=6, y=2.7, label="Virginica", size=5)
> 
> iris_plot_2
> # adding shadowed box
> iris_plot_3 <- iris_plot_2 +
+   annotate("rect", xmin=0, xmax=2.6, ymin=0, ymax=0.8, alpha=0.1, fill="red") + 
+   annotate("rect", xmin=2.6, xmax=4.9, ymin=0.8, ymax=1.5, alpha=0.1, fill="green") + 
+   annotate("rect", xmin=4.8, xmax=7.2, ymin=1.5, ymax=2.7, alpha=0.1, fill="blue")
> 
> iris_plot_3

 

 

 

 

 

Noise나 이상값(outlier)는 없어보이므로 별도 전처리 없이 그대로 데이터 가져다 쓰면 되겠네요.

 

자, 이제 드디어 K-평균군집(K-means Clustering)을 R로 돌려보겠습니다.  군집의 개수 K = 3 으로 하겠습니다. (iris 품종으로 Setosa, Versicolor, Virginica 의 3종류가 있다고 우리가 이미 알고 있기 때문에 K 3으로 한 것임.  일반적으로 K-평균군집분석을 할 때 Y Label에 대해서 모르거나 없는 상태에서 비지도학습(Un-supervised Learning)으로서 데이터 속에 숨겨진 패턴을 컴퓨터 보고 한번 찾아보라고 마이닝을 시키게 됨.  다시 한번 말하지만, 이번 iris 데이터셋은 Y Lable을 알고 있는 상태에서 K-평균군집분석 결과를 좀더 이해하기 쉽도록 비교해서 보여주는 것 뿐이며, Y Lable 모른 채, 혹은 없는 상태에서 군집분석을 수행하게 됨)

 

K-평균군집의 R script는..... 음..... 아래 한줄이 끝입니다. -_- 

 

kmeans(dataset, k)  # k : number of clusters

 

> # K-means clustering with Petal.Length and Petal.Width
> iris_k_means <- kmeans(iris[,c("Petal.Length", "Petal.Width")], 3) 

 

 

 

K-평균군집의 객체 iris_k_means 를 호출하면 아래와 같이 K-평균 군집 결과를 일목요연하게 볼 수 있습니다. 

 

- K-means clustering with 3 clusters of sizes 50, 52, 48

   : 군집의 개수(k)가 3개, 군집 1/2/3별 크기가(개체 개수) 50개, 52개, 48개

- Cluster means

   : 군집 1/2/3 별 두개의 변수 Petal.Length, Petal.Width의 평균 좌표 

     (=> profiling 하기에 good!)

- Clustering vector

   : 각 개체별 군집 벡터
- Within cluster sum of squares by cluster

   : 각 군집의 중심(centroid)와 각 군집에 속한 개체간 거리의 제곱합

- Available components

   : 군집분석 결과의 구성요소
     => 필요한거 있으면 이 객체(object)를 indexing해서 쓰면 요긴함
 

 

> iris_k_means
K-means clustering with 3 clusters of sizes 50, 52, 48

Cluster means:
  Petal.Length Petal.Width
1     1.462000    0.246000
2     4.269231    1.342308
3     5.595833    2.037500

Clustering vector:
  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2
 [52] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 2 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3
[103] 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 3 3 3

Within cluster sum of squares by cluster:
[1]  2.02200 13.05769 16.29167
 (between_SS / total_SS =  94.3 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"        
[8] "iter"         "ifault"   

 

 

 

 

아래처럼 names() 함수를 써서 군집분석 결과의 객체들에 무엇이 있는지 확인해볼 수도 있습니다.

 

- cluster : 각 개체별 할당된 군집 번호, 1부터 k번까지 군집 숫자 

             (A vector of integers (from 1:k) indicating the cluster to which each point is allocated.)

- centers : 군집의 중심 (centroid) 좌표 행렬 (A matrix of cluster centres.)

- totss : 제곱합의 총합 (total sum of squares)

- withinss : 군집 내 군집과 개체간 거리의 제곱합 벡터.

               (Vector of within-cluster sum of squares, one component per cluster)

- tot.withinss : 군집 내 군집과 개체간 거리의 제곱합의 총합, 즉, sum(withinss)

                   (Total within-cluster sum of squares, i.e. sum(withinss))

- betweenss : 군집과 군집 간 중심의 거리 제곱합

                   (The between-cluster sum of squares, i.e. totss-tot.withinss)

- size : 각 군집의 개체의 개수 (The number of points in each cluster)

- iter : 반복 회수 (The number of (outer) iterations)

- ifault : 전문가용의 발생 가능한 알고리즘 문제의 indocator (?? 이거 뭔지 잘 모르겠음... -_-??)
           (integer: indicator of a possible algorithm problem – for experts)

 

> # objects of k-means clustering in R
> names(iris_k_means)
[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"    "size"        
[8] "iter"         "ifault"

 

 

 

 

그럼, 군집의 크기(객체의 개수)를 한번 확인해볼까요?  table() 함수를 써도 되고 size 객체를 가져다가 확인해도 됩니다. 두 가지 방법 모두 아래에 소개하였습니다.

 

> # cluster size > table(iris_k_means$cluster) 1 2 3 50 52 48


>
iris_k_means$size [1] 50 52 48 > > prop.table(iris_k_means$size) [1] 0.3333333 0.3466667 0.3200000

 

 

 

 

각 군집 1, 2, 3의 중심(centroid)도 확인해보겠습니다.

 

> # centroid of clusters
> iris_k_means$centers
  Petal.Length Petal.Width
1     1.462000    0.246000
2     4.269231    1.342308
3     5.595833    2.037500 

 

 

 

마지막으로, 각 개체들을 군집별로 색깔을 달리해서 산점도(scatter plot) 그려보겠습니다.  덤으로 각 군집의 중심(centroid)을 검정색 점으로 해서 덮입혀보았습니다.

 

위의 [plot 1 : original scatter plot]에서 iris species 로 색깔을 구분해서 그린 산점도와 아래의 군집(cluster)별로 색깔을 구분해서 그린 산점도가 거의 유사하지요?

 

> # Scatter plot of Iris' K-menas clusters
> cluster <- iris_k_means$cluster
> iris_k_means_x_y <- cbind(cluster, iris[,c("Petal.Length", "Petal.Width")])
> head(iris_k_means_x_y)
  cluster Petal.Length Petal.Width
1       1          1.4         0.2
2       1          1.4         0.2
3       1          1.3         0.2
4       1          1.5         0.2
5       1          1.4         0.2
6       1          1.7         0.4
> 
> sapply(iris_k_means_x_y, class)
     cluster Petal.Length  Petal.Width 
   "integer"    "numeric"    "numeric" 
> iris_k_means_x_y <- transform(iris_k_means_x_y, 
+                               cluster = as.factor(cluster))
> 
> sapply(iris_k_means_x_y, class)
     cluster Petal.Length  Petal.Width 
    "factor"    "numeric"    "numeric" 
> 
> 
> library(ggplot2)
> iris_k_means_x_y_plot <- ggplot(data=iris_k_means_x_y, 
+                                 aes(x=Petal.Length, y=Petal.Width, colour=cluster)) + 
+   geom_point(shape=19, size=4) + 
+   ggtitle("Scatter Plot of Iris' K-means clusters")
> 
> iris_k_means_x_y_plot
> 
> 
> # adding centroid points of cluster 1, 2, 3
>   # centers(centroids) by cluster 1, 2, 3
> iris_k_means_centers <- iris_k_means$centers
> iris_k_means_centers
  Petal.Length Petal.Width
1     1.462000    0.246000
2     4.269231    1.342308
3     5.595833    2.037500
> 
> iris_k_means_x_y_plot_2 <- iris_k_means_x_y_plot +
+   annotate("point", x = 1.462, y = 0.246, size = 6, color = "black") + 
+   annotate("point", x = 5.595, y = 2.037, size = 6, color = "black") + 
+   annotate("point", x = 4.269, y = 1.342, size = 6, color = "black") + 
+   
+   annotate("text", x=1.462, y=0.4, label="Cluster 1", size=5) + 
+   annotate("text", x=5.595, y=2.2, label="Cluster 2", size=5) + 
+   annotate("text", x=4.269, y=1.5, label="Cluster 3", size=5)
>  
> iris_k_means_x_y_plot_2

 

 

 

 

 

이상으로 K-중심 군집의 하나인 K-평균군집(K-means Clustering)에 대해서 알아보았습니다.

 

다음번 포스팅에서는 프로토타입 기반(Prototype-based) 군집분석의 두번째 기법으로 퍼지 군지(Fuzzy Clustering)에 대해서 알아보겠습니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감~♡'를 꾸욱 눌러주세요.

 

 

[Reference]

(1) "Introduction to Data Mining", Pang-Ning Tan(Michigan State University), Michael Steinbach(University of Minnesota), Vipin Kumar(University of Minnesota), Addison-Wesley Companion Book

(2) "Clustering Algorithm", Ana Fred, INSTITUTO SUPERIOR TECNICO, Universidade Techica de Lisboa, 2009

(3) "R, SAS, MS-SQL을 활용한 데이터마이닝", 이정진 지음, 자유아카데미, 2011

(4) "Data Mining Cluster Analysis : Basic Concepts and Algorithms", Tan, Steinbach, Kumar, 2004

(5) Wikipedia :

    https://en.wikipedia.org/wiki/K-means_clustering

    https://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set

   

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. YOD 2018.06.04 18:21  댓글주소  수정/삭제  댓글쓰기

    seed값 고정 안하고 초기 중심점을 랜덤하게 잡으면 같은 데이터, 같은 k값이어도 최종 결과물은 달라질 수 있다는 말씀이시죠?

응집형 계층적 군집화(agglomerative hierarchical clustering) 방법 중에서

 

 - 지난번 포스팅에서는 중심 연결법 (Centroid Linkage Method)를 다루었으며,

 

 - 이번 포스팅에서는 Ward 연결법 (Ward Linkage Method) 에 대해서 알아보겠습니다. 

 

(참고로, Ward는 이 방법을 제시했던 학자인 Joe H. Ward 의 이름을 딴 것입니다)

 

그동안의 응집형 계층적 군집화에서 다루었던 연결법들로 단일 연결법(single linkage), 완전 연결법(complete linkage), 평균 연결법(average linkage), 중심 연결법(centroid linkage)은 모두 '유클리드 제곱거리(euclidean squared distance)'에 기반해서 군집을 형성하는 방법들 이었습니다.

 

 

 

반면에, 이번에 소개하는 Ward 연결법(ward linkage)은 두 군집 간의 유사성을 두 군집이 합쳐졌을 때의 오차 제곱합(ESS : error sum of squares)의 증가분에 기반해서 측정합니다. (Similarity of two clusters is based on the increase in squared error when two clusters are merged).  즉, 거리 행렬(distance matrix)를 구할 때 오차제곱합의 증분(increase of ESS)을 두 군집 사이의 거리로 측정하게 됩니다.

 

 

 

단일 연결법이 노이즈나 이상치(noise and outlier)에 민감한 반면에, Ward 연결법은 노이즈나 이상치에 덜 민감한 장점이 있습니다. 그리고 Ward 연결법은 비슷한 크기의 군집끼리 묶어주는 경향이 있습니다.

 

중심 연결법과 Ward 연결법의 유사성 측정 수식이 비슷한데요, 중심 연결법의 유사성 측도 대비 Ward 연결법에는 가중값이 추가되었다는 점이 다릅니다. (분자는 같고, 분모가 서로 다름)

 

기본 컨셉은 다음번 포스팅의 프로토타입 기반 분할적 군집화의 하나인 "K 평균 군집화(K-means clustering)"와 유사한 측면이 있습니다.  

 

 

예제를 가지고 풀어보면서 설명을 이어가 보겠습니다.

 

(미리 말씀드리자면, 손으로 푼 결과와 R로 계산한 결과가 서로 다르게 나왔습니다. 어디서 차이가 생기는 건지 파악을 못했습니다.  혹시 이 블로그를 보시고 제가 잘못 계산한 부분을 찾으셨다면 댓글로 알려주시면 감사하겠습니다. ㅜ_ㅜ)

 

 

1) 데이터셋 준비, 탐색적 분석

 

응집형 계층적 군집화이므로 처음에 아래의 5개의 점, 5개의 군집에서부터 시작합니다.

 

 

 

R script도 같이 제시하면서 설명하겠습니다.  먼저, 데이터 입력 및 plotting (↑ 위의 산점도 그래프) 입니다.

 

> ##--------------------------------------------
> ## (1) Agglomerative Hierarchical Clustering 
> ##   (b) Prototype-based
> ##    (1-5) Ward Linkage
> ##--------------------------------------------
> 
> x <- c(1, 2, 2, 4, 5)
> y <- c(1, 1, 4, 3, 4)
> 
> xy <- data.frame(cbind(x, y))
> 
> xy
  x y
1 1 1
2 2 1
3 2 4
4 4 3
5 5 4
> 
> # scatter plot of xy
> my_par = par(no.readonly = TRUE)
> par(mfrow = c(1, 1))
> plot(xy, pch = 19, xlab = c("x coordinate"), ylab = c("y coordinate"), 
+      xlim = c(0, 6), ylim = c(0, 6), 
+      main = "scatter plot of xy")
> 
> # adding student label
> text(xy[,1], xy[,2], labels = abbreviate(rownames(xy)), 
+      cex = 0.8, pos = 1, col = "blue") # pos=1 : at the bottom
> 
> 
> # adding dotted line
> abline(v=c(3), col = "gray", lty = 2) # vertical line
> abline(h=c(3), col = "gray", lty = 2) # horizontal line

 

 

 

2) 유사성 측도로서 거리 행렬(Distance matrix) D 계산하기

 

각 데이터를 군집으로 하는 첫번째 단계에서는 ESS의 증분은 '유클리드 제곱거리(squares of Euclidean distance)'이므로 거리 행렬을 계산하면 아래와 같습니다.

 

 

[distance matrix - no.1]

 

 

유클리드 제곱거리를 구하는 R script 입니다. dist(xy, method="euclidean") 에다가 뒤에 "^2"를 붙여서 제곱을 했습니다.

 

> # proximity matrix : squares of euclidean distance matrix for 6 points
> dist(xy, method = "euclidean")^2
   1  2  3  4
2  1         
3 10  9      
4 13  8  5   
5 25 18  9  2

 

 

  • P1과 P2의 거리가 '1'로서 가장 가까우므로 (즉, 유사하므로) 
    → (P1, P2)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 4개로 줄었습니다.

 

2차원 데이터에 대해서는 아래처럼 부분집합그림(Nested cluster diagram)을 그려볼 수 있습니다.

 

 

(여기까지는 단일 연결법, 완전 연결법, 평균 연결법, 중심 연결법과 동일합니다)

 

 

3) 군집(P1, P2)의 중심 구하기

 

새로 묶인 군집(P1, P2)의 중심(centroid)을 가중평균을 이용해서 구해보면

μ(P1+P2) = {1*(1, 1) + 1*(2, 1)}/(1+1) = {(1, 1) + (2, 1)}/2 = (1.5, 1) 이 됩니다.

 

(여기까지는 중심 연결법과 동일)

 

[centroid coordinate of clusters - no.1]

 

 

 

아래의 부분집합그림에 보면 군집 (P1, P2) 의 중심(centroid) 위치에 노란색 별이 추가되었습니다.

 

 

 

 

4) 군집(P1, P2)와 P3, P4, P5 간 Ward 연결법(Ward linkage method)으로 계산한 수정된 거리행렬(distance matrix) 구하기

 

한개만 예를 들자면, 군집 (P1, P2)와 개체 P5 간의 Ward 연결법에 의한 거리는 위의 [centroid coordinate of clusters - no.1] 의 중심 좌표를 가지고 ESS 증분으로 구하면

d{(P1, P2), P5} = {(1.5-5)^2 + (1-4)^2}/(1/2 + 1/1) = (12.25 + 9)/(3/2) = 21.25*2/3 = 14.17 이 됩니다.

 

[distance matrix - no.2]

  • P4와 P5의 거리가 '2'로서 가장 가까우므로  
    → (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 이제 군집이 처음 5개에서 3개로 줄었습니다.

 

 

5) 새로운 군집 (P4, P5)의 중심(centroid) 구하기

 

[centroid coordinate of clusters - no.2]

 

 

수정된 2차원 부분집합그림은 아래와 같습니다. (P1, P2) 군집에 이어 두번째로 (P4, P5) 군집이 묶였습니다.  노란색 별은 군집의 중심(centroid)를 의미합니다.

 

 

 

 

 

6) 군집 (P1, P2), P3, (P4, P5) 간의 거리를 Ward 연결법(Ward linkage method)으로 계산하여 수정한 거리행렬(distance matrix) 구하기

 

[distance matrix - no.3]

 

  • 개체 P3와 군집 (P4, P5)의 거리가 '4.3'로서 가장 가까우므로 
    → P3과 (P4, P5)를 새로운 군집으로 묶어줍니다(merge). 반복(repeat)을 거듭할 수록 군집이 줄어서 이제 2개가 되었습니다. 

 

7) 새로 합쳐진 군집 {P3, (P4, P5)} 의 중심(centroid)를 가중 평균을 사용해서 구하기

 

[centroid coordinate of clusters - no.3]

 

 

 

여기까지 진행한 군집화 결과를 반영해 수정한 부분집합그림은 아래와 같습니다.

 

 

 

 

8) 군집 (P1, P2)와 {P3, (P4, P5)} 의 중심 간 거리를 Ward 연결법(Ward linkage method)으로 계산하여 수정한 거리 행렬(distance matrix) 구하기

 

 

  • 마지막으로 두개 남은 군집 (P1, P2)와 {P3, (P4, P5)}를 묶어줍니다(merge).  
    → 드디어 반복(repeat)을 거듭한 끝에 군집이 1개로 줄어들었습니다. 
        → 종료 (End) 

 

마지막 군집이 병합된 이후의 수정된 부분집합그림은 아래와 같습니다.

 

 

 

이상의 'Ward 연결법(Ward linkage method)'에 의한 응집형 계층적 군집화를 위한 R script는 아래와 같습니다.

 

> # Agglomerative Hierarchical Clustering : Centroid Linkage method
> hc_ward <- hclust(dist(xy)^2, method="ward.D")
> hc_ward

Call:
hclust(d = dist(xy)^2, method = "ward.D")

Cluster method   : ward.D 
Distance         : euclidean 
Number of objects: 5

 

 

 

 

9) 덴드로그램(Dendrogram)으로 응집형 계층적 군집화(by Ward 연결법) 결과 확인해보기

 

아래 덴드로그램의 y축이 바로 군집 간 거리 (Ward 연결법으로 구한) 를 나타냅니다.

plot(x, hang = -1) 옵션을 설정하면 아래 그램의 오른쪽 덴드로그램처럼 군집 묶어주는 선이 y=0 부터 시작합니다.

 

> # dendrogram
> my_par = par(no.readonly = TRUE)
> par(oma = c(0, 0, 1, 0))
> par(mfrow = c(1, 2))
> plot(hc_ward)
> plot(hc_ward, hang = -1) # hang = -1 : line from the bottom

 

 

 

 

rev() 함수를 사용하면 군집 모델에 대한 정보를 알 수 있습니다.

 - $method : 연결 방법(linkage method)

 - $height : 군집 간 거리(distance between clusters)

 - $merge : 군집 간 병합 순서 (merge sequence)

 

> # custering information
> rev(hc_ward)
$dist.method
[1] "euclidean"

$call
hclust(d = dist(xy)^2, method = "ward.D")

$method
[1] "ward.D"

$labels
NULL

$order
[1] 1 2 3 4 5

$height
[1]  1.000000  2.000000  8.666667 28.333333

$merge
     [,1] [,2]
[1,]   -1   -2
[2,]   -4   -5
[3,]   -3    2
[4,]    1    3

 

 

 

군집 간 유사성 측도로서 'ESS 증분'을 사용해서 거리 행렬을 손으로 푼 결과와 R로 계산한 결과가 서로 다르게 나왔습니다. 어디서 차이가 생기는 건지 파악을 못했습니다.  혹시 이 블로그를 보시고 제가 잘못 계산한 부분을 찾으셨다면 댓글로 알려주시면 감사하겠습니다. ㅜ_ㅜ

 

 

[Reference]

(1) "Introduction to Data Mining", Pang-Ning Tan(Michigan State University), Michael Steinbach(University of Minnesota), Vipin Kumar(University of Minnesota), Addison-Wesley Companion Book

(2) "Clustering Algorithm", Ana Fred, INSTITUTO SUPERIOR TECNICO, Universidade Techica de Lisboa, 2009

(3) "R, SAS, MS-SQL을 활용한 데이터마이닝", 이정진 지음, 자유아카데미, 2011

(4) "Data Mining Cluster Analysis : Basic Concepts and Algorithms", Tan, Steinbach, Kumar, 2004

(5) Wikipedia  

    - ward method : https://en.wikipedia.org/wiki/Ward%27s_method

 

 

계층적 군집화 방법 중 분리형(Top-down) 방식의 다이아나 방법(DIANA method)는 복잡도가 너무 높아 실제 많이 사용하지 않는 알고리즘이므로 별도 포스팅하지 않고 건너뛰겠습니다.

 

다음번 포스팅에서는 (2) 분할적 군집화(Partitional clustering) 알고리즘의 (2-1) 프로토타입 기반(Prototype-based) 군집화 중에서 (2-1-1) K-평균 군집화(K-means clustering)에 대해서 알아보도록 하겠습니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 텅스텐화살 2016.08.17 18:00 신고  댓글주소  수정/삭제  댓글쓰기

    무슨 차이인지는 모르겠지만, 위키피디아에서 나온 ward's method(https://en.wikipedia.org/wiki/Ward%27s_method)를 이용하여 계산하면, R에서 제곱이 아닌 보통 dist(xy)로 계산한 것과 동일한 값이 나오네요. 혹시 해결하시는 데에 도움이 되실까 싶어 댓글 남깁니다. 친절하고 자세한 설명에 큰 도움이 되고 있습니다. 감사합니다.

  2. R Friend R_Friend 2016.08.18 08:56 신고  댓글주소  수정/삭제  댓글쓰기

    텅스텐화살님, 댓글 써주셔서 감사합니다. 퇴근 후에 집에서 찬찬히 위키 읽어보면서 연구해보겠습니다.

  3. 조민욱 2017.06.20 07:03  댓글주소  수정/삭제  댓글쓰기

    공부하면서 참고했습니다. 계속 머리를 싸맨결과 새로운 iteration에서의 matrix의 모든 데이터들에 직전 iteration의 최소 값을 더해주어 기록해야하는 것 같습니다

  4. 2019.10.03 14:47  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

이번 포스팅에서는 여러개의 데이터 프레임을 한꺼번에 하나의 데이터프레임으로 묶는 몇가지 방법을 알아보고, 성능 측면을 비교해보겠습니다.

 

이번 포스팅은 andrew 님이 r-bloggers.com 에 썼던을 그대로 가져다가 번역을 한 내용입니다. 

 

[ Source ] Concatenating a list of data frames , June 6, 2014, By andrew

 

결론 먼저 말씀드리면, data.table package의 rbindlist(data) 함수가 속도 면에서 월등히 빠르네요.

 

 

 

[ R로 여러개의 데이터프레임을 한꺼번에 하나의 데이터프레임으로 묶기 ]

 

 

 

 

0) 문제 (The problem)

 

아래처럼 3개의 칼럼으로 구성된 100,000 개의 자잘한 데이터 프레임을 한개의 커다란 데이터 프레임으로 합치는 것이 풀어야 할 문제, 미션입니다.

 

data = list() 로 해서 전체 데이터 프레임들을 data라는 리스트로 만들어서 아래 각 방법별 예제에 사용하였습니다.

 

> ###########################################
> ## Concatenating a list of data frames
> ## do.call(rbind, data)
> ## ldply(data, rbind)
> ## rbind.fill(data)
> ## rbindlist(data) ** winner! **
> ###########################################
> 
> ## The Problem
> 
> data = list()
> 
> N = 100000
> 
> for (n in 1:N) {
+   data[[n]] = data.frame(index = n, 
+                          char = sample(letters, 1), 
+                          z = runif(1))
+ }
> 
> data[[1]]
  index char         z
1     1    j 0.2300154

 

 

 

 

1) The navie solution : do.call(rbind, data)

 

가장 쉽게 생각할 수 있는 방법으로 base package에 포함되어 있는 rbind() 함수를 do.call 함수로 계속 호출해서 여러개의 데이터 프레임을 위/아래로 합치는 방법입니다. 

 

이거 한번 돌리니 정말 시간 오래 걸리네요.  @@~  낮잠 잠깐 자고 와도 될 정도로요.

 

 

> ## (1) The Naive Solution
> head(do.call(rbind, data))
  index char          z
1     1    j 0.23001541
2     2    f 0.63555284
3     3    d 0.65774397
4     4    y 0.46550511
5     5    b 0.02688307
6     6    u 0.19057217

 

 

 

 

2-1) plyr package : ldply(data, rbind)

 

두번째 방법은 plyr package의 ldply(data, rbind) 함수를 사용하는 방법입니다.

 

> ## (2) Alternative Solutions #1 and #2
> ## (2-1) plyr package : ldply(data, rbind)
> install.packages("plyr")
> library(plyr)
> head(ldply(data, rbind))
  index char          z
1     1    j 0.23001541
2     2    f 0.63555284
3     3    d 0.65774397
4     4    y 0.46550511
5     5    b 0.02688307
6     6    u 0.19057217

 

 

 

 

 

2-2) plyr package : rbind.fill(data)

 

세번째 방법은 plyr package의 rbind.fill(data) 함수를 사용하는 방법입니다.  결과는 앞의 두 방법과 동일함을 알 수 있습니다.

 

> ## (2-2) plyr package : rbind.fill(data)
> library(plyr)
> head(rbind.fill(data))
  index char          z
1     1    j 0.23001541
2     2    f 0.63555284
3     3    d 0.65774397
4     4    y 0.46550511
5     5    b 0.02688307
6     6    u 0.19057217

 

 

 

 

 

3) data.table package : rbindlist(data)

 

마지막 방법은 data.table package의 rbindlist(data) 함수를 사용하는 방법입니다.

 

> ## (3) Alternative Solution 
> ## data.table package : rbindlist(data)
> install.packages("data.table")
> library(data.table)
> head(rbindlist(data))
   index char          z
1:     1    j 0.23001541
2:     2    f 0.63555284
3:     3    d 0.65774397
4:     4    y 0.46550511
5:     5    b 0.02688307
6:     6    u 0.19057217

 

 

 

 

4) 벤치마킹 테스트 (bechmarking test)

 

 

> ## Benchmarking (performance comparison)
> install.packages("rbenchmark")
> library(rbenchmark)
> benchmark(do.call(rbind, data),
+           ldply(data, rbind), 
+           rbind.fill(data), 
+           rbindlist(data))

                  test replications  elapsed relative user.self sys.self user.child sys.child

1 do.call(rbind, data)          100 11387.82  668.692  11384.15     1.54         NA        NA
2   ldply(data, rbind)          100  4983.72  292.644   4982.90     0.52         NA        NA
3     rbind.fill(data)          100  1480.46   86.932   1480.23     0.17         NA        NA
4      rbindlist(data)          100    17.03    1.000     16.86     0.17         NA        NA

 

 

패키지/함수별 성능 비교를 해본 결과 data.table 패키지의 rbindlist(data) 함수가 월등히 빠르다는 것을 알 수 있습니다.  위의 벤치마킹 결과를 보면, 속도가 가장 빨랐던 rbindlist(data)를 1로 놨을 때, 상대적인 속도(relative 칼럼)를 보면 rbind.fill(data)가 86.932로서 rbindlist(data)보다 86배 더 오래걸리고, ldply(data, rbind)가 292.644로서 rbindlist(data)보다 292배 더 오래걸린다는 뜻입니다.  do.call(rbind, data)는 rbindlist(data) 보다 상대적으로 668.692배 더 시간이 걸리는 것으로 나오네요.

 

rbindlist(data)가 훨등히 속도가 빠른 이유는 두가지인데요,

 

(1) rbind() 함수가 각 데이터 프레임의 칼럼 이름을 확인하고, 칼럼 이름이 다를 경우 재정렬해서 합치는데 반해, data.table 패키지의 rbindlist() 함수는 각 데이터 프레임의 칼럼 이름을 확인하지 않고 단지 위치(position)를 기준으로 그냥 합쳐버리기 때문이며,

(따라서, rbindlist() 함수를 사용하려면 각 데이터 프레임의 칼럼 위치가 서로 동일해야 함)

 

(2) rbind() 함수는 R code로 작성된 반면에, data.table 패키지의 rbindlist() 는 C 언어로 코딩이 되어있기 때문입니다.

 

많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. AshtrayK 2016.09.09 14:57 신고  댓글주소  수정/삭제  댓글쓰기

    do.call에 대해서 설명 부탁드려요~

    • R Friend R_Friend 2016.09.09 18:55 신고  댓글주소  수정/삭제

      do.call ()은 괄호안의 함수를 일괄 적용할때 사용하는 함수입니다. lapply()와 혼동될수도 있는데요,

      do.call() applies a given function to list as a whole , so there is only one function call.

      lapply() applies a given function for each element in a list, so there will be several function calls.

      포스팅 본문 예제에 do.call() 대신 lapply(X, rbind)를 쓰면 엉뚱한 결과가 나올겁니다.

큰 자리수의 숫자를 불러오기하면 지수 표기법(exponential notation, scientific notation)으로 되어 있어서 읽기가 힘든 경우가 있습니다.

 

가령 '900000' 이라는 숫자를 R에서 읽어들였더니 '9e+5'라고 쓰여있다면 좀 어색하지요? 

 

이럴 경우 '9e+5'라고 프린트되어 있는 표기를 '900000'처럼 고정형 숫자 표기법(fixed notation)으로 바꿔주고 싶을 때 R에서 사용하는 방법 2가지를 소개하고자 합니다.

 

(1) 하나는 전역적으로(global environment) 영향을 미치는 옵션으로 : options(scipen = xxx) 이구요,

 

(2) 또 하나는 국소적으로(local) 특정 변수에 대해서 설정을 하는 옵션으로 : format(var, scientific = FALSE)

 

입니다.

 

아주 간단한 예를 들어서 설명을 해보겠습니다.

 

 

 

 

가령, "2016년 7월 9일 10시 30분 50초"의 시간을 나타내는 숫자가 "20160709103050" 처럼 입력되어 있는 데이터가 있다고 칩시다.  이를 options("scipen" = -100) 또는 options(scipen = -100)인 상태에서 입력을 받고서 프린트를 해보면 "2.016071e+13" 이라고 나옵니다.

(괄호 안의 숫자는 충분히 큰 숫자를 써주면 됩니다. options("scipen" = -30) 이라고 해도 동일하게 지수형태로 표기될겁니다)

 

 

> ################################################# > ## changing from exponential to numeric notation > ## options("scipen" = 100) > ## format(df$var, scientific = FALSE) > ################################################# >

> options("scipen" = -100)
> number <- c(20160709103050)
> number
[1] 2.016071e+13
> str(number)
 num 2.02e+13

 

 

 

이걸 options("scipen" = 100) 또는 options(scipen = 100)이라고 하고 다시 number를 프린트해보면 아래처럼 "20160709103050" 으로 원하는 형태로 잘 나옵니다. (괄호 안의 숫자는 충분히 큰 숫자를 써주면 됩니다.)

 

이 옵션은 R 사용환경 전체에 영향을 미칩니다.  즉, 특정 변수는 지수형 표기, 특정 변수는 숫자표기로 할 수는 없다는 뜻입니다.

 

> options("scipen" = 100)
> number
[1] 20160709103050 

 

 

참고로, options(scipen = x) 의 default 값은 '0' 입니다.

 

 

날짜/시간 얘기가 나온 김에 조금 더 진도를 나가 보자면요,

시계열분석이나 년/월/일/시간/분/초 단위로 구분해서 연산을 해야 하는 경우에 as.POSIXlt() 함수로 날짜/시간 type으로 바꾸어주면 편합니다. 

 

아래에 as.POSIXlt로 숫자형 number를 날짜/시간 유형으로 바꿔보려고 했더니 "NA" 값이 나오네요. 

 

> datetime_1 <- as.POSIXlt(number, # numeric
+                          format = '%Y%m%d%H%M%S', 
+                          origin = "1970-01-01", 
+                          tz ="UTC")
> 
> datetime_1 # NA
[1] NA

 

 

 

 

이럴경우 특정 변수만 format(var, scientific = FALSE) 옵션을 사용해서 포맷을 바꾸어주면 numeric이었던 것이 character로 바뀌었고, 큰 따옴표도 생겼습니다.

 

> # scientific = FALSE
> number_2 <- format(number, scientific = FALSE)
> number_2
[1] "20160709103050"
> str(number_2)
 chr "20160709103050"

 

 

 

 

이렇게 문자형으로 바꾸고 나서 다시 as.POSIXlt(char_var, format = '%Y%m%d%H%M%S', origin="1970-01-01", tz = UTC) 로 날짜/시간 유형으로 변환하면 잘 작동하네요.

 

> datetime_2 <- as.POSIXlt(number_2, # character
+                          format = '%Y%m%d%H%M%S', 
+                          origin = "1970-01-01", 
+                          tz ="UTC") # UTC : universal time
> 
> datetime_2 # "2016-07-09 10:30:50 UTC"
[1] "2016-07-09 10:30:50 UTC"

 

 

 

 

format() 대신에 as.character(numeric_var) 를 사용해서 숫자형을 문자형으로 바꾸어주고 나서, as.POSIXlt(char_var, format = '%Y%m%d%H%M%S', origin="1970-01-01", tz = UTC) 로 날짜/시간 유형으로 변환해도 똑같이 잘 작동합니다.

 

> # as.character
> number_3 <- as.character(number)
> number_3
[1] "20160709103050"
> str(number_3)
 chr "20160709103050"
> 
> datetime_3 <- as.POSIXlt(number_3, # character
+                          format = '%Y%m%d%H%M%S', 
+                          origin = "1970-01-01", 
+                          tz ="UTC") # UTC : universal time
> 
> datetime_3 # "2016-07-09 10:30:50 UTC"
[1] "2016-07-09 10:30:50 UTC" 

 

 

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꼬옥 눌러주세요.

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. Liam 2016.08.22 09:35 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요?
    options("scipen = 100") 이라고 하면 NULL 이라고 나와서,,
    options(scipen = 100) 이라고 했더니 정상적으로 작동하는 것 같습니다 ^^;
    저만 그런 건지 모르겠는데 참고 부탁 드릴게요

    • R Friend R_Friend 2016.08.22 10:58 신고  댓글주소  수정/삭제

      Liam님, 반갑습니다. ^^
      options("scipen" = 100) 과
      options(scipen = 100) 은 동일하게 작동하며, 말씀해주신대로 options("scipen = 100" ) 은 잘못된 표기였습니다. #으로 부가설명붙였던 곳에 제가 실수로 잘못 표기했던 부분 수정하였습니다. 댓글 감사합니다.

  2. Comet 2019.12.28 23:42  댓글주소  수정/삭제  댓글쓰기

    R을 독학하고 있는 중인데, 좋은 블로그를 발견한 것 같습니다!

    앞으로 종종 이용하겠습니다. 좋은 자료 감사합니다!