티스토리 뷰
<요약>
그룹별 적용
- apply() : 2차원 데이터를 행, 열 방향으로 연산 # 적용방향 = 1:같은 행별, 2:같은 열별, c(1,2): 원소별
원소별 적용
- sapply() : 벡터에 함수를 반복 적용(벡터로 출력) # 데이터 색인 시 벡터가 편리하므로, 주로 sapply()를 사용
- lapply() : 벡터에 함수를 반복 적용(리스트로 출력)
- mapply() : 벡터에 함수를 반복 적용(리스트로 출력) # sapply()와 유사, 다수의 인자를 받는 함수를 적용하기 위해 사용
그룹별 연산
- tapply() : 그룹별 연산
* 작성 방법
apply(iris[,-5], 2, mean)
sapply(iris[,-5], mean)
lapply(iris[,-5], mean)
mapply(mean, iris[,-5])
tapply(iris$Petal.Length, iris$Species, mean)
Apply 계열 함수
apply 계열 함수란 데이터에 임의의 함수를 적용하여 결과를 얻기 위한 함수입니다.
적용 함수라고 불리고, 벡터 연산을 위해 보통 for문을 사용하는데 보다 간단하고 빠르게 벡터 연산을 수행하기 위해 사용합니다.
1. 그룹별 적용
apply() : 2차원 데이터를 행, 열 방향으로 연산
apply(데이터 셋, 적용방향, 적용(그룹)함수)
# 적용방향 = 1:같은 행별, 2:같은 열별, c(1,2): 원소별
apply() 함수는 2차원데이터(배열, 행렬, 데이터 프레임)에 적용 가능합니다.
apply() 함수는 함수가 적용되는 과정에서 데이터를 벡터 형식으로 묶어서 함수에 전달합니다. 그러므로, 벡터를 받을 수 있는 함수에만 적용 가능하겠죠!
또, apply() 함수는 그룹 연산을 위한 목적의 함수이므로 그룹 함수를 적용하는것을 권합니다.
참고: '행별'의 의미는 R에서는 서로 같은 행끼리를 의미하고, Python에서는 서로 다른 행 끼리를 의미합니다.
> m1 <- matrix(1:9, ncol = 3)
> m1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
> apply(m1,1,sum) # 행별(서로 같은 행끼리) sum 연산
[1] 12 15 18
> apply(m1,2,sum) # 열별(서로 같은 열끼리) sum 연산
[1] 6 15 24
> apply(m1,c(1,2),sum) # 원소별 sum 연산 (apply()함수에서 1:1 함수의 적용은 권하지 않음)
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
> head(iris)
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
...
> apply(iris[,-5], 2, mean) # 컬럼별 평균 계산
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
#Q. 두 번째 글자만 추출하는 사용자 정의함수 생성 및 적용
> m2 <- matrix(c('a1', 'a2', 'a3','a4'), nrow = 2)
> m2
[,1] [,2]
[1,] "a1" "a3"
[2,] "a2" "a4"
# 1) 사용자 정의 함수 생성
> library(stringr) # str_sub() 함수 사용을 위함
> f2 <- function(x){
+ return(str_sub(x,2,2))
+ }
# 2) 함수 적용
> f2(m2) # 사용자 정의 함수를 바로 호출한 경우
[1] "1" "2" "3" "4"
> apply(m2,1,f2) # apply()로 함수를 적용한 경우
[,1] [,2]
[1,] "1" "2"
[2,] "3" "4"
#Q. 연습문제
> disease <- read.csv("전염병발병현황.csv", stringsAsFactors = F)
> str(disease)
'data.frame': 12 obs. of 6 variables:
$ 월별 : chr "1월" "2월" "3월" "4월" ...
$ 콜레라 : int 6 5 1 1 1 3 14 15 3 1 ...
$ 장티푸스: int 175 165 200 200 194 227 179 163 145 107 ...
$ 이질 : int 550 253 263 507 343 272 224 347 105 142 ...
$ 대장균 : int 7 9 13 13 27 92 201 114 43 39 ...
$ A형간염 : int 351 535 1003 856 959 928 630 505 364 230 ...
> disease
월별 콜레라 장티푸스 이질 대장균 A형간염
1 1월 6 175 550 7 351
2 2월 5 165 253 9 535
...
11 11월 3 97 377 27 190
12 12월 4 121 679 21 167
# 1) 월별 발병수의 총합
> apply(disease[,-1],1,sum)
[1] 1089 967 1480 1577 1524 1522 1248 1144 660 519 694 992
# 2) 전염병별 발병수의 총합
> apply(disease[,-1],2,sum)
콜레라 장티푸스 이질 대장균 A형간염
57 1973 4062 606 6718
행, 열의 합 또는 평균 계산 함수
apply() 함수에서 사용할 수 없는 NA처리, na.rm 기능을 적용하기 위해 사용합니다.
> rowSums(m1, na.rm = T) # 행별(서로 같은 행끼리) 합
> rowMeans(m1, na.rm = T) # 행별(서로 같은 행끼리) 평균
> colSums(m1, na.rm = T) # 열별(서로 같은 열끼리) 합
> colMeans(m1, na.rm = T) # 열별(서로 같은 열끼리) 평균
2. 원소별 적용
sapply() : 벡터에 함수를 반복 적용(벡터로 출력)
lapply() : 벡터에 함수를 반복 적용(리스트로 출력)
mapply() : 벡터에 함수를 반복 적용(리스트로 출력)
sapply(벡터, 함수, 함수의 인자) # 데이터 색인 시 벡터가 편리하므로, 주로 sapply()를 사용
lapply(벡터, 함수, 함수의 인자)
mapply(함수, 함수의 인자) # sapply()함수와 유사하지만, 다수의 인자를 받는 함수를 적용하기 위해 사용
# emp에서 SAL이 3000 이상이면 SAL의 10%를, 미만이면 8%를 출력
> bonus <- function(s) {
+ if(s >= 3000){
+ return(s * 1.1)
+ } else {
+ return(s * 1.08)
+ }
+ }
# bonus(emp$SAL) 함수 수행 시 if문은 여러 값(벡터)을 연산할 수 없으므로, 단 하나의 리턴값만 연산이 수행됩니다.
그렇기 때문에 for문이 필요하겠죠!
# 혹여나 결과가 제대로 나오는 것 처럼 보이더라도 자세히 확인해보면 첫 번째 값의 진리값 기준으로 계산이 수행됩니다.
# 이럴 때! apply 계열 함수 사용 시 for문을 사용하지 않더라도 간단하고, 성능적으로도 빠르게 벡터 연산을 가능하게 해줍니다.
> sapply(emp$SAL, bonus) # 벡터로 출력
[1] 864 1728 1350 3213 1350 3078 2646 3300 5500 1620 1188 1026 3300 1404
> lapply(emp$SAL, bonus) # 리스트로 출력
[[1]]
[1] 864
[[2]]
[1] 1728
...
[[13]]
[1] 3300
[[14]]
[1] 1404
> unlist(lapply(emp$SAL, bonus)) # lapply()함수의 결과 리스트를 다시 벡터로 변환하기 위한 방법 (unlist 함수)
[1] 864 1728 1350 3213 1350 3078 2646 3300 5500 1620 1188 1026 3300 1404
> mapply(mean, iris[,-5])
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
#. apply(), sapply(), lapply() 비교
> m1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
> f1 <- function(x) {
+ return(x+3)
+ }
> apply(m1, 2, f1) # 데이터 m1에 그룹별(행,열) 함수의 적용
[,1] [,2] [,3]
[1,] 4 7 10
[2,] 5 8 11
[3,] 6 9 12
> sapply(m1, f1) # 데이터 m1에 각 원소별 함수의 적용(벡터로 출력) - matrix로 형태 변환이 필요
[1] 4 5 6 7 8 9 10 11 12
> lapply(m1, f1) # 데이터 m1에 각 원소별 함수의 적용(리스트로 출력)
[[1]]
[1] 4
[[2]]
[1] 5
...
[[8]]
[1] 11
[[9]]
[1] 12
#. apply 계열 함수들의 함수의 추가인자 전달 방식
> m1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 5 8
[3,] 3 6 9
> m1[2,2] <- NA
> m1
[,1] [,2] [,3]
[1,] 1 4 7
[2,] 2 NA 8
[3,] 3 6 9
> apply(m1, 1, sum) # NA가 포함된 행의 결과는 NA로 리턴
[1] 12 NA 18
> rowSums(m1, na.rm = T)
[1] 12 10 18
> help(apply) # apply(X, MARGIN, FUN, ...) : ... 에는 함수의 인자를 작성할 수 있습니다.
> apply(m1,1,sum,na.rm = T) # sum 이라는 함수가 na.rm 옵션을 가지고 있으므로 apply() 함수에서 사용 가능
[1] 12 10 18
# 어떤 함수더라도 apply 함수를 만나면 해당 함수의 옵션을 사용할 수 있습니다.
#Q. 입력한 문자열에서 입력한 개수만큼 처음부터 추출
# f("aabcx",3) = aab
> f <- function(x,n){
+ return(substr(x,1,n))
+ }
> f("aabcx",3)
[1] "aab"
# 이름의 앞 3글자만 추출
> f(emp$ENAME,3) # 참고. 파이썬에서는 함수 안에 벡터를 작성해도 벡터연산이 불가함
[1] "SMI" "ALL" "WAR" "JON" "MAR" "BLA" "CLA" "SCO" "KIN" "TUR" "ADA" "JAM" "FOR" "MIL"
> apply(emp$ENAME, c(1,2), f, 3) # apply()함수에는 벡터 데이터가 올 수 없습니다.
Error in apply(emp$ENAME, c(1, 2), f, 3) :
dim(X)는 반드시 양의 값을 가지는 길이를 가져야 합니다
> sapply(emp$ENAME, f, 3) # sapply()함수는 벡터 데이터 연산 가능,
함수에 인자 전달이 필요하다면 함수 뒤에 기입해주어야 합니다.
SMITH ALLEN WARD JONES MARTIN BLAKE CLARK SCOTT KING TURNER ADAMS JAMES FORD MILLER
"SMI" "ALL" "WAR" "JON" "MAR" "BLA" "CLA" "SCO" "KIN" "TUR" "ADA" "JAM" "FOR" "MIL"
3. 그룹별 연산
tapply() : 그룹별 연산
tapply(vector, index, function, ...)
tapply(연산대상(벡터), 그룹 지표, 함수, 함수인자)
# 앞에는 계산하고자하는 대상을, 뒤에는 그룹핑할 수 있는 그룹의 지표(논리값 or 숫자 or 문자열)
> tapply(c(1,2,3,4),c(1,1,2,2),sum)
1 2
3 7
> tapply(emp$SAL, emp$DEPTNO, mean, na.rm = T) # 같은 부서(10,20,30) 내의 급여 평균
10 20 30
2916.667 2175.000 1566.667
> tapply(iris$Petal.Length, iris$Species, mean) # iris의 같은 종류 별 Petal Length의 평균
setosa versicolor virginica
1.462 4.260 5.552
*. rnorm : 정규분포를 따르는 분포 중, 임의의 수를 출력하고자 할 때 사용
rnorm(개수, 평균, 표준편차)
평균 : 어떤 값들의 집합의 적절한 특징을 나타내거나 요약하는 것을 의미
정규분포 : 수집된 자료의 분포를 근사하는 데에 자주 사용되며, 이것은 중심극한정리에 의하여 독립적인 확률변수들의
평균은 정규분포에 가까워지는 성질이 있기 때문(연속 확률 분포의 하나)
표준편차 : 자료의 산포도(데이터가 얼마나 퍼져있나를 나타내는 용어)를 나타내는 수치로, 분산의 양의 제곱근으로 정의
(출처: 위키백과, 네이버 지식백과)
# 평균이 0이고 표준편차가 1인 난수 10개 생성
> rnorm(10,0,1)
[1] 0.52293395 0.16371174 -1.36251762 -0.47018805 0.70748778 0.06825669 1.37206336 -0.98367573 1.83710522
[10] -0.46185635
> mapply(rnorm, 10,0,1) # mapply() 함수에서 인자로 동시에 여러 값 입력 가능
[,1]
[1,] -0.22139742
[2,] 1.06827309
[3,] -0.07316873
[4,] -1.64685767
[5,] -0.67088724
[6,] -0.27782831
[7,] -0.14553122
[8,] -0.27055841
[9,] 0.37373581
[10,] -0.44668074
> mapply(rnorm, c(2,4,6),c(0,10,100),c(1,1,1)) # for문을 사용하지 않아도 apply() 함수를 통해 함수 반복 수행 가능
[[1]]
[1] -1.648982 0.399832
[[2]]
[1] 9.922510 9.748816 9.582573 10.479338
[[3]]
[1] 100.35910 101.10890 99.51336 99.57302 100.46906 100.34759
Q.prac
# apply 관련 연습문제(fruits.csv 파일 사용)
> fruits <- read.csv("fruits.csv", stringsAsFactors = F)
> head(fruits)
year name qty price
1 2000 apple 6 6000
2 2000 banana 2 1000
3 2000 peach 7 3500
4 2000 berry 9 900
5 2001 apple 10 10000
6 2001 banana 7 3500
> str(fruits)
'data.frame': 12 obs. of 4 variables:
$ year : int 2000 2000 2000 2000 2001 2001 2001 2001 2002 2002 ...
$ name : chr "apple" "banana" "peach" "berry" ...
$ qty : int 6 2 7 9 10 7 3 15 13 10 ...
$ price: int 6000 1000 3500 900 10000 3500 1500 1500 13000 5000 ...
# Q1) qty가 10 이상일 때 수량의 합과 미만일 때 수량의 합을 각각 계산
> sum(fruits[fruits$qty >= 10,"qty"]) # qty 10 이상
[1] 59
> sum(fruits[fruits$qty < 10,"qty"]) # qty 10 미만
[1] 39
> tapply(fruits$qty, fruits$qty>=10, sum) # tapply의 첫 번째 인자는 반드시 벡터(data frame은 사용 불가)
FALSE TRUE
39 59
# Q2) 과일 이름별 price의 평균 출력
> tapply(fruits$price, fruits$name, mean)
apple banana berry peach
9666.667 3166.667 1166.667 2500.000
# Q3) year컬럼에서 년도를 두 자리 표현식으로 변경
> f3 <- function(x,y,z){
+ return(substr(x,y,z))
+ }
> f3(fruits$year,3,4) # 함수를 바로 사용한 방법
[1] "00" "00" "00" "00" "01" "01" "01" "01" "02" "02" "02" "02"
> sapply(fruits$year, f3, 3, 4) # sapply()로 사용자 정의 함수를 사용한 방법
[1] "00" "00" "00" "00" "01" "01" "01" "01" "02" "02" "02" "02"
> sapply(fruits$year, substr, 3, 4) # sapply()로 내장 함수를 사용한 방법 - (대상, 함수, 인자...)
[1] "00" "00" "00" "00" "01" "01" "01" "01" "02" "02" "02" "02"
> mapply(substr, fruits$year, 3, 4) # mapply()로 내장 함수를 사용한 방법 - (함수, 대상, 인자...)
[1] "00" "00" "00" "00" "01" "01" "01" "01" "02" "02" "02" "02"
Q.deep
# 심화문제
# 1. card 파일을 읽고
> card <- read.csv("card_history.csv", stringsAsFactors = F)
> str(card)
'data.frame': 30 obs. of 7 variables:
$ NUM : int 1 2 3 4 5 6 7 8 9 10 ...
$ 식료품 : chr "19,400" "22,200" "24,600" "22,300" ...
$ 의복 : chr "143,000" "120,400" "88,500" "124,800" ...
$ 외식비 : chr "8,600" "7,000" "7,500" "7,700" ...
$ 책값 : chr "29,000" "26,000" "22,000" "78,000" ...
$ 온라인소액결제: chr "5,600" "3,300" "7,500" "3,900" ...
$ 의료비 : chr "19,200" "13,000" "16,600" "28,100" ...
> card # 천 단위 구분기호 제거 후 숫자형으로 변경 필요
NUM 식료품 의복 외식비 책값 온라인소액결제 의료비
1 1 19,400 143,000 8,600 29,000 5,600 19,200
2 2 22,200 120,400 7,000 26,000 3,300 13,000
...
29 29 31,300 93,800 6,600 30,000 5,400 29,200
30 30 24,600 163,100 6,900 30,000 4,800 10,000
# 1) 각 항목별 총 합
> library(stringr) # str_replace_all 함수 사용을 위함
> str_replace_all(card,',','') # 문자열 함수는 대부분 데이터 프레임 확장 불가 -> 적용함수 사용(원소별 치환)
Warning message:
In stri_replace_all_regex(string, pattern, fix_replacement(replacement), :
argument is not an atomic vector; coercing
> d1 <- sapply(card, str_replace_all,',','') # 문자열 함수는 원소별 적용함수를 사용해야합니다.
> card <- as.data.frame(d1)
> card
NUM 식료품 의복 외식비 책값 온라인소액결제 의료비
1 1 19400 143000 8600 29000 5600 19200
2 2 22200 120400 7000 26000 3300 13000
...
29 29 31300 93800 6600 30000 5400 29200
30 30 24600 163100 6900 30000 4800 10000
> as.numeric(card) # 그러나! 형 변환 함수는 2차원 이상의 데이터 구조 적용 불가
Error: (list) object cannot be coerced to type 'double'
# 해결방법. 사용자 정의 함수를 생성하여 천단위 구분기호 제거 후 숫자형 변경
> fcard <- function(x) {
+ return(as.numeric(str_replace_all(x,',','')))
+ }
> card <- as.data.frame(sapply(card,fcard))
> apply(card[,-1], 2, sum)
식료품 의복 외식비 책값 온라인소액결제 의료비
795500 3947700 281400 945000 186300 633600
################################################################
# 2. movie.csv 파일을 읽고
> movie <- read.csv("movie_ex1.csv", stringsAsFactors = F, encoding = 'utf-8')
> head(movie)
년 월 일 지역.시도 지역.시군구 지역.읍면동 성별 연령대 이용_비율...
1 2018 2 1 강원도 강릉시 임당동 여 50대 0.00016
2 2018 2 1 강원도 강릉시 임당동 남 30대 0.00165
3 2018 2 1 강원도 강릉시 임당동 남 50대 0.00049
4 2018 2 1 강원도 속초시 조양동 남 40대 0.00148
5 2018 2 1 강원도 속초시 조양동 남 50대 0.00008
6 2018 2 1 강원도 속초시 조양동 여 40대 0.00091
> str(movie)
'data.frame': 66870 obs. of 9 variables:
$ 년 : int 2018 2018 2018 2018 2018 2018 2018 2018 2018 2018 ...
$ 월 : int 2 2 2 2 2 2 2 2 2 2 ...
$ 일 : int 1 1 1 1 1 1 1 1 1 1 ...
$ 지역.시도 : chr "강원도" "강원도" "강원도" "강원도" ...
$ 지역.시군구 : chr "강릉시" "강릉시" "강릉시" "속초시" ...
$ 지역.읍면동 : chr "임당동" "임당동" "임당동" "조양동" ...
$ 성별 : chr "여" "남" "남" "남" ...
$ 연령대 : chr "50대" "30대" "50대" "40대" ...
$ 이용_비율...: num 0.00016 0.00165 0.00049 0.00148 0.00008 0.00091 0.00082 0.00165 0.00041 0.00016 ...
# 1) 요일별 이용률 평균
> library(stringr)
# 참고.
# 년월일을 합치기 위한 방법
> paste(movie$년,movie$월, movie$일, sep = '')
> str_c(movie$년, movie$월, movie$일)
# 날짜 파싱 과정
> as.Date("201911", '%Y%m%d') # 날짜로 인식 불가, 월과 일의 구분이 애매함
> as.Date("2019/1/1", '%Y/%m/%d') # 구분기호를 넣어주면 월과 일이 두 자리 표현식(01)이 아니더라도 가능
# My)
> movie_day <- as.Date(str_c(movie$년, '-', movie$월, '-', movie$일), '%Y-%m-%d')
> library(lubridate)
> as.character(wday(movie_day, label=T))
[1] "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목"
...
[985] "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목" "목"
# sol 1) paste 함수 사용
> v1 <- (paste(movie$년, '/', movie$월, '/', movie$일, sep = ''))
> movie$day1 <- as.character(as.Date(v1, '%Y/%m/%d'),'%A') # as.Date는 날짜 포맷 변경이 아닌 파싱용 함수입니다.
# R의 날짜 default 포맷이 0000-00-00 이므로 0000/00/00 형식으로 파싱해도 적용이 안되는 것이죠.
> tapply(movie$이용_비율..., movie$day1, mean)
금요일 목요일 수요일 월요일 일요일 토요일 화요일
0.0017283872 0.0015061331 0.0013655414 0.0009007030 0.0017006749 0.0021701041 0.0009055736
# sol 2) str_c 함수 사용
> v2 <- str_c(movie$년, sprintf('%02d', movie$월), sprintf('%02d', movie$일))
> movie$day2 <- as.character(as.Date(v2, '%Y%m%d'),'%A')
> per <- tapply(movie$이용_비율..., movie$day2, mean)
> per[c(4,7,3,2,1,6,5)] # 요일 정렬은 색인을 활용하여 할 수 있습니다. (요일 정렬순서를 색인값에 전달)
월요일 화요일 수요일 목요일 금요일 토요일 일요일
0.0009007030 0.0009055736 0.0013655414 0.0015061331 0.0017283872 0.0021701041 0.0017006749
################################################################
# 3. delivery_02 파일을 읽고
> delivery <- read.csv("delivery_02.csv", stringsAsFactors = F, encoding = 'utf-8')
> head(delivery)
일자 시간대 업종 시도 시군구 읍면동 통화건수
1 20180201 0 음식점-족발/보쌈전문 서울특별시 강남구 논현동 5
2 20180201 0 음식점-족발/보쌈전문 서울특별시 강남구 역삼동 5
...
5 20180201 0 음식점-족발/보쌈전문 서울특별시 동작구 신대방동 5
6 20180201 0 음식점-족발/보쌈전문 서울특별시 노원구 상계동 5
# 1) 각 읍면동별 통화건수의 총 합을 구하되,
# 각 동은 숫자를 포함하고 있는 경우 숫자를 제외한 동까지 표현하도록 함
# (ex 을지로6가 => 을지로)
> strsplit('을지로6가','[0-9]')[[1]][1] # 코드 구현 전, 단계별로 확인해보면서 진행하는 것이 중요합니다.
[1] "을지로"
> strsplit('을지로','[0-9]')[[1]][1]
[1] "을지로"
> strsplit(delivery$읍면동,'[0-9]')[[1]][1] # 하지만, 문자열 함수는 벡터 연산 불가
[1] "논현동"
> str_split(delivery$읍면동,'[0-9]')[[1]][1]
[1] "논현동"
> f2 <- function(x) { # 적용함수 사용
+ return(strsplit(x,'[0-9]')[[1]][1])
+ }
> delivery$town <- sapply(delivery$읍면동,f2)
> tapply(delivery$통화건, delivery$town, sum)
Q.prac2
# 1. emp 데이터를 사용하여 각 부서별 최대 연봉값을 구하여라
> 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
4 7566 JONES MANAGER 7839 1981-04-02 0:00 2975 NA 20
5 7654 MARTIN SALESMAN 7698 1981-09-28 0:00 1250 1400 30
6 7698 BLAKE MANAGER 7839 1981-05-01 0:00 2850 NA 30
> tapply(emp$SAL, emp$DEPTNO, max)
10 20 30
5000 3000 2850
##########################################################
# 2. 위의 데이터를 활용하여 부서별 최대값을 갖는 직원의 이름, 부서, 연봉, 최대연봉을 함께 출력하여라.
(marge 를 사용하지 않고 apply() 함수를 사용하여 풀어보기)
> emp[emp$SAL == 5000 & emp$DEPTNO == 10, c("ENAME", "DEPTNO", "SAL")] # 이런 형태로 만들것임
ENAME DEPTNO SAL
9 KING 10 5000
> df1 <- as.data.frame(tapply(emp$SAL, emp$DEPTNO, max)) # data frame 형태로 변환
> df1 <- cbind(row.names(df1), df1) # 행 이름을 첫 번째 컬럼에 넣어주기 위해 cbind 사용
> colnames(df1) <- c('DEPTNO', 'SAL')
> df1
DEPTNO SAL
10 10 5000
20 20 3000
30 30 2850
# apply()함수를 사용하면 (DEPTNO, SAL) 행 별로 전달 가능
# apply()함수는 데이터를 하나로 묶어서 vector 형식으로 전달됨. 그 중 일부를 뽑기 위해 벡터의 숫자 색인 사용
> f_max <- function(x){
+ emp[emp$SAL == x[2] & emp$DEPTNO == x[1], c("ENAME", "DEPTNO", "SAL")]
+ }
> apply(df1, 1, f_max)
$`10`
ENAME DEPTNO SAL
9 KING 10 5000
$`20`
ENAME DEPTNO SAL
8 SCOTT 20 3000
13 FORD 20 3000
$`30`
ENAME DEPTNO SAL
6 BLAKE 30 2850
##########################################################
# 3. data2 데이터를 읽고
> train <- read.csv("data2.csv", stringsAsFactors = F, encoding = 'utf-8')
> head(train)
노선번호 시간 승차 하차
1 line_1 506 88,136 35,394
2 line_1 607 114,628 195,028
3 line_1 708 259,282 483,162
4 line_1 809 384,892 1,165,703
5 line_1 910 315,797 791,704
6 line_1 1011 340,972 585,759
> str(train)
'data.frame': 80 obs. of 4 variables:
$ 노선번호: chr "line_1" "line_1" "line_1" "line_1" ...
$ 시간 : int 506 607 708 809 910 1011 1112 1213 1314 1415 ...
$ 승차 : chr " 88,136 " " 114,628 " " 259,282 " " 384,892 " ...
$ 하차 : chr " 35,394 " " 195,028 " " 483,162 " " 1,165,703 " ...
# 1) 다음과 같이 노선별 승하차의 총 합을 표현
# line_1 line_2 line_3 line_4
# XXXXX XXXXX XXXXX XXXXX
> f_train <- function(x) {
+ return(as.numeric(str_replace_all(x,',','')))
+ }
> library(stringr)
> train[,3:4] <- as.data.frame(sapply(train[,3:4],f_train))
> str(train)
'data.frame': 80 obs. of 4 variables:
$ 노선번호: chr "line_1" "line_1" "line_1" "line_1" ...
$ 시간 : int 506 607 708 809 910 1011 1112 1213 1314 1415 ...
$ 승차 : num 88136 114628 259282 384892 315797 ...
$ 하차 : num 35394 195028 483162 1165703 791704 ...
> tapply(train$승차+train$하차, train$노선번호, sum)
line_1 line_2 line_3 line_4
19097780 95377046 33039606 39873125
# 2) 오전 오후의 승하차의 총 합을 표현
# 오전 오후
# XXXX XXXXX
> str_replace_all('506','[0-9][0-9]$','') # 뒤의 두 숫자 삭제
[1] "5"
> str_replace_all('1506','[0-9][0-9]$','') # 뒤의 두 숫자 삭제
[1] "15"
> train$time <- as.numeric(str_replace_all(train$시간,'[0-9][0-9]$',''))
# 논리값 사용 후 컬럼이름 지정(데이터가 큰 경우 작업을 나눠서 하는 것이 유리)
> v_time <- tapply(train$승차+train$하차, train$time >= 12, sum)
> names(v_time) <- c('오전','오후')
> v_time
오전 오후
61837486 125550071
# 오전 오후 바로 출력(데이터가 적은 경우 사용)
> tapply(train$승차+train$하차,
+ ifelse(train$time >= 12, '오후','오전'),
+ sum)
오전 오후
61837486 125550071
# my answer
> f_train2 <- function(x) {
+ return(as.numeric(str_sub(gettextf('%04d', x),1,2)))
+ }
> train$시간 <- sapply(train[,2], f_train2)
> train$time <- ifelse(train$시간>=0 & train$시간<=12, "오전", "오후")
> tapply(train$승차+train$하차, train$time, sum)
오전 오후
70394130 116993427
Q.deep2
참고: KIC 캠퍼스 머신러닝기반의 빅데이터분석 양성과정
'R > Process' 카테고리의 다른 글
[R] 데이터 정렬 - order(), sort(), orderBy() (0) | 2019.01.07 |
---|---|
[R] 데이터 요약 통계, 분위수 확인 - summary(), quantile(), summartBy() (0) | 2019.01.07 |
[R] 데이터 포맷 변경 함수 - sprintf(), gettextf() 함수 (0) | 2019.01.04 |
[R] 함수의 정의 (0) | 2019.01.02 |
[R] 연산 (0) | 2019.01.02 |