데이터프레임 자료구조를 갖추게 되면 기본적인 데이터 분석과 모형 구축을 위한 준비는 된 것으로 본다. 경우에 따라서 프로그래밍의 기본기인 반복작업을 수행하는 경우가 있어 이런 경우 purrr
팩키지 함수형 프로그래밍을 도입하게 되면 많은 도움을 받을 수 있다.
for
루프 1변수별로 평균을 구하는 문제를 생각해보자.
iris
데이터iris
데이터셋은 숫자형 변수가 4개, 범주형 변수가 1개로 구성되어 있고 관측점은 150개다.
# A tibble: 150 x 5
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<dbl> <dbl> <dbl> <dbl> <fct>
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3 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 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
# … with 140 more rows
데이터프레임 각 변수를 mean()
함수에 넣어서 평균을 구하는 코드는 다음과 같다. 함수가 4번 반복되고, iris
데이터프레임 명칭도 4번 반복되고 있는 문제가 있다.
[1] 5.843333
[1] 3.057333
[1] 3.758
[1] 1.199333
for
루프 반복for
루프로 반복시켜 문제를 앞서 지적된 문제를 고쳐보다. iris
데이터셋의 마지막 범주 변수는 제외하고 나머지 숫자형 변수에 대해서만 평균을 계산하는데 일반적인 패턴은 다음과 같다.
for
루프 반복을 돌리면서 변수별 평균을 계산한 결과값을 1번에서 정의한 벡터나 리스트에 저장한다.# iris_mean <- c()
iris_mean <- vector("double", ncol(iris)-1)
for(i in seq_along(iris[,-5])) {
iris_mean[i] <- mean(iris[, i])
}
iris_mean
[1] 5.843333 3.057333 3.758000 1.199333
R 내장함수로 apply
계열 함수를 사용하면 for
루프를 제거하고 단순하게 원한느 결과를 얻을 수 있다.
apply
함수를 지정한다.iris[,-5]
1
행방향, 2
열방향mean
평균Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
purrr
purrr
팩키지의 함수인 map_*
계열 함수를 사용해서 코드를 우아하게 작성해 보자. 반환되는 값이 실수형이라 map_dbl()
을 지정하고 반복을 돌리고자하는 데이터프레임을 넣어준다. 각 변수별로 적용시킬 함수를 mean
넣어주면 한줄로 결과를 얻을 수 있다.
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
for
루프평균과 중앙값 등 변수별 요약통계량을 담아야 하는 경우 리스트(list
)에 벡터를 넣는 자료구조를 준비하고 각 변수별로 반복루프를 돌려 작업을 수행한다.
iris_desc <- list()
iris_mean <- vector("double", ncol(iris) -1)
iris_median <- vector("double", ncol(iris) -1)
for(i in seq_along(iris[,-5])) {
iris_mean[i] <- mean(iris[,i])
iris_median[i] <- median(iris[,i])
iris_desc[[i]] <- list("평균" = iris_mean[i],
"중앙값"= iris_median[i])
names(iris_desc)[i] <- names(iris)[i]
}
listviewer::jsonedit(iris_desc)
purrr
반복purrr
팩키지의 map
함수와 사용자 정의 함수(calc_desc_stat
)를 조합시킬 경우 좀더 깔금하게 반복 코드를 정리할 수 있다.
calc_desc_stat <- function(var_name) {
return(list("평균" = mean(var_name), "중앙값" = median(var_name)))
}
# calc_desc_stat(iris[,1])
map(iris[,-5], ~calc_desc_stat(.x)) %>%
enframe %>%
mutate(평균 = map_dbl(value, "평균"),
중앙값 = map_dbl(value, "중앙값"))
# A tibble: 4 x 4
name value 평균 중앙값
<chr> <list> <dbl> <dbl>
1 Sepal.Length <named list [2]> 5.84 5.8
2 Sepal.Width <named list [2]> 3.06 3
3 Petal.Length <named list [2]> 3.76 4.35
4 Petal.Width <named list [2]> 1.20 1.3
apply
반복유사하게 apply
계열 함수를 사용해서도 동일한 기능을 구현할 수 있다.
Sepal.Length Sepal.Width Petal.Length Petal.Width
평균 5.843333 3.057333 3.758 1.199333
중앙값 5.8 3 4.35 1.3
dplyr
방식dplyr
팩키지 기능을 사용해서 명수명을 골라내고 summarise_all()
함수를 계산하고 나서 이를 후속 분석을 위한 데이터프레임으로 만들어 놓는 작업을 다음과 같이 작성할 수도 있다.
iris %>%
select(contains(".")) %>%
summarise_all(list("평균" = mean,
"중위값" = median)) %>%
gather(변수명, 값) %>%
separate(변수명, into=c("변수명", "통계량"), sep="_") %>%
spread(통계량, 값)
변수명 중위값 평균
1 Petal.Length 4.35 3.758000
2 Petal.Width 1.30 1.199333
3 Sepal.Length 5.80 5.843333
4 Sepal.Width 3.00 3.057333