/ R

R 기초강의(14) - R 데이터 조작

R 강좌는 여러 절로 구성되어 있습니다.


R 데이터 조작

데이터 분석 업무에서는 데이터 모델링이나 시각화에 적합한 데이터를 얻기위해 복잡한 과정을 거치게 됩니다. 실제 분석 프로젝트에서 절반 이상의 시간을 데이터 구축과 변환과 조작, 필터링과 전처리 작업에 할당합니다.

이번에는 데이터 조작에 특화된 몇 가지 package를 살펴보도록 하겠습니다.

데이터 분석을 하기 위해서는 먼저 데이터의 구조를 파악해야 하며 R은 분석할 데이터의 구조를 쉽게 파악할 수 있도록 몇가지 함수를 제공합니다.

데이터 구조를 파악하기 위해 사용하는 기본 함수

  • class() : 자료구조(메모리구조)를 파악하기 위한 함수.
  • head() : 데이터의 앞부분을 추출하는 함수. 기본값은 data frame일 경우 앞에서 6행까지 추출.
  • tail() : 데이터의 뒷부분을 추출하는 함수. 기본값은 data frame일 경우 뒤에서 6행까지 추출.
  • View() : View 창에서 데이터 출력.
  • dim() : data frame의 행과 열의 개수를 구하는 함수. 선형구조에 적용 불가능.
  • nrow() : data frame의 행의 개수를 구하는 함수. 선형구조에 적용 불가능.
  • ncol() : data frame의 column의 개수를 구하는 함수. 선형구조에 적용 불가능.
  • str() : 데이터에 들어있는 column들의 속성을 확인하기 위한 함수.
  • summary() : 요약 통계량을 산출하는 함수.
  • ls() : data frame의 column 항목을 vector로 추출하는 함수.
  • length() : 벡터의 길이, data frame일 경우 column의 개수를 구하는 함수.

실습

  • 위에서 언급된 각 함수를 이용해 데이터 구조를 파악해 보자.
  • 사용하는 데이터 셋은 ggplot2 package의 mpg data set, hflights package의 hflights data set, 그리고 간단한 엑셀파일을 사용하자.
  • mpg : 1999년에서 2008년 까지 인기 차종 38개에 대한 연비데이터.
  • hflights : 2011년도 미국 휴스턴에서 출발하는 모든 비행기의 이륙과 착륙 정보가 기록된 것으로 약 22만건의 관측치와 21개의 column으로 구성된 데이터 셋
  • mpg reference manual
  • hflights package reference manual
# ggplot2 설치
install.packages("ggplot2")
library(ggplot2)

# mpg data set 이용
mpg
mpg = as.data.frame(mpg)   # mpg data frame

class(mpg)
ls(mpg)
# 주요컬럼
# 컬럼의 명세 : R Document(https://www.rdocumentation.org/)

# manufacturer : 제조회사
# displ : 배기량(Displacement)
# cyl : 실린더 개수
# drv : 구동 방식( f: 전륜구동, r : 후륜구동, 4 : 4륜구동)
# hwy : 고속도로 연비 (miles per gallon) 
#                     1gal : 3.8리터, 1mile : 1609.34m
# class : 자동차 종류
# model : 자동차 모델명
# year : 생산연도
# trans : 변속기 종류
# cty : 도시 연비
# fl : 연료 종류
head(mpg)
tail(mpg)
View(mpg)
dim(mpg)         # 234행, 11 column
nrow(mpg)
ncol(mpg)
str(mpg)
ls(mpg)
length(mpg)      # data frame인 경우 column의 개수
summary(mpg)
rev(head(mpg))   # vector인 경우 원소의 순서를 역순으로
# data frame인 경우 column을 역순으로

# 여기서 잠깐!! 4분위에 대해서 실습하고 넘어가자
# 사분위수 : 측정값을 오름차순으로 정렬한 후 4등분했을 때
# 각 등위에 해당하는 값을 의미.
# 중앙값(중위수)는 2사분위값에 해당.

# 총 13개의 데이터
data = c(1, 3, 5, 7, 9, 10, 11, 13, 14, 17, 20, 23, 25)
summary(data)
# 1사분위 => 1 + (0.25 * 12) = 4(번째 값) => 7
# 3사분위 => 1 + (0.75 * 12) = 10(번째 값) => 17

#########################################################
# hflights package설치
# 주요컬럼
# Year(년), Month(일), DayofMonth(일), DayofWeek(요일)
# AirTime(비행시간), DepTime(출발시간), ArrTime(도착시간)
# TailNum(항공기 일련번호), DepDelay(출발지연시간)
# ArrDelay(도착지연시간), Distance(비행거리)
# 다른컬럼의 의미는 reference 참조

install.packages("hflights")
library(hflights)

class(hflights)   # data frame
head(hflights)    # 상위 6행 출력
tail(hflights)    # 하위 6행 출력
View(hflights)    # View 창으로 출력
dim(hflights)     # 행,열 (227496,21)     
str(hflights)     # 변수의 속성확인
summary(hflights) # 요약 통계
                  # mean : 평균
                  # median : 중앙값
# 평균은 편향된 데이터와 이상치 때문에 그릇된 정보를
# 제공하는 경우가 있음. 이런 경우 값을 작은수에서
# 큰 수로 정렬한 후 중앙에 있는 값을 계산해서 사용
ls(hflights)      # column명을 vector로 출력
nrow(hflights)    # 행의 개수
ncol(hflights)    # 열의 개수
length(hflights)  # 열의 개수
# matrix에 length()를 적용하면 열의 개수가 아니라
# 모든 원소의 개수를 리턴한다.
rev()             # vector인 경우 원소의 순서를 역순으로
# data frame인 경우 column을 역순으로

######################################################
# iris dataset
# 통계학자인 피셔(Fisher) 가 소개한 데이터.
# 붓꽃(iris)의 3가지 종(setosa, versicolor, virginica)에 대해 
# 꽃받침(sepal)과 꽃잎(petal)의 길이를 정리한 데이터

# R에 기본으로 내장
# Species : 붓꽃의 종. setosa, versicolor, virginica (Factor)
# Sepal.Width : 꽃받침의 너비 (numeric)
# Sepal.Length : 꽃받침의 길이 (numeric)
# Petal.Width : 꽃잎의 너비 (numeric)
# Petal.Length : 꽃잎의 길이 (numeric)

head(iris)
rev(head(iris))
View(iris)
str(iris)
summary(iris)
rev(iris)


# 2016년 2017년 ATM 이용건수와 이용금액
# 제공된 2016_2017_이용건수_및_이용금액.xlsx 파일 이용

install.packages("xlsx")
library(xlsx)

df <- read.xlsx(file.choose(),
                sheetIndex = 1,
                encoding = "UTF-8")
head(df)
View(df)
str(df)
summary(df)
rev(ls(df))

plyr package

plyr package는 두 개 이상의 data frame을 대상으로 Key 값을 이용하여 하나의 data frame으로 병합하거나 집단변수를 기준으로 data frame 변수에 함수를 적용하여 요약집계 결과를 구할 때 유용한 함수를 제공하는 package입니다.

실습

# plyr package

# 1. data frame 병합

install.packages("plyr")
library(plyr)

x <- data.frame(id=c(1,2,3,4,6),
                height=c(160,175,180,177,194))
y <- data.frame(id=c(5,4,1,3,2),
                weight=c(55,73,80,94,77))

# join() : key값을 기준으로 두 개의 data frame을 하나로 병합
# left join이 default

inner_df = join(x,y, by="id", type="inner")  # inner join
inner_df

left_df = join(x,y, by="id", type="left") # left join
left_df

right_df = join(x,y, by="id", type="right") # right join
right_df

full_df = join(x,y, by="id", type="full") # full join
full_df


x <- data.frame(id=c(1,2,3,4,6),
                gender=c("M","F","M","F","M"),
                height=c(160,175,180,177,194))
y <- data.frame(id=c(5,4,1,3,2),
                gender=c("M","F","M","F","M"),
                weight=c(55,73,80,94,77))

df <- join(x,y, by=c("id","gender"), type="inner")
df

# 2. 그룹별 기술 통계량 구하기
# tapply() : 집단별 통계치를 구해주며 한번에 1개의 통계치만 구할 수 있다.

str(iris)
unique(iris$Species)
result = tapply(iris$Sepal.Length, iris$Species, FUN=mean)
result
result = tapply(iris$Sepal.Length, iris$Species, FUN=sd)
result

# ddply() : 한번에 여러개의 통계치를 구할 수 있다.

result = ddply(iris, 
               .(Species), 
               summarise,
               avg=round(mean(Sepal.Length),2),
               sd=round(sd(Sepal.Length),2),
               max=max(Sepal.Length))
result
View(result)    # data frame

dplyr package

dplyr package는 기존의 plyr package의 성능과 기능을 개선한 package입니다. plyr package는 R 언어로 구현되었기 때문에 속도가 느렸으나 dplyr package는 C++로 개발되어서 속도가 빠른 장점이 있습니다.

또한 data frame을 제어하는데 특화된 함수를 제공함으로 정형화된 데이터를 처리하는데 적합하며 %>% 기호를 이용하여 함수들을 나열하는 방식(chaining)으로 코드를 작성할 수 있습니다.

dplyr package에서 제공되는 주요 함수

  • tbl_df() : 데이터 셋에서 console창의 크기만큼 데이터 셋을 추출하는 기능
  • rename() : 데이터 셋의 column명을 수정하는 기능
  • filter() : 데이터 셋에서 조건에 맞는 데이터 셋을 추출하는 기능
  • select() : 데이터 셋을 대상으로 특정 column들을 추출하는 기능
  • arrange() : 데이터 셋을 특정 column으로 정렬하는 기능
  • mutate() : 데이터 셋에 새로운 column을 추가하는 기능
  • group_by() : 데이터 셋의 범주형 column을 대상으로 그룹화하는 기능
  • summaries() : 데이터 셋의 특정 column으로 요약 집계하는 기능
  • left_join() : 데이터 셋 left join 기능(다른 join도 함수로 제공)
  • bind_rows() : 데이터 셋을 행 기준으로 합치는 기능
  • bind_cols() : 데이터 셋을 열 기준으로 합치는 기능

실습

# dplyr package

install.packages("dplyr")
library(dplyr)
library(xlsx)

excel_data <- read.xlsx(file.choose(),
                        sheetIndex = 1,
                        encoding = "UTF-8")
excel_data

# 1. tbl_df()
df <- tbl_df(excel_data)
df    # 현재 R의 console 크기에서 볼 수 있는 만큼 결과 출력
      
View(df)


# 2. rename(data frame, newVar = var, ...)
df <- rename(excel_data, 
             Y17_AMT = AMT17,
             Y16_AMT = AMT16)
df    # column명 변경


# 3. filter(data frame, 조건1, 조건2, ... )
df <- filter(excel_data, 
             AREA == "서울" & AMT17 >500000)
df

df <- filter(excel_data, 
             AREA == "서울", 
             SEX == "M",
             AMT16 > 350000)
df

df <- filter(excel_data, 
             AREA %in% c("서울","경기"), 
             SEX == "M",
             AMT16 > 350000)
df



# 4. arrange(data frame, column1, desc(column2), ...)
df <- filter(excel_data, 
             SEX != "M") %>%
      arrange(AREA, 
              desc(AMT17))
df


# 5. select(data frame, column1, column2, ...)
df <- filter(excel_data, 
             SEX == "M") %>%
      arrange(AREA, 
              desc(AMT17)) %>%
      select(ID,AREA:Y17_CNT)
df

df <- filter(excel_data, 
             SEX == "M") %>%
      arrange(AREA, 
              desc(AMT17)) %>%
      select(-SEX)
df



# 6. 새로운 column 생성
df <- filter(excel_data, 
             SEX == "M") 

df$AMT15 = df$AMT16 + 10000
df

df$VIP = ifelse(df$AMT15 > 500000, TRUE, FALSE)
df


# 7. mutate(data fame, column명1=수식1, column명2=수식2)
df <- filter(excel_data, 
             SEX == "M") %>%
      mutate(AMT15=AMT16 + 10000, 
             AMT14=AMT15+10000,
             VIP=ifelse(AMT15 > 500000, TRUE, FALSE))
df


# 8. summaries(data frame, 추가할column명1=함수(column명))
df <- filter(excel_data, 
             SEX == "M") %>%
      mutate(AMT15=AMT16 + 10000, 
             AMT14=AMT15+10000) %>%
      summarise(sum=sum(AMT14, na.rm=T), cnt=n())
df


# 9. group_by(data frame, 범주형 column)
df <- filter(excel_data, 
             AMT17 > 300000) %>%
      group_by(SEX) %>%
      summarise(sum=sum(AMT17, na.rm=T), cnt=n()) 
df

# 10. bind_rows(), bind_cols()
df1 <- data.frame(x=c(1,2,3))
df1
df2 <- data.frame(y=c(4,5,6))
df2

bind_rows(df1,df2)
df2 = rename(df2, x = y)
bind_rows(df1,df2)

bind_cols(df1,df2)

reshape2 package

reshape2 package를 설명하기 전에 다음의 파일을 data frame으로 읽어들여보자.

sample_mpg <- read.csv(file="C:/R_workspace/R_Lecture/data/sample_mpg.csv",
               sep = ",",
               header = T,
               fileEncoding = "UTF-8") 

sample_mpg   # 일반적인 data frame 형태

melt_sample_mpg <- read.csv(file="C:/R_workspace/R_Lecture/data/melt_sample_mpg.csv",
                            sep = ",",
                            header = T,
                            fileEncoding = "UTF-8")
melt_sample_mpg   # column이 row로 표현된 형태!!


# raw data의 데이터 형태가 읽어들인 형태처럼 되어 있을 경우
# 어떻게 처리해야 하는가?

# 평균 도시연비는?

# 1. 일반적인 data frame인 경우
mean(sample_mpg$cty)

# 2. column이 row로 표현된 경우
melt_sample_mpg %>% 
  filter(variable == "cty") %>%
  summarise(avg_cty = mean(value))

# 평균 연비를 구해서 새로운 column을 구하려면?

# 1. 일반적인 data frame인 경우
sample_mpg %>%
  mutate(avg_rate = (cty+hwy)/2)
       
# 2. column이 row로 표현된 경우
## 감도 안온다!!

reshape2 package는 수집한 데이터를 분석하기 편한 형태로 가공할 때 사용하는 대표적인 package입니다.

reshape2 package안에는 대표적인 2개의 함수가 존재하며 주로 이 함수를 이용합니다.

melt() 함수

  • column을 행(row)으로 바꾸어서 가로로 긴 형태의 데이터를 세로로 길게 전환하는 함수입니다.
  • melt는 변수(variable)에 대해 값(value)를 매칭하는 방식으로 넓게 퍼진 데이터를 길게 변형합니다.
  • melt의 기본 동작방식은 numeric data를 포함하고 있는 모든 열들을 variable로 만드는 것입니다.
  • melt(data frame, id.var=”기준 열”, measure.vars=c(“…”,”…”)

실습

  • R에 기본으로 내장되어 있는 data set인 airquality를 이용하여 melt() 함수를 실습해보자.
install.packages("reshape2")
library(reshape2)

df = airquality
class(df)
str(df)     # 153행 6열
df

View(df)

# melt()의 기본은 기준이 될 열을 지정하지 않으면 
# numeric data type의 모든 열을 행으로 변환
melt(df)       
melt(df, na.rm = T)         # value값이 NA인 것은 제외

nrow(melt(df))              # 생성된 총 row수 : 153 * 6 = 918

# month를 기준으로 데이터를 행으로 변환
melt(df,id.vars="Month")    
nrow(melt(df,id.vars="Month"))  # 생성된 총 row수 : 153 * 5 = 765

# month와 day를 기준으로 데이터를 행으로 변환
melt(df,id.vars=c("Month","Day"))
nrow(melt(df,id.vars=c("Month","Day"))) 
# 생성된 총 row수 : 153 * 4 = 612

# month와 day를 기준으로 ozone데이터만 행으로 변환
melt(df,
     id.vars=c("Month","Day"),
     measure.vars="Ozone")


# smiths data set을 이용하여 melt를 실습해보자

smiths

melt_smiths <- melt(smiths,
                    id.vars=c("subject"))
melt_smiths   # 8행


melt_smiths <- melt(smiths,
                    id.vars=c("subject","time"))
melt_smiths   # 6행

melt_smiths <- melt(smiths,
                    id.vars=c("subject","time","age"))
melt_smiths   # 4행

melt_smiths <- melt(smiths,
                    id.vars=c("subject","time","age"),
                    na.rm = T)
melt_smiths   # 3행


# 다른 데이터 셋으로 실습
install.packages("ggplot2")

library(ggplot2)
library(reshape2)
library(dplyr)
mpg

df <- as.data.frame(mpg)
melt(df)

melt(df, id.vars="model")

melt(df, id.vars=c("manufacturer","model"),
     measure.vars="cty")

dcast() 함수

  • 세로로 길게 늘어진 데이터(melt된 데이터)를 가로로 변행해야 하는 경우에 사용
  • cast() 함수 종류 중 data frame의 형태를 반환하는 경우에는 dcast()를 사용합니다.
  • melt된 데이터를 원상 복구하거나 통계 함수를 이용한 변형에 사용됩니다.

실습

df = airquality; df

melt_df <- melt(df,id.vars=c("Month","Day")); head(melt_df)


# Month를 기준으로 각 값들의 mean
dcast_df <- dcast(melt_df,
                  formula=Month ~ variable,
                  fun=mean,
                  na.rm=T)
dcast_df


# 모든 column 원상 복구
dcast_df <- dcast(melt_df,
                  formula=Month + Day ~ ...)
dcast_df

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

df <- as.data.frame(mpg)

melt_df <- melt(df, id.vars=c("manufacturer","model"),
                measure.vars="cty")
melt_df;

dcast_df <- dcast(melt_df,
                  formula=manufacturer ~ variable,
                  fun=mean)
dcast_df

## cf. 각 제조사별 cty의 평균

df <- as.data.frame(mpg)

df %>% group_by(manufacturer) %>%
       summarise(avg_cty=mean(cty))

## 입력받은 데이터파일을 정상적인 data frame으로 변환

melt_sample_mpg

result <- dcast(melt_audi_df,
                manufacturer + model + year + 
                  cyl + trans + drv + fl + class ~ d_name,
                value.var = "d_value")

result

End.


이 포스트의 내용은 아래의 책과 사이트를 참조했습니다. 조금 더 자세한 사항을 알고 싶으시면 책을 참조하거나 해당 사이트를 방문하세요!!