3. 데이터 전처리 3

3.1 Tidy data

3.1.1 Tidy data의 필요성 - stack(), unstack()

  • 각 행에 분류 처리에 필요한 기준이 있어야 프로그래밍에서 처리가 가능
  • 실제 데이터에는 그렇지 않은 경우가 많음 -> 기준 컬럼갖도록 변환 (tidy data)
  1. 기준 컬럼 없는 데이터
gangwon=c(200,500,400)
gyeongnam=c(400,300,500)
chungbuk=c(600,300,400)
sales=data.frame(gangwon,gyeongnam,chungbuk)
sales
##   gangwon gyeongnam chungbuk
## 1     200       400      600
## 2     500       300      300
## 3     400       500      400
  1. stack() 함수 이용하여 Tidy data로 변환
sales=stack(sales)
sales
##   values       ind
## 1    200   gangwon
## 2    500   gangwon
## 3    400   gangwon
## 4    400 gyeongnam
## 5    300 gyeongnam
## 6    500 gyeongnam
## 7    600  chungbuk
## 8    300  chungbuk
## 9    400  chungbuk
summaryBy(values~ind,sales)
##         ind values.mean
## 1   gangwon    366.6667
## 2 gyeongnam    400.0000
## 3  chungbuk    433.3333
  1. unstack() 이용하여 원래대로 돌리기
sales2=unstack(sales)
sales2
##   gangwon gyeongnam chungbuk
## 1     200       400      600
## 2     500       300      300
## 3     400       500      400

3.1.2 Tidy data의 필요성 - reshape2::melt(), dcast(), acast()

  1. reshape2::melt()
sales3=melt(sales2)
## No id variables; using all as measure variables
sales3
##    variable value
## 1   gangwon   200
## 2   gangwon   500
## 3   gangwon   400
## 4 gyeongnam   400
## 5 gyeongnam   300
## 6 gyeongnam   500
## 7  chungbuk   600
## 8  chungbuk   300
## 9  chungbuk   400
summaryBy(value~variable,sales3)
##    variable value.mean
## 1   gangwon   366.6667
## 2 gyeongnam   400.0000
## 3  chungbuk   433.3333
  1. dcast(), acast() : melt() 원래대로 돌리기
  • dcast는 데이터 프레임으로 반환
sales4=dcast(sales3,value~variable)
sales4
##   value gangwon gyeongnam chungbuk
## 1   200     200        NA       NA
## 2   300      NA       300      300
## 3   400     400       400      400
## 4   500     500       500       NA
## 5   600      NA        NA      600
class(sales4)
## [1] "data.frame"
  • acast는 행렬로 반환
sales5=acast(sales3,value~variable)
sales5
##     gangwon gyeongnam chungbuk
## 200     200        NA       NA
## 300      NA       300      300
## 400     400       400      400
## 500     500       500       NA
## 600      NA        NA      600
class(sales5)
## [1] "matrix"
  1. melt()는 기준열 생성이 가능.
  • 1:4 까지 선택하면 나머지 변수들이 원래 데이터 아래쪽에 붙게됨.
  • 5:9 까지의 컬럼들의 컬럼 제목과 값들이 아래쪽에 붙음.
head(melt(french_fries,id.vars=1:4)) # 1:4 컬럼을 선택 
##   time treatment subject rep variable value
## 1    1         1       3   1   potato   2.9
## 2    1         1       3   2   potato  14.0
## 3    1         1      10   1   potato  11.0
## 4    1         1      10   2   potato   9.9
## 5    1         1      15   1   potato   1.2
## 6    1         1      15   2   potato   8.8
  1. melt(data,na.rm=T)
head(melt(french_fries,id.vars=1:4,na.rm = T))
##   time treatment subject rep variable value
## 1    1         1       3   1   potato   2.9
## 2    1         1       3   2   potato  14.0
## 3    1         1      10   1   potato  11.0
## 4    1         1      10   2   potato   9.9
## 5    1         1      15   1   potato   1.2
## 6    1         1      15   2   potato   8.8

3.1.3 연습

apple=c(6,10,13)
banana=c(2,8,10)
peach=c(7,3,5)
berry=c(8,15,11)
year=c(2000,2001,2002)
fruit=data.frame(apple,banana,peach,berry,year)
fruit
##   apple banana peach berry year
## 1     6      2     7     8 2000
## 2    10      8     3    15 2001
## 3    13     10     5    11 2002
  1. stack(), summaryBy()이용하여 각 과일 평균 판매량 구하기
sfruit=stack(fruit,select = 1:4)
sfruit
##    values    ind
## 1       6  apple
## 2      10  apple
## 3      13  apple
## 4       2 banana
## 5       8 banana
## 6      10 banana
## 7       7  peach
## 8       3  peach
## 9       5  peach
## 10      8  berry
## 11     15  berry
## 12     11  berry
summaryBy(values~ind,sfruit)
##      ind values.mean
## 1  apple    9.666667
## 2 banana    6.666667
## 3  peach    5.000000
## 4  berry   11.333333
  1. melt()활용하여 과일 데이터 과일 별 평균 판매량 구하기
mfruit=melt(fruit,id.vars = 5)
mfruit
##    year variable value
## 1  2000    apple     6
## 2  2001    apple    10
## 3  2002    apple    13
## 4  2000   banana     2
## 5  2001   banana     8
## 6  2002   banana    10
## 7  2000    peach     7
## 8  2001    peach     3
## 9  2002    peach     5
## 10 2000    berry     8
## 11 2001    berry    15
## 12 2002    berry    11
summaryBy(value~variable,mfruit)
##   variable value.mean
## 1    apple   9.666667
## 2   banana   6.666667
## 3    peach   5.000000
## 4    berry  11.333333

3.2 data.table::data.table()

3.2.1 특징

  • 속도가 매우 빠르므로, 대용량의 데이터를 빠르게 처리 할 필요가 있을 때 고려.
  • 색인을 이용하므로 조건에 맞는 데이터를 더 빨리 찾는다.
  • 참조 방식을 이용하므로 데이터 복사시 자원 소비가 줄어든다.
  • 조건 연산을 더 편리하고 빠르게 수행한다.

3.2.2 데이터 테이블 생성 1 - data.table()

  1. 직접 입력
name=c("janmes","mary","kevin")
age=c(22,23,24)
dt=data.table(name,age)
dt
##      name age
## 1: janmes  22
## 2:   mary  23
## 3:  kevin  24
class(dt)
## [1] "data.table" "data.frame"
  1. read 함수로 읽은 뒤 as.data.table로 변환
iris.dt=as.data.table(iris)
head(iris.dt)
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          5.1         3.5          1.4         0.2  setosa
## 2:          4.9         3.0          1.4         0.2  setosa
## 3:          4.7         3.2          1.3         0.2  setosa
## 4:          4.6         3.1          1.5         0.2  setosa
## 5:          5.0         3.6          1.4         0.2  setosa
## 6:          5.4         3.9          1.7         0.4  setosa
  1. fread() / fwrite()
  • fread() 함수를 이용하면 대용량 자료에서 read.csv() 대비 약 20배 이상의 속도를 가진다.
  • fwrite() 역시 빠르다.
DT=fread("data/Titanic/train.csv")
class(DT)
## [1] "data.table" "data.frame"

3.2.3 차이점

  • 자료 유형은 데이터 테이블이지만, 데이터 프레임과 동일하게 취급된다.
  • 또한 문자열을 팩터로 자동 편환하지 않는다.
  • 기본 값으로 첫 5개와 마지막 5개의 행을 출력한다.
str(dt)
## Classes 'data.table' and 'data.frame':   3 obs. of  2 variables:
##  $ name: chr  "janmes" "mary" "kevin"
##  $ age : num  22 23 24
##  - attr(*, ".internal.selfref")=<externalptr>

3.2.4 접근 방법

  • 접근 방법은 데이터 프레임과 동일
dt[1,] # 행 접근
##      name age
## 1: janmes  22
dt[,1] # 열 접근 
##      name
## 1: janmes
## 2:   mary
## 3:  kevin

3.2.5 단일 컬럼 접근 방법

  • 컬럼명에 따옴표가 없으면 벡터로 결과 산출
  • 컬럼명에 따옴표가 있으면 데이터테이블로 결과 산출
head(iris.dt[,Sepal.Length])
## [1] 5.1 4.9 4.7 4.6 5.0 5.4
head(iris.dt[,"Sepal.Length"])
##    Sepal.Length
## 1:          5.1
## 2:          4.9
## 3:          4.7
## 4:          4.6
## 5:          5.0
## 6:          5.4

3.2.6 복수 컬럼 접근 방법

  • 컬럼명에 따옴표가 없으면 벡터로 결과 산출
  • 컬럼명에 따옴표가 있으면 데이터테이블로 결과 산출
a=head(iris.dt[,c(Sepal.Length,Petal.Width)])
a
## [1] 5.1 4.9 4.7 4.6 5.0 5.4
class(a)
## [1] "numeric"
b=head(iris.dt[,c("Sepal.Length","Petal.Width")])
b
##    Sepal.Length Petal.Width
## 1:          5.1         0.2
## 2:          4.9         0.2
## 3:          4.7         0.2
## 4:          4.6         0.2
## 5:          5.0         0.2
## 6:          5.4         0.4
class(b)
## [1] "data.table" "data.frame"

3.2.7 컬럼 접근시 주의점

  • 컬럼명에 따옴표가 없으면 벡터로 산출 되므로, 여러 컬럼 산출 시에는 서로 다른 종류가 있으면 안된다.
ex=iris.dt[1,c(Sepal.Length,Sepal.Width,Species)]
class(iris.dt$Species) # factor
## [1] "factor"
class(ex) # numeric으로 변환
## [1] "numeric"

3.2.8 조건식으로 검색

  • 조건식으로 데이터 검색 시에 데이터 부분을 밝히지 않아도 된다.
head(iris.dt[iris.dt$Petal.Length>1.5,])
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          5.4         3.9          1.7         0.4  setosa
## 2:          4.8         3.4          1.6         0.2  setosa
## 3:          5.7         3.8          1.7         0.3  setosa
## 4:          5.4         3.4          1.7         0.2  setosa
## 5:          5.1         3.3          1.7         0.5  setosa
## 6:          4.8         3.4          1.9         0.2  setosa
head(iris.dt[Petal.Length>1.5,]) # 데이터 부분 밝히지 않음 
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1:          5.4         3.9          1.7         0.4  setosa
## 2:          4.8         3.4          1.6         0.2  setosa
## 3:          5.7         3.8          1.7         0.3  setosa
## 4:          5.4         3.4          1.7         0.2  setosa
## 5:          5.1         3.3          1.7         0.5  setosa
## 6:          4.8         3.4          1.9         0.2  setosa

3.2.9 행열 검색

  • 데이터 테이블을 사용하면 보다 빠른 계산이 가능하다.
iris.dt=data.table(iris)
aggregate(Sepal.Width ~ Species,data=iris.dt,FUN = mean)
##      Species Sepal.Width
## 1     setosa       3.428
## 2 versicolor       2.770
## 3  virginica       2.974
tapply(iris.dt$Sepal.Length,iris.dt$Species,mean)
##     setosa versicolor  virginica 
##      5.006      5.936      6.588
iris.dt[,mean(Sepal.Length),by=Species]
##       Species    V1
## 1:     setosa 5.006
## 2: versicolor 5.936
## 3:  virginica 6.588
iris.dt[,list(my.length=mean(Sepal.Length)),by=Species]
##       Species my.length
## 1:     setosa     5.006
## 2: versicolor     5.936
## 3:  virginica     6.588

3.2.10 연습문제

  1. PIMA 불러와서 데이터 테이블로 변환
pima=read.csv("data/Pima_Data/diabetes.csv")
pimadt=data.table(pima)
  1. 100번째 환자 출력
pimadt[100]
##    Pregnancies Glucose BloodPressure SkinThickness Insulin  BMI
## 1:           1     122            90            51     220 49.7
##    DiabetesPedigreeFunction Age Outcome
## 1:                    0.325  31       1
  1. 당뇨 환자와 아닌 환자 인슐린 수치 평균 구하기
pimadt[,list(insulin.mean=mean(Insulin)),by=Outcome]
##    Outcome insulin.mean
## 1:       1     100.3358
## 2:       0      68.7920

3.3 dplyr 패키지

3.3.1 dplyr 특징

  • 직관적인 작업 수행 가능

3.3.2 dplyr::%>%

  • 앞의 함수가 반환한 객체를 다음 함수의 첫 번째 인자로 보낸다.
  • 이런 연산들을 체인으로 연결시킬 수 있다.
iris%>%head()
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
iris%>%head()%>%dim()
## [1] 6 5
dim(head(iris))
## [1] 6 5
  • 괄호를 중첩시키거나 임시변수를 만드는 방식을 피하게 해준다.
x1=c(1,2,3,4,5)
x2=as.data.frame(x1)
class(x2)
## [1] "data.frame"
x=c(1,2,3,4,5)
class(as.data.frame(x))
## [1] "data.frame"
x=c(1,2,3,4,5)
x%>%as.data.frame()%>%class()
## [1] "data.frame"

3.3.3 dplyr::select()

library(dplyr)
## Warning: package 'dplyr' was built under R version 3.5.1
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:data.table':
## 
##     between, first, last
## The following object is masked from 'package:reshape':
## 
##     rename
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
head(iris[,c("Sepal.Length","Species")])
##   Sepal.Length Species
## 1          5.1  setosa
## 2          4.9  setosa
## 3          4.7  setosa
## 4          4.6  setosa
## 5          5.0  setosa
## 6          5.4  setosa
head(select(iris,Sepal.Length,Species))
##   Sepal.Length Species
## 1          5.1  setosa
## 2          4.9  setosa
## 3          4.7  setosa
## 4          4.6  setosa
## 5          5.0  setosa
## 6          5.4  setosa
iris%>%select(Sepal.Length,Species)%>%head()
##   Sepal.Length Species
## 1          5.1  setosa
## 2          4.9  setosa
## 3          4.7  setosa
## 4          4.6  setosa
## 5          5.0  setosa
## 6          5.4  setosa

3.3.4 dplyr::filter()

iris%>%filter(Species=="setosa")%>%head()
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
iris%>%filter(Sepal.Length<5.0)%>%head()
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          4.9         3.0          1.4         0.2  setosa
## 2          4.7         3.2          1.3         0.2  setosa
## 3          4.6         3.1          1.5         0.2  setosa
## 4          4.6         3.4          1.4         0.3  setosa
## 5          4.4         2.9          1.4         0.2  setosa
## 6          4.9         3.1          1.5         0.1  setosa
iris%>%filter(Species=="setosa"&Sepal.Length<5.0)%>%head()
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          4.9         3.0          1.4         0.2  setosa
## 2          4.7         3.2          1.3         0.2  setosa
## 3          4.6         3.1          1.5         0.2  setosa
## 4          4.6         3.4          1.4         0.3  setosa
## 5          4.4         2.9          1.4         0.2  setosa
## 6          4.9         3.1          1.5         0.1  setosa

3.3.5 dplyr::summarize()

summarize(iris,mean(Sepal.Length))
##   mean(Sepal.Length)
## 1           5.843333
summarize(iris,Sepal.Avg=mean(Sepal.Length))
##   Sepal.Avg
## 1  5.843333
iris%>%summarize(Sepal.Avg=mean(Sepal.Length))
##   Sepal.Avg
## 1  5.843333

3.3.6 dplyr::group_by()

iris%>%group_by(Species)%>%summarize(Sepal.Avg=mean(Sepal.Length))
## # A tibble: 3 x 2
##   Species    Sepal.Avg
##   <fct>          <dbl>
## 1 setosa          5.01
## 2 versicolor      5.94
## 3 virginica       6.59

3.3.7 연습 문제1

  1. group_by를 이용해 평균을 구한 결과를 tapply를 이용해 계산하시오.
tapply(pima$BMI,pima$Outcome,mean)
##        0        1 
## 30.30420 35.14254
dt=data.table(pima)
dt[,mean(BMI),by="Outcome"]
##    Outcome       V1
## 1:       1 35.14254
## 2:       0 30.30420

3.3.8 dplyr::arrange()

iris%>%arrange(Sepal.Length)%>%head()
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          4.3         3.0          1.1         0.1  setosa
## 2          4.4         2.9          1.4         0.2  setosa
## 3          4.4         3.0          1.3         0.2  setosa
## 4          4.4         3.2          1.3         0.2  setosa
## 5          4.5         2.3          1.3         0.3  setosa
## 6          4.6         3.1          1.5         0.2  setosa

3.3.9 연습 문제2

  1. filter 함수를 사용하여 당뇨병 환자의 bmi평균을 산출 하시오
pima%>%filter(Outcome==1)%>%summarize(mean(BMI))
##   mean(BMI)
## 1  35.14254
  1. group_by 함수를 사용하여 당뇨병 여부에 따라 blood pressure의 평균을 산출하시오
pima%>%group_by(Outcome)%>%summarize(mean(BloodPressure))
## # A tibble: 2 x 2
##   Outcome `mean(BloodPressure)`
##     <int>                 <dbl>
## 1       0                  68.2
## 2       1                  70.8
  1. 가장 높은 BMI를 갖는 환자는 몇 번째 환자인가? order를 사용하여 구현 / 환자의 BMI를 쓰시오.
pima[order(pima$BMI,decreasing=TRUE),]%>%head()
##     Pregnancies Glucose BloodPressure SkinThickness Insulin  BMI
## 178           0     129           110            46     130 67.1
## 446           0     180            78            63      14 59.4
## 674           3     123           100            35     240 57.3
## 126           1      88            30            42      99 55.0
## 121           0     162            76            56     100 53.2
## 304           5     115            98             0       0 52.9
##     DiabetesPedigreeFunction Age Outcome
## 178                    0.319  26       1
## 446                    2.420  25       1
## 674                    0.880  22       0
## 126                    0.496  26       1
## 121                    0.759  25       1
## 304                    0.209  28       1