티스토리 뷰
plyr 패키지
plyr 패키지는 데이터 분할, 적용, 조합 세 단계로 데이터를 처리하는 함수를 제공해줍니다.
배열(a), 데이터 프레임(d), 리스트(l)에 적용 가능
> install.packages("plyr")
> library(plyr)
1. adply() 함수 : 그룹 연산
adply() 함수는 apply()함수와 동일하지만 apply()는 벡터로 리턴, adply()는 데이터 프레임으로 리턴이 가능합니다.
apply(데이터 셋, 적용방향, 적용(그룹)함수)
# 적용방향 = 1:같은 행별, 2:같은 열별, c(1,2): 원소별
plyr :: adply( .데이터, # 행렬, 배열, 데이터 프레임
.margins, # 적용방향 = 1: 같은 행렬, 2: 같은 열별, c(1,2): 원소별
.fun = NULL ) # 적용 함수
> library(googleVis)
> Fruits # googleVis 패키지와 함께 들어있는 데이터 셋
Fruit Year Location Sales Expenses Profit Date
1 Apples 2008 West 98 78 20 2008-12-31
2 Apples 2009 West 111 79 32 2009-12-31
...
8 Oranges 2010 East 98 91 7 2010-12-31
9 Bananas 2010 East 81 71 10 2010-12-31
> apply(Fruits[,4:6],2,sum) # apply() 함수는 벡터형태로 출력(그룹함수를 적용하기 좋음)
Sales Expenses Profit
845 710 135
> adply(Fruits[,4:6],2,sum) # adply() 함수는 데이터 프레임 형태로 출력
X1 V1
1 Sales 845
2 Expenses 710
3 Profit 135
> subway <- read.csv("subway2.csv", stringsAsFactors = F, skip = 1)
> str(subway)
> head(subway)
전체 구분 X05.06 X06.07 X07.08 X08.09 X09.10 X10.11 X11.12 X12.13 X13.14 X14.15 X15.16 ...
1 서울역(1) 승차 17465 18434 50313 93398 78705 86342 93585 97707 102608 101710 93849 ...
2 하차 7829 48553 110250 233852 121983 79628 75577 70984 80388 80263 79592 ...
3 시 청(1) 승차 2993 4473 7633 10404 13328 16953 25467 27265 36393 41128 48352 ...
4 하차 4142 19730 67995 175458 83777 48363 47519 42646 45465 42882 38720 ...
5 종 각 승차 7371 7836 14545 24578 23691 32290 47470 57187 76131 78721 87480 ...
6 하차 4861 29757 93579 245221 153933 88680 83402 78253 84694 80095 71320 ...
> toNum <- function(x) { # ,(콤마) 제거 사용자 지정 함수
+ as.numeric(str_replace_all(x, ',', ''))
+ }
> library(stringr)
> subway[,-c(1,2)] <- sapply(subway[,-c(1,2)],toNum) # sapply()에 함수를 적용
> apply(subway[,-c(1,2)],2,sum) # vector 형태로 출력
X05.06 X06.07 X07.08 ... X22.23 X23.24 X24.01
1338165 3953180 10184565 ... 8782129 4997008 1204504
> adply(subway[,-c(1,2)],2,sum) # data frame 형태로 출력
X1 V1
1 X05.06 1338165
2 X06.07 3953180
3 X07.08 10184565
...
18 X22.23 8782129
19 X23.24 4997008
20 X24.01 1204504
2. ddply() 함수 중요 @@@ : 그룹 연산 (다양한 옵션) => 그룹 연산에서 많이 사용
데이터 프레임을 input으로 데이터 프레임을 ouput 해주는 함수입니다.
분할, 적용, 조합을 수행하여 그룹별 연산을 사용하기 위한 용도이고, tapply(), agrregate() 함수와 유사합니다.
tapply(vector, index, function) # 벡터만 가능
aggregate(x,
by, # group by 컬럼, list로 전달, 여러 컬럼 전달 가능
FUN) # 함수
aggregate(formula, # 연산 대상 ~ group by 컬럼
data, # 위를 포함한 전체 데이터 셋
FUN)
ddply(data, # 전체 데이터 셋
.(), # group by 컬럼
ddply-func, # ddply 내부 함수 (transform, mutate, summarise, subset...)
fun) # 그룹함수(컬럼이름과 함께 전달)
ddply 내부 함수
# transform() : 원본 데이터 + ddply의 연산 결과를 컬럼 이름 지정과 함께 출력
# mutate() : transform과 유사하지만 순차적 연산처리를 할 경우 사용
# summarise() : 그룹 연산 결과를 그룹 수에 맞게 요약 정보 제공
# subset() : 그룹별 연산 결과를 사용하여 특정 조건을 만족하는 데이터만 출력
# transform() : 원본 데이터 + ddply의 연산 결과를 컬럼 이름 지정과 함께 출력
> Fruits
Fruit Year Location Sales Expenses Profit Date
1 Apples 2008 West 98 78 20 2008-12-31
2 Apples 2009 West 111 79 32 2009-12-31
...
8 Oranges 2010 East 98 91 7 2010-12-31
9 Bananas 2010 East 81 71 10 2010-12-31
> ddply(Fruits, .(Year), transform, average = mean(Sales)) # 년도별 Sales 평균
Fruit Year Location Sales Expenses Profit Date average
1 Apples 2008 West 98 78 20 2008-12-31 93.00000
2 Oranges 2008 East 96 81 15 2008-12-31 93.00000
...
8 Oranges 2010 East 98 91 7 2010-12-31 89.33333
9 Bananas 2010 East 81 71 10 2010-12-31 89.33333
> ddply(Fruits, .(Fruit), transform, average = mean(Sales)) # 과일 종류별 Sales 평균
Fruit Year Location Sales Expenses Profit Date average
1 Apples 2008 West 98 78 20 2008-12-31 99.33333
2 Apples 2009 West 111 79 32 2009-12-31 99.33333
...
8 Oranges 2009 East 93 80 13 2009-12-31 95.66667
9 Oranges 2010 East 98 91 7 2010-12-31 95.66667
# mutate() : transform과 유사하지만 순차적 연산처리를 할 경우 사용
> ddply(Fruits, .(Year), mutate, average = mean(Sales), avg_per = average/100)
# 인자의 결과(average)를 다음 인자(avg_per)에 순차적으로 사용
Fruit Year Location Sales Expenses Profit Date average avg_per
1 Apples 2008 West 98 78 20 2008-12-31 93.00000 0.9300000
2 Oranges 2008 East 96 81 15 2008-12-31 93.00000 0.9300000
...
8 Oranges 2010 East 98 91 7 2010-12-31 89.33333 0.8933333
9 Bananas 2010 East 81 71 10 2010-12-31 89.33333 0.8933333
# student 데이터를 사용하여 각 학년별 키와 몸무게의 평균 구하기
> ddply(std, .(GRADE), transform, avg_height = mean(HEIGHT), avg_weight = mean(WEIGHT))
STUDNO NAME ID GRADE JUMIN BIRTHDAY TEL HEIGHT WEIGHT DEPTNO1
1 9711 이윤나 prettygirl 1 7.808192e+12 1978/08/19 00:00:00 055)278-3649 162 48 101
2 9712 안은수 silverwt 1 7.801052e+12 1978/01/05 00:00:00 02)381-5440 175 63 201
...
19 9414 김재수 gunmandu 4 7.512251e+12 1975/12/25 00:00:00 02)6255-9875 177 83 201
20 9415 박동호 pincle1 4 7.503032e+12 1975/03/03 00:00:00 031)740-6388 182 70 202
DEPTNO2 PROFNO avg_height avg_weight # 그룹 연산 결과를 전체 데이터셋에 각 행마다 추가
1 NA NA 170.4 62.4
2 NA NA 170.4 62.4
...
19 NA 4001 175.8 68.2
20 NA 4003 175.8 68.2
> ddply(std, .(GRADE), mutate, avg_height = mean(HEIGHT), avg_height2 = avg_height / 100)
STUDNO NAME ID GRADE JUMIN BIRTHDAY TEL HEIGHT WEIGHT DEPTNO1
1 9711 이윤나 prettygirl 1 7.808192e+12 1978/08/19 00:00:00 055)278-3649 162 48 101
2 9712 안은수 silverwt 1 7.801052e+12 1978/01/05 00:00:00 02)381-5440 175 63 201
...
19 9414 김재수 gunmandu 4 7.512251e+12 1975/12/25 00:00:00 02)6255-9875 177 83 201
20 9415 박동호 pincle1 4 7.503032e+12 1975/03/03 00:00:00 031)740-6388 182 70 202
DEPTNO2 PROFNO avg_height avg_height2 # 순차적 연산처리
1 NA NA 170.4 1.704
2 NA NA 170.4 1.704
...
19 NA 4001 175.8 1.758
20 NA 4003 175.8 1.758
> ddply(std, .(GRADE), summarise, avg_height = mean(HEIGHT), avg_weight = mean(WEIGHT))
GRADE avg_height avg_weight # 그룹별 연산
1 1 170.4 62.4
2 2 175.6 67.4
3 3 166.6 51.4
4 4 175.8 68.2
Q.
# 1. 학년별 최대 키 (3가지 방법)
# tapply()함수 사용
> tapply(std$HEIGHT, std$GRADE, max) # 벡터로 리턴
1 2 3 4
179 184 177 182
# aggregate()함수 사용
> aggregate(HEIGHT ~ GRADE, std, max) # 데이터 프레임으로 리턴
GRADE HEIGHT
1 1 179
2 2 184
3 3 177
4 4 182
# ddply()함수 사용
> max_height <- ddply(std, .(GRADE), summarise, max_height = max(HEIGHT))
> max_height
GRADE max_height
1 1 179
2 2 184
3 3 177
4 4 182
##################################################################
# 2. 학년별 최대 키를 갖는 사람의 이름, 학년, 키
# sol 1) merger() 함수 사용
> merge(std, max_height, by.x = c('GRADE','HEIGHT'), # merge는 동등비교만 가능
+ by.y = c('GRADE', 'max_height'))[,c("NAME","GRADE","HEIGHT")]
NAME GRADE HEIGHT
1 김주현 1 179
2 노정호 2 184
3 오나라 3 177
4 박동호 4 182
# sol 2) ddply() 함수 사용
> ddply(std, .(GRADE), subset, HEIGHT == max(HEIGHT),)[,c("NAME","GRADE","HEIGHT")]
# 원본 데이터 컬럼 == 연산 결과 (조건이니까 '==' 로 써줘야함)
NAME GRADE HEIGHT
1 김주현 1 179
2 노정호 2 184
3 오나라 3 177
4 박동호 4 182
##################################################################
# 3. 학년별 평균키보다 큰 학생의 정보 출력
> ddply(std, .(GRADE), subset, HEIGHT > mean(HEIGHT),)[,c("NAME","GRADE","HEIGHT")]
NAME GRADE HEIGHT # ddply() 함수는 merge() 함수와 다르게 대소비교 가능
1 안은수 1 175
2 인영민 1 173
3 김주현 1 179
4 일지매 2 182
5 노정호 2 184
6 오나라 3 177
7 임세현 3 171
8 이진욱 4 180
9 김재수 4 177
10 박동호 4 182
Deep.
# 1. 2013년_프로야구선수_성적.csv 파일을 불러와서 다음을 수행
> ball <- read.csv("2013년_프로야구선수_성적.csv", stringsAsFactors = F)
> head(ball)
순위 선수명 포지션 팀 경기 타수 득점 안타 X2루타 X3루타 홈런 타점 도루 볼넷 삼진 병살 타율 장타율 출루율 연봉
1 1 김태균 1루수 한화 101 345 41 110 24 0 10 52 0 73 67 14 0.319 0.475 0.444 15.0
2 2 박병호 1루수 넥센 128 450 91 143 17 0 37 117 10 92 96 7 0.318 0.602 0.437 5.0
3 3 최정 3루수 SK 120 434 75 137 18 0 28 83 24 64 109 10 0.316 0.551 0.429 7.0
# 1) 경기수가 120 경기 이상인 선수만 출력하기
ball[ball$경기>=120,]
# 2) 경기수가 120 경기 이상이면서 득점도 80 점 이상인 선수만 출력하기
ball[ball$경기 >= 120 & ball$득점 >= 80,]
# 3) 팀별 출루율의 평균 출력
> tapply(ball$출루율, ball$팀, mean)
KIA LG SK 넥센 롯데 삼성 한화
0.3940 0.3990 0.4115 0.4370 0.4210 0.4135 0.4440
> aggregate(출루율 ~ 팀, ball, mean)
팀 출루율
1 KIA 0.3940
2 LG 0.3990
3 SK 0.4115
4 넥센 0.4370
5 롯데 0.4210
6 삼성 0.4135
7 한화 0.4440
> library(plyr)
> ddply(ball, .(팀), summarise, 평균_출루율 = mean(출루율)) # aggreate()함수와 동일 + 컬럼명 설정 가능
팀 평균_출루율
1 KIA 0.3940
2 LG 0.3990
3 SK 0.4115
4 넥센 0.4370
5 롯데 0.4210
6 삼성 0.4135
7 한화 0.4440
# 4) 각 선수의 선수명, 포지션, 팀과 해당 팀의 평균 출루율 데이터 조회하기
> ddply(ball, .(팀), transform, 평균_출루율 = mean(출루율))[,c("선수명","포지션","팀","평균_출루율")]
선수명 포지션 팀 평균_출루율
1 나지완 좌익수 KIA 0.3940
2 정성훈 3루수 LG 0.3990
3 박용택 중견수 LG 0.3990
4 최정 3루수 SK 0.4115
5 박정권 1루수 SK 0.4115
6 박병호 1루수 넥센 0.4370
7 손아섭 우익수 롯데 0.4210
8 박석민 3루수 삼성 0.4135
9 배영섭 중견수 삼성 0.4135
10 김태균 1루수 한화 0.4440
###########################################################
# 2. emp2.csv 파일을 불러와서 다음을 수행
> emp2<- read.csv("emp2.csv", stringsAsFactors = F)
> head(emp2)
EMPNO NAME BIRTHDAY DEPTNO EMP_TYPE TEL HOBBY PAY POSITION PEMPNO
1 19900101 나사장 1964-01-25 0:00 1 정규직 054)223-0001 음악감상 100000000 대표이사 NA
2 19960101 전부장 1973-03-22 0:00 1000 정규직 02)6255-8000 독서 72000000 부장 19900101
3 19970201 최일도 1975-04-15 0:00 1000 정규직 02)6255-8005 운동 50000000 과장 19960101
...
> str(emp2)
# PAY 컬럼에 '-'를 제외한 후 숫자 타입으로 변경
### 1.ifelse구문으로 전체 컬럼 수정
> emp2$PAY
[1] "100000000" "72000000" "50000000" ... "22000000" "20000000" "20000000" "-"
> ifelse(emp2$PAY == '-', NA, as.numeric(emp2$PAY))
# 값이 커서 지수 형식으로 출력, 치환 대상이 데이터의 일부라면 ifelse()를 사용한 치환은 성능상 비추
[1] 1.0e+08 7.2e+07 5.0e+07 6.0e+07 5.6e+07 7.5e+07 ... NA
### 2. 수정 대상만 선택해서 NA 치환 후 숫자 변경
> emp2[emp2$PAY=='-','PAY'] <- NA # 치환 대상이 일부라면, 색인을 통해 조건에 만족하는 데이터만 치환
> as.numeric(emp2$PAY)
[1] 1.0e+08 7.2e+07 5.0e+07 6.0e+07 5.6e+07 ... 2.2e+07 2.0e+07 2.0e+07 NA
### 3. PAY 컬럼의 '-' 값을 read.csv로 불러올 때 NA 처리
(데이터가 방대해질 수록 불필요한 작업은 성능에 마이너스이므로 데이터를 불러올 때 NA로 처리하는 것이 좋음)
> emp2 <- read.csv("emp2.csv", stringsAsFactors = F, na.strings = '-') # c('-',' ') 벡터로 na.strings에 여러 값 설정 가능
# 1) 현재 직급이 없는 직원은 사원으로 치환
> library(stringr)
> emp2$POSITION
[1] "대표이사" "부장" "과장" "차장" ... "-" "-" "-" "-" "-" "-"
> emp2[is.na(emp2$POSITION), 'POSITION'] <- '사원' # 색인을 통한 데이터 치환
# str_replace_all(emp2$POSITION,'-','사원') # 데이터를 na.strings 옵션 없이 불러올 경우, str_replace_all()함수를 사용한 방법도 ifelse()함수를 사용한 치환보다 성능이 좋긴 하지만. emp2$POSITION 벡터 전체를 가지고 함수가 동작하므로, 색인을 통한 데이터 치환보다는 좋지 않습니다.
# 또한, NA는 문자열이 아닌데, str_replace_all() 함수에서 'NA'로 작성 시 문법에 맞지 않으므로 문자열 치환에서 NA는 치환 불가
# 2) 직급별 평균연봉을 출력(tapply, aggregate, ddaply)
> emp2[is.na(emp2$PAY), 'PAY'] <- 0
> tapply(emp2$PAY, emp2$POSITION, mean)
과장 대리 대표이사 부장 사원 차장
51500000 35000000 100000000 71666667 22600000 60000000
# tapply(emp2$PAY, emp2$POSITION, mean, na.rm = T)
# 이 방법도 있지만, mean() 함수에서 na.rm = TRUE 설정 시, NA인 row는 카운트를 하지 않으므로 상황에 맞게 사용하면 됩니다.
> aggregate(PAY ~ POSITION, emp2, mean) # aggregate()함수는 자동으로 na를 0으로 생각하고 연산
POSITION PAY
1 과장 51500000
2 대리 35000000
3 대표이사 100000000
4 부장 71666667
5 사원 22600000
6 차장 60000000
> ddply(emp2, .(POSITION), summarise, mean_pay = mean(PAY))
# ddply(emp2, .(POSITION), summarise, mean_pay = mean(PAY, na.rm = T))
POSITION mean_pay
1 과장 51500000
2 대리 35000000
3 대표이사 100000000
4 부장 71666667
5 사원 22600000
6 차장 60000000
# 3) 직급별 최대연봉자의 사원번호,이름,직급,연봉출력
ddply(emp2, .(POSITION), subset, PAY == max(PAY))[,c("EMPNO","NAME","POSITION","PAY")]
###########################################################
# 3. emp.csv 파일을 불러와서 다음을 수행
> emp<- read.csv("emp.csv", stringsAsFactors = F)
> head(emp)
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
1 7369 SMITH CLERK 7902 1980-12-17 0:00 800 NA 20
2 7499 ALLEN SALESMAN 7698 1981-02-20 0:00 1600 300 30
3 7521 WARD SALESMAN 7698 1982-02-22 0:00 1250 500 30
...
# 1) 부서별 각 사원의 이름을 다음과 같은 형태로 출력
# DEPTNO ENAME
# 1 10 CLARK, KING, MILLER
# 2 20 SMITH, JONES, SCOTT, ADAMS, FORD
# 3 30 ALLEN, WARD, MARTIN, BLAKE, TURNER, JAMES
# 그룹 연산의 수행과정
# tapply(emp$ENAME, emp$DEPTNO, function)
# 1. 분리 : 그룹별 데이터의 분리(연산 컬럼만) -> 연산 대상 = ENAME
# 2. 적용 : 분리된 연산 대상을 '각각' 함수에 적용
# 3. 결합 : 위의 연산 결과를 다시 결합하여 화면에 출력
> library(stringr)
> str_c(emp[emp$DEPTNO == 10, 'ENAME'], collapse = ',') # 다음과 같이 출력 가능
[1] "CLARK,KING,MILLER"
# 결합 함수의 주의점 !!!
> v1 <- c('a','b','c')
> str_c('a','b','c') # 함수 내에 분리된 인자들을 연결
[1] "abc"
> str_c(v1) # 여러개의 값이 들어있는 하나의 인자를 연결하기 때문에 옵션이 필요
[1] "a" "b" "c"
> str_c('a','b','c', sep = ',') # 분리된 인자들을 연결
[1] "a,b,c"
> str_c(v1, collapse = ',')
[1] "a,b,c"
> paste('a','b','c', sep = ',') # 문자 결합
[1] "a,b,c"
> paste(v1, collapse = ',') # 벡터 내 문자 결합
[1] "a,b,c"
> library(stringr)
> tapply(emp$ENAME, emp$DEPTNO, str_c, collapse = ',')
10 20 30
"CLARK,KING,MILLER" "SMITH,JONES,SCOTT,ADAMS,FORD" "ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES"
> aggregate(ENAME ~ DEPTNO, emp, str_c, collapse = ', ') # collapse 옵션을 사용하지 않아도 ,(콤마)로 연결되어 출력 가능
DEPTNO ENAME
1 10 CLARK, KING, MILLER
2 20 SMITH, JONES, SCOTT, ADAMS, FORD
3 30 ALLEN, WARD, MARTIN, BLAKE, TURNER, JAMES
> library(plyr)
> ddply(emp, .(DEPTNO), summarise, ENAME=str_c(ENAME, collapse = ', '))
DEPTNO ENAME
1 10 CLARK, KING, MILLER
2 20 SMITH, JONES, SCOTT, ADAMS, FORD
3 30 ALLEN, WARD, MARTIN, BLAKE, TURNER, JAMES
'R > Process' 카테고리의 다른 글
[R] 교차 테이블로 데이터 구조 변경하기 - melt, dcast 함수 (2) | 2019.01.08 |
---|---|
[R] R에서 SQL문법으로 데이터 조작하기 - sqldf 패키지 (0) | 2019.01.08 |
[R] 그룹 연산 - tapply, aggregate 함수 (0) | 2019.01.08 |
[R] 데이터에서 최소(min), 최대(max)를 갖는 색인 출력 - which.min(), which.max() (0) | 2019.01.07 |
[R] 데이터 병합(merge) - Join, Outer Join, Self Join (0) | 2019.01.07 |