티스토리 뷰

반응형

<요약>

그룹별 적용

- 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)

가락동   가산동   가양동   갈월동   갈현동   개봉동   개포동   거여동   경운동   공덕동   공릉동   관수동   광장동   교남동 
   10710    11172    20655      370      385    22711     2729      410      761     1286     4959      690     1678     2084 
...
합정동   행당동   현석동   홍은동   홍익동   홍제동   화곡동   화양동   회현동   효창동   후암동   휘경동   흑석동   흥인동 
    8966      690       10     2710      290    16116    30048     1391      480      560    12148      430     1183      590




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

# 심화 문제
# 1. student.csv 파일을 불러와서
> student <- read.csv("student.csv", stringsAsFactors = F, encoding = 'utf-8')
> head(student)
> str(student)
# 1) 남,여별 키의 평균
> student$gender <- ifelse(substr(student$JUMIN,7,7)=='1', '남', '여')
> tapply(student$HEIGHT, student$gender, mean)
      남       여 
176.3333 165.7500 
# 2) 3,4학년 학생의 이름, 학번(studno), 키와 몸무게 출력
> student[student$GRADE %in% c(3,4),c("NAME", "STUDNO", "HEIGHT", "WEIGHT", "GRADE")]
     NAME STUDNO HEIGHT WEIGHT GRADE
1  이진욱   9411    180     72     4
2  서재수   9412    172     64     4
...
9  구유미   9514    160     58     3
10 임세현   9515    171     54     3
# 3) id 컬럼에 숫자가 포함되어 있는 경우 숫자의 삭제처리
> library(stringr)
> student$ID <- str_replace_all(student$ID,'[0-9]','')

##########################################################

# 2. subway2.csv 파일을 읽고
> subway <- read.csv("subway2.csv", stringsAsFactors = F, skip = 1)
> head(subway)
> str(subway)
# 1) 시간대별 승하차 총합
> toNum <- function(x) {
+   as.numeric(str_replace_all(x, ',', ''))
+ }
> library(stringr)
> subway[,-c(1,2)] <- sapply(subway[,-c(1,2)],toNum)
> apply(subway[,-c(1,2)],2,sum)
  X05.06   X06.07   X07.08  ...   X22.23   X23.24   X24.01 
 1338165  3953180 10184565  ...  8782129  4997008  1204504 
# 2) 역별 출근시간대(05~10시)의 승하차 총합
> subway$전체[2]
[1] ""
> is.na(subway$전체[2])
[1] FALSE
> is.null(subway$전체[2])
[1] FALSE
> f_fillna <- function(x){
+   v1 <- c()
+   for(i in 1:length(x)){
+     if(x[i] == ""){
+       v1 <- c(v1,x[i-1])
+     } else{
+       v1 <- c(v1,x[i])
+     }
+   }
+   return(v1)
+ }
> subway$전체 <- f_fillna(subway$전체)
> head(subway)
       전체 구분 X05.06 X06.07 X07.08 ... X22.23 X23.24 X24.01
1 서울역(1) 승차  17465  18434  50313  ...  88902  49049   4558
2 서울역(1) 하차   7829  48553 110250 ...  50266  32182  13943
3  시 청(1) 승차   2993   4473   7633  ...  69327  24653   1691
4  시 청(1) 하차   4142  19730  67995...   8552   5349   1603
> tapply(subway$X05.06 + subway$X06.07 + subway$X07.08 + subway$X08.09 + subway$X09.10,  
+        subway$전체, 
+        sum)
    강 남     강 변     건 대    경복궁  고속터미   교대(2)   교대(3)     구 의  구로공단    구파발    ... 
  1303760    792423    592564    376575    558865    656049    184478    434690   1071360    228266    ...


Q.prac3
# employment.csv 파일을 읽고 
> employ <- read.csv("employment.csv", stringsAsFactors = F, na.strings = '-')
> employ
                     고용형태           X2007           X2007.1             X2007.2             X2007.3         X2007.4         X2007.5       
1                    고용형태 총근로일수 (일) 총근로시간 (시간) 정상근로시간 (시간) 초과근로시간 (시간) 월급여액 (천원) 
2                  전체근로자            22.3             188.8               176.5                12.3           1,847           1,732   
3    전체근로자(특수형태포함)            <NA>              <NA>                <NA>                <NA>           1,858       
4                  정규근로자            22.7             193.2               179.3                13.9           2,027           1,893   
5                비정규근로자            20.8             171.4               165.3                 6.1           1,145           1,108   
6  비정규근로자(특수형태포함)            <NA>              <NA>                <NA>                <NA>           1,313    
...
## 컬럼명과 첫 번째 행의 각 세부항목을 결합하여 새로운 컬럼 이름 설정
> v1 <- substr(colnames(employ),2,5)[-1]
> v1
 [1] "2007" "2007" "2007" "2007" "2007" "2007" "2007" "2007" "2007" "2008" "2008" "2008" "2008" "2008" "2008" "2008" "2008" "2008" ... "2012" "2012" "2012" "2012" "2013" "2013" "2013" "2013" "2013" "2013" "2013" "2013" "2013"

> employ[1,-1]
            X2007           X2007.1             X2007.2             X2007.3         X2007.4         X2007.5         X2007.6    ...   
1 총근로일수 (일) 총근로시간 (시간) 정상근로시간 (시간) 초과근로시간 (시간) 월급여액 (천원) 정액급여 (천원) 초과급여 (천원)  ...
> strsplit(employ[1,-1], ' ')[[1]][1]  # split 함수(문자 함수) 벡터 연산 불가 -> 적용 함수 필요

> f_split <- function(x) {
+   strsplit(x, ' ')[[1]][1]
+ }
> v2 <- sapply(employ[1,-1], f_split)
> v2
           X2007          X2007.1          X2007.2          X2007.3          X2007.4          X2007.5          X2007.6          X2007.7   ...       
    "총근로일수"     "총근로시간"   "정상근로시간"   "초과근로시간"       "월급여액"       "정액급여"       "초과급여"   "연간특별급여" ...
...
> colnames(employ)[-1] <- str_c(v1, v2,sep = '_')
> employ <- employ[-1,]
> head(employ)
                    고용형태 2007_총근로일수 2007_총근로시간 2007_정상근로시간 2007_초과근로시간 2007_월급여액 2007_정액급여 ...
2                 전체근로자            22.3           188.8             176.5              12.3         1,847         1,732           114   ...    
3   전체근로자(특수형태포함)            <NA>            <NA>              <NA>              <NA>         1,858         1,748   ...   
4                 정규근로자            22.7           193.2             179.3              13.9         2,027         1,893           134   ...      
...
## 모든 컬럼(첫 번째 제외)을 숫자로 변경
> f_numeric <- function(x){
+   as.numeric(str_replace_all(x,',',''))
+ }
> employ[,-1] <- sapply(employ[,-1], f_numeric)     # 첫 번째 컬럼을 제외 

# 1) 각 년도별 월급여액 평균
> data1 <- employ[,str_detect(colnames(employ),'월급여액')]
> data1
   2007_월급여액 2008_월급여액 2009_월급여액 2010_월급여액 2011_월급여액 2012_월급여액 2013_월급여액
2           1847          1945          1960          2023          2102          2216          2288
3           1858          1952          1964          2025          2103          2215          2310
4           2027          2151          2199          2285          2385          2502          2566
...
> apply(data1, 2, mean)
2007_월급여액 2008_월급여액 2009_월급여액 2010_월급여액 2011_월급여액 2012_월급여액 2013_월급여액 
     1416.667      1458.500      1461.250      1493.250      1568.167      1634.917      1760.667 
# 2) 각 년도별 총 근로일수 평균
> data2 <- employ[,str_detect(colnames(employ),'총근로일수')]
> data2
   2007_총근로일수 2008_총근로일수 2009_총근로일수 2010_총근로일수 2011_총근로일수 2012_총근로일수 2013_총근로일수
2             22.3            21.7            22.5            22.2            21.6            20.9            20.3
3               NA              NA              NA              NA              NA              NA              NA
4             22.7            22.2            23.1            22.9            22.5            21.7            21.0
5             20.8            19.9            20.4            20.0            19.1            18.4            17.9
> apply(data2, 2, mean, na.rm = T)      # 근로일 수가 점차 줄고 있는 것을 확인 가능
2007_총근로일수 2008_총근로일수 2009_총근로일수 2010_총근로일수 2011_총근로일수 2012_총근로일수 2013_총근로일수 
       21.36667        20.40000        21.06667        20.55556        20.15556        19.30000        18.92222  




참고: KIC 캠퍼스 머신러닝기반의 빅데이터분석 양성과정

반응형
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday