3.1 Tidy data
3.1.1 Tidy data의 필요성 - stack(), unstack()
- 각 행에 분류 처리에 필요한 기준이 있어야 프로그래밍에서 처리가 가능
- 실제 데이터에는 그렇지 않은 경우가 많음 -> 기준 컬럼갖도록 변환 (tidy data)
- 기준 컬럼 없는 데이터
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
- stack() 함수 이용하여 Tidy data로 변환
## 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
- 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()
- reshape2::melt()
## No id variables; using all as measure variables
## 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
- dcast(), acast() : melt() 원래대로 돌리기
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
## [1] "data.frame"
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
## [1] "matrix"
- 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
- 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
- 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
- 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()
- 직접 입력
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
## [1] "data.table" "data.frame"
- 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
- 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개의 행을 출력한다.
## 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 접근 방법
## name age
## 1: janmes 22
## 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
## [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
## [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"
## [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 연습문제
- PIMA 불러와서 데이터 테이블로 변환
pima=read.csv("data/Pima_Data/diabetes.csv")
pimadt=data.table(pima)
- 100번째 환자 출력
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## 1: 1 122 90 51 220 49.7
## DiabetesPedigreeFunction Age Outcome
## 1: 0.325 31 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.2 dplyr::%>%
- 앞의 함수가 반환한 객체를 다음 함수의 첫 번째 인자로 보낸다.
- 이런 연산들을 체인으로 연결시킬 수 있다.
## 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] 6 5
## [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()
## 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
- 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
- filter 함수를 사용하여 당뇨병 환자의 bmi평균을 산출 하시오
pima%>%filter(Outcome==1)%>%summarize(mean(BMI))
## mean(BMI)
## 1 35.14254
- 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
- 가장 높은 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