이번 포스팅에서는 R을 사용하여 예측이나 분류 모델링을 할 때 기본적으로 필요한 두가지 작업인 


(1) DataFrame을 Train set, Test set 으로 분할하기 

     (Split a DataFrame into Train and Test set)

   - (1-1) 무작위 샘플링에 의한 Train, Test set 분할 

             (Split of Train, Test set by Random Sampling)

   - (1-2) 순차 샘플링에 의한 Train, Test set 분할 

             (Split of Train, Test set by Sequential Sampling)

   - (1-3) 층화 무작위 샘플링에 의한 Train, Test set 분할 

             (Split of Train, Test set by Stratified Random Sampling)


(2) 여러개의 숫자형 변수를 가진 DataFrame을 표준화하기 

      (Standardization of Numeric Data)

   - (2-1) z-변환 (z-transformation, standardization)

   - (2-2) [0-1] 변환 ([0-1] transformation, normalization)


(3) 여러개의 범주형 변수를 가진 DataFrame에서 가변수 만들기 

      (Getting Dummy Variables)


에 대해서 소개하겠습니다. 



예제로 사용할 Cars93 DataFrame을 MASS 패키지로 부터 불러오겠습니다. 변수가 무척 많으므로 예제를 간단하게 하기 위해 설명변수 X로 'Price', 'Horsepower', 'RPM', 'Length', 'Type', 'Origin' 만을 subset 하여 가져오고, 반응변수 y 로는 'MPG.highway' 변수를 사용하겠습니다. 



# get Cars93 DataFrame from MASS package

library(MASS)

data(Cars93)

str(Cars93)

'data.frame': 93 obs. of 27 variables: $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4... $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1... $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3... $ 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... $ 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 ... $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2... $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3... $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4... $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ... $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ... $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800... $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690... $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1... $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ... $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ... $ Length : int 177 195 180 193 186 189 200 216 198 206 ... $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ... $ Width : int 68 71 67 70 69 69 74 78 73 73 ... $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ... $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ... $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ... $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495... $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1... 

$ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...



X <- subset(Cars93, select=c('Price', 'Horsepower', 'RPM', 'Length', 'Type', 'Origin'))

head(X)

A data.frame: 6 × 6
PriceHorsepowerRPMLengthTypeOrigin
<dbl><int><int><int><fct><fct>
15.91406300177Smallnon-USA
33.92005500195Midsizenon-USA
29.11725500180Compactnon-USA
37.71725500193Midsizenon-USA
30.02085700186Midsizenon-USA
15.71105200189MidsizeUSA



table(X$Origin)

USA non-USA 48 45



y <- Cars93$MPG.highway

y

  1. 31
  2.  
  3. 25
  4.  
  5. 26
  6.  
  7. 26
  8.  
  9. 30
  10.  
  11. 31
  12.  
  13. 28
  14.  
  15. 25
  16.  
  17. 27
  18.  
  19. 25
  20.  
  21. 25
  22.  
  23. 36
  24.  
  25. 34
  26.  
  27. 28
  28.  
  29. 29
  30.  
  31. 23
  32.  
  33. 20
  34.  
  35. 26
  36.  
  37. 25
  38.  
  39. 28
  40.  
  41. 28
  42.  
  43. 26
  44.  
  45. 33
  46.  
  47. 29
  48.  
  49. 27
  50.  
  51. 21
  52.  
  53. 27
  54.  
  55. 24
  56.  
  57. 33
  58.  
  59. 28
  60.  
  61. 33
  62.  
  63. 30
  64.  
  65. 27
  66.  
  67. 29
  68.  
  69. 30
  70.  
  71. 20
  72.  
  73. 30
  74.  
  75. 26
  76.  
  77. 50
  78.  
  79. 36
  80.  
  81. 31
  82.  
  83. 46
  84.  
  85. 31
  86.  
  87. 33
  88.  
  89. 29
  90.  
  91. 34
  92.  
  93. 27
  94.  
  95. 22
  96.  
  97. 24
  98.  
  99. 23
  100.  
  101. 26
  102.  
  103. 26
  104.  
  105. 37
  106.  
  107. 36
  108.  
  109. 34
  110.  
  111. 24
  112.  
  113. 25
  114.  
  115. 29
  116.  
  117. 25
  118.  
  119. 26
  120.  
  121. 26
  122.  
  123. 33
  124.  
  125. 24
  126.  
  127. 33
  128.  
  129. 30
  130.  
  131. 23
  132.  
  133. 26
  134.  
  135. 31
  136.  
  137. 31
  138.  
  139. 23
  140.  
  141. 28
  142.  
  143. 30
  144.  
  145. 41
  146.  
  147. 31
  148.  
  149. 28
  150.  
  151. 27
  152.  
  153. 28
  154.  
  155. 26
  156.  
  157. 38
  158.  
  159. 37
  160.  
  161. 30
  162.  
  163. 30
  164.  
  165. 43
  166.  
  167. 37
  168.  
  169. 32
  170.  
  171. 29
  172.  
  173. 22
  174.  
  175. 33
  176.  
  177. 21
  178.  
  179. 30
  180.  
  181. 25
  182.  
  183. 28
  184.  
  185. 28




  (1) DataFrame을 Train set, Test set 으로 분할하기 (Split a DataFrame into Train and Test set)


(1-1) 무작위 샘플링에 의한 Train, Test set 분할 (Split of Train, Test set by Random Sampling)


간단하게 일회성으로 무작위 샘플링 하는 것이면 sample() 함수로 난수를 생성해서 indexing을 해오면 됩니다. 

(* 참고 : https://rfriend.tistory.com/58)



# (1) index for splitting data into Train and Test set

set.seed(1004) # for reprodicibility

train_idx <- sample(1:nrow(X), size=0.8*nrow(X), replace=F) # train-set 0.8, test-set 0.2

test_idx <- (-train_idx)


X_train <- X[train_idx,]

y_train <- y[train_idx]

X_test <- X[test_idx,]

y_test <- y[test_idx]


print(paste0('X_train: ', nrow(X_train)))

print(paste0('y_train: ', length(y_train)))

print(paste0('X_test: ', nrow(X_test)))

print(paste0('y_test: ', length(y_test)))

[Out]:

[1] "X_train: 74" [1] "y_train: 74" [1] "X_test: 19" [1] "y_test: 19"





(1-2) 순차 샘플링에 의한 Train, Test set 분할 (Split of Train, Test set by Sequential Sampling)


시계열 분석을 할 경우 시간 순서(timestamp order)를 유지하는 것이 필요하므로 (1-1)의 무작위 샘플링을 하면 안되며, 시간 순서를 유지한 상태에서 앞서 발생한 시간 구간을 training set, 뒤의(미래의) 시간 구간을 test set 으로 분할합니다. 



# sequential sampling

test_size <- 0.2

test_num <- ceiling(nrow(X) * test_size)

train_num <- nrow(X) - test_num


X_train <- X[1:train_num,]

X_test <- X[(train_num+1):nrow(X),]

y_train <- y[1:train_num]

y_test <- y[(train_num+1):length(y)]





(1-3)  층화 무작위 샘플링에 의한 Train, Test set 분할 (Split of Train, Test set by Stratified Random Sampling)


위의 (1-1)과 (1-2)에서 소개한 무작위 샘플링, 순차 샘플링을 사용한 train, test set split 을 random_split() 이라는 사용자 정의함수(user-defined function)으로 정의하였으며, 층화 무작위 샘플링(stratified random sampling)을 사용한 train_test_split() 사용자 정의 함수도 이어서 정의해 보았습니다. (python sklearn의 train_test_split() 함수의 인자, 반환값이 유사하도록  정의해보았습니다) (* 참고 : https://rfriend.tistory.com/58)



# --- user-defined function of train_test split with random sampling

random_split <- function(X, y

                         , test_size

                         , shuffle

                         , random_state) {

    

    test_num <- ceiling(nrow(X) * test_size)

    train_num <- nrow(X) - test_num

    

    if (shuffle == TRUE) {

        # shuffle == True

        set.seed(random_state) # for reprodicibility

        test_idx <- sample(1:nrow(X), size=test_num, replace=F)

        train_idx <- (-test_idx)

            

        X_train <- X[train_idx,]

        X_test <- X[test_idx,]

        y_train <- y[train_idx]

        y_test <- y[test_idx]

    } else {

        # shuffle == False

        X_train <- X[1:train_num,]

        X_test <- X[(train_num+1):nrow(X),]

        y_train <- y[1:train_num]

        y_test <- y[(train_num+1):length(y)]

    }

    

    return (list(X_train, X_test, y_train, y_test))

}



# --- user defined function of train_test_split() with statified random sampling

train_test_split <- function(X, y

                             , test_size=0.2

                             , shuffle=TRUE

                             , random_state=2004

                             , stratify=FALSE, strat_col=NULL){

                        

    if (stratify == FALSE){ # simple random sampling

        split <- random_split(X, y, test_size, shuffle, random_state)

        X_train <- split[1]

        X_test  <- split[2]

        y_train <- split[3]

        y_test  <- split[4]

    } else { # --- stratified random sampling

        strata <- unique(as.character(X[,strat_col]))

        X_train <- data.frame()

        X_test  <- data.frame()

        y_train <- vector()

        y_test  <- vector()

        for (stratum in strata){

            X_stratum <- X[X[strat_col] == stratum, ]

            y_stratum <- y[X[strat_col] == stratum]

            split_stratum <- random_split(X_stratum, y_stratum, test_size, shuffle, random_state)

            X_train <- rbind(X_train, data.frame(split_stratum[1]))

            X_test  <- rbind(X_test,  data.frame(split_stratum[2]))

            y_train <- c(y_train, unlist(split_stratum[3]))

            y_test  <- c(y_test,  unlist(split_stratum[4]))

        }

    }

    return (list(X_train, X_test, y_train, y_test))

}

 



위에서 정의한 train_test_splie() 사용자 정의 함수를 사용하여 'Origin' ('USA', 'non-USA' 의 두 개 수준을 가진 요인형 변수) 변수를 사용하여 층화 무작위 샘플링을 통한 train, test set 분할 (split of train and test set using stratified random sampling in R) 을 해보겠습니다, 



split_list <- train_test_split(X, y

                               , test_size=0.2

                               , shuffle=TRUE

                               , random_state=2004

                               , stratify=TRUE, strat_col='Origin')


X_train <- data.frame(split_list[1])

X_test  <- data.frame(split_list[2])

y_train <- unlist(split_list[3])

y_test  <- unlist(split_list[4])



print(paste0('Dim of X_train: ', nrow(X_train), ', ', ncol(X_train)))

print(paste0('Dim of X_test:  ', nrow(X_test), ', ', ncol(X_test)))

print(paste0('Length of y_train: ', length(y_train)))

print(paste0('Length of y_test:  ', length(y_test)))

[Out]:
[1] "Dim of X_train: 74, 6"
[1] "Dim of X_test:  19, 6"
[1] "Length of y_train: 74"
[1] "Length of y_test:  19"



X_test

A data.frame: 19 × 6
PriceHorsepowerRPMLengthTypeOrigin
<dbl><int><int><int><fct><fct>
448.0815500168Smallnon-USA
233.92005500195Midsizenon-USA
398.4555700151Smallnon-USA
4012.5905400164Sportynon-USA
329.11725500180Compactnon-USA
538.3825000164Smallnon-USA
4510.01246000172Smallnon-USA
9020.01345800180Compactnon-USA
4212.11025900173Smallnon-USA
1616.31704800178VanUSA
720.81704800200LargeUSA
1140.12956000204MidsizeUSA
739.0745600177SmallUSA
1213.41105200182CompactUSA
823.71804000216LargeUSA
239.2926000174SmallUSA
1716.61654000194VanUSA
7411.11105200181CompactUSA
1415.11604600193SportyUSA


table(X$Origin)

[Out]: USA non-USA 48 45



table(X_test$Origin)

[Out]: USA non-USA 10 9


y_test

  1. [Out]: 33
  2.  
  3. 25
  4.  
  5. 50
  6.  
  7. 36
  8.  
  9. 26
  10.  
  11. 37
  12.  
  13. 29
  14.  
  15. 30
  16.  
  17. 46
  18.  
  19. 23
  20.  
  21. 28
  22.  
  23. 25
  24.  
  25. 41
  26.  
  27. 36
  28.  
  29. 25
  30.  
  31. 33
  32.  
  33. 20
  34.  
  35. 31
  36.  
  37. 28





참고로 (1-1) 무작위 샘플링에 의한 Train, Test set 분할을 위의 (1-3)에서 정의한 train_test_split() 사용자 정의 함수를 사용해서 하면 아래와 같습니다. (shuffle=TRUE)



# split of train, test set by random sampling using train_test_split() function

split_list <- train_test_split(X, y

                               , test_size=0.2

                               , shuffle=TRUE

                               , random_state=2004

                               , stratify=FALSE)


X_train <- data.frame(split_list[1])

X_test  <- data.frame(split_list[2])

y_train <- unlist(split_list[3])

y_test  <- unlist(split_list[4])




참고로 (1-2) 순차 샘플링에 의한 Train, Test set 분할을 위의 (1-3)에서 정의한 train_test_split() 사용자 정의 함수를 사용해서 하면 아래와 같습니다. (shuffle=FALSE)



# split of train, test set by sequential sampling using train_test_split() function

split_list <- train_test_split(X, y

                               , test_size=0.2

                               , shuffle=FALSE

                               , random_state=2004

                               , stratify=FALSE)


X_train <- data.frame(split_list[1])

X_test  <- data.frame(split_list[2])

y_train <- unlist(split_list[3])

y_test  <- unlist(split_list[4])

 




  (2) 여러개의 숫자형 변수를 가진 DataFrame을 표준화하기 (Standardization of Nuemric Data)


(2-1) z-변환 (z-transformation, standardization)


X_train, X_test 데이터셋에서 숫자형 변수(numeric variable)와 범주형 변수(categorical varialble)를 구분한 후에, 숫자형 변수로 이루어진 DataFrame 에 대해서 z-표준화 변환 (z-standardization transformation)을 해보겠습니다. (* 참고 : https://rfriend.tistory.com/52)


여러개의 변수를 가진 DataFrame이므로 X_mean <- apply(X_train_num, 2, mean) 로 Train set의 각 숫자형 변수별 평균을 구하고, X_stddev <- apply(X_train_num, 2, sd) 로 Train set의 각 숫자형 변수별 표준편차를 구했습니다. 


그리고 scale(X_train_num, center=X_mean, scale=X_stddev) 로 Train set의 각 숫자형 변수를 z-표준화 변환을 하였으며, scale(X_test_num, center=X_mean, scale=X_stddev) 로 Test set의 각 숫자형 변수를 z-표준화 변환을 하였습니다. 


이때 조심해야 할 것이 있는데요, z-표준화 변환 시 사용하는 평균(mean)과 표준편차(standard deviation)는 Train set으로 부터 구해서 --> Train set, Test set 에 적용해서 z-표준화를 한다는 점입니다. 왜냐하면 Test set는 미래 데이터(future data), 볼 수 없는 데이터(unseen data) 이므로, 우리가 알 수 있는 집단의 평균과 표준편차는 Train set으로 부터만 얻을 수 있기 때문입니다.  (많은 분석가가 그냥 Train, Test set 구분하기 전에 통채로 scale() 함수 사용해서 표준화를 한 후에 Train, Test set으로 분할을 하는데요, 이는 엄밀하게 말하면 잘못된 순서입니다)



# split numeric, categorical variables

X_train_num <- X_train[, c('Price', 'Horsepower', 'RPM', 'Length')]

X_train_cat <- X_train[, c('Type', 'Origin')]

X_test_num  <- X_test[ , c('Price', 'Horsepower', 'RPM', 'Length')]

X_test_cat  <- X_test[ , c('Type', 'Origin')]


# (1) Z Standardization

# (1-1) using scale() function

X_mean   <- apply(X_train_num, 2, mean)

X_stddev <- apply(X_train_num, 2, sd)


print('---- Mean ----')

print(X_mean)

print('---- Standard Deviation ----')

print(X_stddev)

[Out]:
[1] "---- Mean ----"
     Price Horsepower        RPM     Length 
  20.22703  146.08108 5278.37838  183.67568 
[1] "---- Standard Deviation ----"
     Price Horsepower        RPM     Length 
  9.697073  51.171149 594.730345  14.356620 



X_train_scaled <- scale(X_train_num, center=X_mean, scale = X_stddev)

head(X_train_num_scaled)

A matrix: 6 × 4 of type dbl
PriceHorsepowerRPMLength
1-0.44621989-0.11883811.7177896-0.46498935
41.801881070.50651430.37264220.64947906
51.007827061.21003570.70892910.16189913
41-0.044036690.27200720.8770725-0.60429791
43-0.28122166-0.11883810.54078560.09224485
46-1.05465089-1.05686670.4567139-1.23118639


# note that 'mean' and 'stddev' are calculated using X_train_num dataset (NOT using X_test_num)

X_test_scaled <- scale(X_test_num, center=X_mean, scale = X_stddev)

head(X_test_num_scaled)

A matrix: 6 × 4 of type dbl
PriceHorsepowerRPMLength
44-1.2608987-1.27183150.3726422-1.0918778
21.41001031.05369760.37264220.7887876
39-1.2196491-1.77993030.7089291-2.2760005
40-0.7968411-1.09595120.2044988-1.3704949
30.91501560.50651430.3726422-0.2560265
53-1.2299615-1.2522893-0.4680750-1.3704949



# combine X_train_scaled, X_train_cat

X_train_scaled <- cbind(X_train_num_scaled, X_train_cat)


# combine X_trest_scaled, X_test_cat

X_test_scaled <- cbind(X_test_num_scaled, X_test_cat)





(2-2) [0-1] 변환 ([0-1] transformation, normalization)


각 숫자형 변수별 최소값(min)과 최대값(max)을 구해서 [0-1] 사이의 값으로 변환해보겠습니다. 

(* 참고 : https://rfriend.tistory.com/52)



# (2) [0-1] Normalization

# 0-1 transformation

X_max <- apply(X_train_num, 2, max)

X_min <- apply(X_train_num, 2, min)

X_train_num_scaled <- scale(X_train_num, center = X_min, scale = (X_max - X_min))

X_test_num_scaled <- scale(X_test_num, center = X_min, scale = (X_max - X_min))


head(X_train_num_scaled)

A matrix: 6 × 4 of type dbl
PriceHorsepowerRPMLength
10.155963300.32489450.92592590.4615385
40.555963300.45991560.62962960.6666667
50.414678900.61181430.70370370.5769231
410.227522940.40928270.74074070.4358974
430.185321100.32489450.66666670.5641026
460.047706420.12236290.64814810.3205128



head(X_test_num_scaled)

A matrix: 6 × 4 of type dbl
PriceHorsepowerRPMLength
440.011009170.075949370.62962960.3461538
20.486238530.578059070.62962960.6923077
390.01834862-0.033755270.70370370.1282051
400.093577980.113924050.59259260.2948718
30.398165140.459915610.62962960.5000000
530.016513760.080168780.44444440.2948718


# combine X_train_scaled, X_train_cat

X_train_scaled <- cbind(X_train_num_scaled, X_train_cat)


# combine X_trest_scaled, X_test_cat

X_test_scaled <- cbind(X_test_num_scaled, X_test_cat)





 (3) 여러개의 범주형 변수를 가진 DataFrame에서 가변수 만들기 (Getting Dummy Variables) 


(3-1) caret 패키지의 dummyVars() 함수를 이용하여 DataFrame 내 범주형 변수로부터 가변수 만들기



library(caret)


# fit dummyVars()

dummy <- dummyVars(~ ., data = X_train_cat, fullRank = TRUE)


# predict (transform) dummy variables

X_train_cat_dummy <- predict(dummy, X_train_cat)

X_test_cat_dummy <- predict(dummy, X_test_cat)


head(X_train_cat_dummy)

A matrix: 6 × 6 of type dbl
Type.LargeType.MidsizeType.SmallType.SportyType.VanOrigin.non-USA
001001
010001
000001
010001
010001
010000


head(X_test_cat_dummy)

A matrix: 6 × 6 of type dbl
Type.LargeType.MidsizeType.SmallType.SportyType.VanOrigin.non-USA
75000100
76010000
77100000
78000001
79001000
80001001





(3-2) 조건문 ifelse() 함수를 이용하여 수작업으로 가변수 만들기 

        (creating dummy variables manually using ifelse())


아무래도 (3-1)의 caret 패키지를 이용하는 것 대비 수작업으로 할 경우 범주형 변수의 개수와 범주형 변수 내 class 의 종류 수가 늘어날 수록 코딩을 해야하는 수고가 기하급수적으로 늘어납니다. 그리고 범주형 변수나 class가 가변적인 경우 데이터 전처리 workflow를 자동화하는데 있어서도 수작업의 하드코딩의 경우 에러를 야기하는 문제가 되거나 추가적인 비용이 될 수 있다는 단점이 있습니다. 


범주형 변수 내 범주(category) 혹은 계급(class)이 k 개가 있으면 --> 가변수는 앞에서 부터 k-1 개 까지만 만들었습니다. (회귀모형의 경우 dummy trap 을 피하기 위해)



# check level (class) of categorical variables

unique(X_train_cat$Type)

  1. [Out]: Small
  2.  
  3. Midsize
  4.  
  5. Compact
  6.  
  7. Large
  8.  
  9. Sporty
  10.  
  11. Van

unique(X_train_cat$Origin)

  1. [Out]: non-USA
  2.  
  3. USA


# get dummy variables from train set

X_train_cat_dummy <- data.frame(

    type_small = ifelse(X_train_cat$Type == "Small", 1, 0)

    , type_midsize = ifelse(X_train_cat$Type == "Midsize", 1, 0)

    , type_compact = ifelse(X_train_cat$Type == "Compact", 1, 0)

    , type_large = ifelse(X_train_cat$Type == "Large", 1, 0)

    , type_sporty = ifelse(X_train_cat$Type == "Sporty", 1, 0)

    , origin_nonusa = ifelse(X_train_cat$Origin == "non-USA", 1, 0)

)


head(X_train_cat_dummy)

A data.frame: 6 × 6
type_smalltype_midsizetype_compacttype_largetype_sportyorigin_nonusa
<dbl><dbl><dbl><dbl><dbl><dbl>
100001
010001
001001
010001
010001
010000


# get dummy variables from test set

X_test_cat_dummy <- data.frame(

    type_small = ifelse(X_test_cat$Type == "Small", 1, 0)

    , type_midsize = ifelse(X_test_cat$Type == "Midsize", 1, 0)

    , type_compact = ifelse(X_test_cat$Type == "Compact", 1, 0)

    , type_large = ifelse(X_test_cat$Type == "Large", 1, 0)

    , type_sporty = ifelse(X_test_cat$Type == "Sporty", 1, 0)

    , origin_nonusa = ifelse(X_test_cat$Origin == "non-USA", 1, 0)

)


head(X_test_cat_dummy)

A data.frame: 6 × 6
type_smalltype_midsizetype_compacttype_largetype_sportyorigin_nonusa
<dbl><dbl><dbl><dbl><dbl><dbl>
000010
010000
000100
001001
100000
100001





  (4) 숫자형 변수와 범주형 변수 전처리한 데이터셋을 합쳐서 Train, Test set 완성하기



# combine X_train_scaled, X_train_cat

X_train_preprocessed <- cbind(X_train_num_scaled, X_train_cat_dummy)

head(X_train_preprocessed)

A data.frame: 6 × 10
PriceHorsepowerRPMLengthtype_smalltype_midsizetype_compacttype_largetype_sportyorigin_nonusa
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
0.15596330.34693880.92592590.4615385100001
0.48623850.59183670.62962960.6923077010001
0.39816510.47755100.62962960.5000000001001
0.55596330.47755100.62962960.6666667010001
0.41467890.62448980.70370370.5769231010001
0.15229360.22448980.51851850.6153846010000


 

# combine X_trest_scaled, X_test_cat

X_test_preprocessed <- cbind(X_test_num_scaled, X_test_cat_dummy)

head(X_test_preprocessed)

A data.frame: 6 × 10
PriceHorsepowerRPMLengthtype_smalltype_midsizetype_compacttype_largetype_sportyorigin_nonusa
<dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
750.188990830.428571430.29629630.70512821000010
760.203669720.591836730.44444440.69230769010000
770.311926610.469387760.37037040.46153846000100
780.390825690.346938780.81481480.55128205001001
790.067889910.122448980.44444440.44871795100000
800.018348620.073469390.66666670.06410256100001




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

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 장비별로 On, Off 상태 변화 간의 가동 시간(run time)을 구하고, 이렇게 구한 장비별 가동 시간의 평균을 집계해보겠습니다. 




먼저, 장비(device), 날짜/시간(time), status('ON', 'OFF') 의 3개 변수로 구성된 간단한 예제 DataFrame을 만들어보겠습니다. 



> #===========================

> # time difference by device

> #===========================

> device <- c('A', 'A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'C')

> time <- c('2018-07-01 10:20:23', '2018-07-01 10:30:55', '2018-07-01 11:11:01',

+           '2018-07-01 11:51:41', '2018-07-02 07:11:02', '2018-07-02 09:00:33',

+           '2018-07-02 09:20:24', '2018-07-02 12:12:21', '2018-07-02 14:01:09',

+           '2018-07-02 18:11:41', '2018-07-02 19:21:51', '2018-07-02 20:30:00')

> status <- c('ON', 'ON', 'ON', 'OFF', 'ON', 'ON', 'OFF', 'ON', 'OFF', 'ON', 'ON', 'OFF')

> df <- data.frame(device, time, status)


> df

   device                time status

1       A 2018-07-01 10:20:23     ON   <-- start

2       A 2018-07-01 10:30:55     ON

3       A 2018-07-01 11:11:01     ON

4       A 2018-07-01 11:51:41    OFF    <-- end


5       B 2018-07-02 07:11:02     ON    <-- start

6       B 2018-07-02 09:00:33     ON

7       B 2018-07-02 09:20:24    OFF    <-- end


8       C 2018-07-02 12:12:21     ON    <-- start

9       C 2018-07-02 14:01:09    OFF     <-- end

10      C 2018-07-02 18:11:41     ON    <-- start

11      C 2018-07-02 19:21:51     ON

12      C 2018-07-02 20:30:00    OFF     <-- end




다음으로 장비(device)와 날짜/시간(time)을 기준으로 정렬(sort)을 하겠습니다. 



> # sort df by device and time in ascending order

> df <- df[order(device, time),]

> df

   device                time status

1       A 2018-07-01 10:20:23     ON

2       A 2018-07-01 10:30:55     ON

3       A 2018-07-01 11:11:01     ON

4       A 2018-07-01 11:51:41    OFF

5       B 2018-07-02 07:11:02     ON

6       B 2018-07-02 09:00:33     ON

7       B 2018-07-02 09:20:24    OFF

8       C 2018-07-02 12:12:21     ON

9       C 2018-07-02 14:01:09    OFF

10      C 2018-07-02 18:11:41     ON

11      C 2018-07-02 19:21:51     ON

12      C 2018-07-02 20:30:00    OFF




각 장비(device) 별로 'ON' 이후에 다음 'OFF' 까지의 사이 중간에 끼어있는 'ON'은 필요가 없으므로 삭제를 해서 새로운 'df_2' 이름의 DataFrame을 만들어보겠습니다. 


[ 전처리 후의 Output Image ]


   device                time status

1       A 2018-07-01 10:20:23     ON   <-- start

2       A 2018-07-01 10:30:55     ON

3       A 2018-07-01 11:11:01     ON

4       A 2018-07-01 11:51:41    OFF    <-- end


5       B 2018-07-02 07:11:02     ON    <-- start

6       B 2018-07-02 09:00:33     ON

7       B 2018-07-02 09:20:24    OFF    <-- end


8       C 2018-07-02 12:12:21     ON    <-- start

9       C 2018-07-02 14:01:09    OFF     <-- end

10      C 2018-07-02 18:11:41     ON    <-- start

11      C 2018-07-02 19:21:51     ON

12      C 2018-07-02 20:30:00    OFF     <-- end

 





> # set of device

> device_set <- as.character(unique(df$device))

> # blank DataFrame to store the preprocessed dataset

> df_2 <- data.frame()

> for (i in 1:length(device_set)){

+   # split dataframe by device

+   device_i <- device_set[i]

+   df_i <- df[df$device == device_i, ]

+   

+   # add the first time of df_i to df2

+   df_2 <- rbind(df_2, df_i[1,])

+   

+   # add the time if the device is turned off or turned on again

+   for (j in 1:(nrow(df_i)-1)){

+     if((df_i$status[j] == 'ON' & df_i$status[j+1] == 'OFF') | 

+        (df_i$status[j] == 'OFF' & df_i$status[j+1] == 'ON')){

+       df_2 <- rbind(df_2, df_i[j+1,])

+     }

+   }

+ }

> df_2

   device                time status

1       A 2018-07-01 10:20:23     ON

4       A 2018-07-01 11:51:41    OFF

5       B 2018-07-02 07:11:02     ON

7       B 2018-07-02 09:20:24    OFF

8       C 2018-07-02 12:12:21     ON

9       C 2018-07-02 14:01:09    OFF

10      C 2018-07-02 18:11:41     ON

12      C 2018-07-02 20:30:00    OFF





lag() window function을 사용해서 장비, 날짜/시간, 상태를 한칸씩 밑으로 내리고(Lag), 장비 이름과 상태를 기준으로 필요한 행만 남기고 나머지는 삭제하겠습니다. 



> # lag window function

> library(dplyr)

> df_2_lag <- mutate(df_2, 

+                    device_lag = lag(device, 1), 

+                    time_lag = lag(time, 1), 

+                    status_lag = lag(status, 1))

> # filtering

> df_2_lag_filtered <- df_2_lag %>% 

 filter(device == device_lag & status == 'OFF')

> df_2_lag_filtered

  device                time status device_lag            time_lag status_lag

1      A 2018-07-01 11:51:41    OFF          A 2018-07-01 10:20:23         ON

2      B 2018-07-02 09:20:24    OFF          B 2018-07-02 07:11:02         ON

3      C 2018-07-02 14:01:09    OFF          C 2018-07-02 12:12:21         ON

4      C 2018-07-02 20:30:00    OFF          C 2018-07-02 18:11:41         ON




이제 장비(device)별로 'ON' 이후 다음번 'OFF' 까지의 가동 시간(run_time)을 difftime() 함수를 사용해서 구해보겠습니다. 이때 strptime() 함수를 사용해서 문자열 값을 '년/월/일/시간/분/초("%Y-%m-%d %H:%M:%S")' 형태로 만들어준 후에 difftime() 함수를 사용할 수 있습니다.  이렇 구한 가동시간은 '시간(hour)' 단위 값입니다. (분으로 환산하려면 곱하기 60, 초로 환산하려면 곱하기 60*60 을 해주면 됩니다) 



> # calculate the run_time

> df_2_lag_filtered$run_time <- as.numeric(difftime(strptime(df_2_lag_filtered$time, "%Y-%m-%d %H:%M:%S"),

+                                                   strptime(df_2_lag_filtered$time_lag, "%Y-%m-%d %H:%M:%S")))

> df_2_lag_filtered

  device                time status device_lag            time_lag status_lag run_time

1      A 2018-07-01 11:51:41    OFF          A 2018-07-01 10:20:23         ON 1.521667

2      B 2018-07-02 09:20:24    OFF          B 2018-07-02 07:11:02         ON 2.156111

3      C 2018-07-02 14:01:09    OFF          C 2018-07-02 12:12:21         ON 1.813333

4      C 2018-07-02 20:30:00    OFF          C 2018-07-02 18:11:41         ON 2.305278

 




마지막으로, 각 장비(device)별 가동시간(run_time)의 평균을 구해보겠습니다. 



> # average of run_time by device

> run_time_by_device <- df_2_lag_filtered %>% 

+   group_by(device) %>% 

+   summarise(run_time_avg = mean(run_time))

> run_time_by_device

# A tibble: 3 x 2

  device run_time_avg

  <fct>         <dbl>

1 A              1.52

2 B              2.16

3 C              2.06




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

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



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 최고 2019.10.25 10:36  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사드립니다~

  2. 2019.11.06 15:47  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  3. 2019.11.19 22:47  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

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

      암명하세요 r린이님,

      교호작용항은 두 변수를 곱하는 방식으로 표현하시면 됩니다.

      가령, y~ be5a0 + beta1*x1 + beta2*x2 + beta3*x1*x2 (<== 교호작용항) 이런식으로요.

  4. 2019.11.19 23:13  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  5. 2019.11.19 23:17  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

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

      위의 댓글에 남겨주신 코드 중에

      %*% 를 * 로 수정해서 해보실래요?

      %*% 은 내적(dot product)을 할 때 사용하는 operator 여서 scalar (한개의 값, dot)을 반환합니다. (https://rfriend.tistory.com/145 )

      그냥 element-wise multiplication을 하시려면 * 만 사용하시면 됩니다.

      %*%를 사용해서 두 변수를 곱했기 때문에 값이 한개의 scalar값으로 반환되면서 벡터 길이가 다르다는 에러메시지가 난거 같습니다.

  6. 2019.11.19 23:30  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  7. r린이 2019.11.19 23:41  댓글주소  수정/삭제  댓글쓰기

    감사합니다. 좋은 밤 되세요!

  8. 2020.01.10 18:43  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

R의 다양한 패키지들을 사용하다 보면 함수를 사용한 후의 산출물 결과가 자신이 생각했던 결과와 다를 경우 R 패키지 함수의 소스 코드가 어떻게 되어있는 것인지 궁금할 때가 있습니다. 


이번 포스팅에서는 R 패키지에 있는 함수, 메소드의 소스 코드를 들여다볼 수 있는 2가지 방법을 소개하겠습니다. 


(1) getAnywhere(function_name)

(2) package_name:::function_name





가령, 두 벡터의 차집합(difference of sets)을 구할 때 사용하는 함수가 base 패키지의 setdiff() 함수입니다. 


> x <- c(1, 1, 2, 3, 4, 5)

> y <- c(4, 5, 6, 7)

> setdiff(x, y)

[1] 1 2 3




위에서 예로 든 base 패키지의 setdiff() 함수에 대한 소스 코드를 위의 2가지 방법을 사용해서 살펴보겠습니다. 



 (1) getAnywhere(function_name)


R package 의 이름을 몰라도 함수, 메소드의 이름만 알고 있으면 R package 이름과 namespace, 그리고 소스 코드를 친절하게 다 볼 수 있으므로 매우 편리합니다. 



> getAnywhere(setdiff)

A single object matching ‘setdiff’ was found

It was found in the following places

  package:base

  namespace:base

with value


function (x, y) 

{

    x <- as.vector(x)

    y <- as.vector(y)

    unique(if (length(x) || length(y)) 

        x[match(x, y, 0L) == 0L]

    else x)

}

<bytecode: 0x000000000ff1e8d0>

<environment: namespace:base>

 




 (2) R_package_name:::function_name


R package 이름을 알고 있다면 package_name:::function_name 의 형식으로 ':::' 를 사용해서 함수의 소스 코드를 들여다볼 수 있습니다. 위 (1)번의 getAnywhere() 함수가 기억이 잘 안날 때 쉽게 ':::' 를 사용해서 소스 코드 볼 때 편리합니다. 



> base:::setdiff

function (x, y) 

{

    x <- as.vector(x)

    y <- as.vector(y)

    unique(if (length(x) || length(y)) 

        x[match(x, y, 0L) == 0L]

    else x)

}

<bytecode: 0x000000000ff1e8d0>

<environment: namespace:base>




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

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 (1) 시계열 정수의 순차 3개 묶음 패턴 별 개수를 구하고, (2) 각 패턴별로 발생 빈도 기준으로 내림차순 정렬하는 방법을 소개하겠습니다. 



먼저 S = {1, 2, 3, 4, 5} 의 정수 집합에서 복원추출(replacement) 하여 무작위 샘플 1,000개를 생성해보겠습니다. 



> #----------------------------------

> # counting and sorting by patterns

> #----------------------------------

> set.seed(123) # for reproducibility

> sample <- sample(x=1:5, size=1000, replace=TRUE)

> sample

   [1] 2 4 3 5 5 1 3 5 3 3 5 3 4 3 1 5 2 1 2 5 5 4 4 5 4 4 3 3 2 1 5 5 4 4 1 3 4 2 2 2 1 3 3 2 1 1 2

  [48] 3 2 5 1 3 4 1 3 2 1 4 5 2 4 1 2 2 5 3 5 5 4 3 4 4 4 1 3 2 2 4 2 1 2 4 3 4 1 3 5 5 5 1 1 4 2 4

  [95] 2 1 4 1 3 3 3 2 3 5 3 5 5 4 3 1 5 2 1 5 4 1 3 5 3 3 4 2 2 2 2 5 1 1 1 4 4 5 4 4 3 4 5 4 5 3 2

 [142] 3 1 1 5 2 2 1 2 4 5 3 2 2 1 2 3 2 3 2 3 2 4 2 2 3 4 2 3 2 4 1 5 4 4 4 2 3 5 3 5 2 4 2 3 3 2 3

 [189] 5 5 2 2 5 4 5 3 3 4 1 3 2 5 4 3 3 5 2 2 1 1 3 2 2 4 1 4 2 3 5 5 2 5 4 4 1 2 3 3 4 5 4 3 3 1 2

 [236] 2 1 5 1 5 3 4 1 4 2 4 2 5 5 4 2 2 3 2 3 4 1 3 3 5 5 5 4 5 3 3 2 2 1 3 5 1 1 1 4 4 5 3 1 4 4 1

 [283] 2 2 1 2 1 2 1 4 2 1 1 5 4 5 5 1 1 4 4 1 4 4 4 3 1 1 3 3 2 3 4 1 2 5 5 2 2 5 5 2 1 4 1 1 5 1 2

 [330] 5 4 2 4 5 2 3 4 4 4 5 3 1 3 2 3 2 5 2 2 4 1 5 3 1 4 5 4 3 2 5 1 1 5 2 2 4 1 1 3 3 3 2 5 3 3 5

 [377] 4 2 2 5 3 4 2 4 3 5 1 2 1 2 5 2 4 4 1 2 2 1 1 5 5 1 5 3 2 3 4 1 2 4 2 5 2 3 2 1 5 2 3 1 3 4 3

 [424] 2 5 4 2 2 1 5 5 3 2 5 2 4 1 3 1 1 2 3 4 1 5 4 4 1 2 4 3 2 2 1 2 5 5 4 1 1 5 3 3 1 3 2 2 4 2 5

 [471] 1 5 3 4 4 3 4 1 2 4 2 5 2 4 5 1 1 4 3 5 5 1 3 4 1 5 2 1 4 4 2 2 2 1 2 1 3 3 5 2 3 1 5 2 4 1 3

 [518] 4 1 5 2 2 2 1 3 4 5 1 5 2 5 4 3 5 1 4 2 5 3 2 5 1 5 2 4 5 1 5 5 2 2 2 3 5 3 3 4 3 3 1 1 5 4 2

 [565] 4 4 2 5 3 5 3 5 3 3 5 4 3 1 3 3 5 5 5 2 5 1 2 4 5 3 3 1 5 1 1 5 3 2 5 1 2 4 2 2 1 5 1 5 2 2 4

 [612] 1 1 5 3 4 5 4 5 1 5 5 4 2 1 2 2 5 2 2 4 5 3 4 1 2 5 4 5 3 3 4 5 2 1 4 4 1 2 4 4 5 3 3 4 5 5 1

 [659] 3 2 5 5 1 4 2 1 5 1 4 1 1 3 1 2 3 3 5 2 3 3 4 4 5 5 5 3 3 1 3 2 1 4 3 1 2 3 1 1 3 2 5 2 1 5 2

 [706] 4 3 2 1 3 4 3 4 4 3 2 1 1 5 1 3 4 4 5 4 4 5 4 4 1 3 5 4 5 1 2 5 5 2 1 5 1 1 3 2 3 4 1 4 5 2 2

 [753] 1 1 3 3 3 5 1 2 3 2 3 3 3 1 1 3 4 4 5 5 4 5 1 2 2 3 4 5 2 5 1 4 5 3 2 1 2 5 5 3 2 3 5 3 1 2 2

 [800] 2 3 2 1 1 2 5 3 3 4 4 1 4 2 5 4 1 2 1 3 3 4 2 4 5 4 1 3 1 2 4 1 4 3 4 4 3 4 4 2 1 5 4 4 1 3 2

 [847] 5 1 2 1 2 4 5 4 3 3 5 1 1 5 3 1 4 3 4 2 1 1 3 3 1 1 4 3 5 4 1 4 5 4 1 5 4 1 4 5 3 2 2 2 3 4 4

 [894] 3 5 4 1 2 1 4 5 3 5 3 4 3 4 5 5 1 1 1 5 3 2 5 3 3 2 2 5 1 2 5 4 4 2 3 1 4 5 2 4 3 5 1 3 5 5 1

 [941] 5 3 2 3 1 1 3 2 3 2 3 1 5 1 2 5 4 1 3 3 3 3 4 1 5 3 3 1 4 3 4 1 5 2 2 4 5 1 2 3 3 4 5 4 5 4 1

 [988] 1 1 3 1 2 3 3 3 5 4 2 4 1

 




다음으로 순차적으로 3개의 정수를 묶음으로 하여 1개씩 이동하면서 패턴을 정의하고(패턴1 {X1, X2, X3}, 패턴2 {X2, X3, X4}, ..., 패턴n-2 {Xn-2, Xn-1, X}, 위의 정수 난수 샘플을 가지고 예를 들면, 첫번째 3개 묶음의 패턴은 '2_4_3', 두번째 3개 묶음의 패턴은 '4_3_5', 세번째 3개 묶음의 패턴은 '3_5_5', ... ), 각 패턴별로 발생 빈도를 세어보겠습니다


먼저 비어있는 pattern_list 라는 이름의 리스트(list) 를 만들어놓구요, 


for loop 반복문을 사용하여 '패턴1' {X1, X2, X3}, '패턴2' {X2, X3, X4}, ..., '패턴n-2' {Xn-2, Xn-1, X} 을 생성합니다. 


if else 조건문을 사용하여, 만약 3개 정수 묶음의 패턴이 pattern_list의 키(key)에 이미 들어있는 것이면 +1 을 추가해주구요, 그렇지 않다면 (처음 발생하는 패턴이라면) pattern_list의 키(key)에 새로 발생한 패턴을 키로 추가해주고 값(value)으로 1을 부여해줍니다. 



> # blank list to store the patterns' count

> pattern_list <- {}

> # count per patterns

> for (i in 1:(length(sample)-2)){

+   

+   pattern <- paste(sample[i], sample[i+1], sample[i+2], sep="_")

+   

+   if (pattern %in% names(pattern_list)) {

+     pattern_list[[pattern]] <- pattern_list[[pattern]] + 1

+   } else {

+     pattern_list[[pattern]] <- 1

+   }

+ }

> pattern_list

2_4_3 4_3_5 3_5_5 5_5_1 5_1_3 1_3_5 3_5_3 5_3_3 3_3_5 5_3_4 3_4_3 4_3_1 3_1_5 1_5_2 5_2_1 2_1_2 

    6     7    10     9     6     6    10    15    11     7     5     5     5    13     7    12 

1_2_5 2_5_5 5_5_4 5_4_4 4_4_5 4_5_4 4_4_3 4_3_3 3_3_2 3_2_1 2_1_5 1_5_5 4_4_1 4_1_3 1_3_4 3_4_2 

   11     8     9    11     9    13     7     5     8     9    10     5    11    14     9     6 

4_2_2 2_2_2 2_2_1 2_1_3 1_3_3 2_1_1 1_1_2 1_2_3 2_3_2 3_2_5 2_5_1 3_4_1 1_3_2 2_1_4 1_4_5 4_5_2 

    8     8    14     6    11     8     3     9    11    13    10    15    12     8     8     6 

5_2_4 2_4_1 4_1_2 1_2_2 2_2_5 2_5_3 5_3_5 5_4_3 4_3_4 3_4_4 4_4_4 3_2_2 2_2_4 2_4_2 4_2_1 1_2_4 

    9    10    12     7     7     7     6     8    10    11     4     8     8     9     7    10 

5_5_5 5_1_1 1_1_4 1_4_2 4_2_4 1_4_1 3_3_3 3_2_3 2_3_5 1_5_4 5_4_1 3_3_4 1_1_1 1_4_4 3_4_5 5_4_5 

    4    10     6     7     7     3     7    15     6     7    10    11     4     7     9     9 

4_5_3 5_3_2 2_3_1 3_1_1 1_1_5 5_2_2 2_4_5 3_2_4 2_2_3 2_3_4 4_2_3 4_1_5 4_4_2 3_5_2 2_3_3 5_5_2 

   13    11     7     8    12    12     9     2     6     9     5     8     5     4     7     7 

2_5_4 1_1_3 4_1_4 5_2_5 3_3_1 3_1_2 1_5_1 5_1_5 1_5_3 4_2_5 5_4_2 3_5_1 5_3_1 3_1_4 1_2_1 4_5_5 

   10    11     8     4     8     6     8     9    11     7     7     6     5     5     7     5 

4_1_1 5_1_2 5_2_3 3_1_3 2_5_2 4_3_2 3_5_4 2_4_4 5_5_3 1_3_1 4_5_1 1_4_3 5_1_4 

    6    11     5     5     7     5     6     3     3     4     7     6     4 

 




이제 발생 빈도를 기준으로 패턴 리스트를 내림차순 정렬(sort in descending order)을 해보겠습니다. 

'5_3_3', '3_4_1', '3_2-3' 패턴이 총 15번 발생해서 공동 1등을 하였네요. 



> # sorting pattern_list in descending order

> sort(pattern_list, decreasing = TRUE)

5_3_3  3_4_1  3_2_3  4_1_3 2_2_1 1_5_2 4_5_4 3_2_5 4_5_3 2_1_2 1_3_2 4_1_2 1_1_5 5_2_2 3_3_5 1_2_5 

   15     15      15     14    14    13    13    13    13    12    12    12    12    12    11    11 

5_4_4 4_4_1 1_3_3 2_3_2 3_4_4 3_3_4 5_3_2 1_1_3 1_5_3 5_1_2 3_5_5 3_5_3 2_1_5 2_5_1 2_4_1 4_3_4 

   11    11    11    11    11    11    11    11    11    11    10    10    10    10    10    10 

1_2_4 5_1_1 5_4_1 2_5_4 5_5_1 5_5_4 4_4_5 3_2_1 1_3_4 1_2_3 5_2_4 2_4_2 3_4_5 5_4_5 2_4_5 2_3_4 

   10    10    10    10     9     9     9     9     9     9     9     9     9     9     9     9 

5_1_5 2_5_5 3_3_2 4_2_2 2_2_2 2_1_1 2_1_4 1_4_5 5_4_3 3_2_2 2_2_4 3_1_1 4_1_5 4_1_4 3_3_1 1_5_1 

    9     8     8     8     8     8     8     8     8     8     8     8     8     8     8     8 

4_3_5 5_3_4 5_2_1 4_4_3 1_2_2 2_2_5 2_5_3 4_2_1 1_4_2 4_2_4 3_3_3 1_5_4 1_4_4 2_3_1 2_3_3 5_5_2 

    7     7     7     7     7     7     7     7     7     7     7     7     7     7     7     7 

4_2_5 5_4_2 1_2_1 2_5_2 4_5_1 2_4_3 5_1_3 1_3_5 3_4_2 2_1_3 4_5_2 5_3_5 1_1_4 2_3_5 2_2_3 3_1_2 

    7     7     7     7     7     6     6     6     6     6     6     6     6     6     6     6 

3_5_1 4_1_1 3_5_4 1_4_3 3_4_3 4_3_1 3_1_5 4_3_3 1_5_5 4_2_3 4_4_2 5_3_1 3_1_4 4_5_5 5_2_3 3_1_3 

    6     6     6     6     5     5     5     5     5     5     5     5     5     5     5     5 

4_3_2 4_4_4 5_5_5 1_1_1 3_5_2 5_2_5 1_3_1 5_1_4 1_1_2 1_4_1 2_4_4 5_5_3 3_2_4 

    5     4     4     4     4     4     4     4     3     3     3     3     2

 




위의 패턴별 발생 빈도수 기준으로 정렬된 결과를 DataFrame으로 변환해서 상위 10개 패턴을 프린트해보겠습니다. 



> # convert a list to DataFrame

> cnt_per_pattern_sorted <- sort(pattern_list, decreasing = TRUE)

> pattern <- names(cnt_per_pattern_sorted)

> df <- data.frame(pattern, cnt_per_pattern_sorted)

> rownames(df) <- NULL

> # display top 10 patterns

> df[1:10,]

   pattern     cnt_per_pattern_sorted

1    5_3_3                     15

2    3_4_1                     15

3    3_2_3                     15

4    4_1_3                     14

5    2_2_1                     14

6    1_5_2                     13

7    4_5_4                     13

8    3_2_5                     13

9    4_5_3                     13

10   2_1_2                     12

 



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

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



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 최성원 2019.10.16 14:15  댓글주소  수정/삭제  댓글쓰기

    항상 감사드립니다. 질문이 있습니다!
    수집된 패턴들을 가지고 다른 분석을 하고 싶은데요, 시계열 군집분석이나 다변량 군집분석이요...
    그래서 데이터 타입을 "list" 형태로 변환해봤는데 에러가 나더라구요.
    제가 데이터 타입에 대한 깊은 이해가... 부족해서 그런것 같은데
    질문은...
    위의 방법으로 패턴 수집했을때 데이터가 어떤 형태로 저장되는 건가요?
    그리고 리스트나 데이터 프레임의 형태로 저장은 어떻게 하나요?

    • R Friend R_Friend 2019.10.16 14:22 신고  댓글주소  수정/삭제

      안녕하세요.

      현재 분석 결과는 리스트로 저장을 해둔 상태입니다.

      만약 패턴별 발생빈도 리스트를 데이터프레임으로 변경을 하고 싶으시면,

      df <- data.frame(matrix(unlist(pattern_list), nrow=length(pattern_list), byrow=T), stringAsFactors=FALSE)

      로 하시면 됩니다. (만약 character 를 factor type으로 해서 분석할 경우라면 stringAsFactors 옵션을 빼주면 됩니다)

      ps. 제가 이번주는 너무 바빠서 더이상은 못도와드리겠스니다. 죄송합니다. 이해해주세요. ㅜ.ㅜ

    • 최성원 2019.10.16 16:28  댓글주소  수정/삭제

      지금도 너무 감사드려요 ㅠㅠ

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

      네, 이해해주셔서 고맙습니다. 아무쪼록 순탄하게 잘 해결하시기를 바래요.

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

      본문 하단에 DataFrame으로 변환하는 코드도 update 했습니다. ^^

  2. 질문드립니다. 2019.10.24 10:16  댓글주소  수정/삭제  댓글쓰기

    매번 친절하게 알려주셔서 감사드립니다^^

    데이터 프레임에 제품 On/off 시간에 대해서 수집하고 있는데
    제품을 동작하고 끄기까지의 사용시간을 계산하고 싶은데 너무 초보라
    이해도가 부족해서 코딩을 하기가 힘들어서 문의드립니다..
    아래처럼 시작 시간 정지 시간 별로 시간 데이터가 입력이 되는 구조입니다.

    DEVICE | time | state | sum |
    ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
    A |2018-07-01 10:20:23| ON | |
    A |2018-07-01 10:30:55| ON | |
    A |2018-07-01 11:11:01| ON | |
    A |2018-07-01 11:51:41| OFF | |
    B |2018-07-02 07:11:02| ON | |
    B |2018-07-02 09:00:33| ON | |
    B |2018-07-02 09:20:24| OFF | |
    C |2018-07-02 12:12:21| ON | |
    C |2018-07-02 14:01:09| OFF | |
    C |2018-07-02 18:11:41| ON | |
    C |2018-07-02 19:21:51| OFF | |

    DEVICE 별로 시작과 정지 사이의 시간을 계산해서 합한 후에 열을 추가해서 기입하고
    A 제품처럼 ON과 OFF 사이에 ON 이 여러개 들어있으면 이것들을 제거하고 처음ON과
    마지막 OFF 사이만 계산하게끔 하고 싶습니다..ㅠ
    최종적인 아웃풋이 제품 별 평균 사용시간을 정의하는 것입니다.
    도움 부탁드립니다..ㅠ

  3. JHK 2019.12.02 01:40  댓글주소  수정/삭제  댓글쓰기

    이전의 질문에 대한 답변 감사했습니다!

    본문내용과는 좀 다르지만 전처리에 대한 질문을 하고싶어서요!

    데이터에서
    한id변수에 대해 기록된 정보가 여러개라서 예를 들어 id 변수에서 001이라는 아이디를 가진 사람의 정보가 약 200여개씩 존재합니다

    id
    001
    001
    001
    .
    .
    .
    이때 다른 것은 각 행마다 기록되는 시간이 다른데요, 그 기록된 시간마다 work라는 변수의 내용이 다릅니다.

    id work time
    001 작동준비 2018-10-23 03:22
    001 작동시작 2018-10-23 03:25
    001 ab투여 2018-10-23 03:43
    .
    .
    .

    이런식으로 구성되어 있는데요
    저는 work변수의 작동시작이라는 값이 해당하는 행에서 가지는 time변수의 시각을 기준으로 이전의 시각을 가지는 행들을 불러오고 싶습니다.

    어떻게 해야할지 잘 모르겠네요 ㅜㅜ
    time변수는 다루기 편하게 문자열과 공백을 지우고 대소비교가 가능하도록
    201810230322 이런식으로 바꾸어 놓은 상태입니다!

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

      제가 저녁에 답글 남길께요

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

      안녕하세요 JHK님,

      만약 id 별로 work 중에 '작동시작' 이 "단 한번만 존재"한다는 가정 하에

      (1) id별로 work = '작동시작' 인 행만을 가져와서 dataframe을 만들고 (df2)

      (2) 이를 id 키를 기준으로 원래의 dataframe(df)에 df2를 merge 한 후에

      (3) df2의 작동시작 시간(work_prepair_time) 보다 작은 time 인 경우만 가져와서 새로운 dataframe(df3)을 만드는 순서로 코드 작성해 보았습니다.

      (아래 코드에서 'w2' = '작동시작'으로 간주하시면 됩니다)

      > id <- c(rep('001', 3), rep('002', 4))
      > work <- c('w1', 'w2', 'w3', 'w0', 'w1', 'w2', 'w3')
      > time <- c(201810230322, 201810230325, 201810230343, 201810230222, 201810230322, 201810230325, 201810230343)
      >
      > df <- data.frame(id, work, time)
      > df
      id work time
      1 001 w1 201810230322
      2 001 w2 201810230325
      3 001 w3 201810230343
      4 002 w0 201810230222
      5 002 w1 201810230322
      6 002 w2 201810230325
      7 002 w3 201810230343
      >
      > work_prepair_time <- subset(df,
      + select = c('id', 'time'),
      + subset = (work == 'w2'))
      > names(work_prepair_time) <- c('id', 'work_prepair_time')
      > work_prepair_time
      id work_prepair_time
      2 001 201810230325
      6 002 201810230325
      >
      > df2 <- merge(df, work_prepair_time, by = 'id')
      > df2
      id work time work_prepair_time
      1 001 w1 201810230322 201810230325
      2 001 w2 201810230325 201810230325
      3 001 w3 201810230343 201810230325
      4 002 w0 201810230222 201810230325
      5 002 w1 201810230322 201810230325
      6 002 w2 201810230325 201810230325
      7 002 w3 201810230343 201810230325
      >
      > df3 <- subset(df2,
      + select = c('id', 'work', 'time'),
      + subset = (time < work_prepair_time))
      >
      > df3
      id work time
      1 001 w1 201810230322
      4 002 w0 201810230222
      5 002 w1 201810230322


      만약, 각 id 별로 work에서 '작동시작' 이 두번 이상 발생하는 경우도 있다면, 이럴 경우 처리하는 로직이 무엇이냐에 따라서 코드를 다르게 짜야 할텐데요, 혹시 이런 경우라면 댓글 남겨주세요.

    • JHK 2019.12.03 07:06  댓글주소  수정/삭제

      정말 친절한 설명 감사해요!ㅜㅜ!!
      막상 데이터분석을 해보니 전처리가 정말
      힘든 것 같아요! 감사합니다 :)

    • R Friend R_Friend 2019.12.03 09:28 신고  댓글주소  수정/삭제

      문제가 해결되었다니 기쁘네요.

회사의 보안 정책 상 사내 폐쇄망으로 운영함에 따라 R 패키지를 인터넷으로부터 바로 다운로드하지 못하는 경우가 있습니다. 이처럼 폐쇄망일 경우 R 패키지를 설치하는 것이 '지옥 그 자체!' 일 수 있습니다. 왜냐하면 특정 R 패키지를 설치하려면 그와 의존성이 있는 다른 패키지를 설치해야만 하고, 그 의존성이 있는 패키지는 또 다른 의존성이 있는 패키지가 설치되어야만 하고.... 꼬리에 꼬리를 무는 의존성 있는 패키지들을 설치하다보면 입에서 투덜거림이 궁시렁 궁시렁 나오게 됩니다. -_-;;; 

 

이럴 때 편리하게 사용할 수 있는 'R 패키지 다운로드 & 의존성 있는 R 패키지도 같이 한꺼번에 다운로드 하기' 하는 방법을 소개하겠습니다. 

 

 

먼저 R 패키지를 다운로드 받아 놓을 'r-pkt' 폴더를 만들어보겠습니다. 

> mainDir <- "/Users/ihongdon/Downloads"

> subDir <- "r-pkg"

dir.create(file.path(mainDir, subDir), showWarnings = FALSE)

 

다음으로 tools 패키지의 package_dependencies() 함수를 이용하여 다운도르하려는 특정 R 패키지와 이 패키지의 의존성 있는 패키지(dependencies of package)들 정보를 가져오는 사용자 정의 함수 getPackages() 를 만들어보겠습니다. 

# UDF for get packages with dependencies

getPackages <- function(packs){

  packages <- unlist(

    # Find (recursively) dependencies or reverse dependencies of packages.

    tools::package_dependencies(packs, available.packages(),

                         which=c("Depends", "Imports"), recursive=TRUE)

  )

  packages <- union(packs, packages)

  

  return(packages)

}

 

 

자, 이제 준비가 되었으니 예제로 "dplyr"와 "ggplot2" 의 두 개 패키지와 이들과 의존성을 가지는 패키지들을 getPackages() 사용자 정의 함수로 정보를 가져온 후에, download.packages() 함수로 '/Users/ihongdon/Downloads/r-pkg' 폴더로 다운로드 해보도록 하겠습니다. 

> packages <- getPackages(c("dplyr", "ggplot2"))

download.packages(packages, destdir=file.path(mainDir, subDir))

 

trying URL 'https://cran.rstudio.com/src/contrib/dplyr_0.8.0.1.tar.gz'

Content type 'application/x-gzip' length 1075146 bytes (1.0 MB)

==================================================

downloaded 1.0 MB

trying URL 'https://cran.rstudio.com/src/contrib/ggplot2_3.1.1.tar.gz'

Content type 'application/x-gzip' length 2862022 bytes (2.7 MB)

==================================================

downloaded 2.7 MB

trying URL 'https://cran.rstudio.com/src/contrib/assertthat_0.2.1.tar.gz'

Content type 'application/x-gzip' length 12742 bytes (12 KB)

==================================================

downloaded 12 KB

trying URL 'https://cran.rstudio.com/src/contrib/glue_1.3.1.tar.gz'

Content type 'application/x-gzip' length 122950 bytes (120 KB)

==================================================

downloaded 120 KB

trying URL 'https://cran.rstudio.com/src/contrib/magrittr_1.5.tar.gz'

Content type 'application/x-gzip' length 200504 bytes (195 KB)

==================================================

downloaded 195 KB

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'methods' at the repositories

trying URL 'https://cran.rstudio.com/src/contrib/pkgconfig_2.0.2.tar.gz'

Content type 'application/x-gzip' length 6024 bytes

==================================================

downloaded 6024 bytes

trying URL 'https://cran.rstudio.com/src/contrib/R6_2.4.0.tar.gz'

Content type 'application/x-gzip' length 31545 bytes (30 KB)

==================================================

downloaded 30 KB

trying URL 'https://cran.rstudio.com/src/contrib/Rcpp_1.0.1.tar.gz'

Content type 'application/x-gzip' length 3661123 bytes (3.5 MB)

==================================================

downloaded 3.5 MB

trying URL 'https://cran.rstudio.com/src/contrib/rlang_0.3.4.tar.gz'

Content type 'application/x-gzip' length 858992 bytes (838 KB)

==================================================

downloaded 838 KB

trying URL 'https://cran.rstudio.com/src/contrib/tibble_2.1.1.tar.gz'

Content type 'application/x-gzip' length 311836 bytes (304 KB)

==================================================

downloaded 304 KB

trying URL 'https://cran.rstudio.com/src/contrib/tidyselect_0.2.5.tar.gz'

Content type 'application/x-gzip' length 21883 bytes (21 KB)

==================================================

downloaded 21 KB

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'utils' at the repositories

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'tools' at the repositories

trying URL 'https://cran.rstudio.com/src/contrib/cli_1.1.0.tar.gz'

Content type 'application/x-gzip' length 40232 bytes (39 KB)

==================================================

downloaded 39 KB

trying URL 'https://cran.rstudio.com/src/contrib/crayon_1.3.4.tar.gz'

Content type 'application/x-gzip' length 658694 bytes (643 KB)

==================================================

downloaded 643 KB

trying URL 'https://cran.rstudio.com/src/contrib/fansi_0.4.0.tar.gz'

Content type 'application/x-gzip' length 266123 bytes (259 KB)

==================================================

downloaded 259 KB

trying URL 'https://cran.rstudio.com/src/contrib/pillar_1.3.1.tar.gz'

Content type 'application/x-gzip' length 103972 bytes (101 KB)

==================================================

downloaded 101 KB

trying URL 'https://cran.rstudio.com/src/contrib/purrr_0.3.2.tar.gz'

Content type 'application/x-gzip' length 373701 bytes (364 KB)

==================================================

downloaded 364 KB

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'grDevices' at the repositories

trying URL 'https://cran.rstudio.com/src/contrib/utf8_1.1.4.tar.gz'

Content type 'application/x-gzip' length 218882 bytes (213 KB)

==================================================

downloaded 213 KB

trying URL 'https://cran.rstudio.com/src/contrib/digest_0.6.18.tar.gz'

Content type 'application/x-gzip' length 128553 bytes (125 KB)

==================================================

downloaded 125 KB

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'grid' at the repositories

trying URL 'https://cran.rstudio.com/src/contrib/gtable_0.3.0.tar.gz'

Content type 'application/x-gzip' length 368081 bytes (359 KB)

==================================================

downloaded 359 KB

trying URL 'https://cran.rstudio.com/src/contrib/lazyeval_0.2.2.tar.gz'

Content type 'application/x-gzip' length 83482 bytes (81 KB)

==================================================

downloaded 81 KB

trying URL 'https://cran.rstudio.com/src/contrib/MASS_7.3-51.4.tar.gz'

Content type 'application/x-gzip' length 487233 bytes (475 KB)

==================================================

downloaded 475 KB

trying URL 'https://cran.rstudio.com/src/contrib/mgcv_1.8-28.tar.gz'

Content type 'application/x-gzip' length 915991 bytes (894 KB)

==================================================

downloaded 894 KB

trying URL 'https://cran.rstudio.com/src/contrib/plyr_1.8.4.tar.gz'

Content type 'application/x-gzip' length 392451 bytes (383 KB)

==================================================

downloaded 383 KB

trying URL 'https://cran.rstudio.com/src/contrib/reshape2_1.4.3.tar.gz'

Content type 'application/x-gzip' length 36405 bytes (35 KB)

==================================================

downloaded 35 KB

trying URL 'https://cran.rstudio.com/src/contrib/scales_1.0.0.tar.gz'

Content type 'application/x-gzip' length 299262 bytes (292 KB)

==================================================

downloaded 292 KB

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'stats' at the repositories

trying URL 'https://cran.rstudio.com/src/contrib/viridisLite_0.3.0.tar.gz'

Content type 'application/x-gzip' length 44019 bytes (42 KB)

==================================================

downloaded 42 KB

trying URL 'https://cran.rstudio.com/src/contrib/withr_2.1.2.tar.gz'

Content type 'application/x-gzip' length 53578 bytes (52 KB)

==================================================

downloaded 52 KB

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'graphics' at the repositories

trying URL 'https://cran.rstudio.com/src/contrib/nlme_3.1-139.tar.gz'

Content type 'application/x-gzip' length 793473 bytes (774 KB)

==================================================

downloaded 774 KB

trying URL 'https://cran.rstudio.com/src/contrib/Matrix_1.2-17.tar.gz'

Content type 'application/x-gzip' length 1860456 bytes (1.8 MB)

==================================================

downloaded 1.8 MB

Warning in download.packages(packages, destdir = file.path(mainDir, subDir)) :

  no package 'splines' at the repositories

trying URL 'https://cran.rstudio.com/src/contrib/stringr_1.4.0.tar.gz'

Content type 'application/x-gzip' length 135777 bytes (132 KB)

==================================================

downloaded 132 KB

trying URL 'https://cran.rstudio.com/src/contrib/labeling_0.3.tar.gz'

Content type 'application/x-gzip' length 10722 bytes (10 KB)

==================================================

downloaded 10 KB

trying URL 'https://cran.rstudio.com/src/contrib/munsell_0.5.0.tar.gz'

Content type 'application/x-gzip' length 182653 bytes (178 KB)

==================================================

downloaded 178 KB

trying URL 'https://cran.rstudio.com/src/contrib/RColorBrewer_1.1-2.tar.gz'

Content type 'application/x-gzip' length 11532 bytes (11 KB)

==================================================

downloaded 11 KB

trying URL 'https://cran.rstudio.com/src/contrib/lattice_0.20-38.tar.gz'

Content type 'application/x-gzip' length 359031 bytes (350 KB)

==================================================

downloaded 350 KB

trying URL 'https://cran.rstudio.com/src/contrib/colorspace_1.4-1.tar.gz'

Content type 'application/x-gzip' length 2152594 bytes (2.1 MB)

==================================================

downloaded 2.1 MB

trying URL 'https://cran.rstudio.com/src/contrib/stringi_1.4.3.tar.gz'

Content type 'application/x-gzip' length 7290890 bytes (7.0 MB)

==================================================

downloaded 7.0 MB

      [,1]           [,2]                                                       

 [1,] "dplyr"        "/Users/ihongdon/Downloads/r-pkg/dplyr_0.8.0.1.tar.gz"     

 [2,] "ggplot2"      "/Users/ihongdon/Downloads/r-pkg/ggplot2_3.1.1.tar.gz"     

 [3,] "assertthat"   "/Users/ihongdon/Downloads/r-pkg/assertthat_0.2.1.tar.gz"  

 [4,] "glue"         "/Users/ihongdon/Downloads/r-pkg/glue_1.3.1.tar.gz"        

 [5,] "magrittr"     "/Users/ihongdon/Downloads/r-pkg/magrittr_1.5.tar.gz"      

 [6,] "pkgconfig"    "/Users/ihongdon/Downloads/r-pkg/pkgconfig_2.0.2.tar.gz"   

 [7,] "R6"           "/Users/ihongdon/Downloads/r-pkg/R6_2.4.0.tar.gz"          

 [8,] "Rcpp"         "/Users/ihongdon/Downloads/r-pkg/Rcpp_1.0.1.tar.gz"        

 [9,] "rlang"        "/Users/ihongdon/Downloads/r-pkg/rlang_0.3.4.tar.gz"       

[10,] "tibble"       "/Users/ihongdon/Downloads/r-pkg/tibble_2.1.1.tar.gz"      

[11,] "tidyselect"   "/Users/ihongdon/Downloads/r-pkg/tidyselect_0.2.5.tar.gz"  

[12,] "cli"          "/Users/ihongdon/Downloads/r-pkg/cli_1.1.0.tar.gz"         

[13,] "crayon"       "/Users/ihongdon/Downloads/r-pkg/crayon_1.3.4.tar.gz"      

[14,] "fansi"        "/Users/ihongdon/Downloads/r-pkg/fansi_0.4.0.tar.gz"       

[15,] "pillar"       "/Users/ihongdon/Downloads/r-pkg/pillar_1.3.1.tar.gz"      

[16,] "purrr"        "/Users/ihongdon/Downloads/r-pkg/purrr_0.3.2.tar.gz"       

[17,] "utf8"         "/Users/ihongdon/Downloads/r-pkg/utf8_1.1.4.tar.gz"        

[18,] "digest"       "/Users/ihongdon/Downloads/r-pkg/digest_0.6.18.tar.gz"     

[19,] "gtable"       "/Users/ihongdon/Downloads/r-pkg/gtable_0.3.0.tar.gz"      

[20,] "lazyeval"     "/Users/ihongdon/Downloads/r-pkg/lazyeval_0.2.2.tar.gz"    

[21,] "MASS"         "/Users/ihongdon/Downloads/r-pkg/MASS_7.3-51.4.tar.gz"     

[22,] "mgcv"         "/Users/ihongdon/Downloads/r-pkg/mgcv_1.8-28.tar.gz"       

[23,] "plyr"         "/Users/ihongdon/Downloads/r-pkg/plyr_1.8.4.tar.gz"        

[24,] "reshape2"     "/Users/ihongdon/Downloads/r-pkg/reshape2_1.4.3.tar.gz"    

[25,] "scales"       "/Users/ihongdon/Downloads/r-pkg/scales_1.0.0.tar.gz"      

[26,] "viridisLite"  "/Users/ihongdon/Downloads/r-pkg/viridisLite_0.3.0.tar.gz

[27,] "withr"        "/Users/ihongdon/Downloads/r-pkg/withr_2.1.2.tar.gz"       

[28,] "nlme"         "/Users/ihongdon/Downloads/r-pkg/nlme_3.1-139.tar.gz"      

[29,] "Matrix"       "/Users/ihongdon/Downloads/r-pkg/Matrix_1.2-17.tar.gz"     

[30,] "stringr"      "/Users/ihongdon/Downloads/r-pkg/stringr_1.4.0.tar.gz"     

[31,] "labeling"     "/Users/ihongdon/Downloads/r-pkg/labeling_0.3.tar.gz"      

[32,] "munsell"      "/Users/ihongdon/Downloads/r-pkg/munsell_0.5.0.tar.gz"     

[33,] "RColorBrewer" "/Users/ihongdon/Downloads/r-pkg/RColorBrewer_1.1-2.tar.gz"

[34,] "lattice"      "/Users/ihongdon/Downloads/r-pkg/lattice_0.20-38.tar.gz"   

[35,] "colorspace"   "/Users/ihongdon/Downloads/r-pkg/colorspace_1.4-1.tar.gz"  

[36,] "stringi"      "/Users/ihongdon/Downloads/r-pkg/stringi_1.4.3.tar.gz"

 

위에 다운로드된 패키지들의 리스트를 보면 알 수 있는 것처럼, 'dplyr'과 'ggplot2'의 두 개 패키지를 다운로드 하려고 했더니 이들이 의존성을 가지고 있는 [3] 'assertthat' 패키지부터 ~ [36] 'stringi' 패키지까지 34개의 패키지가 추가로 다운로드 되었습니다. 

 

외부의 인터넷 연결이 되는 환경에서 다운받아 놓은 이들 R packages 파일들을 저장매체에 저장하여 가져가서 폐쇄망으로 되어 있는 서버에 복사를 한 후에, 수작업으로 R package 설치하면 되겠습니다. (이때 의존성 있는 패키지들까지 알아서 다 설치가 되도록 여러번 설치 작업을 반복해주면 됩니다. 

 

##==============================================##

폐쇄망 환경에서 Greenplum DB에 R 패키지 설치하는 방법은 

==> https://rfriend.tistory.com/442 

포스팅을 참고하세요. 

##==============================================##

 

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

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 열심남 2019.05.02 15:06 신고  댓글주소  수정/삭제  댓글쓰기

    아래 포스팅 참고하시면, 스크립트로 저장해 놓고 사용이 가능합니다.
    저도 회사에선 이렇게 사용했습니다. ^^;

    의존성있는 패키지 전체를 csv형태로 저장해서 스크립트를 이용해서 반복할때 편하게 사용했습니다.

    https://dr-hkim.github.io/Install-Packages-without-Internet-Connection/

  2. 123 2019.09.20 14:35  댓글주소  수정/삭제  댓글쓰기

    봐도 모르겠어요.. 스크립트와 스크립트 사용법을 주시면 다른 초보 개발자들이 쓸수있지않을까요.... 저도 어렵지만 다른분들도...

이번 포스팅에서는 행 기준으로 숫자형 변수들의 합을 구한 다음에, 그룹별로 행 기준 합이 최대인 전체 행을 선별하는 방법을 소개하겠습니다. 


말로 설명한 내용만으로는 얼른 이해가 안 올 수도 있겠는데요, 이번에 해볼 내용은 아래의 이미지를 참고하시면 이해가 쉬울 듯 합니다. 




예제로 사용할 하나의 그룹 변수(V1)와 나머지 9개의 숫자형 변수(V2~V10)로 구성된 간단한 DataFrame을 만들어보겠습니다. 



> ##------------------------------------------------------

> ## selecting distinct object using dplyr chain operator

> ##------------------------------------------------------

> rm(list=ls())

> set.seed(123) # for reproducibility

> V1 <- c(rep("apple", 5), rep("banana", 5), rep("tomato", 5)) # group

> V2 <- sample(x=1:10, size=15, replace=T)

> V3 <- sample(x=1:10, size=15, replace=T)

> V4 <- sample(x=1:10, size=15, replace=T)

> V5 <- sample(x=1:10, size=15, replace=T)

> V6 <- sample(x=1:10, size=15, replace=T)

> V7 <- sample(x=1:10, size=15, replace=T)

> V8 <- sample(x=1:10, size=15, replace=T)

> V9 <- sample(x=1:10, size=15, replace=T)

> V10 <- sample(x=1:10, size=15, replace=T)

> df <- data.frame(V1, V2, V3, V4, V5, V6, V7, V8, V9, V10)

> df

       V1 V2 V3 V4 V5 V6 V7 V8 V9 V10

1   apple  3  9 10  2  7  3  2  9   7

2   apple  8  3 10  3  1  4  7 10   4

3   apple  5  1  7  5  4  7  4  7   4

4   apple  9  4  8  3  3  4  7  5   3

5   apple 10 10  1  9  9  2  4  2   4

6  banana  1  9  5  1  5  3  2 10  10

7  banana  6  7  8  5  9  7  8  4   2

8  banana  9  7  3  8  9  5  1  1   1

9  banana  6 10  4  2  8  8  5 10   2

10 banana  5  7  3  6  5  2  6  8   7

11 tomato 10  8  2  3  8  5  6  2   7

12 tomato  5  6  5  2  7 10  4  6   9

13 tomato  7  6  5  8  8  9  5 10   7

14 tomato  6  3  4  9  1  9 10  6   8

15 tomato  2  2  2  4  5  2  5  5   6

 

> rm(V1, V2, V3, V4, V5, V6, V7, V8, V9, V10)





rowSums() 함수를 사용하여서 행(row) 기준의 숫자형 변수들 모두에 대해 합계를 구하여 'V_sum' 이라는 새로운 변수를 추가해보겠습니다. 



> # summation in a row direction

> df$V_sum <- rowSums(df[,2:10])

> df

       V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V_sum


1   apple  3  9 10  2  7  3  2  9   7    52

2   apple  8  3 10  3  1  4  7 10   4    50

3   apple  5  1  7  5  4  7  4  7   4    44

4   apple  9  4  8  3  3  4  7  5   3    46

5   apple 10 10  1  9  9  2  4  2   4    51


6  banana  1  9  5  1  5  3  2 10  10    46

7  banana  6  7  8  5  9  7  8  4   2    56

8  banana  9  7  3  8  9  5  1  1   1    44

9  banana  6 10  4  2  8  8  5 10   2    55

10 banana  5  7  3  6  5  2  6  8   7    49


11 tomato 10  8  2  3  8  5  6  2   7    51

12 tomato  5  6  5  2  7 10  4  6   9    54

13 tomato  7  6  5  8  8  9  5 10   7    65

14 tomato  6  3  4  9  1  9 10  6   8    56

15 tomato  2  2  2  4  5  2  5  5   6    33

 




위의 행 기준 합계로 보면 'apple' 그룹에서는 1번째 행의 합이 52로 가장 크며, 'banana' 그룹에서는 7번째 행의 합이 56으로서 가장 크고, 'tomato' 그룹에서는 13번째 행의 합이 65로서 가장 큽니다. 이를 1번째, 7번째, 13번째 전체 행을 선별해보겠습니다. (빨간색으로 표시한 부분)



> library(dplyr)

> df_group_distinct_max <- df %>% 

+   arrange(V1, desc(V_sum)) %>% 

+   group_by(V1) %>% slice(1:1)

> df_group_distinct_max <- data.frame(df_group_distinct_max[1:10])

> df_group_distinct_max

      V1 V2 V3 V4 V5 V6 V7 V8 V9 V10

1  apple  3  9 10  2  7  3  2  9   7

2 banana  6  7  8  5  9  7  8  4   2

3 tomato  7  6  5  8  8  9  5 10   7

 


* dplyr 패키지 사용법은 

https://rfriend.tistory.com/234 , 

https://rfriend.tistory.com/236

참고하시기 바랍니다


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


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



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 


(1) R Environment 상에 존재하는 여러개의 DataFrame 중에서 이름이 특정 조건을 만족하는 DataFrame을 선별하여 

 : ls(pattern = "xx")


(2) 하나의 List 로 묶는 방법, 

 : mget()


(3) List로 부터 특정 DataFrame을 Indexing 하는 방법

 : list[[1]], or  list[["name"]]


에 대해서 소개하겠습니다. 



먼저, 이번 포스팅의 예제의 특성 상 Environment 와 Console을 깨끗하게 청소하는 것부터 시작하겠습니다.  rm(list=ls()) 로 Environment에 있는 모든 객체를 삭제하고, cat("\014")로 Console을 백지상태로 만들어주었습니다. 



# To make an environment, console clear 

rm(list=ls()) # clearing environment

cat("\014") # clearing Console




객체 이름에 'data_'를 공통으로 포함한 3개의 DataFrame 예제와, 객체 이름에 'file_'을 포함한 3개의 DataFrame 예제를 만들어보겠습니다. 



# To make several sample DataFrames

data_1 <- data.frame(var1 = c(1, 2), var2 = c(3, 4))

data_2 <- data.frame(var1 = c('a', 'b'), var2 = c('c', 'd'))

data_3 <- data.frame(var1 = c(TRUE, TRUE), var2 = c(FALSE, TRUE))

file_1 <- data.frame(var1 = c(1, 2), var2 = c(3, 4))

file_2 <- data.frame(var1 = c('a', 'b'), var2 = c('c', 'd'))

file_3 <- data.frame(var1 = c(TRUE, TRUE), var2 = c(FALSE, TRUE))

  [DataFrame samples]

 



Environment에 생성된 객체를 확인해보려면 ls() 함수를 사용하면 됩니다. 그리고 특정 문자열을 포함한 객체만을 선별해서 찾아보려면 ls(pattern = "xx") 처럼 pattern = "xx" 를 추가하면 됩니다. 



> # To list up all objects in an environment

> ls()

[1] "data_1" "data_2" "data_3" "file_1" "file_2" "file_3"

> # To list up objects which have a matching 'pattern' in a DataFrame name

> ls(pattern = "data_")

[1] "data_1" "data_2" "data_3"

> ls(pattern = "file_")

[1] "file_1" "file_2" "file_3"

 




다음으로, mget() 함수를 사용하여 ls(pattern = "data_"), ls(pattern = "file_"로 각각 선별한 DataFrame 객체들을 2개의 List로 각각 묶어서 생성해보겠습니다.  



> # To combine all DataFrame into a List using mget()

> list_df_data <- mget(ls(pattern = "data_"))

> list_df_data

$data_1

  var1 var2

1    1    3

2    2    4


$data_2

  var1 var2

1    a    c

2    b    d


$data_3

  var1  var2

1 TRUE FALSE

2 TRUE  TRUE


> list_df_file <- mget(ls(pattern = "file_"))

> list_df_file

$file_1

  var1 var2

1    1    3

2    2    4


$file_2

  var1 var2

1    a    c

2    b    d


$file_3

  var1  var2

1 TRUE FALSE

2 TRUE  TRUE






마지막으로, List에 묶인 DataFrame을 [[ ]] 을 사용하여 위치(숫자) 혹은 이름으로 Indexing 하는 방법을 아래에 소개합니다. 



> # To index one of object in a List using [[ ]]

> list_df_data[[1]] # using 'number'

  var1 var2

1    1    3

2    2    4

> list_df_data["data_1"] # using 'name'

$data_1

  var1 var2

1    1    3

2    2    4 



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

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



Posted by R Friend R_Friend

댓글을 달아 주세요

문자열이나 숫자를 특정 형식으로 길이를 지정해주면 데이터를 출력했을 때 깨끗하게 정리가 되어 보이기 때문에 가독성이 좋아집니다. 


혹은 데이터가 특정 형식(format)으로 DB에 이미 지정이 되어 있어서 데이터 간 병합이나 join을 하기 위해 특정 형식으로 데이터를 표준화 해주어야 할 경우가 있습니다. 


이번 포스팅에서는 {base} package의 sprintf() 함수를 사용해서 


 - (1) 문자열을 매개변수 width 길이로 만들고, 빈 자리는 '0'으로 채우기 : sprintf("%05d", var)


 - (2) 소수점 숫자(numeric)의 자리수를 지정해주기 : sprintf(".5f", var)


하는 방법에 대해서 알아보겠습니다. 


이번 포스팅의 함수 sprintf()는 데이터 전처리할 때 종종 사용하는 편이예요. 



 (1) 문자열을 특정 길이로 만들고, 빈 자리수만큼 '0'을 채우기 : sprintf("%05d", var)





1자리, 2자리, 3자리, 4자리를 가진 데이터를 가지고 예제로 사용할 간단한 DataFrame을 만들어보겠습니다. 



> # making a sample DataFrame

> df <- data.frame(var1 = c(1, 11, 111, 1111))

> df

  var1

1    1

2   11

3  111

4 1111

 




위의 예제 데이터셋을 가지고, 칼럼 var1의 데이터를 


- '1 자리수를 가진 문자열'로 만들되, '1자리수가 안되면 모자라는 자리수 만큼'0'으로 채우기'

- '2 자리수를 가진 문자열'로 만들되, '2자리수가 안되면 모자라는 자리수 만큼'0'으로 채우기'

- '3 자리수를 가진 문자열'로 만들되, '3자리수가 안되면 모자라는 자리수 만큼'0'으로 채우기'

- '4 자리수를 가진 문자열'로 만들되, '4자리수가 안되면 모자라는 자리수 만큼'0'으로 채우기'

- '5 자리수를 가진 문자열'로 만들되, '5자리수가 안되면 모자라는 자리수 만큼'0'으로 채우기'


를 해보겠습니다. 


만약 매개변수 자리수보다 데이터의 길이가 더 크다면 '0'이 채워지지는 않습니다.  아래의 예제의 결과를 View(df)로 해서 보면 원래의 변수 var1과 sprintf() 함수를 사용해서 만든 var1_01d 변수의 데이터 출력 형식이 다른 것을 알 수 있습니다. 


그리고 class 함수로 데이터 형식을 살펴보니 원래 변수 var1은 숫자형(numeric)이지만 sprintf() 함수로 만든 새로운 변수는 요인형(factor)의 문자열로 바뀌어 있음을 알 수 있습니다. 



> #-------------------------

> # (1) sprintf(%03d, var) : Format number as fixed width, with leading zeros

> df <- transform(df, 

+                 var1_01d = sprintf("%01d", var1), 

+                 var1_02d = sprintf("%02d", var1), 

+                 var1_03d = sprintf("%03d", var1), 

+                 var1_04d = sprintf("%04d", var1), 

+                 var1_05d = sprintf("%05d", var1))

> df

  var1 var1_01d var1_02d var1_03d var1_04d var1_05d

1    1        1       01      001     0001    00001

2   11       11       11      011     0011    00011

3  111      111      111      111     0111    00111

4 1111     1111     1111     1111     1111    01111



> View(df)



> sapply(df, class)

     var1  var1_01d  var1_02d  var1_03d  var1_04d  var1_05d

"numeric"  "factor"  "factor"  "factor"  "factor"  "factor"




 (2) 소수점 숫자(numeric)의 자리수를 지정해주기 : sprintf(".5f", var)


무리수인 자연상수 e의 소수점 10째 자리까지의 수를 대상으로 sprintf("%.5f", e) 함수를 사용해서 소수점의 자리수를 설정해보겠습니다. "%.숫자f"의 숫자 만큼 소수점을 표시해주는데요, 반올림을 해서 표시해줍니다. 아래의 예제를 보시면 금방 이해할 수 있을 것입니다. 



> #-------------------------

> # (3) sprintf("%.5f", x) : formatting decimal point, 

> e <- c(2.7182818284) # mathematical constant, the base of the natural logarithm

> sprintf("%.0f", e)

[1] "3"

> sprintf("%.1f", e)

[1] "2.7"

> sprintf("%.2f", e)

[1] "2.72"

> sprintf("%.3f", e)

[1] "2.718"

> sprintf("%.5f", e)

[1] "2.71828"

> sprintf("%.10f", e)

[1] "2.7182818284"

 




아래의 예시는 sprintf("%숫자.f, e)로 '숫자' 부분에 매개변수로 정수 부분의 자리수를 지정해주는 예시입니다. 소수점의 자리도 모두 포함해서 '숫자' 부분 매개변수만큼의 길이로 표시 형식을 맞추어줍니다. 



> e <- c(2.7182818284) # mathematical constant, the base of the natural logarithm

> sprintf("%1.1f", e)

[1] "2.7"

> sprintf("%2.1f", e)

[1] "2.7"

> sprintf("%3.1f", e)

[1] "2.7"

> sprintf("%5.1f", e)

[1] "  2.7"

> sprintf("%10.1f", e)

[1] "       2.7"

>  



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

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





Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 2017.10.16 23:45  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  2. 2017.10.16 23:48  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

보통 외부 데이터 불러오기 할 때 {utils} package의 read.table() 함수를 사용하곤 합니다. 


크기가 작은 데이터라면 별 문제를 못느낄 텐데요, 만약 데이터 사이즈가 수 메가, 기가 단위의 큰 데이터라면 데이터를 불러들이는데 너무 오랜 시간이 걸려서 문제가 될 수 있습니다. 


대용량 데이터 처리에 아주 뛰어난 성능을 발휘하는 data.table 패키지의 fread() 함수를 사용하면 큰 용량의 외부 데이터도 빠르게 불러올 수 있습니다. 


(물론 R은 메모리에 데이터를 올려놓고 처리/분석을 하므로 하둡에서 말하는 수테라급의 대용량에는 필적을 못하구요, 분산병렬처리도 아니긴 합니다. 이 포스팅에서 말하는 대용량은 책보고 공부할 때 사용하는 수십, 수백개 row 를 가진 예제 데이터 대비 실전에서 사용하는 수십만, 수백만, 수천만 row 데이터를 말하는 것입니다. ^^;)


아래에 간단한 샘플 데이터를 만들어서 {utils} 패키지의 read.table() 함수와 {data.table} 패키지의 fread() 함수의 데이터 불러오는데 소요되는 시간을 비교해보았습니다. 



[ 외부 데이터 읽어오기 : {utils} 패키지 read.table() 함수 vs. {data.table} 패키지 fread(0 함수 ]




1. 샘플 데이터 만들기 : 1 백 만개 row, 변수 2개를 가지는 데이터 프레임



# generating large scaled data

my_data <- data.frame(var1 = rnorm(n = 1000000, mean = 0, sd = 1), 

                      var2 = rnorm(n = 1000000, mean = 2, sd = 3))

 



> str(my_data)

'data.frame': 1000000 obs. of  2 variables:

 $ var1: num  -0.556 1.787 0.498 -1.967 0.701 ...

 $ var2: num  1.669 0.597 4.452 1.405 6.936 ...

 



# exporting to text file

write.table(my_data, 

            "/Users/Desktop/R/my_data.txt",

            sep = "|",

            row.names = FALSE, 

            quote = FALSE)

 




2. {utils} 패키지의 read.table() 함수를 사용해서 my_data.txt 불러오기


system.time() 함수로 데이터를 불어오는데 소요된 시간을 재어보았더니 7.287초가 나왔습니다. 

(매번 할 때마다 소요 시간이 조금씩 차이가 날 수는 있습니다)



# reading text file : (1) read.table() of {utils} package

> system.time(my_data_readtable <- read.table("/Users/Desktop/R/my_data.txt",

+                                             sep = "|", 

+                                             header = TRUE, 

+                                             stringsAsFactors = FALSE))

   user  system elapsed 

  7.161   0.100   7.287

 




3. {data.table} 패키지의 fread() 함수를 사용해서 my_data.txt 불러오기


data.table 패키지는 기본 패키지가 아니므로 먼저 별도 설치(install.packages) 및 호출(library)이 필요합니다. 

(매번 할 때마다 소요 시간이 조금씩 차이가 날 수는 있습니다)



# reading text file : (2) fread() of {data.table} package

install.packages("data.table")

library(data.table)

 



system.time() 함수로 my_data를 불러오는데 걸린 시간을 재어봤더니 0.256초가 걸렸습니다. 



> system.time(my_data_fread <- fread("/Users/Desktop/R/my_data.txt", 

+                                    sep = "|", 

+                                    header = TRUE, 

+                                    stringsAsFactors = FALSE))

   user  system elapsed 

  0.242   0.014   0.256

 




1백만 행을 가진 데이터프레임을 읽어오는데 있어, 앞서 read.table() 함수가 7.287 초 걸렸던데 반해, fread() 함수는 0.256 초밖에 걸리지 않았습니다.  fread() 함수는 read.table() 함수를 사용했을 때 대비 약 96.5% 정도 속도가 더 적게 걸린 것입니다.  놀랍지요?!!! 



> 0.256/7.287

[1] 0.03513106

 



R 은 대용량 데이터에는 맥을 못춰라고 지레 평가절하하기 보다는 {data.table} 패키지의 fread() 함수로 대용량 데이터 불러오기 속도 문제를 공략해보시지요. 


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

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


Posted by R Friend R_Friend

댓글을 달아 주세요

개발 프로젝트에 가보면 서버와 클라이언트 간 데이터 전송할 때 XML 보다 기능은 적지만 보다 간단하고 파싱도 빠른 JSON(JavaScript Object Notation, (/ˈsən/ JAY-sən)) 데이터 포맷을 많이 사용합니다. 


JSON 은 JavaScript Object Notation 이라는 이름처럼 JavaScript 에서 유래하기는 했습니다만, 프로그래밍 언어에 독립적으로 기능하는 데이터 포맷입니다. 


JSON 이 무엇인가를 이해하는데 있어, 어떤 사람을 기술하는데 JSON 표기를 사용한 아래의 예제를 참고하시면 도움이 될 듯 합니다. 



[ 사람을 기술하는데 사용한 JSON 표기 예시 (JSON representation describing a person) ]


{
  "firstName": "John",
  "lastName": "Smith",
  "isAlive": true,
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021-3100"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    },
    {
      "type": "mobile",
      "number": "123 456-7890"
    }
  ],
  "children": [],
  "spouse": null
}

 * source : https://en.wikipedia.org/wiki/JSON




개발하는 분이라면 위 예제의 JSON 데이터 포맷이 아주 익숙할 것입니다만, 개발 경험이 없는 분석가의 경우 매우 생소하게 느낄 수 있습니다.  R에서 사용하는 데이터 구조로 스칼라, 벡터, 행렬, 배열, 데이터프레임, 리스트 등이 있는데요, 특히 행렬, 데이터프레임의 경우 2차원의 행(row)과 열(column)로 데이터셋이 구성이 되어 있습니다. JSON과는 많이 다르기 때문에 처음 JSON을 본 분석가는 아마 '이거 뭐지?' 하고 당황할 것 같습니다 (제가 그랬어요. ^^;).


그나마 위의 SJON 데이터는 들여쓰기(indentation)이 이쁘게 되어 있어서 가독성이 좋은 것이구요, 개발자들이 건네주는 SJON 파일을 보면 아래처럼 들여쓰기 없이 옆으로 죽~ 이어져 있어서 가독성이 매우 떨어지다 보니 더 당황하게 되는거 같습니다. @@~


{"firstName":"John","lastName":"Smith","isAlive":true,"age":25,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null} 



그래서 보통은 JSON parser 의 도움을 받아서 들여쓰기(indentation)을 해서 데이터 구조, 위계체계를 살펴보곤 합니다. 

아래는 http://www.jsonparseronline.com/ 이라는 사이트에 들어가서 JSON 데이터(왼쪽)를 parsing (오른쪽) 해본 것입니다. 


[ JSON parser (http://www.jsonparseronline.com) ]





위의 데이터를 자세히 보시면 "address""phoneNumbers"는 다른 항목과는 달리 하위에 nested data 들을 가지고 있습니다. 일종의 데이터 위계체계가 있는 셈인데요, 아래 예시처럼 어떤 사람에 대한 데이터를 JSON 데이터 포맷으로 구조화하는 방식이 합리적이고 효율적으로 보입니다. 다만, R이나 Python (numpy, pandas 모듈), SAS, SPSS 등의 분석 툴을 사용하는 분석가라면 생소할 수 있다는점을 빼면 말이지요. 





서론이 길었습니다. 


이번 포스팅에는 R의 jsonlite 패키지를 사용해서 


(1) JSON 포맷의 데이터를 R DataFrame 으로 변환

     (converting from JSON to R DataFrame)


(2) R DataFrame 을 JSON 포맷의 데이터로 변환

     (converting from R DataFrame to JSON)


하는 방법에 대해서 알아보겠습니다. 


jsonlite 패키지를 사용하면 JSON 데이터 포맷을 구조를 R이 알아서 잘 이해를 해서 R 분석가가 익숙한 R DataFrame으로 자동으로 바꾸어 주며, 그 반대로 R DataFrame을 JSON 데이터 포맷으로도 바꾸어주니 매우 편리한 패키지입니다. 



먼저, jsonlite 패키지와 (웹에서 JSON 데이터를 호출할 때 사용하는) httr 패키지를 설치하고 불러와 보겠습니다. 



install.packages("jsonlite")

library(jsonlite)


install.packages("httr")

library(httr)




 (1) JSON 포맷의 데이터를 R DataFrame 으로 변환 (converting from JSON to R DataFrame)

      : fromJSON() 함수


아래는 jsonlite 패키지의 fromJSON() 함수를 사용해서 "https://api.github.com/users/hadley/repos" 의 github API 로부터 JSON 데이터를 요청(request)해서 R DataFrame으로 변환해 본 예제입니다. 



[ (R로 불러오기 전의) JSON 원본 데이터 포맷 (https://api.github.com/users/hadley/repos) ]





자세히 보면 "owner" 는 nested data 로 login, id 등의 데이터를 가지고 있습니다. 






30개의 관측치와 69개의 변수를 가지고 있는 DataFrame으로 잘 변환이 되었음을 알 수 있습니다. 


[ R jsonlite package를 사용해서 JSON 데이터를 R DataFrame 으로 변환한 모습 ]



> # (1) converting JSON to R DataFrame

> df_repos <- fromJSON("https://api.github.com/users/hadley/repos")

> str(df_repos)

'data.frame': 30 obs. of  69 variables:

 $ id               : int  40423928 40544418 14984909 12241750 5154874 9324319 20228011 82348 888200 3116998 ...

 $ name             : chr  "15-state-of-the-union" "15-student-papers" "500lines" "adv-r" ...

 $ full_name        : chr  "hadley/15-state-of-the-union" "hadley/15-student-papers" "hadley/500lines" "hadley/adv-r" ...

 $ owner            :'data.frame': 30 obs. of  17 variables:

  ..$ login              : chr  "hadley" "hadley" "hadley" "hadley" ...

  ..$ id                 : int  4196 4196 4196 4196 4196 4196 4196 4196 4196 4196 ...

  ..$ avatar_url         : chr  "https://avatars0.githubusercontent.com/u/4196?v=3" "https://avatars0.githubusercontent.com/u/4196?v=3" "https://avatars0.githubusercontent.com/u/4196?v=3" "https://avatars0.githubusercontent.com/u/4196?v=3" ...

  ..$ gravatar_id        : chr  "" "" "" "" ...

  ..$ url                : chr  "https://api.github.com/users/hadley" "https://api.github.com/users/hadley" "https://api.github.com/users/hadley" "https://api.github.com/users/hadley" ...

  ..$ html_url           : chr  "https://github.com/hadley" "https://github.com/hadley" "https://github.com/hadley" "https://github.com/hadley" ...

  ..$ followers_url      : chr  "https://api.github.com/users/hadley/followers" "https://api.github.com/users/hadley/followers" "https://api.github.com/users/hadley/followers" "https://api.github.com/users/hadley/followers" ...

  ..$ following_url      : chr  "https://api.github.com/users/hadley/following{/other_user}" "https://api.github.com/users/hadley/following{/other_user}" "https://api.github.com/users/hadley/following{/other_user}" "https://api.github.com/users/hadley/following{/other_user}" ... 

 ...이하 생략 ...


> names(df_repos)

 [1] "id"                "name"              "full_name"         "owner"             "private"          

 [6] "html_url"          "description"       "fork"              "url"               "forks_url"        

[11] "keys_url"          "collaborators_url" "teams_url"         "hooks_url"         "issue_events_url" 

[16] "events_url"        "assignees_url"     "branches_url"      "tags_url"          "blobs_url"        

[21] "git_tags_url"      "git_refs_url"      "trees_url"         "statuses_url"      "languages_url"    

[26] "stargazers_url"    "contributors_url"  "subscribers_url"   "subscription_url"  "commits_url"      

[31] "git_commits_url"   "comments_url"      "issue_comment_url" "contents_url"      "compare_url"      

[36] "merges_url"        "archive_url"       "downloads_url"     "issues_url"        "pulls_url"        

[41] "milestones_url"    "notifications_url" "labels_url"        "releases_url"      "deployments_url"  

[46] "created_at"        "updated_at"        "pushed_at"         "git_url"           "ssh_url"          

[51] "clone_url"         "svn_url"           "homepage"          "size"              "stargazers_count" 

[56] "watchers_count"    "language"          "has_issues"        "has_projects"      "has_downloads"    

[61] "has_wiki"          "has_pages"         "forks_count"       "mirror_url"        "open_issues_count"

[66] "forks"             "open_issues"       "watchers"          "default_branch"   





nested data 를 가진 "owner" 에 딸린 데이터 항목으로 login, id 등 총 17개 데이터 항목이 있군요. 



> # nested DataFrame in owner

> names(df_repos$owner)

 [1] "login"               "id"                  "avatar_url"          "gravatar_id"        

 [5] "url"                 "html_url"            "followers_url"       "following_url"      

 [9] "gists_url"           "starred_url"         "subscriptions_url"   "organizations_url"  

[13] "repos_url"           "events_url"          "received_events_url" "type"               

[17] "site_admin"

 




nested data 를 가진 "owner" 변수에 대해 하위 변수까지 위계 구조를 반영해서 데이터를 indexing 해오는 방법은 아래를 참고하세요. 4가지 방법 모두 동일한 결과를 반환합니다. 



> # different indexing, the same results

> df_repos[1:3,]$owner$login

[1] "hadley" "hadley" "hadley"

> df_repos[1:3,"owner"]$login

[1] "hadley" "hadley" "hadley"

> df_repos$owner[1:3,"login"]

[1] "hadley" "hadley" "hadley"

> df_repos$owner[1:3,]$login

[1] "hadley" "hadley" "hadley"

 




 (2) R DataFrame 을 JSON 포맷의 데이터로 변환 (converting from R DataFrame to JSON)

       : toJSON() 함수


위에서 JSON 포맷 데이터를 웹에서 호출해서 "df_repos"라는 이름의 R DataFrame으로 변환을 했었는데요, 이번에는 jsonlite패키지의 toJSON() 함수를 사용해서 거꾸로 R DataFrame 을 원래의 JSON 데이터 포맷으로 변환해보겠습니다. 



> # (2) converting R DataFrame to JSON

> json_repos <- toJSON(df_repos)

 




R DataFrame 데이터를 JSON 데이터 포맷으로 변환한 결과를 cat() 함수, prettify() 함수, minify() 함수를 사용해서 차례대로 살펴보겠습니다. 


cat() 함수, minify() 함수는 R의 head() 함수와 기능이 비슷합니다. 

 


> cat(json_repos)

[{"id":40423928,"name":"15-state-of-the-union","full_name":"hadley/15-state-of-the-union","owner":{"login":"hadley","id":4196,"avatar_url":"https://avatars0.githubusercontent.com/u/4196?v=3","gravatar_id":"","url":"https://api.github.com/users/hadley","html_url":"https://github.com/hadley","followers_url":"https://api.github.com/users/hadley/followers","following_url":"https://api.github.com/users/hadley/following{/other_user}","gists_url":"https://api.github.com/users/hadley/gists{/gist_id}","starred_url":"https://api.github.com/users/hadley/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hadley/subscriptions","organizations_url":"https://api.github.com/users/hadley/orgs","repos_url":"https://api.github.com/users/hadley/repos","events_url":"https://api.github.com/users/hadley/events{/privacy}","received_events_url":"https://api.github.com/users/hadley/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/hadley/15-s... <truncated>

 




# not including indentation, whitespace

minify(json_repos)

 






prettify() 함수는 들여쓰기, 공백을 사용해서 JSON 데이터를 파싱해서 표기해줌으로써 아래에 화면캡쳐해 놓은 것처럼 가독성이 매우 좋습니다.  nested data 를 가진 "owner" 데이터에 대해서 아래처럼 정확하게 원래의 JSON 데이터포맷으로 변환을 해놨습니다. jsonlite 패키지 참 똑똑하지요? ^^



# including indentation, whitespace

prettify(json_repos, indent = 4)




JSON 데이터 포맷 관련해서 serializeJSON, stream_in, stream_out 등 추가적인 기능이 필요한 분은 아래 [Reference]의 jsonlite package 매뉴얼을 참고하시기 바랍니다. 


[Reference] https://cran.r-project.org/web/packages/jsonlite/jsonlite.pdf


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


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



Posted by R Friend R_Friend

댓글을 달아 주세요