728x90
반응형

 요즘 일 때문에 많이 바빴더니 몇 일째 포스트 하지 못했다. 퇴근 후에 취미 생활을 즐기기 위해선, 운동을 하긴 해야겠다.

 이전 포스트에선 csv 파일을 가지고 오는 방법과 인코딩 문제에 대해 다뤄보았다. R을 사용하기 까다롭게 만드는 문제 중 하나가 인코딩 문제이고, 반면에 파이썬은 UTF-8을 기반으로 작성했기 때문에, 위 문제에서 보다 자유로운 편이다.

 R을 사용하다보면, 분석 환경 세팅 난이도는 그리 높지 않은 편인데(오프라인 환경이나 보안이 강한 환경에서도 큰 문제없이 설치가 가능하다), 데이터를 Import(가져올 때), Export(내보낼 때) 문제가 종종 발생한다. 이 문제는 대부분 인코딩 문제 거나, 데이터 자체에 R로 Import 될 때, 문제를 일으킬 수 있는 인자가 있기 때문이다.
(예를 들어 \n과 같은 문자는 줄을 바꾸는 문자인데, 이런 특수한 역할을 하는 문자가 섞인 데이터에선 데이터의 형태가 변형되어 버릴 수 있다. 이 문제는 R에만 국한된 것은 아니지만, 이 기회에 살짝 언급하고 가겠다.)

  이번 포스트에선 R로 엑셀 파일을 가지고 오고 내보내는 방법에 대해 학습해보도록 하겠다.

 

 

엑셀 파일을 R에서 불러와보자.

  • R에서 엑셀 파일을 불러오기 위해선 readxl 패키지를 사용해야한다.
  • read_excel()
    :  엑셀 파일을 읽어오는 함수
  • 주요 parameter
    : read_excel("파일위치/파일이름.확장자", range = "A1:B3": 엑셀 시트 내에서 내가 불러오고자 하는 영역, col_names = TRUE: 첫 행이 컬럼의 이름인지, sheet = 1, 불러올 시트)
    • read_excel의 특징은 자기가 원하는 sheet를 불러올 수 있다는 것이다.
    • read_excel은 Excel의 특징인 시트 내 좌표를 이용해서 내가 원하는 구간만 데이터를 가져올 수 있다. 
library("readxl")
read_excel(file.choose(), col_names = TRUE, range = "A1:D6", sheet = 1)
  • file 이름(워크 스페이스가 기본 경로이며, 다른 경로를 쓰고 사용하고 싶은 경우엔 해당 경로를 추가하면 된다.)
    이번 포스트에선 file.choose()를 넣어보았다.

  • file.choose()를 매개변수로 넣게 되면, 우리에게 익숙한 파일을 불러오는 창을 볼 수 있다.
  • file.choose()는 R에 익숙할수록 딱히 필요 없는 기능이지만, 파일의 경로에 대해 익숙하지 않은 초보에겐 추천하는 기능이다.
    (일단 파일을 불러와야 뭘 해볼 것 아닌가!)
## # A tibble: 5 x 4
##   name  class  math English
##   <chr> <dbl> <dbl>   <dbl>
## 1 사나      1    80      70
## 2 다연      1    85      65
## 3 쯔위      1    70      90
## 4 모모      1    65      60
## 5 예림      2    80      70

 

 

겸사겸사 워크 스페이스 아래에서 경로를 가지고 장난을 쳐보자.

  • 워크스페이스 경로는 D 드라이브의 RWorkSpace 파일이다.
  • 워크스페이스 경로에 손대지 말고, 해당 파일 안에 있는 Data 파일의 exam_exl.xlsx 파일을 불러와보자.

 

library("readxl")
getwd()
## [1] "D:/RWorkSpace"
read_excel("./Data/exam_exl.xlsx", col_names = TRUE, sheet = 1)
  • ./Data 에서 .은 워크스페이스의 경로(정확히는 현 위치)를 의미한다.
## # A tibble: 15 x 6
##    name  class  math English Korean science
##    <chr> <dbl> <dbl>   <dbl>  <dbl>   <dbl>
##  1 사나      1    80      70     65      90
##  2 다연      1    85      65     75      70
##  3 쯔위      1    70      90     65      60
##  4 모모      1    65      60     55      70
##  5 예림      2    80      70     65      55
##  6 현희      2    75      85     60      80
##  7 정은      2    90      60     70      85
##  8 혜원      2    65      80     75      80
##  9 준식      2    75      70     90      65
## 10 재성      3    70      90     65      85
## 11 민철      3    60      85     75      90
## 12 현택      3    75      65     80      70
## 13 현승      3    90      70     65      85
## 14 윤기      3    80      65     90      70
## 15 기훈      3    95      55     85      90
  • 위 방식을 이용하면, 파일 관리를 보다 수월하게 할 수 있다.

 

 

 

 

엑셀 파일로 저장해보자.

  • 엑셀로 파일을 불러왔으니, 이번엔 엑셀로 파일을 내보 내보자.
  • 위에서 사용한 패키지인 readxl은 말 그대로 엑셀을 읽어오는 패키지이기 때문에, 엑셀로 파일을 내보내려면, writexl이라는 패키지를 설치해야 한다.
  • 그러나 가능한 데이터는 엑셀 파일로 저장하지는 말도록 하자(데이터가 엑셀로 와서 어쩔 수 없는 상태에 엑셀을 R로 읽는 것이지, 데이터 관리를 엑셀로 하는 것은 좋지 않다. 이에 대한 내용은 후술 하도록 하겠다.)
  • write_xlsx()
    : 데이터를 엑셀 형태로 저장한다.
  • 주요 parameter
    : write_xlsx(data, path = "경로/파일이름.xlsx")
library("writexl")
data = data.frame(ID = c("A01", "A02", "A03", "A04", "A05"),
                  math = c(60, 80, 70, 90, 65))

write_xlsx(data, path = "./Data/test.xlsx")
  • 위 과정을 거치면 아래와 같이 엑셀 파일이 생성되는 것을 확인할 수 있다.

 

 

 

 

엑셀 파일로 데이터 관리를 하는 것은 좋을까?

 이번 포스트에선 엑셀로 파일을 올리고 내리는 법을 알아보았다. 그런데 과연 엑셀로 파일을 관리할 일이 있을까? 엑셀은 데이터를 관리하는 데 있어서 상당히 많이 사용되지만, 동시에 단점도 많은 툴이기 때문에, 개인적으로 비추한다. 엑셀을 사용할 바에 csv 파일로 파일을 읽고 쓰는 것을 추천한다.

 엑셀의 단점은 다음과 같다.

  • 엑셀에 단위가 매우 큰 숫자를 넣는 경우 E(지수 형태)로 변환이 되어, 실제 값의 정보가 손실된다.

  • 많은 양의 데이터(행이 65,536개 이상)를 관리할 수 없다.

 물론, 위 문제가 R에서 패키지를 이용해서 파일을 불러오고 내보내는 상황에서도 적용되는지 확신은 못하지만, 당신이 R에 익숙해지면 익숙해질수록, 엑셀은 느려 터지고, 불편한 툴이라는 생각이 들어서 자동으로 멀리하게 될 것이다. R은 코드를 통해서 작동하므로, 엑셀보다는 진입장벽이 있긴 하지만, R에 익숙해지고 나면, 속도, 기능 등 모든 면에서 R이 압도적이므로, 엑셀로 파일을 관리하는 일은 사라지게 될 것이다.
(필자는 엑셀로 수정할 바에 차라리 R로 코딩을 하는 것을 선호할 정도로 R은 쉽고 빠르다!)

 

 

 이번 포스트에선 엑셀로 R에 파일을 올리고 내리는 방법에 대해 학습해보았다. 다음 포스트에선 텍스트 파일을 R로 다루는 방법에 대해 학습해보겠다.

728x90
반응형
728x90
반응형

 데이터 분석을 위해선 먼저 분석을 할 수 있는 환경을 만들어야 하고, 그다음엔 분석을 할 데이터를 가지고 와야 한다. 이번 포스트에선 R에서 데이터를 가지고 오는 방법에 대해 학습해보도록 하겠다.

 

데이터 가져오기(Data Import)

  • R은 일반적으로 csv 파일, R 전용 데이터 파일인 RData(확장자: .rda 또는 .rdata), 엑셀 파일 등으로 데이터를 입출력한다.
  • 이외에도 DB, Json, SPSS 파일 등 다양한 형태로 데이터 입출력 관리를 한다.
  • 이번 포스트에서는 csv, RData, SPSS, 엑셀 파일로 데이터를 관리하는 방법에 대해 학습해보도록 하겠다.
  • DB를 통해 데이터를 관리하는 것은 추후 RMySQL 코드를 다루면서 짚고 넘어가도록 하겠다.

 

 

디렉터리(Directory) 확인하기.

  • 디렉터리란, 다른 말로 폴더(Folder),  카탈로그(Catalog)라고도 하는데, 디렉터리라는 이 말보다 디렉터리를 부르는 다른 용어인 폴더라는 단어가 더 이해하기 쉬울 수 있다.
  • 디렉터리는 상위 디렉토리(부모 디렉토리)와 하위 디렉토리(자녀 디렉토리)로 구성된 트리 형태의 구조를 가지고 있다.
  • R에서 당신이 확인할 디렉터리는 바로 워킹 디렉토리(Working Directory)이다.
  • 워킹 디렉토리는 R에서 작업하는 파일들이 기본적으로 읽고 쓰이는 공간이다.
  • 디렉토리 관련 기본 코드
    • getwd()
      : 현재 워킹 디렉터리를 확인한다.
    • setwd("폴더 주소")
      : "폴더 주소"로 워킹 디렉토리를 잡아준다.
      ※ 만약 당신이 워킹 디렉터리의 주소를 폴더의 url 주소를 복사 붙여 넣기 한다면, 슬래쉬(/) 역 슬래쉬로(\) 잡혀서 들어가므로, 이를 바꿔주어야 한다.
setwd("D:/Rworkspace")
getwd()
## [1] "D:/Rworkspace"

 

 

 

파일을 가지고 와보자.

  • 파일 경로에 한글이 들어가 있는 경우, 오류가 발생할 수 있으므로, 경로와 대상 파일의 명칭을 반드시 영어로 바꿔주자.
  • list.file()
    : 워킹 디렉터리에 있는 파일 리스트를 가지고 온다.
    (만약 괄호 안에 다른 경로를 넣는다고 하면, 그 경로에 있는 파일 리스트를 가지고 온다.)
  • 파일을 가지고 오는 방법은 다음과 같다.
    (csv, excel, spss, txt, rdata 파일 모두 함수 안에서 파라미터로 불러오고자 하는 파일의 경로나, file.choose()로 파일을 클릭해서 가져올 수 있다. 아래 내용이 잘 이해가 안 될 수 있는데, 일단 넘기고 따라서 학습해보자)
    • 워킹 디렉터리 내 파일을 가지고 오는 경우: "파일명.확장자"
    • 가져오려는 파일의 위치가 가변적인 경우, "파일위치/파일명.확장자"로 불러오기
    • file.choose()를 사용하여 파일을 선택하여 불러오기 
  • R은 기본적으로 제공하는 데이터셋이 있으며, 해당 목록은 data() 함수를 통해서 볼 수 있다.

 

 

csv 파일을 가지고 와보자.

  • CSV(Comma-separated Values)는 말 그대로 쉼표(Comma)로 값들이 구분된 형태이다. 
  • csv 파일은 엑셀, SAS, SPSS, R, Python 등 데이터를 다루는 대부분의 프로그램에서 읽고 쓰기가 가능한 범용 데이터 파일이다.
  • csv 형식은 다양한 프로그램에서 지원하고 엑셀 파일에 비해 용량이 매우 적기 때문에 데이터를 주고받는 경우 자주 사용된다.
  • 단점으로는, csv파일은 쉼표로 구분자가 들어가 있으므로, 데이터 자체에 쉼표가 들어가 있는 경우, 데이터 취급이 곤란해진다(구분자를 탭 문자로 바꾼 TSV 등을 사용한다.)
  • read.csv()
    : csv 파일을 가지고 온다.
  • 주요 Parameter
    : read.csv(file, header = FALSE, sep = ",", na.strings = "NA", stringsAsFactors = TRUE, fileEncoding = "", encoding = "unknown")
    # 파일 이름 / 첫 행을 헤더로 처리하여, 컬럼의 이름으로 할지 여부 / 구분자 / 데이터에 결측 값이 있는 경우 대응시킬 값 지정 / 문자열을 펙터로 가지고 올지 여부 / 가지고 오는 파일의 인코딩 / 문자열을 표시할 때의 인코딩
  • stringsAsFactors는 가능한 FALSE로 하여 가지고 오자(Default = TRUE)이다. 만약, 디폴트 값 그대로 가지고 온다면, 모든 문자형이 Factor로 들어오기 때문에, 텍스트 데이터를 조작하기가 꽤 어려워진다. 만약, 텍스트 데이터가 단순한 범주형 척도로써 존재한다면, TRUE로 가지고 와도 큰 문제가 없다.
  • write.csv()
    : csv 파일로 저장한다.
  • 주요 Parameter
    : write.csv(data, file="", row.names=TRUE, encoding = "unknown")
    # 파일로 저장할 데이터 / 데이터를 저장할 파일명(경로) / 행 이름을 csv 파일에 포함하여 저장할지 여부 / 파일을 어떤 인코딩으로 저장할지
  • 먼저 list.files()를 실시하여, 워킹 디렉터리에 위치한 파일의 정확한 이름을 확인하자.

 

 

워킹 디렉터리에서 파일 이름으로 파일을 가지고 와보자.

  • list.files()를 보고 직접 타이핑해서 가지고 오자.
# 워킹 디렉토리에 있는 파일을 리스트업 해보자.
list.files()
##[1] "asdfsadf.html"      "asdfsadf.Rmd"       "exam_csv.csv"         "exam_exl.xlsx"      "exam_spss.sav"      "kyrbs2016.sav"     
##[7] "old_2011.csv"       "sda.html"           "sda.Rmd"            "ypdata_new_w10.sav"
  • 위 파일들에서 exam_csv.csv라는 csv 파일을 가지고 와보자.
  • 해당 csv 파일은 Office 365의 Excel을 이용해서 만든 파일이며, 파일의 구조는 아래 사진과 같다. 

  • 해당 파일을 불러와보자.
read.csv("exam_csv.csv", header = TRUE, sep = ",", na.strings = "NA", stringsAsFactors = TRUE)
##Error in type.convert.default(data[[i]], as.is = as.is[i], dec = dec, : '<82><82><98>'에서 유효하지 않은 멀티바이트 문자열이 있습니다
  • 자 아주 간단한 파일이지만, 갑자기 "Error in type.convet.default(data[[i]], as.is = as.is[i], dec = dec, : '<ec><82><ac><eb><82><98>' 에서 유효하지 않은 멀티바이트 문자열이 있습니다."라며 오류가 뜬다!
  • 위 오류가 발생한 이유는, office 365 엑셀의 인코딩 문제인데, 일반적으로는 fileEncoding과 encoding 파라미터를 손봐주면 된다.
  • 한글에 관련된 인코딩은 크게 3개가 있다.
    • utf-8
    • CP949
    • euc-kr
  • 아래와 같이 인코딩 파라미터를 잡아줘서 해결해보자.
  • 엑셀 파일을 저장할 때, CSV UTF-8(쉼표로 분리)(*.csv)로 해주었다.
read.csv("exam_csv.csv", header = TRUE, stringsAsFactors = TRUE, fileEncoding = "UTF-8", encoding = "CP949")
## Error in read.table(file = file, header = header, sep = sep, quote = quote, : 입력에 가능한 라인들이 없습니다
  • 위와 같은 또 다른 에러가 뜨면서 읽어오질 못하였다.
  • 위 문제 해결 방법은 아주 단순 무식하지만 확실한 방법과 약간 고급진 방법 2가지가 있다.

 

 

단순한 방법으로 해결해보자.

  • 위 문제는 오피스 365 엑셀에서만 발생하는 문제인데, 오피스 365를 쓰지 않으면, 애초에 발생하지 않는다.
  • csv는 ","가 구분자로 작용하여, 단어와 단어를 구분하는 형태이다.
  • 그렇다면 엑셀에서 만든 csv 파일을 txt에서 가져와서 인코딩 형태를 바꿔서 저장해보자.
  • 리눅스 환경이라면
    • "file -bi 파일명"으로 파일의 인코딩을 확인하고
    • "iconv -c -f 원래 인코딩 -t 바꿀 인코딩 파일명 > 새로운 파일명"으로 인코딩을 쉽게 바꿔줄 수 있다
  • 윈도우 환경이라면 "마우스 오른쪽 클릭 > 연결 프로그램 > 메모장"을 사용하면, csv 파일을 메모장으로 열 수 있다.

 

  • "파일 > 다른 이름으로 저장"으로 들어가서 인코딩을 확인해보자!
  • 보면 UTF-8(BOM)으로 돼있는데, 이 BOM이 문제다!
  • 인코딩을 UTF-8 또는 ANSI로 바꿔보자

 

  • 인코딩을 UTF-8로 한다면, fileEncoding 파라미터를 "UTF-8"로 바꾸면 된다.
  • 인코딩을 ANSI로 한다면, 따로 파라미터를 잡아주지 않아도 된다(CP949나 euc-kr)로 잡아줘도 된다.
    (인코딩에 대해서는 개념으로 추후 다시 한번 다루도록 하겠다.)
list.files()
##  [1] "asdfsadf.html"         "asdfsadf.Rmd"          "exam_csv.csv"         
##  [4] "exam_csv_ANSI.txt"     "exam_csv_encoding.txt" "exam_exl.xlsx"        
##  [7] "exam_spss.sav"         "kyrbs2016.sav"         "old_2011.csv"         
## [10] "sda.html"              "sda.Rmd"               "ypdata_new_w10.sav"
read.csv("exam_csv_ANSI.txt", header = TRUE, stringsAsFactors = TRUE)
##    name class math English Korean science
## 1  사나     1   80      70     65      90
## 2  다연     1   85      65     75      70
## 3  쯔위     1   70      90     65      60
## 4  모모     1   65      60     55      70
## 5  예림     2   80      70     65      55
## 6  현희     2   75      85     60      80
## 7  정은     2   90      60     70      85
## 8  혜원     2   65      80     75      80
## 9  준식     2   75      70     90      65
## 10 재성     3   70      90     65      85
## 11 민철     3   60      85     75      90
## 12 현택     3   75      65     80      70
## 13 현승     3   90      70     65      85
## 14 윤기     3   80      65     90      70
## 15 기훈     3   95      55     85      90
  • 위 방법이 가장 간단하면서도 조금 무식한 해결책이라고 할 수 있다.
  • 데이터도 정상적으로 잘 출력되었다.

 

 

 

조금 고급진 방법으로 해결해보자.

  • 개인적으로는 위의 방법을 추천하지만, 경우에 따라서 좀 더 고급진 방법을 써야 할 수도 있을 것이다.
  • 위 인코딩 문제는 간단하게 말해서 Windows에서 한글이 제대로 인식이 안되고, RStudio에서도 한글이 인식이 제대로 안돼서 발생하는 문제라고 할 수 있는데, R의 System 설정을 손대는 함수 중 언어를 손대는 함수인 Sys.locale()을 이용해서 이를 해결할 수 있다.
  • Sys.setlocale()
    : 로케일을 설정하는 함수로, 로케일은 사용자의 언어, 사용자 인터페이스의 언어를 정해주는 함수이다.
  • 주요 Parameter
    : Sys,setlocale(category = "LC_ALL", locale = "")
Sys.setlocale("LC_ALL", "C") # 언어를 제거해버리자
data = read.csv("exam_csv.csv", header = TRUE, stringsAsFactors = TRUE, encoding = "UTF-8")
data
##       X.U.FEFF.name class math English Korean science
## 1  <U+C0AC><U+B098>     1   80      70     65      90
## 2  <U+B2E4><U+C5F0>     1   85      65     75      70
## 3  <U+CBD4><U+C704>     1   70      90     65      60
## 4  <U+BAA8><U+BAA8>     1   65      60     55      70
## 5  <U+C608><U+B9BC>     2   80      70     65      55
## 6  <U+D604><U+D76C>     2   75      85     60      80
## 7  <U+C815><U+C740>     2   90      60     70      85
## 8  <U+D61C><U+C6D0>     2   65      80     75      80
## 9  <U+C900><U+C2DD>     2   75      70     90      65
## 10 <U+C7AC><U+C131>     3   70      90     65      85
## 11 <U+BBFC><U+CCA0>     3   60      85     75      90
## 12 <U+D604><U+D0DD>     3   75      65     80      70
## 13 <U+D604><U+C2B9>     3   90      70     65      85
## 14 <U+C724><U+AE30>     3   80      65     90      70
## 15 <U+AE30><U+D6C8>     3   95      55     85      90
Sys.setlocale("LC_ALL", "Korean") # 언어를 한글로 바꾸자.
## [1] "LC_COLLATE=Korean_Korea.949;LC_CTYPE=Korean_Korea.949;LC_MONETARY=Korean_Korea.949;LC_NUMERIC=C;LC_TIME=Korean_Korea.949"
data
##    X.U.FEFF.name class math English Korean science
## 1           사나     1   80      70     65      90
## 2           다연     1   85      65     75      70
## 3           쯔위     1   70      90     65      60
## 4           모모     1   65      60     55      70
## 5           예림     2   80      70     65      55
## 6           현희     2   75      85     60      80
## 7           정은     2   90      60     70      85
## 8           혜원     2   65      80     75      80
## 9           준식     2   75      70     90      65
## 10          재성     3   70      90     65      85
## 11          민철     3   60      85     75      90
## 12          현택     3   75      65     80      70
## 13          현승     3   90      70     65      85
## 14          윤기     3   80      65     90      70
## 15          기훈     3   95      55     85      90
  • 위 방법을 통하면, RStudio에서 출력되는 언어를 제거하고, 다시 한글로 출력시켜서, 우리가 원하는 결과가 도출되게 할 수 있다.
  • 개인적으로는 텍스트 파일로 열어서 인코딩을 바꾸는 방법이 쉬우니, 해당 방법을 추천한다.
  • 해당 오류는 오피스 365 버전에서만 발생하는 것으로 확인하였다(다른 버전에서도 발생하는 것은 아직까진 보지 못했다...)

 

 

 

이번엔 CSV 파일 형태로 내보 내보자.

  • write.csv()
    : 데이터를 csv 형식으로 저장한다.
  • 주요 parameter
    : write.csv(data, file = "파일 경로", quote = TRUE, row.names = TRUE, fileEncoding = "")
    # 저장하고자 하는 데이터, 파일의 경로, 문자형 데이터에 ""따옴표를 넣는다, 행 이름을 넣는다, 저장하는 파일의 인코딩
  • 위 parameter만 알아도 csv를 다루는데 큰 문제는 없으나, csv는 굉장히 자주 쓰게 될 파일 형식이므로, F1을 눌러서 자세한 설명을 보도록 하자.
data = data.frame(ID = c("A01", "A02", "A03", "A04", "A05"),
                  math = c(60, 80, 70, 90, 65))

write.csv(data, "test.csv", quote = TRUE, row.names = FALSE, fileEncoding = "CP949")

 

  • 위 코드를 실행하면, 워크스페이스에 내가 원하는 data가 내보내진 것을 볼 수 있다.

 

 

 이번 포스트에선 CSV 파일을 여는 방법과 인코딩 문제에 대해 학습해보았다. 인코딩 문제는 당신이 한국인이고, R을 다룬다면, 지겹게 마주할 수 있는 문제이며, 위의 방식에서 크게 벗어나질 않는다.

 다음 포스트에선 엑셀 파일을 가지고 오는 방법과 파일 경로를 이용해서 파일 관리를 하는 법을 간략하게 다뤄보자.

728x90
반응형
728x90
반응형

dplyr 패키지

dplyr 패키지란?

: dplyr 패키지는 데이터 전처리에서 굉~~~~장히 많이 쓰이는 패키지로, dplyr과 tidyr, reshape2, stringr 이 4가지 패키지만 다룰 수 있어도 데이터 핸들링은 거의 다 할 수 있다고 해도 과언이 아니다. 

 특히 tidyr, reshape2, stringr은 보다 원초적인 상황(데이터가 지저분하게 섞여 있거나, 텍스트 마이닝을 하는 상황)에서 주로 쓰인다면, dplyr은 공공데이터포털 등과 같은 어느정도 정제된(한 셀에 하나의 원소만 들어가 있는) 원시자료를 사용할 때, dplyr 하나만 있어도 충분하다고 할 정도로 다양한 기능을 제공한다고 할 수 있다.

 이제 dplyr 패키지가 얼마나 중요한지는 알겠는데, 데이터 전처리가 대체 무엇이길래 dplyr 패키지가 중요하다는 것일까? 이번엔 데이터 전처리에 대해서 간략하게 알아보자.

 

 

 

 

데이터 전처리(Data Preprocessing)란?

 데이터 전처리는 데이터 가공(Data Manipulation), 데이터 핸들링(Data Handling), 데이터 랭글링(Data Wrangling), 데이터 먼징(Data Munging) 등 다양한 이름으로 불리며, 원시적인 상태의 데이터를 내가 원하는 형태로 바꿔주는 모든 과정을 말한다.

 데이터 전처리는 데이터에서 내가 원하는 정보를 찾아내는 데이터 분석의 모든 과정에서 약 80% 가까운 비중을 차지할 정도로 매우 중요한 작업이며, 우리가 아는 회귀분석, 랜덤포레스트, 군집분석 등과 같은 멋들어진 이름을 가진 분석 기법들은 이 데이터 전처리 작업을 거치지 않는다면, 실시할 수 없다.

 몇 몇 데이터 분석가는 데이터 전처리를 단순하고, 귀찮고, 아까운 시간이 소모되는 과정이라고도 생각하는데, 데이터 전처리 과정에서 데이터를 세세하게 뜯어보며 내가 원하는 형태로 만들어가다보면, 데이터 자체에 대한 이해와 데이터 자체의 특성을 이용해 보다 효율적이고 논리적인 변수를 만들어낼 수 있고, 사용하고자 하는 분석 기법에 맞게 데이터를 만들어주는 과정에서 분석 기법에 대한 심층적인 이해와 같이 다양한 insight를 얻을 수도 있다.

 그만큼 데이터 전처리는 중요하고, 데이터 전처리 과정에서 코딩 실력 역시 많이 늘게 되므로, 꼭 데이터 전처리의 필수 패키지인 dplyr를 공부해보도록 하자. 

 

 

 

 

dplyr 패키지의 특징

  • R 데이터 타입의 꽃이라고 할 수 있는 데이터 프레임 처리에 특화된 패키지이다.
  • C++ 을 기반으로 만들어졌기 때문에 매우 빠르다.
  • 파이프 라인(%>%)이라는 연산자를 사용할 수 있으며, 그로인해 코드의 가독성이 매우 크게 올라간다.
    • 파이프 라인의 단축키는 Alt + Shift + m 이다.
  • R을 효과적으로 사용하려면 apply함수(다음에 자세히 다루겠다.)의 기능을 아주 쉽게 쓸 수 있다.
  • tibble이라는 데이터 프레임을 보다 편리하게 쓸 수 있는 데이터 타입을 제공한다.

자 그럼 이제 본격적으로 dplyr 패키지에 대해 공부해보도록 하자.

 

※ 예제 데이터

# 예제 데이터
name = c("쯔위", "미나", "다현", "모모", "나연", "정연", "사나", "지효", "채영")
math = c(100, 75, 90, 60, 65, 70, 80, 70, 100)
english = c(80, 80, 65, 45, 70, 80, 70, 65, 75)
Korean = c(65, 70, 70, 80, 90, 60, 60, 85, 80)

exam = data.frame(name, math, english, Korean)
exam
##   name math english Korean
## 1 쯔위  100      80     65
## 2 미나   75      80     70
## 3 다현   90      65     70
## 4 모모   60      45     80
## 5 나연   65      70     90
## 6 정연   70      80     60
## 7 사나   80      70     60
## 8 지효   70      65     85
## 9 채영  100      75     80

 

 

 

 

컬럼 이름 바꾸기

  • rename()
    : dplyr 패키지의 rename()이란 함수로, 기존의 dataframe에서 컬럼의 이름을 바꾸려면, colnames()란 함수를 이용해서 벡터로 새로운 컬럼명이 포함된 컬럼의 이름 벡터를 넣어줘야했지만, rename()함수는 내가 원하는 컬럼의 이름만 바꿀 수 있다.
  • 주요 Parameter
    rename(data, afterColumnName1 = beforeColumnName1, afterColumnName2 = beforeColumnName2)
    바꾼 후 컬럼의 이름 = 바꾸기 전 컬럼 이름으로 넣으면 된다.
  • 컬럼 이름에서 english는 Eng, Korean은 Kr로 바꿔보자.

1) 일반적인 함수 사용.

library(dplyr)
# 컬럼을 dplyr의 rename()함수로 바꿔주자.
rename(exam, Eng = english, Kr = Korean)
##   name math Eng Kr
## 1 쯔위  100  80 65
## 2 미나   75  80 70
## 3 다현   90  65 70
## 4 모모   60  45 80
## 5 나연   65  70 90
## 6 정연   70  80 60
## 7 사나   80  70 60
## 8 지효   70  65 85
## 9 채영  100  75 80

 

2) 파이프라인 사용.

# 파이프라인을 써서 컬럼의 이름을 바꿔보자.
exam = exam %>% rename(Eng = english, Kr = Korean)
##   name math Eng Kr
## 1 쯔위  100  80 65
## 2 미나   75  80 70
## 3 다현   90  65 70
## 4 모모   60  45 80
## 5 나연   65  70 90
## 6 정연   70  80 60
## 7 사나   80  70 60
## 8 지효   70  65 85
## 9 채영  100  75 80
  • 자 1)과 2)를 보면, 함수의 재료가 되는 data가 함수 안에 있느냐 밖에 있느냐란 차이가 생겼다.
  • 파이프라인은 dplyr 패키지의 특징 중 하나로, 파이프라인을 쓰면, 최소한의 변수만 지정하여, 데이터 핸들링을 할 수 있다. 그로 인해 코드의 가독성이 증가하고, 변수명을 잘못 써서 오류가 발생하는 일도 줄일 수 있다.
  • 아직은 잘 와닿지 않을테니, 좀 더 학습을 진행해보자.

 

 

 

 

새로운 컬럼 만들기

  • mutate()
    : 기존의 변수를 조합하거나 다른 함수를 적용하여 새로운 컬럼을 만들 수 있다.
  • mutate 함수의 괄호 안에 생성하고 싶은 변수 식을 넣으면 된다.
  • mutate 함수를 이용하여 변수를 n개 만드는 경우 괄호 안에서 변수식을 쉼표로 연결하면 된다.
  • R(기초) 데이터프레임에서 다루었던 "df$newColumn <- 벡터"와 유사하지만 보다 사용하기 직관적이다.
  • 파이프라인을 사용하여, 총점, 평균, 등급 컬럼을 생성해보자.
    (등급은 80점을 초과하는 경우 A등급, 70점을 초과하는 경우 B등급, 나머지는 C등급으로 한다.)
# 총점, 평균, 등급 변수를 생성해보자.
library(dplyr)
exam = exam %>% mutate(total = math + Eng + Kr,
                mean = total/3,
                grade = ifelse(mean > 80, "A", ifelse(mean > 70, "B", "C")))
  • 생성된 결과를 원 데이터 변수의 이름에 담거나 다른 변수의 이름에 담아야만 생성된 결과가 반영된다.
##   name math Eng Kr total     mean grade
## 1 쯔위  100  80 65   245 81.66667     A
## 2 미나   75  80 70   225 75.00000     B
## 3 다현   90  65 70   225 75.00000     B
## 4 모모   60  45 80   185 61.66667     C
## 5 나연   65  70 90   225 75.00000     B
## 6 정연   70  80 60   210 70.00000     C
## 7 사나   80  70 60   210 70.00000     C
## 8 지효   70  65 85   220 73.33333     B
## 9 채영  100  75 80   255 85.00000     A
  • ifelse는 조건문으로, 컬럼을 새로운 컬럼으로 만들어낼 때, 조건을 부여한다.
  • ifelse 함수 사용법
    • ifelse(변수이름 논리연산자, TRUE인 경우 값, FALSE인 경우 값)
    • FALSE인 경우 값에 또 ifelse를 추가할 수 있다.
    • ex1) ifelse(math > 80, "A", "B")
      math가 80 초과일 때 A, 그렇지 않을 때 B로 한다.
    • ex2) ifelse(math > 80, ifelse(math > 70, "B", "C")
      math가 80 초과일 때 A, 그렇지 않은 경우 70 초과일 때 B, 나머지는 C로 한다.
  • ifelse의 값에서 일부 값은 원래 값으로 할 수도 있다. 
    • 점수가 75점 미만인 사람은 "바뀐값"으로, 나머지는 본인의 점수인 NewCol을 만들어보자.
exam %>% mutate(NewCol = ifelse(mean >= 75, mean, "바뀐값"))
##   name math Eng Kr total     mean grade      NewCol
## 1 쯔위  100  80 65   245 81.66667     A    81.66667
## 2 미나   75  80 70   225 75.00000     B          75
## 3 다현   90  65 70   225 75.00000     B          75
## 4 모모   60  45 80   185 61.66667     C      바뀐값
## 5 나연   65  70 90   225 75.00000     B          75
## 6 정연   70  80 60   210 70.00000     C      바뀐값
## 7 사나   80  70 60   210 70.00000     C      바뀐값
## 8 지효   70  65 85   220 73.33333     B      바뀐값
## 9 채영  100  75 80   255 85.00000     A          85
  • 위에 사용한 함수를 보면, 본래의 컬럼 이름인 mean을 그대로 넣은 영역인 본래의 값이 나왔다.

 

 

 

 

필요한 행(Row)만 추출하기

  • filter()
    : 조건을 걸어 사용자가 원하는 행만 출력할 수 있다.
  • 연산자와 데이터 타입에서 공부한 논리연산자를 활용해보자.
# exam에서 grade가 A인 학생들만 추출해보자.
exam %>% filter(grade == "A")
##   name math Eng Kr total     mean grade
## 1 쯔위  100  80 65   245 81.66667     A
## 2 채영  100  75 80   255 85.00000     A
# exam에서 math가 70점보다 높은 학생들을 출력해보자.
exam %>% filter(math >= 70)
##   name math Eng Kr total     mean grade
## 1 쯔위  100  80 65   245 81.66667     A
## 2 미나   75  80 70   225 75.00000     B
## 3 다현   90  65 70   225 75.00000     B
## 4 정연   70  80 60   210 70.00000     C
## 5 사나   80  70 60   210 70.00000     C
## 6 지효   70  65 85   220 73.33333     B
## 7 채영  100  75 80   255 85.00000     A
# exam에서 math가 70점 이상이면서 Eng가 80점 이상인 학생들을 출력해보자.
exam %>% filter(math >= 70 & Eng >= 80)
##   name math Eng Kr total     mean grade
## 1 쯔위  100  80 65   245 81.66667     A
## 2 미나   75  80 70   225 75.00000     B
## 3 정연   70  80 60   210 70.00000     C
# exam에서 math가 90점 이상이거나 Eng가 80점 이상인 학생들을 출력해보자.
exam %>% filter(math >= 90 | Eng >= 80)
##   name math Eng Kr total     mean grade
## 1 쯔위  100  80 65   245 81.66667     A
## 2 미나   75  80 70   225 75.00000     B
## 3 다현   90  65 70   225 75.00000     B
## 4 정연   70  80 60   210 70.00000     C
## 5 채영  100  75 80   255 85.00000     A
# exam에서 grade가 B가 아닌 학생들만 출력해보자.
exam %>% filter(grade != "B")
##   name math Eng Kr total     mean grade
## 1 쯔위  100  80 65   245 81.66667     A
## 2 모모   60  45 80   185 61.66667     C
## 3 정연   70  80 60   210 70.00000     C
## 4 사나   80  70 60   210 70.00000     C
## 5 채영  100  75 80   255 85.00000     A
# exam에서 math가 70점 이상이면서 Eng가 70점 이상이고 Kr가 70점 이상인 학생들만 출력해보자.
exam %>% filter(math >= 70 & Eng >= 70 & Kr >= 70)
##   name math Eng Kr total mean grade
## 1 미나   75  80 70   225   75     B
## 2 채영  100  75 80   255   85     A

 

 

 

 

 

필요한 컬럼(Column)만 추출하기

  • select()
    : 원하는 열만 추출할 수 있다.
  • "R(기초) 데이터프레임"에서 학습한 Indexing 방법과 동일한 기능과 비슷한 사용방법을 가지고 있으며, 사용하지 않아도 큰 불편은 없으나, 파이프라인을 사용하여 dplyr의 다른 함수들과 조합한다면, 상당히 편리한 함수라고 할 수 있다.
# exam에서 math, Eng 컬럼만 가지고 온다.
exam %>% select(math, Eng)
##   math Eng
## 1  100  80
## 2   75  80
## 3   90  65
## 4   60  45
## 5   65  70
## 6   70  80
## 7   80  70
## 8   70  65
## 9  100  75
# exam에서 -math 컬럼을 제외하고 가지고 온다.
exam %>% select(-math)
##   name Eng Kr total     mean grade
## 1 쯔위  80 65   245 81.66667     A
## 2 미나  80 70   225 75.00000     B
## 3 다현  65 70   225 75.00000     B
## 4 모모  45 80   185 61.66667     C
## 5 나연  70 90   225 75.00000     B
## 6 정연  80 60   210 70.00000     C
## 7 사나  70 60   210 70.00000     C
## 8 지효  65 85   220 73.33333     B
## 9 채영  75 80   255 85.00000     A
# exam에서 math부터 Kr까지 가지고 온다.
exam %>% select(math : Kr)
##   math Eng Kr
## 1  100  80 65
## 2   75  80 70
## 3   90  65 70
## 4   60  45 80
## 5   65  70 90
## 6   70  80 60
## 7   80  70 60
## 8   70  65 85
## 9  100  75 80

 

 

 

 

 

순서대로 정렬하기.

  • arrange()
    : 특정 컬럼을 원하는 순서로 정렬할 수 있다.
    arrage(컬럼이름)
  • 오름차순은 Default 값으로 돼 있다.
  • 내림차순을 원하는 경우 arrage(desc(컬럼이름))을 하면 된다.
  • 여러 변수를 기준으로 정렬을 하고 싶은 경우 arrage()함수 안에 컬럼의 이름을 여러 개 넣으면 된다.
# exam에서 math를 오름차순 정렬해보자.
exam %>% arrange(math)
##   name math Eng Kr total     mean grade
## 1 모모   60  45 80   185 61.66667     C
## 2 나연   65  70 90   225 75.00000     B
## 3 정연   70  80 60   210 70.00000     C
## 4 지효   70  65 85   220 73.33333     B
## 5 미나   75  80 70   225 75.00000     B
## 6 사나   80  70 60   210 70.00000     C
## 7 다현   90  65 70   225 75.00000     B
## 8 쯔위  100  80 65   245 81.66667     A
## 9 채영  100  75 80   255 85.00000     A
# exam에서 Eng를 내림차순 정렬해보자.
exam %>% arrange(desc(math))
##   name math Eng Kr total     mean grade
## 1 쯔위  100  80 65   245 81.66667     A
## 2 채영  100  75 80   255 85.00000     A
## 3 다현   90  65 70   225 75.00000     B
## 4 사나   80  70 60   210 70.00000     C
## 5 미나   75  80 70   225 75.00000     B
## 6 정연   70  80 60   210 70.00000     C
## 7 지효   70  65 85   220 73.33333     B
## 8 나연   65  70 90   225 75.00000     B
## 9 모모   60  45 80   185 61.66667     C
# exam에서 math, Eng 순으로 오름차순 정렬해보자.
exam %>% arrange(math, Eng)
##   name math Eng Kr total     mean grade
## 1 모모   60  45 80   185 61.66667     C
## 2 나연   65  70 90   225 75.00000     B
## 3 지효   70  65 85   220 73.33333     B
## 4 정연   70  80 60   210 70.00000     C
## 5 미나   75  80 70   225 75.00000     B
## 6 사나   80  70 60   210 70.00000     C
## 7 다현   90  65 70   225 75.00000     B
## 8 채영  100  75 80   255 85.00000     A
## 9 쯔위  100  80 65   245 81.66667     A
# exam에서 math는 오름차순, Eng는 내림차순 순서로 정렬해보자.
exam %>% arrange(math, desc(Eng))
##   name math Eng Kr total     mean grade
## 1 모모   60  45 80   185 61.66667     C
## 2 나연   65  70 90   225 75.00000     B
## 3 정연   70  80 60   210 70.00000     C
## 4 지효   70  65 85   220 73.33333     B
## 5 미나   75  80 70   225 75.00000     B
## 6 사나   80  70 60   210 70.00000     C
## 7 다현   90  65 70   225 75.00000     B
## 8 쯔위  100  80 65   245 81.66667     A
## 9 채영  100  75 80   255 85.00000     A

 

 

 

 

 

컬럼별 요약하기와 집단별로 요약하기

  • group_by()
    : 한 컬럼 안에 있는 집단별로 그룹을 묶는다.
  • group_by() 안에 컬럼의 이름이 n개가 들어가면, 그룹을 여러 컬럼에 대해서 나눌 수 있다.
  • summarise()
    : 총합, 평균과 같은 요약 통계량을 추출한다.
  • summarise() 함수는 데이터 안에 결측값이 하나라도 있는 경우 결과를 결측값으로 출력한다.
    na.rm = TRUE를 내가 구하려는 요약통계량 함수 안에 넣는 경우, 결측값을 제외하고 결과를 출력한다.
  • 집단별 요약은 위 2개 함수를 동시에 사용하여 실행한다.
  • 위에서 mutate로 만든 평균이나 총합은 특정 변수들을 어떠한 계산식을 통해 만들어낸 새로운 변수라면
  • summurise()는 한 변수에 대한 요약 통계량이라고 할 수 있다.
# exam에서 math의 평균을 구해보자.
exam %>% summarise(mean_math = mean(math))
##   mean_math
## 1  78.88889
# exam에서 grade별 Eng의 총점과 평균 점수, 빈도를 구해보자.
exam %>% group_by(grade) %>% summarise(mean_Eng = mean(Eng),
                                       total_Eng = sum(Eng),
                                       n())
## `summarise()` ungrouping output (override with `.groups` argument)
## # A tibble: 3 x 4
##   grade mean_Eng total_Eng `n()`
##   <chr>    <dbl>     <dbl> <int>
## 1 A         77.5       155     2
## 2 B         70         280     4
## 3 C         65         195     3
  • 위 결과가 dplyr에서 사용되는 데이터프레임과 비슷한 데이터 타입인 tibble이다.
  • tibble은 차원, 변수별 변수 타입을 보여주는 형태라고 할 수 있다.

※ summarise() 함수에서 자주 사용하는 요약통계량 함수.

함수 의미 함수 의미
mean() 평균 min() 최솟값
sd() 표준편차 max() 최댓값
sum() 합계 n() 빈도
median() 중앙값    

 

 

 

 

※ dplyr의 내장 데이터인 starwars를 이용해서 원하는 결과를 만들어보자.

library(dplyr)
# dplyr 패키지의 starwars 데이터의 변수 목록을 가지고 와보자
colnames(starwars)
##  [1] "name"       "height"     "mass"       "hair_color" "skin_color"
##  [6] "eye_color"  "birth_year" "sex"        "gender"     "homeworld" 
## [11] "species"    "films"      "vehicles"   "starships"
  • starwars 데이터의 변수 목록을 보고 무엇을 사용할 것인지 판단해보자.
    변수내 원소의 종류가 비교적 적을 것으로 판단되는 gender와 species에 대해 분석을 진행해보자.
# starwars 데이터에서 gender, species에 대한 빈도표를 뽑아보자.
table(starwars$gender)
##  feminine masculine 
##        17        66
  • table() 함수는 벡터에 대한 빈도 분석을 하는데 사용하는 함수이다.
    (이외에도 여러 기능이 있으나, 이는 이후에 다루도록 하자.)
table(starwars$species)
## 
##         Aleena       Besalisk         Cerean       Chagrian       Clawdite 
##              1              1              1              1              1 
##          Droid            Dug           Ewok      Geonosian         Gungan 
##              6              1              1              1              3 
##          Human           Hutt       Iktotchi        Kaleesh       Kaminoan 
##             35              1              1              1              2 
##        Kel Dor       Mirialan   Mon Calamari           Muun       Nautolan 
##              1              2              1              1              1 
##      Neimodian         Pau'an       Quermian         Rodian        Skakoan 
##              1              1              1              1              1 
##      Sullustan     Tholothian        Togruta          Toong      Toydarian 
##              1              1              1              1              1 
##     Trandoshan        Twi'lek     Vulptereen        Wookiee          Xexto 
##              1              2              1              2              1 
## Yoda's species         Zabrak 
##              1              2
  • 변수 species는 Human이 35명으로 가장 많고, 나머지는 최소값 1, 최대값 6으로 그 수가 그리 많지 않다. 그러므로 Human, NonHuman 이 2가지 집합으로 이루어진 새로운 변수를 만들어보자.
# starwars 데이터에서 species 컬럼을 Human과 NonHuman 2가지로 나눈 isHuman 컬럼을 만들어보자.
starwars = starwars %>% mutate(isHuman = ifelse(species == "Human",  "Human", "NonHuman"))

# starwars 데이터에서 gender, isHuman으로 group_by하여, height에 대한 총합, 평균, 표준편차, 최대값, 최소값, 빈도를 구해보자.
starwars %>% group_by(gender, isHuman) %>% summarise(height_sum = sum(height),
                                                     height_mean = mean(height),
                                                     height_sd = sd(height),
                                                     height_min = min(height),
                                                     height_max = max(height),
                                                     n())
  • 연속형 변수 중에 결측값이 가장 적을 것으로 예측되는 height에 대해 요약 통계량을 생성해보자.
## `summarise()` regrouping output by 'gender' (override with `.groups` argument)
## # A tibble: 5 x 8
## # Groups:   gender [3]
##   gender    isHuman height_sum height_mean height_sd height_min height_max `n()`
##   <chr>     <chr>        <int>       <dbl>     <dbl>      <int>      <int> <int>
## 1 feminine  Human           NA         NA       NA           NA         NA     9
## 2 feminine  NonHum~       1353        169.      33.1         96        213     8
## 3 masculine Human           NA         NA       NA           NA         NA    26
## 4 masculine NonHum~         NA         NA       NA           NA         NA    40
## 5 <NA>      <NA>            NA         NA       NA           NA         NA     4
  • 분석 결과 대부분이 <NA>로 표시된 것을 알 수 있다.
  • <NA>는 값이 없음을 의미한다.
  • 데이터 분석을 하기 위해선 이러한 내가 원하지 않는 결과가 나온 경우 그 원인을 파악해보아야한다.
  • 위에서 말했듯이 summarise() 함수는 분석하려는 대상인 데이터 안에 결측값(<NA>)이 단 하나라도 있는 경우, 요약통계량을 <NA>로 출력한다. 이 경우 na.rm = TRUE를 각 요약 통계량 안에 삽입하여, 다시 결과를 출력해보자.
# starwars 데이터에서 species 컬럼을 Human과 NonHuman 2가지로 나눈 isHuman 컬럼을 만들어보자.
starwars = starwars %>% mutate(isHuman = ifelse(species == "Human",  "Human", "NonHuman"))

# starwars 데이터에서 gender, isHuman으로 group_by하여, height에 대한 총합, 평균, 표준편차, 빈도를 구해보자.
starwars %>% group_by(gender, isHuman) %>% summarise(height_sum = sum(height, na.rm = TRUE),
                                                     height_mean = mean(height, na.rm = TRUE),
                                                     height_sd = sd(height, na.rm = TRUE),
                                                     height_min = min(height, na.rm = TRUE),
                                                     height_max = max(height, na.rm = TRUE),
                                                     n())
## `summarise()` regrouping output by 'gender' (override with `.groups` argument)
## # A tibble: 5 x 8
## # Groups:   gender [3]
##   gender    isHuman height_sum height_mean height_sd height_min height_max `n()`
##   <chr>     <chr>        <int>       <dbl>     <dbl>      <int>      <int> <int>
## 1 feminine  Human         1282        160.      6.98        150        167     9
## 2 feminine  NonHum~       1353        169.     33.1          96        213     8
## 3 masculine Human         4194        182.      8.19        170        202    26
## 4 masculine NonHum~       6750        173.     46.9          66        264    40
## 5 <NA>      <NA>           544        181.      2.89        178        183     4
  • 도출된 결과를 보면 정상적으로 나온 것을 알 수 있다.
  • gender, isHuman에서 <NA>라고 출력된 결과가 있는 것을 볼 수 있다.
  • 해당 결과가 나온 원인을 알기 위해 gender와 isHuman에 대한 결측값 여부에 대한 빈도분석을 실시해보자.
  • is.na(백터)
    : 벡터 안에 결측값이 있는 경우, 해당 결측값을 TRUE로 결측값이 아닌 값을 FALSE로 나타낸다.
  • table(is.na(벡터))
    :위 함수를 이용하면, 해당 벡터 안에 몇 개의 결측값이 존재하는지 쉽게 알 수 있다. 
# is.na()함수를 이용해서 결측값의 갯수를 파악해보았다.
table(is.na(starwars$gender))
## FALSE  TRUE 
##    83     4
table(is.na(starwars$isHuman))
## FALSE  TRUE 
##    83     4
  • 두 함수 모두 결측값이 각각 4개 존재하는 것으로 나타났다.
  • 스칼라에서 공부한 Boolean의 성격을 이용해보면, 두 변수의 결측값이 동일한 객체(행)에 대한 것인지를 볼 수 있다.
# 두 변수의 결측값이 중복되는 것인지 확인해보자.
gender = is.na(starwars$gender)
isHuman = is.na(starwars$isHuman)
table(gender + isHuman)
##  0  2 
## 83  4
  • gender와 isHuman이라는 2개의 벡터를 is.na()를 이용해서 TRUE와 FALSE 2개의 인자로 구성된 벡터를 만들었다.
  • 두 벡터를 합치는 경우, 객체(행)에 대한 위치는 두 벡터 모두 동일하므로, 2개 벡터가 같은 객체에 대해 결측값으로 구성된 경우 2가 나올 것이며(TRUE + TRUE = 2), 2개 벡터가 서로 다른 객체에 대해 결측값이 있는 경우 1이 나올 것이다.(TRUE + FALSE = 1)
  • 결과가 0과 2로 이루어져있으며, 2의 숫자가 4개인 것을 보면, gender와 isHuman의 결측값을 가진 객체가 일치하는 것을 알 수 있다.

 

 

 

※ summarise 함수에서 na.rm = TRUE의 주의 사항

  • gender와 isHuman의 결측값이 동일한 객체(행)에 대해 존재한다는 것을 알았으므로, 이번엔 결측값이 있는 행들을 분석 대상에서 제외하고 보도록 하자.
  • 일반적으로 결측값 제거는 함부로 해서는 안되지만, 이는 사회과학에서 그 결측값의 발생이 어떠한 의도에 의해 발생했을 위험이 있기 때문이다.
  • 해당 데이터는 할리우드 영화인 Starwars에서 캐릭터들의 이름과 키, 몸무게, 머리카락색과 같은 개인정보가 담긴 데이터이다. 결측값이 발생한 대상에 대해 구체적으로 보도록 하자.
starwars[, c("name", "height", "gender", "isHuman", "species")] %>% filter(is.na(gender))
## # A tibble: 4 x 5
##   name           height gender isHuman species
##   <chr>           <int> <chr>  <chr>   <chr>  
## 1 Ric Olie          183 <NA>   <NA>    <NA>   
## 2 Quarsh Panaka     183 <NA>   <NA>    <NA>   
## 3 Sly Moore         178 <NA>   <NA>    <NA>   
## 4 Captain Phasma     NA <NA>   <NA>    <NA>
  • 보면 위 캐릭터들은 성별과 종(species)이 결측값(측정할 수 없는) 캐릭터임을 알 수 있다.
  • 이번 분석은 성별이 있으며 종이 인간/비인간인 객체를 대상으로 하고 있으므로, 이런 경우엔 위 대상들을 분석 대상에서 아예 제거하고 실시해도 된다.
  • 이번엔 결측값이 있는 행을 제거하고 진행해보자

 

1) 결측값이 행에서 아예 제거된 상태의 결과.

# 분석 대상에서 결측값이 제거된 상태에서 분석 결과
chooseColumns = starwars[,c("gender", "isHuman", "height")]
removeNA = na.omit(chooseColumns)

removeNA %>% group_by(gender, isHuman) %>% summarise(height_sum = sum(height),
                                                     height_mean = mean(height),
                                                     height_sd = sd(height),
                                                     height_min = min(height),
                                                     height_max = max(height),
                                                     n())
  • na.omit()은 결측값이 하나라도 있는 행을 제거하는 함수로, 데이터 전처리에서 필수인 결측값 제거에서 꼭 알고 있어야하는 함수이다.
    (추후 결측값에 대해 아주 상세히 다룰 예정이다.)
## `summarise()` regrouping output by 'gender' (override with `.groups` argument)
## # A tibble: 4 x 8
## # Groups:   gender [2]
##   gender    isHuman height_sum height_mean height_sd height_min height_max `n()`
##   <chr>     <chr>        <int>       <dbl>     <dbl>      <int>      <int> <int>
## 1 feminine  Human         1282        160.      6.98        150        167     8
## 2 feminine  NonHum~       1353        169.     33.1          96        213     8
## 3 masculine Human         4194        182.      8.19        170        202    23
## 4 masculine NonHum~       6750        173.     46.9          66        264    39

 

2) na.rm = TRUE를 적용한 결과.

# na.rm = TRUE의 결과.
starwars %>% group_by(gender, isHuman) %>% summarise(height_sum = sum(height, na.rm = TRUE),
                                                     height_mean = mean(height, na.rm = TRUE),
                                                     height_sd = sd(height, na.rm = TRUE),
                                                     height_min = min(height, na.rm = TRUE),
                                                     height_max = max(height, na.rm = TRUE),
                                                     n())
## `summarise()` regrouping output by 'gender' (override with `.groups` argument)
## # A tibble: 5 x 8
## # Groups:   gender [3]
##   gender    isHuman height_sum height_mean height_sd height_min height_max `n()`
##   <chr>     <chr>        <int>       <dbl>     <dbl>      <int>      <int> <int>
## 1 feminine  Human         1282        160.      6.98        150        167     9
## 2 feminine  NonHum~       1353        169.     33.1          96        213     8
## 3 masculine Human         4194        182.      8.19        170        202    26
## 4 masculine NonHum~       6750        173.     46.9          66        264    40
## 5 <NA>      <NA>           544        181.      2.89        178        183     4
  • 위, 아래 표를 비교해보면 총합, 평균, 표준편차, 최소값, 최댓값은 동일하나, n()이 서로 다르고 , 5번째 행이 위엔 없으나 아래에 있는 것을 볼 수 있다.
  • 이는 위의 na.omit()을 이용해서 결측값이 있는 행을 모두 제거한 결과는 행에 서 결측값이 있는 것을 모두 제거하였기 때문에, gender, isHuman, height 이 3가지 변수 중에 <NA>를 하나라도 가진 객체를 분석 대상에서 아예 제거해버렸기 때문에 키에 대한 n()을 제외한 모든 요약 통계량은 같게 나오지만, n()은 na.rm이라는 인자가 아예 존재하지 않기 때문에 다른 결과가 나온 것이다.
  • 즉, 간단하게 말하자면 n()는 결측값이 있던 없던 대상의 수를 세버려기 때문에 height에 결측값이 있다할지라도 수를 세어버리고, na.omit()으로 결측값이 있는 행을 모두 제거한 결과는 성별, 종족이 측정된 값이며, 키를 측정한 대상에 한정한 결과를 가져오는 것이다.
  • na.rm은 단순하게 해당 벡터에 대해서만 결측값을 제거하므로, na.rm을 적용할 수 없는 n()은 조금 조심해서 사용하도록 하자.
  • 위 문제를 명확하게 해결하기 위해선 분석 대상에 대한 명확한 정의가 필요하다.

 

 

 

 

데이터 합치기

  • left_join()
    : 두 데이터 프레임을 동일한 열을 기준으로 열로 합치는 경우 left_join()을 사용한다.
  • left_join(기준이 되는 합쳐지는 쪽, 합칠 데이터)
    • 기준이 될 ID와 같은 동일한 컬럼이 필요하다.
    • left_join()을 응용하여 특정 변수를 기준으로 새로운 변수를 추가할 수 있다.
    • 데이터는 한 번에 2개만 합치는 것이 가능하다.
    • cbind()는 열의 길이만 같다면 통으로 합치지만, left_join()은 기준 변수를 제외한 변수들이 기준 변수의 위치에 맞춰서 합춰진다.
    • 즉, left_join()에서 기준으로 사용할 열에 일치하는 값만 존재한다면(전부 존재 하지 않아도 된다.) 두 데이터 프레임의 길이가 서로 다르더라도 붙일 수 있다.
    • 기준이 되는 합쳐지는 쪽과 합칠 데이터에 대하여, 기준 컬럼의 인자가 동일한 집합이 아닌 경우에도 합쳐지며,기준이 되는 합쳐지는 쪽의 기준 컬럼에 없는 인자들은 모두 NA로 합쳐진다.
  • bind_rows()
    : 두 데이터 프레임의 열 이름이 동일한 경우 행을 위주로 합치는 경우, bind_rows()를 사용한다.
    • 세로로 합치는 경우, 합치는 두 데이터의 변수명이 일치해야한다.
    • 동일하지 않다면 rename()을 하거나, 일치하지 않는 변수를 제거해주자.
    • 합칠 두 데이터 프레임의 컬럼별 변수 타입이 동일해야한다.
      • 특히 character와 factor는 단순하게 눈으로 비교해서는 같게 보이므로, 꼭 주의하도록 하자.
# 한 열이 동일한 2개의 데이터 프레임을 한 열을 기준으로 합쳐보자.
mid_examResult = data.frame(name = c("민철", "재성", "현승", "기훈", "윤기"), midterm = c(60, 80, 90, 70, 85))
final_examResult = data.frame(name = c("민철", "재성", "현승", "기훈", "윤기"), finalterm = c(80, 75, 88, 65, 90))

left_join(mid_examResult, final_examResult)
## Joining, by = "name"
##   name midterm finalterm
## 1 민철      60        80
## 2 재성      80        75
## 3 현승      90        88
## 4 기훈      70        65
## 5 윤기      85        90
# 열의 이름이 동일한 2개의 데이터 프레임을 행끼리 세로로 합쳐보자.
class1 = data.frame(class = 1, name = c("민철", "재성", "현승", "기훈", "윤기"), mean = c(75, 80, 65, 90, 80))
class2  = data.frame(class = 2, name = c("태경", "시라", "재빈", "미선", "선화"), mean = c(70, 85, 80, 75, 90))

bind_rows(class1, class2)
##    class name mean
## 1      1 민철   75
## 2      1 재성   80
## 3      1 현승   65
## 4      1 기훈   90
## 5      1 윤기   80
## 6      2 태경   70
## 7      2 시라   85
## 8      2 재빈   80
## 9      2 미선   75
## 10     2 선화   90

 

 

 

지금까지 dplyr에 대해서 간략한 학습을 해보았다. 

이번 포스트에서 공부한 rename, mutate, filter, select, arrange, group_by, summarise, left_join, bind_rows는 개인적으로 dplyr 패키지에서 이것만 알아도 충분하다 싶어서 뽑은 함수들이며, 추후 dplyr에 대해 더 학습을 하다가, 괜찮은 함수가 있다면, 그 함수들도 추가해보도록 하겠다.

728x90
반응형
728x90
반응형

패키지(Packages)란

오픈 소스인 R의 장점은 다른 사용자들이 만들어놓은 함수들을 쓸 수 있다는 것이다. 이 함수들의 모음을 패키지(Package)라고 하며, R을 이용하는 사람은 인터넷이 된다면, 언제, 어디서든지 이 패키지를 다운 받아서 사용할 수 있다.

  • 패키지 공유는 CLAN(http://cran.r-project.org)의 'Packages'를 눌러서 어떤 패키지가 있는지 볼 수 있으며, 인터넷을 사용할 수 없는 환경에서 R을 써야하는 경우, 해당 싸이트에서 미리 사용할 패키지들을 다운받아올 수 있다. 
  • R은 스크립트 언어(간략히 말하면, 어떤 기능을 쓰고자할 때, 그 기능을 처음부터 만들 필요 없이, 그 기능이 담긴 함수를 사용하면 되는 언어란 소리이다.)이며, 스크립트 언어로써의 기능을 잘 활용하려면, 상황에 맞게 내게 필요한 패키지를 찾는 능력이 필요하다.
  • 각 패키지는 그 사용방법을 익히는데 도움을 주기 위한 내장 데이터셋을 가지고 있다.
  • 패키지 설치 후 패키지 이름에 커서를 올린 상태에서 F1을 누르면 해당 패키지에 대한 자세한 정보를 얻을 수 있는 링크등으로 이동할 수 있다.

 

 

 

패키지 관련 함수

  • available.packages("패키지 이름")
    : 해당 패키지가 CRAN에 등재되어 있는지 확인할 수 있는 함수.
  • install.packages("패키지 이름")
    : 해당 해키지를 설치하는 방법.
    패키지는 한번 설치하면, 더 이상 설치하지 않아도 된다.
  • library(패키지 이름)
    : 패키지를 사용하기 위해 가지고 온다.
    특정 패키지를 사용하고 싶을 때, 반드시 해당 코드를 한 번은 실행해 줘야한다.
  • library(help = 패키지 이름)
    : 패키지에 대한 정보를 출력한다.
    (패키지의 라이센스, 버전, 제작자 등을 표시하고, 패키지 안에 들어 있는 함수등을 표기한다.)
  • update.packages("패키지 이름")
    : 해당 패키지를 업데이트한다.
  • updata.packages()
    : 모든 패키지를 업데이트한다.
  • remove.packages("패키지 이름")
    : 해당 패키지를 삭제한다.
  • remove.packages()
    : 모든 패키지를 삭제한다.
  • 패키지이름::패키지의함수()
    : library() 함수를 사용하지 않고, 특정 패키지의 특정 함수를 사용한다.
    • 여러 패키지를 사용하는 경우, 패키지의 이름은 다르나, 함수가 동일한 경우가 종종 있다. 이 경우, 위 방법처럼 콜론을 2개 연달아 사용하면, 해당 함수가 어떤 패키지의 것인지를 쉽게 구분할 수 있다.
  • data(package = .packages(all.available = TRUE))
    : 설치된 패키지의 모든 내장 데이터를 볼 수 있다.
# dplyr 패키지의 내장데이터를 확인해보자
data_list = data(package = .packages(all.available = TRUE))
data_list$results[data_list$results[,"Package"] == "dplyr", ]
##      Package LibPath                             Item               
## [1,] "dplyr" "C:/RBasicFolder/R/R-4.0.1/library" "band_instruments" 
## [2,] "dplyr" "C:/RBasicFolder/R/R-4.0.1/library" "band_instruments2"
## [3,] "dplyr" "C:/RBasicFolder/R/R-4.0.1/library" "band_members"     
## [4,] "dplyr" "C:/RBasicFolder/R/R-4.0.1/library" "starwars"         
## [5,] "dplyr" "C:/RBasicFolder/R/R-4.0.1/library" "storms"           
##      Title                
## [1,] "Band membership"    
## [2,] "Band membership"    
## [3,] "Band membership"    
## [4,] "Starwars characters"
## [5,] "Storm tracks data"
  • 위 코드는 dplyr 패키지의 내장 데이터 목록을 행렬 형태로 가지고 온 것이다.
  • Item은 내장 데이터의 이름이다.
  • 위에서 "dplyr"만 다른 패키지의 이름으로 바꿔주면, 해당 패키지의 내장 데이터셋 목록을 가지고 올 수 있다.

 

 

 

수동으로 패키지 사용하기

  • R을 효율적으로 사용할 수 있게 해주는 RStudio에는, 설치된 패키지, library로 import된 패키지, 패키지 업데이트 등을 쉽게 할 수 있는 창을 따로 제공하고 있다.

  • 우측 하단의 Packages 버튼을 누르면, 현재 R에 어떤 패키지들이 설치 되어있고, 그 버전은 어떠한지를 구체적으로 볼 수 있다.
  • 특정 패키지의 체크박스를 선택하고, Packages 바로 아래에 있는 Update 버튼을 누르면, 해당 패키지를 업데이트 할 수 있다.
  • 패키지의 이름을 클릭하면 Help창으로 자동 이동되고, 해당 패키지에 어떤 함수가 있는지와 간략한 설명을 볼 수 있다. 또한 내가 궁금한 함수를 클릭하면, 보다 자세한 설명을 볼 수 있다.

 

 

 

패키지와 오류

1) 패키지의 의존성 문제

  • 대부분의 패키지는 설치할 때, 큰 어려움이 없으나, 가끔 아주 강력한 패키지를 설치할 때, 오류가 걸리는 일이 종종 발생할 수 있다.
  • 이는, 환경의 문제로 해당 패키지를 개발할 때, 들어간 소프트웨어가 해당 패키지를 설치하고자 하는 사람의 컴퓨터에 깔려 있지 않아서 발생하는 문제이다.
    (물론 전부 이 경우라고는 할 수 없지만, 대부분 이렇다!)
  • 이 경우에는 설치하고자 하는 패키지의 정보를 확인하고, 이 패키지에 필요한 소프트웨어를 설치해주면 된다.
  • R > CRAN > Korea(하단 아무 거나 클릭) > Packages > Table of available packages, sorted by name(이름 순 정렬) > ctrl + F로 문제 있는 패키지 검색 후 클릭 > SystemRequirements 옆에 있는 내용 확인
  • SystemRequirements 에 있는 소프트웨어가 설치되어야만 해당 패키지를 정상적으로 사용할 수 있다.
  • 추가로 에러 문구를 자세히 읽어보고, 구글링을 생활화하자!
    • 위에서 말한 소프트웨어 설치가 또 쉽지만은 않다. 그러므로, 해당 패키지 설치 방법을 구글에서 검색해보는 것이 베스트다.(이 고통을 나 혼자 겪은 것이 아니므로!)
    • R에서 당신이 겪은 대부분의 문제는 다른 사람도 겪었고, 개발자와의 소통 등을 통해서 대부분 해결된 문제이므로, 구글링을 하거나 Stack Overflow를 이용해서 해당 문제를 찾도록 해보자.

 

 

2) 패키지 업데이트의 문제

  • 일반적으로 우리가 생각할 땐, 업데이트가 된 최신 버전이 가장 좋은 것이라고 생각하기가 쉬운데, 그 말이 R과 같은 오픈소스에서도 똑같이 통용되는 말은 아니다.
  • 업데이트가 되면서 기존에 사용했던 함수에 새로운 파라미터가 주어졌을 수도 있고, 약간 기능이 바뀌었거나, 기존에 쓰고 있던 기능이 사라졌을 수도 있다!
    (물론 제작자가 상식적인 수준에서 변화를 주긴 했겠지만!)
  • 당장 코드를 짤 땐, 큰 지장이 없을 수 있지만, 이미 길게 짜놓은 코드가 에러를 일으킬 가능성이 있으므로, 업데이트가 반드시 만능은 아니라고 생각하면 된다.
  • 특히나, 지금까지 잘 사용했던 패키지가 돌연 업데이트를 멈추게 되는경우, R을 업데이트 했으나, R 패키지는 업데이트 되지 않아 서로 호환되지 않는 문제가 발생할 수도 있다.

 

 

3) 리눅스에서 R 패키지를 다운받는 경우 발생할 수 있는 문제.

  • 리눅스에서 R을 사용하다보면 다음과 같은 문제가 종종 발생할 수 있다.

  • ANTICONF ERROR Configuration failed because libmysqlclient was not found.......
  • 위 오류에서 libmysqlclient 특히 "mysql" 이 부분은 설치하고자 하는 패키지 이름에 따라 바뀔 수 있다.
  • 해당 오류는 말 그대로 내가 설치하고자 하는 패키지를 찾지 못한 문제인데, 해결 방법은 매우 단순하다.
  • 리눅스 터미널에서 내가 사용하고 있는 리눅스 버전에 맞는 방법으로 설치하면 된다.
  • 예를 들어, 내가 CentOS를 사용하고 있다면, CentOS용인 rpm을 이용하여 설치하면 된다.
  • 구체적인 해결 방법은 다음과 같다.
  • 리눅스 터미널 > yum install mysql-devel 입력 및 설치 > RStudio에서 패키지 설치 재실시

 

 

지금까지 R의 패키지에 대해 학습해보았다. 패키지는 설치에서 간간히 문제가 발생할 수는 있지만, 그 방법이 생각보다 단순하고, 패키지 관련 함수들도 install.packages()나 library() 같은 일부 함수를 제외하곤 거의 쓰이지 않기 때문에, 몇 번 쓰다보면 금새 익숙해질 것이다.

물론 몇몇 패키지를 설치하다가 오류가 나면 엄청 스트레스를 받긴 하겠지만, 그러한 문제 하나하나를 해결해나가다 보면, 어느새 실력이 부쩍 올라간 나 자신을 볼 수 있을 것이다.

이번 포스트는 여기서 마치도록 하겠다. 다음 포스트에선 R을 사용하는 데이터 분석가라면 거의 필수적으로 공부해야하는 패키지인 dplyr에 대해 공부해보도록 하자.

728x90
반응형
728x90
반응형

데이터 타입의 중요성은 아무리 강조해도 부족함이 없는데, R 초보자들이 주로 만들어내는 대부분의 오류는 input 데이터 타입과 output 데이터 타입이 서로 달라서 발생한다.

이번 포스트에선, 이 문제를 줄이기 위해 데이터 타입의 판별과 그 변환 방법에 대해 학습해보겠다. 

 

데이터 타입 판별과 타입 변환

  • 데이터의 타입에 따라 사용자가 원하는 결과와 다른 결과가 출력될 수도 있다.
  • 특히, 요인 변수가 숫자로 코딩돼 잇는 상태에서, 양적인 변수로 타입이 설정돼 있다면, 그래프 출력의 오류나, 분석 결과의 오류 등이 발생할 수 있으므로, 주의해야 한다.

1) 데이터 타입 판별 함수

함 수 의 미
class(x) 객체 x의 클래스
str(x) 객체 x의 내부 구조
is.factor(x) 주어진 객체 x가 요인(factor)인가?
is.numeric(x) 주어진 객체 x가 숫자인 벡터인가?
is.character(x) 주어진 객체 x가 문자인 벡터인가?
is.matrix(x) 주어진 객체 x가 행렬인가?
is.array(x) 주어진 객체 x가 배열인가?
is.data.frame(x) 주어진 객체 x가 데이터 프레임인가?

 

2) 데이터 타입 변환 함수

함 수 의 미
as.factor(x) 주어진 객체 x를 요인(factor)로 변환
as.numeric(x) 주어진 객체 x를 숫자인 벡터로 변환
as.character(x) 주어진 객체 x를 문자열 벡터로 변환
as.matrix(x) 주어진 객체 x를 행렬로 변환
as.array(x) 주어진 객체 x를 배열로 변환
as.data.frame(x) 주어진 객체 x를 데이터 프레임으로 변환

 

 

지금까지 R의 데이터 타입 확인과 타입 변환 함수에 대해 알아보았다. 

다음 포스트에선 R에서 사용할 수 있는 함수들을 모아놓은 패키지에 대해 공부해보도록 하자.

728x90
반응형

'R > Basic' 카테고리의 다른 글

R dplyr 패키지와 데이터 전처리  (0) 2020.06.23
R(기초) 패키지란?  (0) 2020.06.23
R(기초) 리스트(List)  (0) 2020.06.22
R(기초) 데이터프레임(DataFrame)(2부)  (0) 2020.06.22
R(기초) 데이터프레임(Data Frame)(1부)  (0) 2020.06.21
728x90
반응형

지금까지 R의 기본적인 데이터 타입인 스칼라, 벡터, 행렬, 배열, 데이터프레임에 대해 공부해보았다.

이번 포스트에서는 R에서 기본적으로 제공하는 마지막 데이터 타입인 List에 대해 공부해보도록 하자.

 

 

리스트(List)

: 리스트는 R에 있는 데이터 타입 중 가장 독특한 데이터 타입이라고 할 수 있는데, 말 그대로 모든 데이터 타입을 담을 수 있는 데이터 타입이 바로 리스트이다.

  • 리스트는 key, value 형태로 이루어져있다.
  • 리스트는 모든 데이터 구조를 포함하는 데이터 구조이다.
  • 여러 데이터 구조를 합하여 하나의 리스트를 만들 수 있다.
  • 다른 언어의 Hash table이나 Dictionary에 해당한다.
  • 서로 다른 변수 타입을 담을 수 있다.
  • 리스트는 배열(Array), 데이터프레임(Data Frame)과 달리 들어가는 데이터들의 길이가 서로 같지 않아도 담을 수 있다.
  • 리스트에 담긴 데이터마다 이름(key)을 부여할 수 있다.
  • list()
    : 리스트를 만드는 함수
# 데이터프레임, 행렬, 벡터가 들어간 리스트를 만들어보자.
vt1 = c("민철", "재성", "기훈", "현승", "현택", "윤기" ,"재빈", "현희", "미선", "선화")
vt2 = c(70, 60, 50, 80, 90, 80, 65, 75, 90, 80)
vt3 = c(80, 70, 85, 65, 55, 70, 75, 80, 65, 75)
vt4 = c(75, 80, 90, 75, 85, 75, 80, 85, 80, 85)

df = data.frame("name" = vt1, "math" = vt2, "english" = vt3, "science" = vt4)

mat = matrix(seq(1, 12), nrow = 4)

vt = c("A", "B", "C", "D")
List = list(data1 = df, data2 = mat, data3 = vt)
List
## $data1
##    name math english science
## 1  민철   70      80      75
## 2  재성   60      70      80
## 3  기훈   50      85      90
## 4  현승   80      65      75
## 5  현택   90      55      85
## 6  윤기   80      70      75
## 7  재빈   65      75      80
## 8  현희   75      80      85
## 9  미선   90      65      80
## 10 선화   80      75      85
## 
## $data2
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12
## 
## $data3
## [1] "A" "B" "C" "D"
  • 리스트 생성 시, 정한 이름으로 각 데이터의 key 값이 생성된 것을 알 수 있다.

 

 

 

리스트의 indexing

  • 리스트의 특징은 리스트에 포함된 데이터들을 key라는 이름으로 불러올 수 있다는 것이다.
  • 리스트의 indexing 방식은 지금까지와 약간 다르므로, 표로 정리해보겠다.
문법 의미
list$key 리스트 list에서 키 값 key에 해당하는 데이터를 가지고 온다.
list[n] 리스트 list에서 n번째 데이터의 서브리스트를 가지고 온다.
list[[n]] 리스트 list에서 n번째 저장된 값을 가지고 온다.
List$data1
##    name math english science
## 1  민철   70      80      75
## 2  재성   60      70      80
## 3  기훈   50      85      90
## 4  현승   80      65      75
## 5  현택   90      55      85
## 6  윤기   80      70      75
## 7  재빈   65      75      80
## 8  현희   75      80      85
## 9  미선   90      65      80
## 10 선화   80      75      85
List[2]
## $data2
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12
  • 위 표에서 설명한 서브리스트가 바로 위 형태이다.
  • list는 key와 value 2가지로 이루어져있으며, 위 List[2]의 결과를 보면, 이 역시 key와 value 2가지로 이루어진 list형임을 알 수 있다.
List[[2]]
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12
  • [[n]]를 사용하면, 서브리스트가 아닌 그 데이터를 바로 가지고 온다.

 

 

리스트형에 대한 설명은 여기까지 하도록 하겠다.

설명이 매우 짧기 때문에 리스트형의 사용 용도가 그리 많지 않을 것으로 생각할 수 있는데, 길이가 다른 데이터 형을 담을 수 있다는 list형은 그 특징만으로도 사용처가 상당히 많다고 할 수 있다.

특히 R에 있는 lapply와 같은 리스트 형을 대상으로 한 함수나, 들어가는 데이터와 나오는 데이터의 길이가 불규칙한 경우, list형을 사용하면 쉽게 해결할 수 있다.

 

지금까지 R의 가장 기초가 되는 데이터 타입에 대해 공부해보았다.

데이터 타입은 R을 쓸 때, 기본 상식처럼 다룰 수 있어야하며, 데이터 타입을 잘 다루는 것이 R로 코드를 짤 때, 기초가 되는 부분이라고 할 수 있다.

다음 포스트에선 지금까지 공부한 타입과 그 판별, 변환 방법에 간략하게 정리를 해보도록 하겠다.

728x90
반응형

'R > Basic' 카테고리의 다른 글

R(기초) 패키지란?  (0) 2020.06.23
R(기초) 데이터 타입 판별과 타입 변환  (0) 2020.06.22
R(기초) 데이터프레임(DataFrame)(2부)  (0) 2020.06.22
R(기초) 데이터프레임(Data Frame)(1부)  (0) 2020.06.21
R(기초) 배열(Array)  (0) 2020.06.19
728x90
반응형

데이터 프레임(Data Frame)

지난 포스트에선 데이터프레임의 생성과 데이터프레임의 정보를 파악하는 법에 대하여 공부해보았다.

이번 포스트에선 데이터프레임에서 새로운 컬럼을 생성하는 방법과 데이터 프레임에 접근하는 법에 대해 공부해보도록 하자.

 

 

 

데이터 프레임 접근

  • 데이터 프레임은 색인과 행과 열의 이름을 통해서 접근할 수 있다.
  • df$colname
    : "데이터프레임$컬럼이름"을 이용하면 데이터프레임에서 원하는 데이터에 접근할 수 있다.
  • df[r, c, drop = TRUE]
    : 데이터프레임 df의 r행, c열의 컬럼에 저장된 데이터를 가지고 올 수 있다.
    r과 c를 벡터로 지정하여 다수의 행과 컬럼을 동시에 가져올 수 있으며, 색인과 행 이름, 열 이름을 지정할 수도 있다. r과 c중 하나만 입력하는 경우, 예를 들어 c 하나만 넣은 경우엔 해당 열에 대한 모든 행 데이터를 가지고 온다.
    • r과 c중 하나만 불러오는 경우, 해당하는 행과 열 데이터만 해당 컬럼의 데이터 타입으로 가지고 오는데, 이러한 형 변환을 원하지 않는 경우엔 drop = FALSE로 지정하면 된다.
  • 인덱싱 방법은 다음과 같다.
    • df$col1
      : 데이터 프레임 df에서 col1 컬럼을 가지고 온다.
    • df[1,]
      : 데이터 프레임 df에서 1번째 행을 가지고 온다.
    • df[c(1,3), 2]
      : 데이터 프레임 df에서 1, 3번째 행을 가지고 오고, 2번째 컬럼을 가지고 온다.
    • df[ , c(2:5)]
      : 데이터 프레임 df에서 2~5번까지 컬럼을 가지고 온다.
    • df[ , -c(2:5)] == df[ , c(-2:-5)]
      : 데이터 프레임 df에서 2~5번 컬럼을 제외하고 가지고 온다.
    • df[ , c("math", "science")]
      : 데이터 프레임 df에서 math와 science 컬럼만 가지고 온다.
  • 위 인덱싱 방법말고도 다른 함수들을 조합해서 가지고 올 수는 있으나, 위 방법만으로도 충분하다.
  • 위 인덱싱 방법들을 응용하여, 내가 가지고 오고 싶은 데이터만 가지고 와보자.
# 데이터 프레임에서 내가 원하는 값만 가지고 와보자.
vt1 = c("민철", "재성", "기훈", "현승", "현택", "윤기" ,"재빈", "현희", "미선", "선화")
vt2 = c(70, 60, 50, 80, 90, 80, 65, 75, 90, 80)
vt3 = c(80, 70, 85, 65, 55, 70, 75, 80, 65, 75)
vt4 = c(75, 80, 90, 75, 85, 75, 80, 85, 80, 85)

exam = data.frame("name" = vt1, "math" = vt2, "english" = vt3, "science" = vt4)
# exam에서 math컬럼만 가지고 오자.
exam$math
##  [1] 70 60 50 80 90 80 65 75 90 80
  • indexing을 하는 경우, 데이터 타입이 바뀔 수 있으므로 주의해야한다.
# exam에서 1번째 행만 가지고 오자.
exam[1,]
##   name math english science
## 1 민철   70      80      75
# exam에서 1, 3행과 2번 컬럼만 가지고 오자.
exam[c(1,3),  2]
## [1] 70 50
# exam에서 2, 3, 4 행과 name, math 컬럼만 가지고 오자.
exam[c(2, 3, 4), c("name", "math")]
##   name math
## 2 재성   60
## 3 기훈   50
## 4 현승   80
# exam에서 3번째 컬럼만 제외하고 가지고 오자.
exam[, -c(3)]
##    name math science
## 1  민철   70      75
## 2  재성   60      80
## 3  기훈   50      90
## 4  현승   80      75
## 5  현택   90      85
## 6  윤기   80      75
## 7  재빈   65      80
## 8  현희   75      85
## 9  미선   90      80
## 10 선화   80      85

 

 

 

 

새로운 컬럼 추가

  • 데이터 프레임에 새로운 컬럼(변수)를 추가하는 방법은 R 자체의 Base 함수를 쓰거나, dplyr과 같은 데이터 핸들링 패키지를 쓰는 방법 등이 있다.
  • 이번 포스트에선 R의 Base 함수를 이용해서 새로운 컬럼을 추가해보자.
  • 데이터 프레임은 indexing 방법이었던, "$변수이름"에 새로운 벡터를 추가하여 컬럼을 추가할 수 있다.
# 컬럼을 추가해보자.
vt1 = c("민철", "재성", "기훈", "현승", "현택", "윤기" ,"재빈", "현희", "미선", "선화")
vt2 = c(70, 60, 50, 80, 90, 80, 65, 75, 90, 80)
vt3 = c(80, 70, 85, 65, 55, 70, 75, 80, 65, 75)
vt4 = c(75, 80, 90, 75, 85, 75, 80, 85, 80, 85)

exam = data.frame("name" = vt1, "math" = vt2, "english" = vt3, "science" = vt4)
# Korean 이라는 과목을 추가해보자
exam$Korean <- c(70, 85, 90, 80, 65, 75, 80, 75, 85, 70)
head(exam)
##   name math english science Korean
## 1 민철   70      80      75     70
## 2 재성   60      70      80     85
## 3 기훈   50      85      90     90
## 4 현승   80      65      75     80
## 5 현택   90      55      85     65
## 6 윤기   80      70      75     75
# 총점을 추가해보자.
exam$total <- exam$math + exam$english + exam$science + exam$Korean
head(exam)
##   name math english science Korean total
## 1 민철   70      80      75     70   295
## 2 재성   60      70      80     85   295
## 3 기훈   50      85      90     90   315
## 4 현승   80      65      75     80   300
## 5 현택   90      55      85     65   295
## 6 윤기   80      70      75     75   300
# 평균점수를 구해보자.
exam$mean <- exam$total/4
head(exam)
##   name math english science Korean total mean
## 1 민철   70      80      75     70   295 73.75
## 2 재성   60      70      80     85   295 73.75
## 3 기훈   50      85      90     90   315 78.75
## 4 현승   80      65      75     80   300 75.00
## 5 현택   90      55      85     65   295 73.75
## 6 윤기   80      70      75     75   300 75.00

 

 

 

지금까지 데이터프레임에 대한 아주 기초적인 학습을 해보았다.

데이터프레임은 지금까지 다룬 내용으로만 끝내기엔, 활용처가 매우 많기 때문에, 다음 포스트인 list형에 대해 학습을 마치고 데이터 핸들링으로 가장 유명한 패키지인 dplyr에 대해 공부를 하면서, 보다 심도 깊게 다뤄보도록 하겠다.

728x90
반응형

'R > Basic' 카테고리의 다른 글

R(기초) 데이터 타입 판별과 타입 변환  (0) 2020.06.22
R(기초) 리스트(List)  (0) 2020.06.22
R(기초) 데이터프레임(Data Frame)(1부)  (0) 2020.06.21
R(기초) 배열(Array)  (0) 2020.06.19
R(기초) 행렬(Matrix)(3부)  (0) 2020.06.19
728x90
반응형

데이터 프레임(Data Frame)

이번 포스트에선 R 데이터 타입의 꽃인 데이터프레임에 대해 학습해보겠다. 데이터프레임은 R에서 가장 중요한 자료형으로, 우리에게 익숙한 액셀의 스프레드시트와 같이 표 형태로 정리한 모습을 가지고 있다.

데이터프레임은 R에서 가장 많이 쓰이는 데이터 타입이며, 대용량의 데이터를 다루기엔 비효율적이라서 빅데이터 분석 시엔 fread를 비롯한 다른 데이터 타입을 사용하지만, 이 역시 기본적으로 데이터 프레임과 비슷한 형태를 가지고 있으며, 다루는 방법 역시 데이터 프레임과 비슷하다.

 

Data Frame의 기본적인 형태

  • 데이터 프레임은 행렬과 같은 모습을 하고 있지만, 행렬과 다르게 다양한 변수, 관측치(Observations), 범주(Category) 등을 표한하기 위해 특화되어 있다.
  • 행렬은 하나의 데이터 타입밖에 사용하지 못하지만, 데이터 프레임은 여러 가지 데이터 타입을 혼용하여 사용할 수 있다.
  • 데이터 프레임의 각 열(Column)별 행(Row)의 길이는 모두 동일하다.
  • 데이터 프레임의 행(Row, Record)은 데이터의 대상이 되는 객체 하나하나 이다.
  • 데이터 프레임의 열(Columns, Variable)은 데이터 대상이 되는 객체의 속성을 나타내는 값이다.
    각각의 Columns은 행렬과 달리 다양한 변수 타입을 가질 수 있다.

 

 

데이터프레임을 만들어보자

  • 데이터 프레임은 벡터, 행렬로 만들 수 있다.
  • 행렬을 그대로 데이터 프레임에 넣거나, 길이가 동일한 벡터들을 컬럼 하나하나에 배정하여 생성하면 된다.
  • data.frame()
    : 데이터 프레임을 생성한다.
  • 주요 Parameter
    : data.frame(stringsAsFactors: 문자열을 자동으로 요인(Factor)형으로 변환해준다.)
    • data.frame에서 중요한 Parameter는 stringsAsFactors말고는 딱히 없다. data.frame에서 다른 Parameter들에 대한 내용을 읽어보고자 한다면, data.frame 코드를 치고 F1을 눌러서 보도록 하자.
    • stringsAsFactors는 간단하지만, 상당히 중요한 Parameter로, 말 그대로 문자열을 요인(Factor)으로 바꿔주는 Parameter이다. 만약 string형인 열을 이용해서 새로운 변수를 만들어내거나, 특정 문자열을 분리해내는 작업을 하지 않는다면, 해당 Parameter를 TRUE로 두어 모두 Factor형으로 바꾸는 것이 유리하다.
    • 그러나, 문자열에 대하여 어떠한 조작을 하는 경우엔, Factor형으론 조작이 매우 힘드므로, 가능하다면 해당 Parameter를 FALSE로 두는 것을 추천한다.

 

1) 벡터를 이용해서 데이터프레임을 만들어보자.

# 벡터로 데이터프레임을 만들어보자
vt1 = c("민철", "재성", "기훈", "현승", "현택")
vt2 = c(70, 60, 50, 80, 90)
vt3 = c(80, 70, 85, 65, 55)

df1 = data.frame(vt1, vt2, vt3)
df1
##    vt1 vt2 vt3
## 1 민철  70  80
## 2 재성  60  70
## 3 기훈  50  85
## 4 현승  80  65
## 5 현택  90  55
  • 데이터 프레임 안에 길이가 동일한 벡터를 넣어주면 된다.
  • 길이가 다른 벡터를 함께 넣는 경우 에러가 뜨며 데이터프레임이 생성되지 않는다.
  • 각 컬럼의 이름은 벡터의 이름으로 정해진다.

 

2) 행렬을 이용해서 데이터프레임을 만들어보자.

# 행렬로 데이터프레임을 만들어보자
vt = c("민철", "재성", "기훈", "현승", "현택", 70, 60, 50, 80, 90, 80, 70, 85, 65, 55)
mat = matrix(vt, ncol = 3, byrow = FALSE)

data.frame(mat)
##    X1  X2  X3
## 1 민철  70  80
## 2 재성  60  70
## 3 기훈  50  85
## 4 현승  80  65
## 5 현택  90  55
  • 데이터 프레임 안에 행렬을 넣어주면 된다.
  • 각 컬럼의 이름은 X1, X2, X3...와 같은 방식으로 생성된다.
  • 행렬은 하나의 변수 타입만 가질 수 있으므로, 행렬을 데이터 프레임으로 바꾸는 경우, 하나의 속성만 가진 형태로 데이터프레임이 만들어진다.
str(data.frame(mat))
'data.frame':	5 obs. of  3 variables:
 $ X1: chr  "민철" "재성" "기훈" "현승" ...
 $ X2: chr  "70" "60" "50" "80" ...
 $ X3: chr  "80" "70" "85" "65" ...
  • 그러니 되도록이면 행렬로 데이터프레임을 만들기보다는 벡터를 이용해서 만들도록 하자.
  • 만약 행렬로 만들어야한다면, 데이터프레임 생성 후, 각 컬럼의 변수 타입을 모두 바꿔주도록 하자.

 

 

 

데이터 프레임 변수의 이름을 바꿔보자.

  • 이번엔 데이터 프레임 컬럼의 이름을 바꿔보자.
  • 데이터 프레임 컬럼 이름 변경은 2가지 방법이 있다.

1) 데이터 프레임 생성 시, 벡터의 이름을 설정해준다.

# 벡터로 데이터프레임을 만들어보자
vt1 = c("민철", "재성", "기훈", "현승", "현택")
vt2 = c(70, 60, 50, 80, 90)
vt3 = c(80, 70, 85, 65, 55)

data.frame("name" = vt1, "math" = vt2, "english" = vt3)
##   name math english
## 1 민철   70      80
## 2 재성   60      70
## 3 기훈   50      85
## 4 현승   80      65
## 5 현택   90      55

 

2) 생성된 데이터 프레임의 이름을 바꿔준다.

  • colnames()
    : data의 column의 이름들을 가지고 온다.
# 변수명을 바꿔보자.
vt1 = c("민철", "재성", "기훈", "현승", "현택")
vt2 = c(70, 60, 50, 80, 90)
vt3 = c(80, 70, 85, 65, 55)

df = data.frame(vt1, vt2, vt3)
colnames(df)
## [1] "vt1" "vt2" "vt3"
  • colnames(df)를 하면, dataframe의  column 이름들을 가지고 온다.
colnames(df) <- c("name", "math", "english")
df
  • colnames(df)에 벡터로 새로운 컬럼의 이름을 부여해보자.
##   name math english
## 1 민철   70      80
## 2 재성   60      70
## 3 기훈   50      85
## 4 현승   80      65
## 5 현택   90      55

 

3) 행의 이름을 바꿔주자.

  • 데이터프레임은 행을 index로 활용할 수 있으며, 행에 변수를 넣을 수도 있다.
  • 단 행의 이름은 절대 중복되서는 안된다.
  • 그러므로, ID를 만들어서 넣거나, 기존의 행 번호를 그대로 사용하도록 하자.
  • rownames()
    : data의 행 이름들을 가지고 온다.
# 행의 이름을 바꿔보자
vt1 = c("민철", "재성", "기훈", "현승", "현택")
vt2 = c(70, 60, 50, 80, 90)
vt3 = c(80, 70, 85, 65, 55)

df = data.frame(vt1, vt2, vt3)
rownames(df)
## [1] "1" "2" "3" "4" "5"
# 데이터프레임의 vt1을 row의 이름으로 사용해보자
rownames(df) <- df[,1]

# 데이터프레임에서 첫번째 컬럼과 행의 이름이 동일하므로, 첫번째 컬럼은 제거해서 가지고 와보자.
df[,-1]
##      vt2 vt3
## 민철  70  80
## 재성  60  70
## 기훈  50  85
## 현승  80  65
## 현택  90  55
  • 이번에 사용한 코드들을 보면, 행렬에서 다뤘던 indexing과 동일한 것을 볼 수 있다.
  • 행렬도 데이터프레임처럼 행의 이름과 열의 이름을 동일한 방법으로 바꿀 수 있다.
  • 데이터프레임의 Indexing에 대해선 다음 포스트에서 더 자세히 다뤄보도록 하겠다.

 

 

 

데이터 프레임의 정보를 살펴보자.

  • 이번엔 데이터프레임의 기본적인 정보를 살펴보는 방법을 보자.
  • str()
    : 데이터프레임의 차원과 각 열에 대한 정보 출력
  • head()
    : 일반적인 데이터프레임은 매우 크므로, 모두 보는 것은 힘들다. head()를 사용하면, 맨 위에서 n개(기본값 6)의 행을 가지고 온다. 데이터프레임을 보고자 한다면, head()를 이용해서 보도록 하자.
  • tail()
    : head()와 반대로 데이터프레임의 맨 아래에서 n개(기본값 6)의 행을 가지고 온다. tail()을 이용하면, 데이터프레임의 맨 아래 부분에 어떠한 특이사항이 있는지를 눈으로 쉽게 확인할 수 있다.
  • summary()
    : 데이터프레임에 있는 변수별 기술통계량을 볼 수 있다.
    변수 타입이 연속형변수인 경우에는 최소값, 최대값, 사분위 수, 평균을 볼 수 있다.
    변수 타입이 문자형과 같은 범주형 변수인 경우에는 변수의 길이, Class, Mode 등을 볼 수 있다.
    (Mode는 R의 기본 배경이 된 언어인 S language와 호환성을 가진 언어로, 간단하게 말하면 과거 버전의 타입 분류 방법이라고 생각하면 된다. R을 사용할 땐, Class가 우선이라고 간략하게 생각하고, Class에만 신경 쓰도록 하자.)
  • dim()
    : 데이터프레임의 차원별 길이를 볼 수 있다.
    데이터 프레임은 행과 열 2개의 차원으로 구성되어 있으며, dim() 함수를 이용하면 행의 수, 열의 수를 볼 수 있다.
  • View()
    : 데이터프레임을 데이터 뷰어창에서 볼 수 있다.
    데이터프레임 뿐만 아니라 행렬, 벡터 등도 볼 수 있다.
# 데이터 프레임의 구조를 살펴보자.
vt1 = c("민철", "재성", "기훈", "현승", "현택", "윤기" ,"재빈", "현희", "미선", "선화")
vt2 = c(70, 60, 50, 80, 90, 80, 65, 75, 90, 80)
vt3 = c(80, 70, 85, 65, 55, 70, 75, 80, 65, 75)

df = data.frame("name" = vt1, "math" = vt2, "english" = vt3)
df
##    name math english
## 1  민철   70      80
## 2  재성   60      70
## 3  기훈   50      85
## 4  현승   80      65
## 5  현택   90      55
## 6  윤기   80      70
## 7  재빈   65      75
## 8  현희   75      80
## 9  미선   90      65
## 10 선화   80      75
# 데이터 프레임의 차원과 각 열에 대한 정보를 알아보자.
str(df)
## 'data.frame':    10 obs. of  3 variables:
##  $ name   : chr  "민철" "재성" "기훈" "현승" ...
##  $ math   : num  70 60 50 80 90 80 65 75 90 80
##  $ english: num  80 70 85 65 55 70 75 80 65 75
# 데이터 프레임의 위부터 n행까지 추출한다(기본값은 6).
head(df, n = 5)
##   name math english
## 1 민철   70      80
## 2 재성   60      70
## 3 기훈   50      85
## 4 현승   80      65
## 5 현택   90      55
# 데이터 프레임의 아래부터 n행까지 추출한다(기본값은 6).
tail(df, n = 5)
##    name math english
## 6  윤기   80      70
## 7  재빈   65      75
## 8  현희   75      80
## 9  미선   90      65
## 10 선화   80      75
# 변수별 요약통계량 출력
summary(df)
##      name                math          english     
##  Length:10          Min.   :50.00   Min.   :55.00  
##  Class :character   1st Qu.:66.25   1st Qu.:66.25  
##  Mode  :character   Median :77.50   Median :72.50  
##                     Mean   :74.00   Mean   :72.00  
##                     3rd Qu.:80.00   3rd Qu.:78.75  
##                     Max.   :90.00   Max.   :85.00
# 데이터 프레임의 차원별 길이 출력
dim(df)
## [1] 10  3
# 데이터 뷰어로 데이터 프레임을 보자.
View(df)

  • View() 함수를 이용하는 경우, 데이터 프레임을 데이터 뷰어라는 새로운 창에서 크게 볼 수 있다.
  • R에서 매우 큰 데이터 프레임을 head()나 tail()가 아닌 그 자체로 불러오는 경우, R이 뻗어버릴 수 도 있지만(너무 큰 데이터를 표현하면, 부하가 매우 크므로, 가능한 dataframe 이름만 쳐서 가지고 오는 행동은 하지 말자) View를 사용해서 데이터 뷰어에서 보면 R이 뻗지는 않는다.
    (물론, 데이터의 크기가 너무 크므로, 보는 것이 굉장히 불편하긴 하다.)

 

 

지금까지 데이터프레임을 만들고 데이터프레임의 기본적인 정보를 보는 법에 대해 학습해보았다.

다음 포스트에선 데이터프레임에 새로운 변수를 추가해보거나, Indexing 하는 방법 등을 공부해보자.

728x90
반응형

'R > Basic' 카테고리의 다른 글

R(기초) 리스트(List)  (0) 2020.06.22
R(기초) 데이터프레임(DataFrame)(2부)  (0) 2020.06.22
R(기초) 배열(Array)  (0) 2020.06.19
R(기초) 행렬(Matrix)(3부)  (0) 2020.06.19
R(기초) 행렬(Matrix)(2부)  (0) 2020.06.19
728x90
반응형

이번 포스트에선 배열(Array)에 대해 공부해보자.

배열(Array)

: 행렬이 2차원의 데이터라면 배열(Array)는 다차원의 데이터라고 할 수 있다. 예를 들어 2x3 차원의 데이터를 행렬로 표현한다면, 2x3x4 차원의 데이터는 배열(Array)로 표현한다.

  • 간단히 말하자면 (NxM)행렬을 Z개 쌓아놓는다고 보면된다.
  • 당연히 Z개 쌓여있는 행렬들의 형태(차원)은 모두 동일하다고 할 수 있다.
  • array()
    : 배열을 만들 수 있다.
  • 주요 Parameter
    : array(data, dim = c(행의 수, 열의 수, 행렬의 수), dimnames = list(c(행의 이름), c(열의 이름), c(행렬의 이름)))
# 배열을 만들어보자.
Vt = seq(from = 1, by = 2, length.out = 12*3)
Ar = array(Vt, dim = c(4, 3, 3), dimnames = list(c("r1", "r2", "r3", "r4"),c("c1", "c2", "c3"),c("M1", "M2", "M3")))
Ar
## , , M1
## 
##    c1 c2 c3
## r1  1  9 17
## r2  3 11 19
## r3  5 13 21
## r4  7 15 23
## 
## , , M2
## 
##    c1 c2 c3
## r1 25 33 41
## r2 27 35 43
## r3 29 37 45
## r4 31 39 47
## 
## , , M3
## 
##    c1 c2 c3
## r1 49 57 65
## r2 51 59 67
## r3 53 61 69
## r4 55 63 71
  • 배열은 기본적으로 행렬이 한 열씩 채워가는 방식으로 생성된다.
  • 배열 역시 행렬과 마찬가지로, 배열을 구성할 벡터의 원소 수가 배열에 필요한 원소 수보다 작은 경우, 벡터의 앞부분부터 행렬의 빈자리에 구성된다.

 

 

배열에서 원하는 데이터 가지고 오기.

  • 배열에서 원하는 데이터를 가지고 오는 것은 행렬과 기본적으로 동일하며, 차원이 한 개 더 증가한 것과 같다.
Ar[,,"M2"]
##    c1 c2 c3
## r1 25 33 41
## r2 27 35 43
## r3 29 37 45
## r4 31 39 47

 

 

배열은 행렬을 다차원으로 담을 수 있는 데이터 타입으로, 전통적인 통계 분석에서는 그다지 필요가 없어보일 수 있다. 그리고, 동일한 2차원의 행렬에 대해서만 array에 적재할 수 있으므로, 모든 형태의 데이터 타입을 담을 수 있는 List에 비해 그 기능이 부족해보일 수 있다.

그러나, 반대로 말하자면 다차원 행렬을 적극적으로 사용해야하는 공학이나 요즘 핫한 빅데이터 분석에서는 array가 매우 유용한 데이터 타입이라고 할 수 있다. 꼭 숙지해놓고, 잘 활용해보도록 하자.

다음 포스트에서는 R 데이터 타입의 꽃인 데이터 프레임에 대해 학습해보도록 하자.

728x90
반응형

'R > Basic' 카테고리의 다른 글

R(기초) 데이터프레임(DataFrame)(2부)  (0) 2020.06.22
R(기초) 데이터프레임(Data Frame)(1부)  (0) 2020.06.21
R(기초) 행렬(Matrix)(3부)  (0) 2020.06.19
R(기초) 행렬(Matrix)(2부)  (0) 2020.06.19
R(기초) 행렬(Matrix)(1부)  (0) 2020.06.18
728x90
반응형

지금까지 행렬 생성과 행렬에 대한 기본적인 조작, 데이터 접근, 행렬 연산 등에 대해 학습해 보았다.

이번에는 포스트에선 역행렬, 가운데 행렬과 같은 약간 독특한 행렬들에 대해 학습해보자.

 

역행렬(Inverse Matrix)

  • 역행렬은 행렬의 역수라고 할 수 있으며, 행렬 A와 곱했을 때 단위 행렬 E가 나오게 하는 행렬을 A의 역행렬이라고 한다.
  • 역행렬은 정방행렬(n x n)에 대해서만 구할 수 있다. 장방행렬(n x m)에 대해서는 구할 수 없다.
  • solve()
    : 수식 A %*% X = B에서 X인 행렬을 구한다. 즉, A와 행렬곱 하여 B가 만들어지게 하는 행렬 X를 구한다.
  • 주요 Parameter
    : solve(A, B, ...)
    B의 자리를 공란으로 넣는다면, A의 역행렬을 구한다.
vt3 = c(4, 2, 3, 0, 1, 0, 2, 3, 0, 2, 1, 4, 0, 2, 1, 3)
mat3 = matrix(vt3, nrow = 4, byrow = TRUE)
mat3
##      [,1] [,2] [,3] [,4]
## [1,]    4    2    3    0
## [2,]    1    0    2    3
## [3,]    0    2    1    4
## [4,]    0    2    1    3
solve(mat3)
##             [,1]       [,2]  [,3]       [,4]
## [1,]  0.33333333 -0.3333333  2.00 -2.3333333
## [2,]  0.08333333 -0.3333333 -0.25  0.6666667
## [3,] -0.16666667  0.6666667 -2.50  2.6666667
## [4,]  0.00000000  0.0000000  1.00 -1.0000000

 

 

 

전치행렬(Transpose Matrix)

  • R(기초) 행렬(Matrix)(2부)에서 잠깐 다뤘던 전치행렬에 대해서 다시 한번 정리하겠다.
  • m*n 행렬의 행과 열을 서로 바꾼 n*m 행렬로 만든 것을 전치 행렬이라고 한다.
  • 주대각선(Main Diagonal)을 기준으로 하여 뒤집은 것을 가리킨다.
    ※ (1,1), (2,2), (3,3).... 과 같이 행과 열의 값이 같은 행렬의 가운데 부분을 주대각선(대각성분)이라 한다.
  • t()
    : 전치행렬로 만든다.

※ 대각성분(주대각선)인 (1,1), (2,2), (3,3)을 기준으로 뒤집은 것이 전치 행렬이다.

mat <- matrix(c(1:12), nrow = 4, byrow = TRUE)
mat
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6
## [3,]    7    8    9
## [4,]   10   11   12
t(mat)
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12

 

 

 

대칭행렬(Symmetric Matrix)

  • 대각성분을 중심으로 대칭인 정방행렬(n*n)을 가리킨다.
  • 대각성분을 중심으로 대칭이므로, 원래 행렬과 전치행렬은 동일하다.
  • 대칭행렬을 만들고 싶다면, 일반 행렬을 생성하고, 상삼각행렬 혹은 하삼각행렬의 위치에 대하여, 그 전치행렬의 값을 덮어 씌우면 된다.
    (무슨 말인지 모르겠지만, 실습을 하며 천천히 따라와보면 이해하게 될 것이다.)
# 대칭행렬을 만들어보자
mat = matrix(c(1:16), nrow = 4, byrow = TRUE)
mat
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12
## [4,]   13   14   15   16
  •  c(1:16)으로 1~16까지 값이 들어간 벡터로, 정방행렬(4*4)을 만들었다.
# 하삼각행렬의 값들을 가져와보자
lower.tri(mat, diag = FALSE)
##       [,1]  [,2]  [,3]  [,4]
## [1,] FALSE FALSE FALSE FALSE
## [2,]  TRUE FALSE FALSE FALSE
## [3,]  TRUE  TRUE FALSE FALSE
## [4,]  TRUE  TRUE  TRUE FALSE
  •  lower.tri()는 하삼각행렬을 만들 때 사용하는 함수로, 뒤에서 다시 한번 다루겠지만, 가운데 성분을 기준으로하여, 아랫쪽을 TRUE로 Masking한다.
    (상삼각행렬을 쓰는 경우엔, upper.tri()를 쓰면 되며, 방법은 동일하다.)
  •  lower.tri()의 parameter인 diag는 대각성분을 포함할 것인지 여부이다.
mat[lower.tri(mat, diag = FALSE)]
## [1]  5  9 13 10 14 15
  • 벡터, 행렬에서 Indexing을 할 때, 우리는 대괄호를 사용하여 가져왔었는데, 이 대괄호는 TRUE로 Masking된 값들을 가져오는 것이다.
# 전치행렬에 대한 하삼각행렬의 위치의 값을 본 행렬의 하삼각행렬 위치에 넣도록 하자.
mat[lower.tri(mat, diag = FALSE)] <- t(mat)[lower.tri(mat, diag = FALSE)]
mat
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    2    6    7    8
## [3,]    3    7   11   12
## [4,]    4    8   12   16
  • 조금 복잡해보이지만, 원리는 되게 단순하다.
  • 대칭행렬은 대각성분을 중심으로 대칭인 행렬이고, 전치행렬은 대각성분을 중심으로 반전된 행렬이다.
  • 즉, 원래의 행렬의 하삼각행렬(or 상삼각행렬)에 전치행렬의 하삼각행렬(or 상삼각행렬)의 위치의 값을 넣으면, 대칭행렬이 만들어지는 것이다.
  • 이를 더 풀어서 써보면 가운데 성분 아래(하삼각행렬의 위치 = TRUE로 Masking 된 곳)에 가운데 성분 위의 값을 가운데 성분을 중심으로 뒤집어서(전치 행렬) 가운데 성분 아래에 그대로 넣었다고 생각하면 된다.

 

 

 

대각 행렬(Diagonal Matrix)

  • 대각행렬은 대칭행렬과 비슷해보이지만, 생성 난이도는 보다 쉬운 행렬이다.
  • 대각행렬은 정방행렬(n*n)에서 대각성분을 제외한 모든 값이 0인 경우를 말한다.
  • diag()
    : 행렬의 대각성분을 가지고 오거나, 대각성분에 다른 값을 넣을 수 있게 해주는 함수, diag(Vector)를 하는 경우, 대각행렬이 생성된다.
  • 항등행렬(Identity Matrix)는 대각성분이 1이고 나머지 원소는 0인 행렬이므로, 대각성분을 모두 1로 생성하면 된다.
# 대각행렬을 만들어보자.
diag(c(1:5))
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    0    0    0    0
## [2,]    0    2    0    0    0
## [3,]    0    0    3    0    0
## [4,]    0    0    0    4    0
## [5,]    0    0    0    0    5
  • 가운데 성분을 제외하고 모두 0인 대각행렬을 만들어보았다.
# 대각성분을 가지고 와보자
mat <- matrix(c(1:16), nrow = 4)
mat
##      [,1] [,2] [,3] [,4]
## [1,]    1    5    9   13
## [2,]    2    6   10   14
## [3,]    3    7   11   15
## [4,]    4    8   12   16
diag(mat)
## [1]  1  6 11 16
  • diag() 함수를 이용하면, 행렬의 대각성분만 벡터로 가지고 올 수 있다.
# 대각성분의 원소를 모두 0으로 만들자
diag(mat) <- 0
mat
##      [,1] [,2] [,3] [,4]
## [1,]    0    5    9   13
## [2,]    2    0   10   14
## [3,]    3    7    0   15
## [4,]    4    8   12    0
  • 행렬의 대각성분에 스칼라 값을 넣어서 대각성분이 0인 행렬을 만들어보았다.
  • 대칭행렬 만들기와 대각성분을 0으로 만들기를 조합하여 코드를 짜면 대칭행렬이면서 대각성분이 0인 행렬을 만들 수 있다.

 

 

 

하삼각행렬(Lower Triangular Matrix)과 상삼각행렬(Upper Triangular Matrix)

  • 하삼각행렬은 대각성분을 중심으로, 그 윗 부분이 모두 0인 정방행렬을 말한다.
  • 상삼각행렬은 대각성분을 중심으로, 그 아랫 부분이 모두 0인 정방행렬을 말한다.
  • lower.tri()
    : 행렬의 가운데 성분을 기점으로(가운데 성분 포함 가능), 아랫 부분을 TRUE로 Masking하는 함수
  • upper.tri()
    : 행렬의 가운데 성분을 기점으로(가운데 성분 포함 가능), 윗 부분을 TRUE로 Masking하는 함수
  • 상삼각행렬은 대각성분을 중심으로, 아랫 부분이 0이므로, lower.tri()함수를 이용해 대각성분 아래쪽을 indexing하여 0을 집어넣으면 된다.
  • 하삼각행렬은 대각성분을 중심으로, 윗 부분이 0이므로, upper.tri()함수를 이용해 대각성분 위쪽을 indexing하여 0을 집어넣으면 된다.
# 상삼각행렬을 만들어보자.
mat = matrix(c(1:16), 4)
lower.tri(mat, diag = FALSE)
  • diag = FALSE 로 Parameter를 부여하여, 가운데 성분은 Masking하지 않도록 하자.
##       [,1]  [,2]  [,3]  [,4]
## [1,] FALSE FALSE FALSE FALSE
## [2,]  TRUE FALSE FALSE FALSE
## [3,]  TRUE  TRUE FALSE FALSE
## [4,]  TRUE  TRUE  TRUE FALSE
mat[lower.tri(mat, diag = FALSE)] <- 0
mat
  • 선택된 가운데 성분의 아랫부분에 0을 넣어서 상삼각행렬을 만들었다.
##      [,1] [,2] [,3] [,4]
## [1,]    1    5    9   13
## [2,]    0    6   10   14
## [3,]    0    0   11   15
## [4,]    0    0    0   16
# 하삼각행렬을 만들어보자.
mat = matrix(c(1:16), 4)
upper.tri(mat, diag = FALSE)
##       [,1]  [,2]  [,3]  [,4]
## [1,] FALSE  TRUE  TRUE  TRUE
## [2,] FALSE FALSE  TRUE  TRUE
## [3,] FALSE FALSE FALSE  TRUE
## [4,] FALSE FALSE FALSE FALSE
mat[upper.tri(mat, diag = FALSE)] <- 0
mat
##      [,1] [,2] [,3] [,4]
## [1,]    1    0    0    0
## [2,]    2    6    0    0
## [3,]    3    7   11    0
## [4,]    4    8   12   16

 

 

자, 지금까지 역행렬, 전치행렬, 대각행렬, 대칭행렬, 상삼각행렬, 하삼각행렬에 대해 알아보았다. 행렬은 이 것보다 훨씬 심도 깊은 분야기 때문에, R의 기초인 데이터 타입 공부에선 기본적으로 알아야하는 부분만 짚고 넘어가도록 하겠다.

다음 포스트에선 배열(Array)에 대해 학습해보도록 하겠다.

728x90
반응형

'R > Basic' 카테고리의 다른 글

R(기초) 데이터프레임(Data Frame)(1부)  (0) 2020.06.21
R(기초) 배열(Array)  (0) 2020.06.19
R(기초) 행렬(Matrix)(2부)  (0) 2020.06.19
R(기초) 행렬(Matrix)(1부)  (0) 2020.06.18
R(기초) 연산자와 변수 타입  (0) 2020.06.18

+ Recent posts