## Warning: package 'knitr' was built under R version 3.2.5

학습 목표


데이터프레임은 무엇인가?

데이터프레임(dataframe)은 가장 대중적인 표형식 데이터에 대한 _사실상 표준(de facto)_으로, 통계 및 시각화에 활용하는 자료구조다.

데이터프레임은 동일한 길이를 갖는 벡터 집합이다. 벡터 각각은 칼럼을 표현하지만 각 벡터는 서로 다른 자료형이 될 수 있다(예를 들어, 문자형, 정수형, 요인형). str() 함수를 사용해서 각 칼럼별 자료형을 조사한다.

데이터프레임은 일일이 손으로 생성할 수도 있지만, 일반적으로 read.csv() 혹은 read.table() 함수로 만들어 낸다; 다른 경우는 로컬 하드디스크(혹은 웹)에서 스프레드쉬트를 가져와서도 생성할 수 있다.

기본디폴트 설정으로 데이터프레임을 가져오거나 생성할 때, 문자열(즉, 텍스트)이 담긴 칼럼은 강제로 요인(factor) 자료형으로 변환된다. 데이터로 작업하려는 의도에 따라 달라질 수 있지만, 문자형(character)으로 칼럼을 그대로 두고자 하는 경우도 있다. 이런 경우 read.csv() 혹은 read.table() 함수에 stringsAsFactors 인자를 FALSE로 설정하면 요인형이 아닌 문자형 그대로 사용할 수 있다:

some_data <- read.csv("data/some_file.csv", stringsAsFactors=FALSE)

data.frame() 함수를 사용해서 수작업으로 데이터프레임을 생성할 수도 있다. 이 함수도 stringsAsFactors 인자를 받는다. 예제를 통해 출력결과를 비교하고, 문자열(character)요인(factor)으로 달리 데이터를 불러 읽어들인 경우 각각을 비교한다.

## Compare the output of these examples, and compare the difference between when
## the data are being read as `character`, and when they are being read as
## `factor`.
example_data <- data.frame(animal=c("dog", "cat", "sea cucumber", "sea urchin"),
                           feel=c("furry", "furry", "squishy", "spiny"),
                           weight=c(45, 8, 1.1, 0.8))
str(example_data)
#> 'data.frame':    4 obs. of  3 variables:
#>  $ animal: Factor w/ 4 levels "cat","dog","sea cucumber",..: 2 1 3 4
#>  $ feel  : Factor w/ 3 levels "furry","spiny",..: 1 1 3 2
#>  $ weight: num  45 8 1.1 0.8
example_data <- data.frame(animal=c("dog", "cat", "sea cucumber", "sea urchin"),
                           feel=c("furry", "furry", "squishy", "spiny"),
                           weight=c(45, 8, 1.1, 0.8), stringsAsFactors=FALSE)
str(example_data)
#> 'data.frame':    4 obs. of  3 variables:
#>  $ animal: chr  "dog" "cat" "sea cucumber" "sea urchin"
#>  $ feel  : chr  "furry" "furry" "squishy" "spiny"
#>  $ weight: num  45 8 1.1 0.8

도전과제

  1. 일일이 한땀한땀 작성된 data.frame에는 일부 실수가 묻어있다. 잘못된 것을 식별할 수 있는가? 잘못된 것을 수정할 수 있는가? 직접 실험하는데 주저하지 마시오!

    author_book <- data.frame(author_first=c("Charles", "Ernst", "Theodosius"),
                              author_last=c(Darwin, Mayr, Dobzhansky),
                              year=c(1942, 1970))
  2. 다음 예제에서 칼럼 각각에 대한 자료형 클래스를 예측해보자. 예측한 것을 str(country_climate) 함수로 점검한다:
    • 예상한 그대로 맞췄나요? 맞았다면 이유는? 틀렸다면 이유는?
    • stringsAsFactors = FALSE 인자를 추가하면 출력결과는 어떻게 바뀌는가?
    • 각 칼럼별로 정확한 자료형을 확실히 보장하려면 어떤 조치가 필요할까?
    country_climate <- data.frame(country=c("Canada", "Panama", "South Africa", "Australia"),
                                   climate=c("cold", "hot", "temperate", "hot/temperate"),
                                   temperature=c(10, 30, 18, "15"),
                                   northern_hemisphere=c(TRUE, TRUE, FALSE, "FALSE"),
                                   has_kangaroo=c(FALSE, FALSE, FALSE, 1))
  3. 데이터프레임을 생성하는데 data.frame(), read.csv() 함수를 소개했지만, 벡터를 이미 갖고 있는 경우는 어떨까? 이런 경우 최선의 방식은 벡터를 data.frame() 함수에 넘기는 것이다.

    color <- c("red", "green", "blue", "yellow")
    counts <- c(50, 60, 65, 82)
    new_datarame <- data.frame(colors = color, counts = counts)

벡터를 생성해서 본인만의 데이터프레임을 생성해본다. class() 함수를 사용해서 새로 생성된 객체에 대한 자료형을 검증한다.

자동 자료형 변환이 흔히 축복이지만, 그렇지 않고 귀찮은 경우도 있다. 자동 자료형 변환이 존재한다는 사실에 주의를 기울이고, 자동변환 규칙을 학습하고 나서 데이터프레임 내부에 올바른 자료형으로 R에 데이터를 불러 읽어올 수 있도록 확실히 확인한다. 그렇지 않다면 데이터입력할 때 오류(예를 들어 숫자만 담겨있어야 되는 칼럼에 문자가 포함)를 탐지하는데 사용한다.

data.frame 객체 조사

head()str() 함수를 사용해서 data.frame 구조와 내용물을 확인하는 방법을 살펴봤다. 다음에 데이터 구조와 내용물에 대한 감을 얻는데 전부는 아니지만 유용한 명령어가 나와 있다.

주의: 이런 함수 대부분은 “제네릭(generic)”이라, data.frame 말고도 다른 객체 자료형에도 사용된다.

인덱스, 순열, 부분집합 뽑아내기

: 은 특수 함수로 오름차순 혹은 내림차순 정수형 숫자벡터를 생성한다. 예를 들어, 1:1010:1 명령어를 테스트한다. 순열 seq() 함수(sequence)를 사용해서 좀더 복잡한 패턴을 생성한다:

seq(1, 10, by=2)
#> [1] 1 3 5 7 9
seq(5, 10, length.out=3)
#> [1]  5.0  7.5 10.0
seq(50, by=5, length.out=10)
#>  [1] 50 55 60 65 70 75 80 85 90 95
seq(1, 8, by=3) # 순열은 상한 아래에서 멈춤.
#> [1] 1 4 7

survey 데이터프레임은 행과 열로 구성되는데 2차원 구조를 갖추고 있다. 데이터프레임에서 특정 데이터를 추출하려면, 추출하려는 “좌표(coordinate)”를 명세하여야 된다. 행숫자가 먼저 오고, 칼럼 숫자가 다음에 온다. 하지만, 좌표를 명세하는 방식에 따라 다른 데이터가 추출된다.

surveys[1]      # (데이터프레임으로) 데이터프레임에 첫번째 칼럼
surveys[,1]     # (벡터로) 데이터프레임에 첫번째 칼럼
surveys[1, 1]   # (벡터로) 데이터프레임의 첫번째 칼럼에 있는 첫번째 원소
surveys[1, 6]   # (벡터로) 데이터프레임의 여섯번째 칼럼에 있는 첫번째 원소
surveys[1:3, 7] # (벡터로) 데이터프레임의 일곱번째 칼럼에 있는 첫번째부터 세번째 원소
surveys[3, ]    # (데이터프레임으로) 세번째 원소가 있는 행의 모든 칼럼
head_surveys <- surveys[1:6, ] # head(surveys) 명령어와 동일

데이터프레임의 특정 부분도 제외할 수 있다.

surveys[,-1]   #첫번째 칼럼을 제외한 데이터프레임 모두
surveys[-c(7:34786),] #head(surveys) 명령어와 동일

data.frame (혹은 matrix)에서 부분집합을 뽑아내는데 숫자 뿐만 아니라, 칼럼을 다음 표기법 중 하나를 사용해서 칼럼으로 뽑아낼 수 있다:

surveys["species_id"]       # 실행결과는 데이터프레임
surveys[, "species_id"]     # 실행결과는 벡터
surveys[["species_id"]]     # 실행결과는 벡터
surveys$species_id          # 실행결과는 벡터

현재 상태로, 상기 표기법은 동일하다. 하지만, 마지막 $ 표기법은 칼럼명을 부분 매칭한다. surveys$d 타이핑하게 되면 “day” 칼럼을 선택하는 결과가 된다. 지름길로, 여타 지름길처럼 위험한 부작용을 갖을 수 있어, 가능하면 회피하는 것이 좋다. RStudio 자동완성 기능을 사용하면, 오탈자 없는 전체 칼럼명을 얻는데 몇자만 타이핑하면 충분하다.

도전과제

  1. data.frame()nrow() 함수는 행갯수를 반환한다. 이를 seq() 함수와 사용해서 새로운 data.frame()을 생성하는데 이름은 surveys_by_10으로 한다. 10번째 행부터 시작해서 매 10번째 행을 뽑아내서 저장시킨다. (10, 20, 30, …)

  2. survey 데이터셋의 year가 1999 행에 해당되는 관측점만 뽑아낸다.

  3. data.frame에서 nrow() 함수로 행의 갯수를 확인한다. nrow() 함수를 사용해서 survey 데이터셋의 가장 마지막 행의 관측점을 갖는 data.frame을 생성하시오.

  4. nrow()를 사용해서 행 인덱스로 활용하는 것을 살펴봤다. - 표기법과 조합해서 survey 데이터셋의 7번째부터 마지막 행까지 제외하는 즉, head(survey)와 같은 결과가 되도록 R코드를 작성하시오.