데이터 프레임 다루기
지금까지 배웠던 자료구조들을 토대로 데이터를 다루는 것을 조금씩 해보겠습니다.
data.frame 에서 row / column 추가하기
- 두 벡터를 각각 row로 하는 dataframe을 만들고 싶으면? : rbind
vec1 <- c('one','two','three')
vec2 <- c(1,2,3)
rbind(vec1, vec2)
## [,1] [,2] [,3]
## vec1 "one" "two" "three"
## vec2 "1" "2" "3"
data.frame(rbind(vec1,vec2))
#
#
#
- 이번에는, 두 벡터를 각각 column으로 하는 dataframe을 만들고 싶으면? : cbind
vec1 <- c('one','two','three')
vec2 <- c(1,2,3)
cbind(vec1, vec2)
#
#
#
#
df <- data.frame(cbind(vec1,vec2))
str(df)
## 'data.frame': 3 obs. of 2 variables:
## $ vec1: Factor w/ 3 levels "one","three",..: 1 3 2
## $ vec2: Factor w/ 3 levels "1","2","3": 1 2 3
- 그런데 위의 방법은 str(df)에서 보듯이, 숫자든, 문자든 모두 factor로 인식하게 됩니다. 그럴땐, cbind.data.frame이라는 함수를 사용합니다.
cbind.data.frame(vec1, vec2)
#
#
#
#
df <- cbind.data.frame(vec1, vec2, stringsAsFactors = F)
str(df)
#
#
#
apply 함수 이해하기
행렬 혹은 data.frame에서 각 row, column에 대해 평균을 계산한다든지, 특정 함수를 적용하고 싶을 때가 있죠. 이럴 때, 가장 기본적으로 생각하는 게 for loop를 활용하여 각 row(혹은 column) 별로 함수를 적용합니다.
예를 들어,
mat <- matrix(c(1,2,3,4,5,6,7,8,9), nrow=3)
for (i in seq(1:nrow(mat))){
print(mean(mat[i,]))
}
#
#
#
그런데 많은 양의 데이터를 for loop 하는 것은 비효율적입니다. 매번 for loop를 돌때마다 함수를 불러와야 하기 때문이죠. 따라서 최대한 for loop를 줄이는 것이 중요합니다!! 그 때 사용하는 함수가 바로 apply입니다. apply는 한 번만 함수를 불러와서 모든 데이터에 적용하기 때문에 훨씬 시간을 줄일 수 있습니다.
mat <- matrix(c(1,2,3,4,5,6,7,8,9), nrow=3)
apply(mat, 1, mean)
#
apply(mat, 2, mean)
#
apply(mat, 1, range)
#
#
#
Quiz
iris 데이터 셋에 적용해보자
- apply를 활용하여 iris의 Species를 제외한 4개 변수에 대해 평균을 아래와 같이 계산하세요.
#
#
- apply를 활용하여 Sepal.Length, Sepal.Width의 최소, 최대값을 아래와 같이 구하세요. (최소, 최댓값을 구하는 함수는 range)
#
#
#
list, vector에 대한 for loop 계산
데이터 프레임과 마찬가지로 list, vector에 대해서도 for loop를 최소화 하는 것이 좋습니다. 대신, apply와 비슷하게 lapply, sapply를 사용합니다. 좀 더 구체적으로 예를 들어보면, 리스트 (1,2,3)을 제곱한 값을 반환하고 싶다고 합시다. 그래서 아래와 같이 계산하면 실행이 안되죠. list는 vector처럼 연산 함수가 적용되지 않습니다.
lst <- list(1,2,3)
lst
lst^2
이럴때, for loop를 쓰지 말고, apply의 사촌격인 lapply, sapply를 사용합니다.
lapply(벡터 혹은 리스트, 함수)
sapply(벡터 혹은 리스트, 함수)
앞에 예를 들었듯이, 벡터 c(1,2,3)을 제곱하고 싶다면,
lst <- list(1,2,3)
lapply(lst, function(x){x^2})
#
#
#
#
#
#
#
#
근데 lapply는 결과값이 list로 나오기 때문에, 벡터로 나오게 하고 싶다면 sapply를 이용하면 됩니다.
sapply(lst, function(x){x^2})
#
- lapply, sapply도 data.frame 에 적용할 수 있는데, apply는 결과값이 data.frame인 반면, lapply, sapply는 결과값이 각각 list, vector 라는 차이가 있습니다. 그리고 기본적으로 각 column에 대해 함수가 적용됩니다.
lst <- lapply(iris[,1:4], sum)
class(lst)
#
vec <- sapply(iris[,1:4], sum)
class(vec)
#
좀 더 예를 들어보죠,
sapply(iris, class)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## "numeric" "numeric" "numeric" "numeric" "factor"
y <- sapply(iris[, 1:4], function(x){ x > 3 })
Quiz
- iris 데이터를 0~1 사이 값으로 바꿔라
hint: 서로 다른 변수의 데이터가 scale이 다를 경우(어떤 변수는 -10~0 사이인데, 다른 변수는 10~1000인 경우), 정규분포를 활용한 정규화 뿐만 아니라, min, max를 활용하여 0~1사이로 바꾸는 방법도 있습니다. 즉, 다음 함수를 각 row에 적용하면 됩니다. x−min(x)max(x)−min(x)
#
#
#
#
#
#
#
apply의 사촌들
이제부터는 apply와 비슷한 원리지만, 각 상황에 맞게 tapply, mapply 가 있는데, 자주 사용되지는 않지만, 한 번 보고 갈게요.
str(iris)
#
#
#
#
#
#
tapply(iris$Sepal.Length, iris$Species, mean)
#
#
Quiz
- Species 별 Sepal.Width 의 분산은?
#
#
mapply
여러 벡터에 동일한 함수를 적용하고 싶을때 사용합니다. 아래와 같이 최대공약수를 구하는 함수 gcd가 있다고 합시다.
gcd <- function(a,b) {
if (b==0) return(a)
else return(gcd(b, a%%b))
}
gcd(6,4)
#
gcd(c(3,6,9), c(12,15,18))
## Warning in if (b == 0) return(a) else return(gcd(b, a%%b)): length > 1 이라
## 는 조건이 있고, 첫번째 요소만이 사용될 것입니다
## Warning in if (b == 0) return(a) else return(gcd(b, a%%b)): length > 1 이라
## 는 조건이 있고, 첫번째 요소만이 사용될 것입니다
## Warning in if (b == 0) return(a) else return(gcd(b, a%%b)): length > 1 이라
## 는 조건이 있고, 첫번째 요소만이 사용될 것입니다
#
mapply(gcd, c(3,6,9), c(12,15,18))
#
참고 : Sampling
기계학습 모델링을 사용하다 보면, 무작위로 데이터를 추출해야할 경우가 생깁니다. 이럴 때 sample 함수를 사용하죠.
sample(1:10, 5)
#
sample(1:10, 5, replace=T)
#
- iris 데이터에서 임의로 전체의 15% 데이터 추출하기
index <- 1:nrow(iris)
train_idx <- sample(index, round(nrow(iris)*0.15))
head(iris[train_idx,])
#
#
#
#
#
#
#