이번 포스팅에서는 데이터프레임(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의 총 개수와 동일?)

수집한 데이터셋의 관측치들 간에 중복(duplication)이 있는 경우가 있습니다.  혹은 여러개의 흩어져 있는 데이터셋을 특정 기준 변수(key)를 사용해서 병합(merge)하는 전처리 작업을 하다 보면 가끔씩 의도하지 않게 중복데이터가 생기는 수가 있습니다.  서로 다른 행인데 각 변수별 관측값들이 동일한 경우 말이지요.  이럴 경우 저장 공간을 중복되는 만큼 더 잡아먹는고 데이터 처리 속도가 더 걸린다는 문제가 있으며, 더 심각하게는 이러한 데이터셋을 그대로 두고 통계 분석을 진행할 경우 분석 결과에 왜곡이 생길 수도 있으므로 중복 관측값 전처리가 필요합니다. (가령, 평균만 하더라도 중복이 있는 관측치가 여럿 있을 경우 분자의 관측치 합과 분모의 관측치 개수가 중복 제거후와는 서로 달라지겠지요?)

 

중복 데이터가 있을 경우에는 먼저 한개의 유일한 관측치(unique elements)만을 남겨놓고 나머지 중복 데이터들은 제거하는 전처리를 거쳐야 합니다. 이를 위해 R의

 

- {base} 패키지 : unique()

- {base} 패키지 : dataframe[!duplicated(var), ]

- {dplyr} 패키지 : distinct()

 

함수를 사용할 수 있습니다.

 

SAS 사용자라면 datastep에서 특정 변수를 기준으로 sorting을 한 후에 first.variable_name 또는 last.variable_name 명령문을 사용했던 것을 생각하시면 이해하기 쉬울 것입니다.  SAS와는 다르게 R은 sorting을 할 필요가 없습니다. (SAS는 merge 할 때도 먼저 sorting을 해줘야 하는 반면, R은 merge 할 때 sorting 해줄 필요 없습니다)

 

단, unique도 그렇고, merge도 그렇고 크기가 작은 데이터셋에는 별 무리가 없지만 대용량 데이터셋에 사용하기에는 처리 성능에 부담이 아주 많이 되는 함수입니다.  수십 기가가 넘는 대용량 사이즈의 데이터셋이라면 하둡 클러스터 내에서 Hive 등으로 중복 데이터 전처리하여 사용하시길 권합니다.

 

 

 

[ 중복없이 유일한 관측치만 남기기 (extracting unique elements) ]

 

 

 

 

R의 unique() 함수는 base package 함수에 내장되어 있으므로 별도의 패키지를 설치할 필요는 없습니다.  데이터 프레임(data frame), 배열(array), 행렬(matrix), 벡터(vector)에 모두 사용할 수 있는데요, 일반적으로 데이터 프레임을 분석에 많이 사용하므로 아래에는 데이터 프레임을 대상으로 unique() 함수 사용하는 방법을 소개하겠습니다.  

 

 

먼저 R 실습을 위해 관측치들 간에 중복이 포함되어 있는 변수 3개짜리 데이터 프레임을 만들어 보겠습니다.

 

> ##------------------------------------------
> ## extracting unique elements : unique()
> ##------------------------------------------
> 
> a1 <- rep(1:10, each = 2)
> a1
 [1]  1  1  2  2  3  3  4  4  5  5  6  6  7  7  8  8  9  9 10 10
> 
> a2 <- rep(c(1, 3, 5, 7, 9), each = 4)
> a2
 [1] 1 1 1 1 3 3 3 3 5 5 5 5 7 7 7 7 9 9 9 9
> 
> a3 <- c(1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 12)
> a3
 [1]  1  1  1  1  3  3  3  3  5  5  6  6  7  7  8  8  9 10 11 12
> 
> a1a2a3 <- data.frame(cbind(a1, a2, a3))
> a1a2a3
   a1 a2 a3
1   1  1  1
2   1  1  1
3   2  1  1
4   2  1  1
5   3  3  3
6   3  3  3
7   4  3  3
8   4  3  3
9   5  5  5
10  5  5  5
11  6  5  6
12  6  5  6
13  7  7  7
14  7  7  7
15  8  7  8
16  8  7  8
17  9  9  9
18  9  9 10
19 10  9 11
20 10  9 12
> 
> str(a1a2a3)
'data.frame':	20 obs. of  3 variables:
 $ a1: num  1 1 2 2 3 3 4 4 5 5 ...
 $ a2: num  1 1 1 1 3 3 3 3 5 5 ...
 $ a3: num  1 1 1 1 3 3 3 3 5 5 ...

 

 

 

 

[예제 1]

변수가 3개 있는 데이터 프레임인데요, a1과 a2의 두 개 변수를 기준으로 중복여부를 체크한 후에, 중복이 있을 경우에는 1개만 선택하고 나머지 중복된 관측치는 제거하는 방법에 대한 예시는 아래와 같습니다.

 

>

> # extracting unique elements by 2 variables > aqa2a3_uniq_var2 <- unique(a1a2a3[, c("a1", "a2")]) > aqa2a3_uniq_var2 a1 a2 1 1 1 3 2 1 5 3 3 7 4 3 9 5 5 11 6 5 13 7 7 15 8 7 17 9 9 19 10 9

 

 

 

 

위의 예제에서 "a1"변수와 "a2" 변수를 기준으로 중복 관측치를 제거하고 유일한 관측치만 남기는 처리 개념을 풀어서 설명하면 아래와 같습니다.  참고로, a1a2a3[, c("a1", "a2")] 은 a1a2a3 데이터 프레임에서 변수 "a1"과 "a2"만 select 하라는 뜻입니다.

 

 

 

 


 

 

[예제 2]

다음으로, "a1", "a2", "a3" 세 개의 변수를 기준으로 중복된 관측치가 있다면 유일한 관측치만 남기고 나머지 중복 관측치는 제거하는 예를 들어보겠습니다.

 

> 

> # extracting unique elements by 3 variables > aqa2a3_uniq_var3 <- unique(a1a2a3[, c("a1", "a2", "a3")]) > aqa2a3_uniq_var3 a1 a2 a3 1 1 1 1 3 2 1 1 5 3 3 3 7 4 3 3 9 5 5 5 11 6 5 6 13 7 7 7 15 8 7 8 17 9 9 9 18 9 9 10 19 10 9 11 20 10 9 12

 

 

 

 

위 R 함수의 처리 과정을 풀어보면 아래와 같습니다.

 

 

 

 

"a1", "a2"의 두 개 변수만을 기준으로 했을 때 대비, "a1", "a2", "a3"의 세 개변수를 기준으로 unique() 함수를 적용했을 때 18번째와 20번째 행이 중복없는 유일한 행으로서 추가로 살아남았음을 알 수 있습니다.

 

 

 


 

[예제 3]

unique(x, fromLast = TRUE) 옵션을 적용하면 중복된 관측치들 중에서 남길 관측치(행, row)를 선택할 때 가장 밑에 있는 관측치(from Last observation)를 기준으로 선택합니다. default 는 fromLast = FALSE 입니다.

 

> 
> # identical element will be kept from the last : fromLast = TRUE 
> # defaulu : fromLast = FALSE
> aqa2a3_uniq_var3_fromLast <- unique(a1a2a3[, c("a1", "a2", "a3")], fromLast = TRUE) 
> aqa2a3_uniq_var3_fromLast  # differnt order
   a1 a2 a3
2   1  1  1
4   2  1  1
6   3  3  3
8   4  3  3
10  5  5  5
12  6  5  6
14  7  7  7
16  8  7  8
17  9  9  9
18  9  9 10
19 10  9 11
20 10  9 12

 

 

 

 

 

[예제2]의 fromLast = FALSE (default 이므로 특별히 명기할 필요는 없음) 일 때와 [예제3]의 fromLast = TRUE 일 때의 차이가 무엇인지 잘 이해가 안 갈 수도 있는데요, 아래 [예제2]와 [예제3]의 데이터프레임들의 row names 의 번호가 서로 다름을 알 수 있습니다. 

 

1번째 행과 2번째 행이 중복된 것의 처리에 대해서만 설명드리자면, [예제2]의 fromLast = FALSE의 경우1번 행(first row)을 가져왔고, [예제3]의 fromLast = TRUE 의 경우 2버 행(second row)를 유일한 행으로 가져왔습니다.

 

 

 

 

[예제2]와 [예제3]의 결과는 동일합니다. 그런데 왜 굳이 이런것이 필요하지 싶을 것입니다. ^^?

위의 예제는 설명의 편의를 위해서 변수 3개 짜리 중복 데이터셋을 초간단으로 만들어본 것인데요, 실전에서 사용하는 데이터셋은 변수가 수십, 수백개, 관측치(행의 개수)도 수천, 수만, 수백만개인 경우가 다반사입니다.  이때 특정 변수를 기준으로 중복인 경우 유일한 관측치를 선별하고, 나머지 변수들은 그대로 사용해야 하는 경우 중복된 관측치의 first observation을 살려둘지 아니면 last obsetvation을 살려둘지에 따라서 중복 제거 기준 변수 이외의 타 변수들의 관측치 값들이 다른 경우에는 고민 좀 해야겠지요?  이럴 경우에 fromLast = FALSE/TRUE 옵션이 필요합니다.  (이럴 경우 SAS처럼 미리 특정 변수를 기준으로 정렬해놔야 겠군요)

 

 


 

 

{base} package에 내장되어 있는 duplicated() 함수를 사용해서 중복값을 제거하고 유일한 값만 선별할 수도 있습니다.  duplicated() 함수를 사용하면 아래 예시처럼 중복되는 행에 대해서 TRUE, FALSE boolean 값을 반환합니다.  이 논리형 값을 가지고 dataframe에서 indexing을 해오는 방식으로 중복값을 처리하고 유일한 값만 남겨놓을 수 있습니다.

 

 

> ##-----------------------
> ## duplicated() function
> ##-----------------------
> 
> # original dataset
> a1a2a3
   a1 a2 a3
1   1  1  1
2   1  1  1
3   2  1  1
4   2  1  1
5   3  3  3
6   3  3  3
7   4  3  3
8   4  3  3
9   5  5  5
10  5  5  5
11  6  5  6
12  6  5  6
13  7  7  7
14  7  7  7
15  8  7  8
16  8  7  8
17  9  9  9
18  9  9 10
19 10  9 11
20 10  9 12
> 
> 
> # returning TRUE for duplicated value
> duplicated(a1a2a3$a1) 
 [1] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE
[12]  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE
> 
> 
> # indexing unduplicated rows using dataframe[!duplicated(dataframe$var1), ]
> a1a2a3_not_duplicated_var1 <- a1a2a3[!duplicated(a1a2a3$a1),]
> a1a2a3_not_duplicated_var1
   a1 a2 a3
1   1  1  1
3   2  1  1
5   3  3  3
7   4  3  3
9   5  5  5
11  6  5  6
13  7  7  7
15  8  7  8
17  9  9  9
19 10  9 11
> 
> # another exmaple
> a1a2a3[!duplicated(a1a2a3$a2),]
   a1 a2 a3
1   1  1  1
5   3  3  3
9   5  5  5
13  7  7  7
17  9  9  9

 

 

 


 

dplyr 패키지의 distinct() 함수도 중복이 없는 유일한 값을 반환합니다.  dplyr 패키지의 distinct() 가 깔끔하기도 하고, dplyr 패키지 내의 여러 데이터 전처리 함수를 함께 이용할 수 있어서 알아두시면 좋겠습니다.

 

 

 


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

 

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

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. Hihello 2017.05.08 14:42  댓글주소  수정/삭제  댓글쓰기

    안녕하세요, 우연히 검색하다가 발견했는데 중복치를 남기지 않고 모두 제거하려면 어떻게 해야할까요?ㅠㅠㅠ

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

      기준 변수에 대해 중복이 있는 조합의 관측치에 대해서 포스팅에서는 first 혹은
      last 중에서 1개는 남겨두었었는데요, 이것 마저도 모두 제거하고 싶다는 말씀이신거지요?

      중복 여부를 판단한 변수 그룹을 가지고 group by 로 집계를 해서 => 개수가 2개 이상인 것은 제외(중복이 존재하므로) 하거나 혹은, 개수가 1개인 관측치만 선택하면 되겠네요.

      집계하는 방법 관련해서는 아래 포스팅 참고하세요.

      http://rfriend.tistory.com/240
      http://rfriend.tistory.com/79

  2. Hihello 2017.05.08 19:18  댓글주소  수정/삭제  댓글쓰기

    아 그니까 제가 지금ㅠㅠ과제를 하는데 이쪽을 복수전공하게 되어서 전혀 감이 안와서요ㅠㅠ 그...이 과제에 대해 여쭤보고 싶은데 따로 제가 쪽지 같은걸 보낼 방법은 없을까요?ㅠㅠㅠ 바쁘시다면 죄송해요ㅠㅠㅠ

  3. gongju5908 2018.03.14 14:44  댓글주소  수정/삭제  댓글쓰기

    도움이 되었습니다.

  4. 2019.02.22 11:55  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

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

      안녕하세요 꾸리꾸리님,

      아래 코드 참고하시기 바랍니다.
      dplyr 패키지의 함수를 사용하였습니다.

      library(dplyr)
      df$V_sum <- rowSums(df[,2:14])
      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)


      * dplyr 패키지 사용법은
      https://rfriend.tistory.com/234 ,
      https://rfriend.tistory.com/236
      를 참고하시기 바랍니다.

      데이터가 대용량이고 연산부하가 많은 분석이라면 Database를 사용하시는 것을 추천드립니다.