이번 포스팅에서는 R의 DataFrame에서 특정 조건에 해당하는 값의 행과, 해당 행의 앞, 뒤 2개 행(above and below 2 rows) 을 동시에 제거하는 방법을 소개하겠습니다. 




예를 들어서, 아래와 같이 var1, var2의 두 개의 변수를 가지는 df라는 이름의 DataFrame이 있다고 했을 때, var2의 값 중 음수(-)인 값을 가지는 행과, 해당 행의 위, 아래 2개 행을 같이 제거(remove, filter) 해서 df2 라는 이름의 새로운 DataFrame을 만들어보겠습니다. 



> var1 <- c(1:12)

> var2 <- c(100, -200, 101, 1102, 50, 300, 100, 400, -100, 82, 90, 80)

> df <- data.frame(var1, var2)

> df

   var1 var2

1     1  100

2     2 -200

3     3  101

4     4 1102

5     5   50

6     6  300

7     7  100

8     8  400

9     9 -100

10   10   82

11   11   90

12   12   80

 



먼저, 칼럼 var2 에서 음수(-)인 값의 조건을 만족하는 값의 행의 위치를 indexing 한 negative_idx 벡터를 만들어보겠습니다. 



> # condition: if var2 value is negative, then TRUE

> negative_idx <- df$var2 < 0

> negative_idx

 [1] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE




다음으로 칼럼 var2의 값이 음수(-)인 값의 위치를 기준으로 해당 값의 행과 앞, 뒤 2개행까지는 제거(remove, filter)하고, 그 외의 값은 유지(keep_idx = TRUE) 하는 keep_idx 라는 벡터를 for loop 반복문과 if 조건문을 사용해서 만들어보겠습니다. 



> keep_idx <- c(rep(TRUE, length(df$var2)))

> keep_idx

 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

> for (i in 1:length(negative_idx)) {

 if (negative_idx[i] == TRUE) {

+     keep_idx[i-2] = FALSE

+     keep_idx[i-1] = FALSE

+     keep_idx[i] = FALSE

+     keep_idx[i+1] = FALSE

+     keep_idx[i+2] = FALSE

 }

+ }

> keep_idx

 [1] FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE

 



이제 df라는 DataFrame에서 위에서 구한 keep_idx = TRUE 인 행만 indexing 해서 (즉, keep_idx = FALSE 인 행은 제거) 새로운 df2 라는 DataFrame을 만들어보겠습니다. 



> # subset only rows with keep_idx = TRUE

> df_filstered <- df[keep_idx, ]

> df_filstered

   var1 var2

5     5   50

6     6  300

12   12   80




위는 예는 조건문과 반복문을 사용해서 indexing 해오는 방법이었구요, windows 함수인 lag(), lead()를 사용해서도 동일한 기능을 수행하는 프로그램을 짤 수도 있습니다. 다만, 이번 포스팅에서 소개한 코드가 좀더 범용적이고 코드도 짧기 때문에 lag(), lead() 함수를 사용한 방법은 추가 설명은 하지 않겠습니다. 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요.



Posted by R Friend R_Friend

댓글을 달아 주세요

이번 포스팅에서는 R로 쇼핑몰 웹사이트의 텍스트를 가져와서 최저가 Top 10 리스트를 선별해서 DataFrame으로 만든 후에, 이를 엑셀로 내보내기를 해보겠습니다. 


아래 코드는 제 블로그에 질문을 남겨주신 분께서 거의 다 짜셨구요, 저는 엑셀로 내보내는 부분의 에러를 바로 잡는 방법을 안내해드렸었습니다. 


textreadr, rvest, xlsx 등의 R 패키지와 사용법에 대해서 블로그의 본문에 정식으로 한번 더 소개해드리면 더욱 많은 분들께서 검색해서 이용하시기에 편리할 듯 해서 본문에 다시 한번 포스팅합니다. 



1. R 패키지 설치 및 로딩



# clear all

rm(list=ls())


# install and load libries

install.packages("textreadr")

install.packages("rvest")

install.packages("xlsx")


library(textreadr)

library(rvest)

library(xlsx)

 




2. 각 제품별로 엑셀 sheet 를 분리해서 최저가 Top 10 업체 결과 내보내기



urlbase <- "https://search.shopping.naver.com/search/all.nhn?query="

low <- c("에어팟","코카콜라","건전지","삼다수")


df <- data.frame()


# for loop statement

for (i in low){

  url <- paste(urlbase, i ,"&cat_id=&frm=NVSHATC",sep="")

  nv <- read_html(url, encoding = "UTF-8")

  st <- html_nodes(nv, '.mall_name') %>% html_text()

  st <- st[1:10]

  price3 <- html_nodes(nv, '._lowPriceByMall') %>% html_nodes('.price') %>% html_text()

  price3 <- gsub('최저가','',price3)

  price3 <- price3[1:10] # 최저가 Top 10

  df <- data.frame(가게명=st, 가격=price3)

  

  # append df to df_all

  df <- rbind(df, data.frame(가게명=url, 가격="r"))

  

  # adding sheetName

  write.xlsx(df, file="C:/Users/admin/Documents/df_sheet.xlsx", 

             sheetName=i, # excel sheet per each product or brand

             col.names=TRUE

             append=TRUE)

 

  # check progress

  print(paste0(i, " has been completed."))

}

 



위의 코드를 실행한 엑셀 파일은 아래와 같이 각 sheet 별로 제품이 구분이 되어있습니다. 






3. 모든 제품에 대해서 하나의 엑셀 sheet에 모두 모아서 최저가 Top 10 결과 내보내기



urlbase <- "https://search.shopping.naver.com/search/all.nhn?query="

low <- c("에어팟","코카콜라","건전지","삼다수")


df_all <- data.frame()


# for loop statement

for (i in low){

  url <- paste(urlbase, i ,"&cat_id=&frm=NVSHATC",sep="")

  nv <- read_html(url, encoding = "UTF-8")

  st <- html_nodes(nv, '.mall_name') %>% html_text()

  st <- st[1:10]

  price3 <- html_nodes(nv, '._lowPriceByMall') %>% html_nodes('.price') %>% html_text()

  price3 <- gsub('최저가','',price3)

  price3 <- price3[1:10]

  df <- data.frame(item=i, 가게명=st, 가격=price3)

  

  # append url info to df_all

  df <- rbind(df, data.frame(item=i, 가게명=url, 가격="r"))

  

  # combining df to df_all one by one

  df_all <- rbind(df_all, df)

  

  # check progress

  print(paste0(i, " has been completed."))

}


# exporting df_all to excel

write.xlsx(df_all, 

           file="C:/Users/admin/Documents/df_all.xlsx", 

           col.names=TRUE)

 




위의 코드를 실행한 엑셀 파일 결과는 아래와 같이 하나의 sheet에 각 제품(item) 변수로 구분이 되어서 한꺼번에 모아서 엑셀로 내보내기가 되었음을 알 수 있습니다. 





많은 도움이 되었기를 바랍니다. 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 김진양 2018.08.15 23:25  댓글주소  수정/삭제  댓글쓰기

    제가 질문 드린 부분이네요!
    알 프렌드님 포스팅에 도움이 되어서 뿌듯합니다^^

  2. lemonpa2 2018.09.15 20:34  댓글주소  수정/삭제  댓글쓰기

    안녕하세요? 올려주신 포스트로 R 공부중입니다.

    "https://search.shopping.naver.com/search/all.nhn?query=삼다수&cat_id=&frm=NVSHATC" 페이지 html source를 찾아봤는데 잘 모르겠어서 문의드립니다.

    html_nodes(nv, '.mall_name') %>% html_text()
    html_nodes(nv, '._lowPriceByMall') %>% html_nodes('.price') %>% html_text()

    -> 이부분에서 '.mall_name', '_lowPriceByMall'이라는 부분을 html 소스에서 찾을 수가 없어서요.

    이부분 한번 설명 부탁드려도 될까요?


    출처: http://rfriend.tistory.com/category/R 분석과 프로그래밍 [R, Python 분석과 프로그래밍 (by R Friend)]

이번 포스팅은 페이스북의 R User Group 에서 아이디어 토론 주제로 올라왔길레 한번 짜봤던 코드입니다.  


'0'과 '1'로 가지는 긴 벡터를 처음부터 끝까지 쭉 훓어가면서 


(1) '0'이 연속으로 3번 나오면 그 구간을 기준으로 나누어서 

=> (2) 나누어진 구간을 새로운 벡터로 생성하기


입니다.  


'0'이 연속으로 나오는 회수는 분석가가 필요로 하는 회수로 지정할 수 있도록 매개변수(argument)로 지정해서 프로그래밍해보겠습니다. 


간단한 예제 벡터로서, '0'과 '1'을 원소로 해서 30개의 무작위수(이항분포 랜덤 샘플링, 0이 80%, 1이 20%)로 구성된 벡터를 생성해보겠습니다. 


 

> rm(list=ls()) # clean all


# Sample vector


> set.seed(123) # for reproducibility

> vec_raw <- sample(c(0, 1), size=30, replace=TRUE, prob=(c(0.8, 0.2)))

> vec_raw

 [1] 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0

> vec <- vec_raw # copy for manipulation




R 프로그래밍을 하고자 하는 일의 아웃풋 이미지는 아래와 같습니다. 

'0'이 연속 세번 나오는 구간을 노란색으로 표시를 했습니다. 이 구간을 기준으로 원래의 벡터를 구분해서 나눈 후에(split), 각각을 새로운 벡터로 생성하여 분리해주는 프로그램을 짜보는 것이 이번 포스팅의 주제입니다. 




아래에 wihle() 반복문, if(), else if() 조건문, assign() 함수를 사용해서 위의 요건을 수행하는 코드를 짜보았습니다. 원래 벡터에서 제일 왼쪽에 있는 숫자가 '0'이 아니면 하나씩 빼서 빈 벡터에 차곡차곡 쌓아 놓구요, '0'이 3개 나란히 있는 '000'이 나타나면 그동안 쌓아두었던 벡터를 잘라내서 다른 이름의 벡터로 저장하고, '000'도 그 다음 이름의 벡터로 저장하면서 원래 벡터에서 '000'을 빼놓도록 하는 반복문입니다. assign() 함수는 저장하는 객체의 이름에 paste()를 사용해서 변화하는 숫자를 붙여주고자 할 때 사용하는 함수인데요, 자세한 사용법 예제는 여기(=> http://rfriend.tistory.com/108)를 참고하세요. 


아래 프로그램에서 'bin_range'에 할당하는 숫자를 변경하면 원하는 회수만큼 '0'이 반복되었을 때 구간을 분할하도록 조정할 수 있습니다.  가령, '0'이 연속 10번 나오면 분할하고 싶으면 bin_range <- 10  이라고 할당해주면 됩니다. 



##---------------------------------------------

# Vector Bin Split by successive '0's criteria

##---------------------------------------------


# Setting

vec_tmp <- c() # null vector

bin_range <- 3 # successive '0's criterion

vec_idx <- 1

vec_num <- 1


# Zero_Bin_Splitter

while (length(vec) > 0){

  if (sum(vec[1:bin_range], na.rm=T) != 0){

    vec_tmp[vec_idx] <- vec[1]

    vec <- vec[-1]

    vec_idx <- vec_idx + 1

  } else if (is.null(vec_tmp)){

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  } else {

    assign(paste0("vec_split_", vec_num), vec_tmp)

    vec_tmp <- c() # initialization

    vec_num <- vec_num + 1

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  }

}


# delete temp vector and arguments

rm(vec, vec_tmp, bin_range, vec_idx, vec_num)




아래는 위의 프로그램을 실행시켰을 때의 결과입니다.  원래 의도했던대로 잘 수행되었네요. 




=====================================================

아래의 코드는 페이스북 R User Group의 회원이신 June Young Lee 님께서 data.table 라이브러리를 사용해서 짜신 것입니다.  코드도 간결할 뿐만 아니라, 위에 제가 짠 코드보다 월등히 빠른 실행속도를 자랑합니다. 대용량 데이터를 빠른 속도로 조작, 처리하려면 data.table 만한게 없지요. 아래 코드는 저도 공부하려고 옮겨 놓았습니다. 


똑같은 일을 하는데 있어서도 누가 어떤 로직으로 무슨 패키지, 함수를 사용해서 짜느냐에 따라서 복잡도나 연산속도가 이렇게 크게 차이가 날 수 있구나 하는 좋은 예제가 될 것 같습니다. 이런게 프로그래밍의 묘미이겠지요? ^^  June Young Lee 님께 엄지척! ^^b



# by June Young Lee


library(data.table)


# 0이 세번 연속으로 들어 있는 아무 벡터 생성

vec <- c(0,0,0,3,10,2,3,0,4,0,0,0,1,2,50,4,0,0,32,1,0,0,0,1,1)


# data.table함수인 rleid이용하여 연속인 행들을 구분하는 idx1 컬럼 생성 

dt <- data.table(v1=vec, idx1=rleid(vec))


# idx1 기준으로 갯수를 세어 N 컬럼 생성 

dt[,N:=.N, by=idx1]


# 0이 세번 연속으로 나온 그룹(=N컬럼변수가 3인)을 idx2로 체크하기 

dt[v1==0&N==3,idx2:=1L]


# split 하기 위한 기준 벡터를 마찬가지로 rleid함수를 이용하여 idx3로 생성


dt[,idx3:=rleid(idx2)]


# data.table의 split 함수 적용하고, lapply 적용하여 v1 칼럼만 추출 

res <- lapply(split(dt, by="idx3"), function(x) x[,v1])

res

# $`1`

# [1] 0 0 0

# $`2`

# [1] 3 10 2 3 0 4

# $`3`

# [1] 0 0 0

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`5`

# [1] 0 0 0

# $`6`

# [1] 1 1


# 0이 3번 연속으로 나온 놈들을 남겨두도록 작성한 이유는, 

# 위치확인 & 확인용입니다. 

# 필요없으면, 아래 정도의 코드를 추가하면 되겠네요.


res2 <- res[!unlist(lapply(res, function(x) length(x)==3&sum(x)==0))]

res2

# $`2`

# [1] 3 10 2 3 0 4

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`6`

# [1] 1 1

 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되셨다면 아래의 '공감~'를 눌러주세요. 



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. karku 2018.02.13 13:43  댓글주소  수정/삭제  댓글쓰기

    늘 도움이 되는 글을 포스팅해주셔서 감사합니다.
    그런데 올려주신 코드를 입력해보면 'vec'객체가 발견되지 않는다는 오류문이 뜹니다.
    혹시 'vec_raw'를 'vec'로 수정하면 되는 건가요? 답글주시면 감사하겠습니다.

아래의 예제와 같이 콤마로 구분되어 있는 문자형 변수(caracter variable with comma seperator delimiter)를 가진 데이터 프레임이 있다고 합시다.

 

두번째의 item 변수에 콤마로 구분되어 들어있는 다수의 항목들을 구분자 콤마(',')를 기준으로 분리(split)한 후에 => 이를 동일한 name을 key로 해서 세로로 주욱~ 늘여서 재구조화하여야 할 필요가 생겼다고 합시다.

 

데이터 구조변환의 전/후 모습을 말로 설명하려니 좀 어려운데요, 아래의 'Before (original dataset) => After (transformation)' 의 그림을 참고하시기 바랍니다.

 

 

 

이걸 R로 하는 방법을 소개합니다.

 


 

먼저 위의 Before 모양으로 간단한 데이터 프레임을 만들어보았습니다.

 


 ##--------------------------------------------------------
## splitting character of dataframe and reshaping dataset
##--------------------------------------------------------

 

# (1) creating sample dataframe
name_1 <- c("John")
item_1 <- c("Apple,Banana,Mango")

name_2 <- c("Jane")
item_2 <- c("Banana,Kiwi,Tomato,Mango")

name_3 <- c("Tom")
item_3 <- c("Apple,Tomato,Cherry,Milk,IceCream")

tr_1 <- cbind(name_1, item_1)
tr_2 <- cbind(name_2, item_2)
tr_3 <- cbind(name_3, item_3)
mart <- data.frame(rbind(tr_1, tr_2, tr_3))

 

# rename
install.packages("dplyr")
library(dplyr)
mart <- rename(mart,
               name = name_1,
               item = item_1)

 

#-------------

> mart
  name                              item
1 John                Apple,Banana,Mango
2 Jane          Banana,Kiwi,Tomato,Mango
3  Tom Apple,Tomato,Cherry,Milk,IceCream
 

 

 

 


 

위의 mart 라는 이름의 'Before' 데이터프레임을 'After' 모양의 데이터 프레임으로 구조 변환시키는 절차 및 R script는 아래와 같습니다.

 

 

[ 문자형 분리(split) 및 데이터 재구조화(reshape) 절차 ]

 

(1) mart_new <- data.table() : mart_new 라는 비어있는 데이터 테이블, 데이터 프레임을 생성  (<= 여기에 차곡차곡 loop돌린 결과를 rbind()로 쌓아가게 됨)

 

(2) n <- nrow(mart) : original dataset인 mart 의 총 행의 개수(nrow(mart))를 n 이라는 벡터로 할당 (<= 이 개수만큼 loop를 돌릴겁니다. 위 예제는 총 3개군요. John, Jane, Tom 이렇게요)

 

(3) for (i in 1:n)  : 1부터 n (즉, 3)까지 반복 수행 <= 매 행별로 아래의 문자열을 구분자를 기준으로 분리 후 세로로 재구조화하는 작업을 반복할 겁니다

 

(4) print(i) : 반복수행 loop 문이 어디쯤 돌고 있나 콘솔 창에서 확인하려고 집어넣었습니다. 굳이 없어도 됩니다.

 

(5) name_index <- as.character(mart[i, 1])  : i번째 행(row)의 1번째 열("name")을 indexing 해옵니다. 즉, i의 순서 1, 2, 3 별로 "John", "Jane", "Tom", 이렇게 순서대로 indexing 해옵니다.  

 

(6) item_index <- as.character(mart[i, 2])  : i번째 행(row)의 2번째 열("item")을 indexing 해옵니다. 

 

(7) item_index_split_temp <- data.frame(strsplit(item_index, split = ',')) : (6)번에서 i번째 행별로 indexing해 온 item을 구분자 'comma (',')'를 기준으로 문자열을 분리(split)한 후에, 이것을 데이터 프레임으로 생성합니다.

 

(8) mart_temp <- data.frame(cbind(name_index, item_index_split_temp))  : (5)번과 (7)번 결과물을 cbind()로 묶은 후에, 이것을 데이터 프레임으로 생성합니다.  cbind()로 결합할 때 name은 1개인데 item이 2개 이상일 경우에는 1개밖에 없는 name이 자동으로 반복으로 item의 개수만큼 재할당이 됩니다.

 

(9) names(mart_temp) <- c("name", "item")  : mart_temp의 변수 이름을 첫번째 변수는 "name", 두번째 변수는 "item"으로 지정해줍니다.

 

(10) mart_new <- rbind(mart_new, mart_temp) : (1)번에서 생성한 (처음에는 비어있는) 데이터프레임에 loop를 돌 때마다 생성이되는 (8, 9)번 데이터프레임을 차곡 차곡 rbind()로 반복적으로 쌓아줍니다.

(11) rm(name_index, item_index, item_index_split_temp, mart_temp)  :  중간 임시 데이터셋 삭제

 

끝.

 

 

[ R script ]

 

 

# (2) splitting charater by delimeter, reshaping data structure by loop programming

install.packages("data.table")
library(data.table)

mart_new <- data.table() # blank data.table

 

n <- nrow(mart) # total number of rows

 

for (i in 1:n){
 

  print(i) # to check progress
 
  name_index <- as.character(mart[i, 1])
  item_index <- as.character(mart[i, 2])
 
  item_index_split_temp <- data.frame(strsplit(item_index, split = ','))
  mart_temp <- data.frame(cbind(name_index, item_index_split_temp))
 
  names(mart_temp) <- c("name", "item")
 
  mart_new <- rbind(mart_new, mart_temp)
}
 

rm(name_index, item_index, item_index_split_temp, mart_temp) # delete temp dataset

 

# ----------------

> mart_new
    name     item
 1: John    Apple
 2: John   Banana
 3: John    Mango
 4: Jane   Banana
 5: Jane     Kiwi
 6: Jane   Tomato
 7: Jane    Mango
 8:  Tom    Apple
 9:  Tom   Tomato
10:  Tom   Cherry
11:  Tom     Milk
12:  Tom IceCream
 

 

 

 


 

Ashtray 님께서 댓글로 남겨주신 R script를 여러 사람이 좀더 쉽게 널리 공유할 수 있도록 아래에 공유합니다 (Ashtray님, R script 공유해주셔서 감사합니다. ^^).

 

구분자가 "\\^"를 기준으로 문자열을 분리하고, for loop 문을 사용해서 세로로 세워서 재구조화하는 절차는 같습니다.  대신 변수 개수가 다르다보니 for loop문이 조금 다르구요, data table이 아니라 list()를 사용했다는점도 다릅니다. 

 

R script 짜다보면, 그리고 다른 분께서 R script 짜놓은것을 보면 "동일한 input과 동일한 output이지만 가운데 process는 방법이 단 한가지만 있는게 아니구나" 하고 저도 배우게 됩니다.

 

 

names(data) <- c("number", "PK", "Call_ID", "Keyword_Count")
data$Keyword_Count <- strsplit(data$Keyword_Count, split="\\^")

datalist <- list()
k <- 1
for(i in 1:nrow(data)){
    for(j in 1:length(unlist(data$Keyword_Count[i]))){
        datalist[[k]] <- list(data$number[i], data$PK[i], data$Call_ID[i],
        data$Keyword_Count[[i]][j])
    k <- k+1
    }
}

data2 <- rbindlist(datalist)
data2[[4]] <- unlist(data2[[4]])

 

 

 


 

혹시 Spark을 사용하는 분이라면 (key, value) pair RDD로 구성된 데이터셋에 대해 flatMapValues() 라는 pair operation 을 사용하면 쉽게 key를 기준으로 세로로 재구성한 RDD를 만들 수 있습니다.  참고하세요.

 

 

 

많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~ ♡'를 꾸욱 눌러주세요.

 

 

[Reference]

1. R 문자함수 : http://rfriend.tistory.com/37

2. R 반복 연산 loop 프로그래밍 : http://rfriend.tistory.com/90

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. AshtrayKim 2016.09.29 15:42  댓글주소  수정/삭제  댓글쓰기

    감사합니다! 제가 한 코드도 공유하겠습니다.
    다만 제 원본 테이블은 name에 해당하는 컬럼이 3개(number, PK, Call_ID)로 나눠져있고,
    item에 해당하는 항목을 ^로 나누었습니다

  2. AshtrayKim 2016.09.29 15:48  댓글주소  수정/삭제  댓글쓰기

    스팸차단이라고 글을 못작성하게 해서.. 핵심적인 부분만 적을게요
    names(data) <- c("number", "PK", "Call_ID", "Keyword_Count")
    data$Keyword_Count <- strsplit(data$Keyword_Count, split="\\^")

    datalist <- list()
    k <- 1
    for(i in 1:nrow(data)){
    for(j in 1:length(unlist(data$Keyword_Count[i]))){
    datalist[[k]] <- list(data$number[i], data$PK[i], data$Call_ID[i],
    data$Keyword_Count[[i]][j])
    k <- k+1
    }
    }

    data2 <- rbindlist(datalist)
    data2[[4]] <- unlist(data2[[4]])

  3. 이상규 2016.11.25 13:22  댓글주소  수정/삭제  댓글쓰기

    감사합니다 !! 정말 도움 많이 되는것 같아요

    혹시 After 에서 Before로 넘어가는 포스팅도 있나요?

    • R Friend R_Friend 2016.11.25 13:35 신고  댓글주소  수정/삭제

      이상규님,
      arules package의 read.transactions() 함수 사용해보세요.

      library(arules)

      tr <- read.transactions(file, format=c("single"), cols=c("transactionID", "itemID"))

    • 이상규 2016.11.27 16:52  댓글주소  수정/삭제

      안녕하세요 알프렌드님

      Before로 넘어가려고 추천해주신 코드로 아래와 같이 코딩했는데

      > tr <- read.transactions("su.csv", format = c("single"), cols = c("JID", "Group"))


      Error in read.transactions("su.csv", format = c("single"), cols = c("JID", :
      'cols' does not match 2 entries in header of file.

      에러가 발생했습니다. 어느 부분이 문제일까요 ㅜㅜ?

      JID는 character
      Group은 Factor형태입니다.

    • R Friend R_Friend 2016.11.27 18:00 신고  댓글주소  수정/삭제

      저도 read.transactions 해보니 에러 나네요. ^^;

      대신에 as(split([dadaframe[,"itemID"], dataframe[,"transactionID"]), "transactions") 함수를 사용해보세요. 아래 R script 참고하세요.

      ##-- making data.frame
      transactionID <- c(rep("tr1", 3), rep("tr2", 4))
      itemID <- c("item1", "item2", "item3", "item1", "item2", "item4", "item5")
      tr_df <- data.frame(transactionID, itemID)
      str(tr_df)

      ## converting data.frame to transactions format
      tr <- as(split(tr_df[,"itemID"], tr_df[,"transactionID"]), "transactions")
      inspect(tr)

      > inspect(tr)
      items transactionID
      [1] {item1,item2,item3} tr1
      [2] {item1,item2,item4,item5} tr2

    • 이상규 2016.11.27 18:03  댓글주소  수정/삭제

      감사합니다! 도전해보고 올게요!!

이번 포스팅에서는 여러개의 데이터 프레임을 한꺼번에 하나의 데이터프레임으로 묶는 몇가지 방법을 알아보고, 성능 측면을 비교해보겠습니다.

 

이번 포스팅은 andrew 님이 r-bloggers.com 에 썼던을 그대로 가져다가 번역을 한 내용입니다. 

 

[ Source ] Concatenating a list of data frames , June 6, 2014, By andrew

 

결론 먼저 말씀드리면, data.table package의 rbindlist(data) 함수가 속도 면에서 월등히 빠르네요.

 

 

 

[ R로 여러개의 데이터프레임을 한꺼번에 하나의 데이터프레임으로 묶기 ]

 

 

 

 

0) 문제 (The problem)

 

아래처럼 3개의 칼럼으로 구성된 100,000 개의 자잘한 데이터 프레임을 한개의 커다란 데이터 프레임으로 합치는 것이 풀어야 할 문제, 미션입니다.

 

data = list() 로 해서 전체 데이터 프레임들을 data라는 리스트로 만들어서 아래 각 방법별 예제에 사용하였습니다.

 

> ###########################################
> ## Concatenating a list of data frames
> ## do.call(rbind, data)
> ## ldply(data, rbind)
> ## rbind.fill(data)
> ## rbindlist(data) ** winner! **
> ###########################################
> 
> ## The Problem
> 
> data = list()
> 
> N = 100000
> 
> for (n in 1:N) {
+   data[[n]] = data.frame(index = n, 
+                          char = sample(letters, 1), 
+                          z = runif(1))
+ }
> 
> data[[1]]
  index char         z
1     1    j 0.2300154

 

 

 

 

1) The navie solution : do.call(rbind, data)

 

가장 쉽게 생각할 수 있는 방법으로 base package에 포함되어 있는 rbind() 함수를 do.call 함수로 계속 호출해서 여러개의 데이터 프레임을 위/아래로 합치는 방법입니다. 

 

이거 한번 돌리니 정말 시간 오래 걸리네요.  @@~  낮잠 잠깐 자고 와도 될 정도로요.

 

 

> ## (1) The Naive Solution
> head(do.call(rbind, data))
  index char          z
1     1    j 0.23001541
2     2    f 0.63555284
3     3    d 0.65774397
4     4    y 0.46550511
5     5    b 0.02688307
6     6    u 0.19057217

 

 

 

 

2-1) plyr package : ldply(data, rbind)

 

두번째 방법은 plyr package의 ldply(data, rbind) 함수를 사용하는 방법입니다.

 

> ## (2) Alternative Solutions #1 and #2
> ## (2-1) plyr package : ldply(data, rbind)
> install.packages("plyr")
> library(plyr)
> head(ldply(data, rbind))
  index char          z
1     1    j 0.23001541
2     2    f 0.63555284
3     3    d 0.65774397
4     4    y 0.46550511
5     5    b 0.02688307
6     6    u 0.19057217

 

 

 

 

 

2-2) plyr package : rbind.fill(data)

 

세번째 방법은 plyr package의 rbind.fill(data) 함수를 사용하는 방법입니다.  결과는 앞의 두 방법과 동일함을 알 수 있습니다.

 

> ## (2-2) plyr package : rbind.fill(data)
> library(plyr)
> head(rbind.fill(data))
  index char          z
1     1    j 0.23001541
2     2    f 0.63555284
3     3    d 0.65774397
4     4    y 0.46550511
5     5    b 0.02688307
6     6    u 0.19057217

 

 

 

 

 

3) data.table package : rbindlist(data)

 

마지막 방법은 data.table package의 rbindlist(data) 함수를 사용하는 방법입니다.

 

> ## (3) Alternative Solution 
> ## data.table package : rbindlist(data)
> install.packages("data.table")
> library(data.table)
> head(rbindlist(data))
   index char          z
1:     1    j 0.23001541
2:     2    f 0.63555284
3:     3    d 0.65774397
4:     4    y 0.46550511
5:     5    b 0.02688307
6:     6    u 0.19057217

 

 

 

 

4) 벤치마킹 테스트 (bechmarking test)

 

 

> ## Benchmarking (performance comparison)
> install.packages("rbenchmark")
> library(rbenchmark)
> benchmark(do.call(rbind, data),
+           ldply(data, rbind), 
+           rbind.fill(data), 
+           rbindlist(data))

                  test replications  elapsed relative user.self sys.self user.child sys.child

1 do.call(rbind, data)          100 11387.82  668.692  11384.15     1.54         NA        NA
2   ldply(data, rbind)          100  4983.72  292.644   4982.90     0.52         NA        NA
3     rbind.fill(data)          100  1480.46   86.932   1480.23     0.17         NA        NA
4      rbindlist(data)          100    17.03    1.000     16.86     0.17         NA        NA

 

 

패키지/함수별 성능 비교를 해본 결과 data.table 패키지의 rbindlist(data) 함수가 월등히 빠르다는 것을 알 수 있습니다.  위의 벤치마킹 결과를 보면, 속도가 가장 빨랐던 rbindlist(data)를 1로 놨을 때, 상대적인 속도(relative 칼럼)를 보면 rbind.fill(data)가 86.932로서 rbindlist(data)보다 86배 더 오래걸리고, ldply(data, rbind)가 292.644로서 rbindlist(data)보다 292배 더 오래걸린다는 뜻입니다.  do.call(rbind, data)는 rbindlist(data) 보다 상대적으로 668.692배 더 시간이 걸리는 것으로 나오네요.

 

rbindlist(data)가 훨등히 속도가 빠른 이유는 두가지인데요,

 

(1) rbind() 함수가 각 데이터 프레임의 칼럼 이름을 확인하고, 칼럼 이름이 다를 경우 재정렬해서 합치는데 반해, data.table 패키지의 rbindlist() 함수는 각 데이터 프레임의 칼럼 이름을 확인하지 않고 단지 위치(position)를 기준으로 그냥 합쳐버리기 때문이며,

(따라서, rbindlist() 함수를 사용하려면 각 데이터 프레임의 칼럼 위치가 서로 동일해야 함)

 

(2) rbind() 함수는 R code로 작성된 반면에, data.table 패키지의 rbindlist() 는 C 언어로 코딩이 되어있기 때문입니다.

 

많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. AshtrayK 2016.09.09 14:57 신고  댓글주소  수정/삭제  댓글쓰기

    do.call에 대해서 설명 부탁드려요~

    • R Friend R_Friend 2016.09.09 18:55 신고  댓글주소  수정/삭제

      do.call ()은 괄호안의 함수를 일괄 적용할때 사용하는 함수입니다. lapply()와 혼동될수도 있는데요,

      do.call() applies a given function to list as a whole , so there is only one function call.

      lapply() applies a given function for each element in a list, so there will be several function calls.

      포스팅 본문 예제에 do.call() 대신 lapply(X, rbind)를 쓰면 엉뚱한 결과가 나올겁니다.

폴더에 자잘하게 쪼개진 여러개의 파일들이 있을 때, 그리고 이 파일들을 일일이 R로 불러오기 해야 할 때, 더그리고 이들 불러온 파일을 한개의 데이터셋을 합쳐야 할 때 (이쪽 동네 전문용어로) 노가다를 하지 않고 좀더 스마트하게 하는 방법을 소개하겠습니다.

 

순서는 다음과 같습니다.

  • (1) 폴더 경로 객체로 만들기
  • (2) 폴더 내 파일들 이름을 list-up 하여 객체로 만들기
  • (3) 파일 개수 객체로 만들기
  • (4) 폴더 내 파일들을 LOOP 돌려서 불러오기 : read.table()
  • (5) 파일을 내보내면서 합치기 : write.table(dataset, APPEND = TRUE)
  • (6) 데이터프레임으로 불러오기, 칼럼 이름 넣기 : read.table(dataset_all, col.names = c())

 

자, 예를 들면서 순서대로 R script 설명하겠습니다.

 

 

아래의 화면캡쳐 예시처럼 MyDocuments > R > FILES 폴더 아래에 daily로 쪼개진 10개의 text 파일들이 들어있다고 해봅시다.  (10개 정도야 일일이 불어올 수도 있겠지만, 100개, 1,000개 파일이 들어있다면?)

 

 

 

 

  • (1) 폴더 경로 객체로 만들기

> # cleaning up environment > rm(list=ls()) >

> # making directory as an object > src_dir <- c("C:/Users/Owner/Documents/R/FILES") # 경로 구분 : '\'를 '/'로 바꿔야 함 >
>
src_dir [1] "C:/Users/Owner/Documents/R/FILES" 

 

 

  • (2) 폴더 내 파일들 이름을 list-up 하여 객체로 만들기 : list.files()

> # listing up name of files in the directory => object
>

>
src_file <- list.files(src_dir) # list
>
> src_file [1] "day_20160701.txt" "day_20160702.txt" "day_20160703.txt" "day_20160704.txt" [5] "day_20160705.txt" "day_20160706.txt" "day_20160707.txt" "day_20160708.txt" [9] "day_20160709.txt" "day_20160710.txt"

 

 

 

"C:/Users/Owner/Documents/R/FILES" 디렉토리에 들어있는 파일들을 열어보면 아래와 같은 데이터들이 들어있습니다. (가상으로 만들어 본 것임)  daily로 집계한 데이터들이 들어있네요.

 

 

  • (3) 파일 개수 객체로 만들기 : length(list)

 

> # counting number of files in the directory => object
> 
> src_file_cnt <- length(src_file) >
> src_file_cnt [1] 10

 

 

 

여기까지 R을 실행하면 아래와 같이 environment 창에 객체들이 생겼음을 확인할 수 있습니다.

 

 


 

  • (4) 폴더 내 파일들을 LOOP 돌려서 불러오기
    => (5) 파일을 내보내면서 합치기 : write.table(dataset, APPEND = TRUE)


    : for(i in 1:src_file_cnt) {read.table()
                                     write.table(dataset, append = TRUE)}

 

> # write.table one by one automatiically, using loop program > for(i in 1:src_file_cnt) { + + # write.table one by one automatiically, using loop program + dataset <- read.table( + paste(src_dir, "/", src_file[i], sep=""), + sep=",", header=F, stringsAsFactors = F) + + # dataset exporting with 'APPEND = TREU' option, filename = dataset_all.txt + write.table(dataset, + paste(src_dir, "/", "dataset_all.txt", sep=""), + sep = ",", + row.names = FALSE, + col.names = FALSE, + quote = FALSE, + append = TRUE) # appending dataset (stacking) + + # delete seperate datasets + rm(dataset) + + # printing loop sequence at console to check loop status + print(i) + } [1] 1 [1] 2 [1] 3 [1] 4 [1] 5 [1] 6 [1] 7 [1] 8 [1] 9 [1] 10

 

 

 

여기까지 실행을 하면 아래처럼 MyDocuments>R>FILES 폴더 아래에 'dataset_all.txt' 라는 새로운 텍스트 파일이 하나 생겼음을 확인할 수 있습니다. 

 


 

 

새로 생긴 'dataset_all.txt' 파일을 클릭해서 열어보면 아래와 같이 'day_20160701.txt' ~ 'day_20160710.txt'까지 10개 파일에 흩어져있던 데이터들이 차곡차곡 쌓여서 합쳐져 있음을 확인할 수 있습니다.

 

 

 

 

  • (6) 데이터 프레임으로 불러오기 : read.table()
         칼럼 이름 붙이기 : col.names = c("var1", "var2", ...)

> # reading dataset_all with column names
> dataset_all_df <- read.table(
+   paste(src_dir, "/", "dataset_all.txt", sep=""), 
+   sep = ",", 
+   header = FALSE, # no column name in the dataset
+   col.names = c("ymd", "var1", "var2", "var3", "var4", "var5", 
+                 "var6", "var7", "var8", "var9", "var10"), # input column names
+   stringsAsFactor = FALSE, 
+   na.strings = "NA") # missing value : "NA"

 

 

 

우측 상단의 environment 창에서 'dataset_all_df' 데이터 프레임이 새로 생겼습니다.

클릭해서 열어보면 아래와 같이 'day_20160701.txt' ~ 'day_20160710.txt'까지 데이터셋이 합쳐져있고, "ymd", "var1" ~ "var10" 까지 칼럼 이름도 생겼습니다.

 

 

 

프로그래밍을 통한 자동화가 중요한 이유, 우리의 시간은 소중하니깐요~! ^^

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡'를 꾸욱 눌러주세요.

 

 

====================================================================

(2018.03.14일 내용 추가)

 

댓글 질문에 '폴더에 있는 개별 파일을 하나씩 읽어와서 하나씩 DataFrame 객체로 메모리상에 생성하는 방법에 대한 질문이 있어서 코드 추가해서 올립니다. 위에서 소개한 방법과 전반부는 동일하구요, 마지막에 루프 돌릴 때 assign() 함수로 파일 이름을 할당하는 부분만 조금 다릅니다.

 

 

#=========================================================
# read all files in a folder and make a separate dataframe
#=========================================================

rm(list=ls()) # clear all

 

# (1) directory
src_dir <- c("D:/admin/Documents/R/R_Blog/326_read_all_files")

 

# (2) make a file list of all files in the folder
src_file <- list.files(src_dir)
src_file

 

 

# (3) count the number of files in the directory => object
src_file_cnt <- length(src_file)
src_file_cnt # 5

 

# (4) read files one by one using looping
#     => make a dataframe one by one using assign function
for (i in 1:src_file_cnt){
  assign(paste0("day_", i), 
         read.table(paste0(src_dir, "/", src_file[i]),
                    sep = ",",
                    header = FALSE))
  print(i) # check progress
}

 

rm(src_dir, src_file, src_file_cnt, i) # delete temp objects
ls() # list-up all dataframes

 

 

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 이전 댓글 더보기
  2. cnk 2018.09.10 19:16  댓글주소  수정/삭제  댓글쓰기

    좋은 자료 감사합니다.

    10개의 파일을 loop를 통해 assign함수를 이용하여 원하는 정보를 읽어서 Environment에 불러 읽는 작업까지 진행하였습니다. 혹시 읽은 파일들을 output file (csv or txt)형식으로 각각 읽은 10개의 파일을 10개의 output으로 만들고 싶은데 어떤식으로 진행해야 할 지 모르겠습니다.

    도움을 주신다면 감사하겠습니다.

    • R Friend R_Friend 2018.09.11 15:08 신고  댓글주소  수정/삭제

      안녕하세요. cnk님,

      (1) ls(pattern = "data_") 함수를 이용하면 R Environment 상에 있는 객체 중에서 특정 패턴을 가진 객체를 리스트업하여 새로운 객체로 만들어주세요. (자세한 내용은 http://rfriend.tistory.com/350 참조)

      (2) For loop 반복문을 사용해서 write.table() 함수를 이용하여 (1)번에서 찾은 여러개의 데이터프레임을 loop를 돌면서 하나씩 text 파일로 내보내기를 해주면 됩니다. paste() 함수를 사용해서 파일 이름이 계속 바뀌게 해주면 됩니다. (write.table() 함수의 자세한 내용은 http://rfriend.tistory.com/17 참조)

  3. 2018.09.11 15:18  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  4. JHJH 2019.01.02 13:35  댓글주소  수정/삭제  댓글쓰기

    위의 방법처럼 폴더 안의 파일들을 NGS_,i 이름의 CSV로 불러온 뒤 각 NGS_i 파일을 for 루프를 이용해서 grep을 해오려고 하는데 잘되지 않네요 ㅠㅠ

    mutation.txt에는 유전자의 이름이 있고, 이를 collapse로 ^,$를 붙혀 exact matching을 하는 방식입니다.
    일일히 grep을 쓰기 너무 힘들어서 루프로 짜보고 있는데 어렵네요...

    gene <-read.table("C:/JHR/pancchr/mutation.txt", header=T, sep="\t")
    v.gene <-as.vector(t(gene))

    for (i in 1:src_file_lnc){
    assign(paste0("list_",i),
    grep(paste(v.gene,collapse = "$|^"),paste0("NGS_",i)[12]))
    print(i)
    }

    이렇게 했을때 list_1을 불러오면 NA가 되버리네요ㅠ 뭐가 문제일까요?

    assign, paste0도 같이 공부해봐야겠습니다ㅠ

    • R Friend R_Friend 2019.01.02 14:25 신고  댓글주소  수정/삭제

      안녕하세요 JH님. 반갑습니다.
      제가 데이터를 가지고 있지 않아서 아래 코드가 정확하게 작동하는지 확인은 할 수가 없는데요, 우선 원하는 결과물에 대해 설명해주신 내용을 토대로 코드를 일부 수정해보았습니다.

      설명해주신 내용 중에 "폴더 안의 파일들을 NGS_,i 이름의 CSV로 불러온 뒤" 라는 부분에 대한 코드가 없는 것처럼 보여서요, 그 부분을 for loop 문 안에 추가를 하였습니다.

      NGS_i 파일들만 특정 폴더에 모두 들어있다고 가정하였습니다.

      댓글란에 코드를 복사해서 넣으면 들여쓰기가 모두 없어지므로 읽기에 좀 어려운데요, R Studio에 코드 복사해서 사용해보시기 바랍니다.

      # (1) directory
      src_dir <- c("D:/admin/Documents/YourDirectory")

      # (2) make a file list of all files in the folder
      src_file <- list.files(src_dir)
      src_file

      # (3) count the number of files in the directory => object
      src_file_cnt <- length(src_file)
      src_file_cnt

      # (4) read files one by one using looping
      # => make a dataframe one by one using assign function
      for (i in 1:src_file_cnt){
      # read NGS files first and make it as a DataFrame
      assign(paste0("NGS_", i),
      read.table(paste0(src_dir, "/", src_file[i]),
      sep = ",",
      header = FALSE))
      # exact matching by using grep
      assign(paste0("list_", i),
      grep(paste(v.gene, collapse = "S|^"), paste0("NGS_", i)[12]))
      print(i) # check progress
      }

  5. JHJH 2019.01.02 20:47  댓글주소  수정/삭제  댓글쓰기

    오 친절한 답변 너무 감사합니다. 한 번에 루프를 넣는 방법이 있었군요ㅠㅠ

    for (i in 1:src_file_lnc){
    >>>assign(paste0("NGS_",i),
    >>>>>>read.csv(paste0(src_dir, "/", src_file[i]),
    >>>>>>stringsAsFactors = T,
    >>>>>>na.strings = T,
    >>>>>>header = T))
    >>>print(i)
    }

    제가 csv로 불러오고 헤더와 stringasfactor를 필요로 해서 이렇게 넣어봣는데 read.table로 하는 것과는 어떤 차이가 있는지 알 수 있을까요??
    NGS_i 파일의 1번쨰 column을 불러오기 위해서[12]를 넣었는데 결과물인 list_i 의 파일들이 NA로 나오는걸 보니 사용하는 함수를 바꾸어보아야할 것 같습니다 조언 감사합니다!!

  6. Yu 2019.01.11 17:50  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.
    덕분에 작업에 시간단축이 굉장히 많이 되었습니다.

    그러나, 알려주신 코드로 진행을 하는데 오류가 조금 있어서..

    (5) for문까지 진행을 하면

    Error in type.convert.default(data[[i]], as.is = as.is[i], dec = dec, : invalid multibyte string at '<ed><96><89>?뺤옄移섏쐞<ec>썝<ed>쉶<ed>쉶<ec>쓽濡<9d>'

    위와 같은 에러코드가 발생합니다.

    컴퓨터 상에 txt파일을 열면 한글이 깨지지 않고 잘 보이는데.. 문제가 무엇인지 모르겠습니다.

    답변 가능하시다면 부탁드리도록 하겠습니다.

    좋은 하루보내세요.

    • R Friend R_Friend 2019.01.11 21:00 신고  댓글주소  수정/삭제

      안녕하세요.

      아무래도 encoding 문제 때문에 에러가 발생한 것 같습니다.
      ( 참고: http://rfriend.tistory.com/10 )

      read.table 로
      read.table("filename.csv", sep=",", header=FALSE, stringsAsFactors=FALSE, fileEncoding="UTF-8")

      혹은,

      read.table("filename.csv", sep=",", header=FALSE, stringsAsFactors=FALSE, fileEncoding="CP949")

      를 한번 시도해보시면 좋겠습니다.

  7. Yu 2019.01.14 10:19  댓글주소  수정/삭제  댓글쓰기

    안녕하십니까.
    이전에 문의 드렸던 인코딩 관련해서 한번 더 문의를 드리겠습니다.
    말씀해주신 방법으로 (fileEncoding="UTF-8"도 시도해보았음)

    for(i in 1:src_file_cnt){
    dataset <- read.table(
    paste(src_dir, "/", src_file[i], sep=""),
    sep=",", header=F, stringsAsFactors = F, fileEncoding="CP949")
    write.table(dataset,
    paste(src_dir, "/", "dataset_all.txt", sep=""),
    sep=",",
    row.names = F,
    col.names = F,
    quote = F,
    append)
    }

    위의 코드를 입력해도

    Error in as.logical(test) :
    cannot coerce type 'closure' to vector of type 'logical'
    In addition: Warning messages:
    1: In read.table(paste(src_dir, "/", src_file[i], sep = ""), sep = ",", : invalid input found on input connection 'C:/Users/CNI/Desktop/YMS/?낅Т/20190107_?뚯쓽濡?9?€/?곸엫?꾩썝???됱젙?먯튂?꾩썝???됱젙?먯튂?꾩썝??txt/-Unlicensed-235-?됱젙?먯튂1李?txt'
    2: In read.table(paste(src_dir, "/", src_file[i], sep = ""), sep = ",", : 'C:/Users/CNI/Desktop/YMS/?낅Т/20190107_?뚯쓽濡?9?€/?곸엫?꾩썝???됱젙?먯튂?꾩썝???됱젙?먯튂?꾩썝??txt/-Unlicensed-235-?됱젙?먯튂1李?txt'에서 readTableHeader에 의하여 발견된 완성되지 않은 마지막 라인입니다

    위와 같은 에러가 발생합니다.

    for문으로 텍스트를 불러 통합된 데이터 저장까지를 진행하고싶은데.. 문제가 무엇인지 모르겠습니다.

    그러나 for문을 사용하지 않고 개별로

    a01 <- readLines("filename.txt", encoding="UTF-8")

    위와 같은 코드를 실행할때는 한글이 깨지지 않고 잘 불러와집니다.

    혹시 폴더안에 있는 파일을 모두 readLine으로 불러 하나의 txt파일로 저장하는 for문을 제공해주실 수 있으실까요..

    죄송하지만 부탁드리겠습니다. 좋은하루보내세요.

    • R Friend R_Friend 2019.01.14 12:13 신고  댓글주소  수정/삭제

      안녕하세요 Yu님,

      남겨주신 코드에 보니 append 만 쓰셨고 append = TRUE 라고 명시적으로 TRUE를 안쓰셨는데요, TRUE 포함해서 시도해보실래요?

      아래 코드는 readLines()를 사용하고, blank DataFrame을 하나 만들어놓고 거기에 메모리 상에서 하나씩 파일을 읽어들여 만든 DataFrame Temp를 세로로 쌓은(stack) 후에 => 마지막에 data_all DataFrame을 export 하는 코드 입니다. 한번 시도해보시고 잘 안되면 댓글 남겨주세요.

      src_dir <- c("YourDirectoryHere")
      src_file <- list.files(src_dir)
      src_file_cnt <- length(src_file)

      data_all <- data.frame() # blank df for stacking later

      for (i in 1:src_file_cnt){
      # read text file
      dataset_tmp <- readLines(paste0(src_dir, "/", src_file[i]),
      encoding="UTF-8")

      # stack it to data_all
      data_all <- rbind(data_all, dataset_tmp) # assume that columns are the same

      print(i, " th file is done") # progress check
      }

      # export data_all DataFrame into text file
      write.table(data_all, paste0(src_dir, "/", data_all.txt))

  8. Yu 2019.01.14 13:28  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.
    우선 빠른 답변 너무나도 감사드립니다.

    첫번째로 append = TRUE 를 포함한 코드에서도 동일한 에러가 발생합니다.
    아마 append = TRUE는 입력을 하였는데 질문을 드릴때 댓글을 작성하다가 지워진 것 같습니다.

    두번째로 제공해주신 코드도 에러가 발생하였습니다.

    src_dir <- c("YourDirectoryHere")
    src_file <- list.files(src_dir)
    src_file_cnt <- length(src_file)

    data_all <- data.frame() # blank df for stacking later

    위 코드 까지는 에러없이 잘 진행이 되었습니다 (참고_ src_file_cnt = 92L 입니다.)

    그러나 for문에 있어서는..

    Error in print.default(i, " th file is done") : invalid 'digits' argument
    In addition: Warning message:
    In print.default(i, " th file is done") : NAs introduced by coercion

    위와 같은 에러코드가 발생하였습니다.

    그 뒤에
    write.table(data_all, paste0(src_dir, "/", data_all.txt))

    위 코드 또한

    Error in paste0(src_dir, "/", data_all.txt) :
    object 'data_all.txt' not found

    위와 같은 에러가 발생하였습니다.

    바쁘실텐데 번거롭게 정말 죄송합니다.

    무엇이 문제인지 전혀 감이 오지 않습니다..ㅠㅠ

    가능하시다면 한번 더 설명을 부탁드리겠습니다.

    참고로 제가 불러오고자 하는 텍스트 파일은 csv나 구분자가 따로 없는 회의록입니다.

    개별 회의록을 모두 불러와 한개의 회의록 데이터를 만들고자 하는 작업입니다.

    답변 부탁드리겠습니다. 늘 감사합니다.

    • R Friend R_Friend 2019.01.14 13:39 신고  댓글주소  수정/삭제

      수정한 코드인데요, 한번 해보실래요?
      rbind()를 쓰려면 칼럼 숫자랑 이름, 데이터 형태가 같아야 하는데요, 회의록이라면 rbind()가 잘 작동할지 불확실하네요.

      src_dir <- c("YourDirectoryHere")
      src_file <- list.files(src_dir)
      src_file_cnt <- length(src_file)

      data_all <- data.frame() # blank df for stacking later

      for (i in 1:src_file_cnt){
      # read text file
      dataset_tmp <- readLines(paste0(src_dir, "/", src_file[i]),
      encoding="UTF-8")

      # stack it to data_all
      data_all <- rbind(data_all, dataset_tmp) # assume that columns are the same

      rm(dataset_tmp) # remove temp df

      print(paste0(i, " th file is done")) # progress check
      }

      # export data_all DataFrame into text file
      write.table(data_all, paste0(src_dir, "/", "data_all.txt"))

  9. 2019.01.14 13:58  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  10. Yu 2019.01.14 14:10  댓글주소  수정/삭제  댓글쓰기

    비밀댓글 확인 가능하신가요?ㅠㅠ

    • R Friend R_Friend 2019.01.14 14:17 신고  댓글주소  수정/삭제

      아래 코드로 실행해보시지요.

      src_dir <- c("YourDirectoryHere")
      src_file <- list.files(src_dir)
      src_file_cnt <- length(src_file)

      for (i in 1:src_file_cnt){
      # read text file
      dataset <- readLines(paste0(src_dir, "/", src_file[i]),
      encoding="UTF-8")
      # export & append
      write.table(dataset,
      paste(src_dir, "/", "dataset_all.txt", sep=""),
      sep=",",
      row.names = F,
      col.names = F,
      quote = F,
      append = TRUE)

      print(paste0(i, " th file is done")) # progress check
      }

  11. Yu 2019.01.14 14:25  댓글주소  수정/삭제  댓글쓰기

    헐대박

    보내주신 코드로 진행하니 개별 파일들이 모두 불러져 하나의 데이터로 통합이 되었습니다.
    정말 너무 감사드립니다.

    ㅠㅠㅠㅠㅠ어떻게 감사의 말씀을 드려야할지 모르겠네요 정말 감사합니다.

    앞으로도 자주 방문하여 많은 공부할 수 있도록 하겠습니다.

    친절한 답변 정말 감사합니다.

  12. 2019.01.22 16:04  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.02.08 12:02 신고  댓글주소  수정/삭제

      안녕하세요.
      아래 코드를 참고해서 실행해보시기 바랍니다.

      #========================================================================
      # Read lots of files and then export it word by word with comma separator
      #========================================================================
      rm(list=ls()) # clear all

      #---------- (0) Prerequisite :
      # I've removed all chinese from the file name due to some error during read.table()


      #----------
      # (1) Read lots of files at the same folder using for loop

      # make a source file list
      src_dir <- c("C:/Users/admin/Downloads/test_office_0123") # set with yours
      src_file <- list.files(src_dir)

      # destination directory
      dst_dir <- c("C:/Users/admin/Downloads") # set with yours
      dst_file_name <- c("sillok_data.txt") # set with yours


      # read many files and export it line by line by looping operation
      for (i in 1:length(src_file)){
      # read a file
      df_tmp <- read.table(paste0(src_dir, "/", src_file[i]),
      sep = ",",
      header = FALSE,
      stringsAsFactors = FALSE)

      name <- strsplit(df_tmp[2,], split = " ")[[1]][2] # name

      for (j in 1:nrow(df_tmp)){
      # split a string by space
      splitted_string <- data.frame(do.call('rbind',
      strsplit(df_tmp[j,],
      split = " ",
      fixed = TRUE)))

      # combining i, j, name, and strings of a line
      df_string <- data.frame(i, j, name, splitted_string)

      # export a file and append it line by line
      write.table(df_string,
      paste0(dst_dir, "/", dst_file_name),
      sep = ",",
      row.names = FALSE,
      col.names = FALSE,
      quote = FALSE,
      append = TRUE)
      }

      print(paste0(i, "/", length(src_file), " is done!")) # to check progress
      }

      rm(df_tmp, df_string, splitted_string, i, j, name) # remove temp file

  13. 2019.08.16 19:37  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  14. R Friend R_Friend 2019.08.16 19:46 신고  댓글주소  수정/삭제  댓글쓰기

    data를 한개씩 불러올때 마다 original data에 merge를 하는 것으로 for loop을 사용햐보세요.

    merge는 한번에 Dataframe 한개씩만 가능해서요, 댓글에 남겨주신 것처럼 여러개의 data를 한꺼번에 merge하려고 하면 에러가 날거예요.

    • 알초보 2019.08.16 20:55  댓글주소  수정/삭제

      댓글 감사합니다ㅠㅠ
      제가 알완전 초보라서요..ㅠㅠ
      혹시 어떻게 코드를 작성하면되질 알수있을까요?
      이 데이터가 1000개인데 오늘 하루종일 엑셀 브이룩으로 찾아도 100개를 못해서요ㅠㅠ

    • R Friend R_Friend 2019.08.16 21:02 신고  댓글주소  수정/삭제

      아래 링크에 R로 엑셀파일 읽어오는 패키지, 코드 참고해보세요.

      https://rfriend.tistory.com/m/313

      나머지 for loop은 블로그 본문 참고하시구요.

  15. 2019.08.17 01:46  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.08.17 09:34 신고  댓글주소  수정/삭제

      merge할때 아래처럼 살짝 바꿔서 해보실래요? left outer join (original data 기준으로 merge) 하려는거지요?

      original_data <- merge(original_data, ReadInMerge, by="Phone", all.x=T)

      merge() 함수는 아래 링크 참조하세요.
      https://rfriend.tistory.com/m/51

    • 알초보 2019.08.17 14:06  댓글주소  수정/삭제

      댓글감사합니다!
      말씀해주신 방향대로 해도
      마지막꺼 하나만 남네요ㅠㅠ
      누적으로 각 csv당 하나씩 한 열로 추가가 불가능한가요,,,

    • R Friend R_Friend 2019.08.19 09:07 신고  댓글주소  수정/삭제

      혹시 dataMerge <- merge(xxxxx) 로 하지 않으셨나요? 병합이 된 결과 객체를 original_data로 바꾸어서 해보세요. original_data에 계속 붙이는 형식으로요.

      original_data <- merge(original_data, ReadInMerge, by="Phone", all.x=T)

  16. 2019.09.03 19:38  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.09.03 21:53 신고  댓글주소  수정/삭제

      안녕하세요.
      지금 문제 상황이 정확하게 어떤 것이지 잘 모르겠습니다.

      R, text parsing, regular expression 등의 키워드를 같이 넣어서 구글링 해보시면 좋겠습니다. readLines() 로 텍스트 읽어서 regular expression 을 목적에 맞게 찾아서 작성하시면 숫자만 선별적으로 골라올 수 있을 거예요.

  17. 2019.10.15 09:55  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • R Friend R_Friend 2019.10.15 09:59 신고  댓글주소  수정/삭제

      안녕하세요.

      데이터 사이즈가 크몀 DB에서 SQL로 처리하는 것을 추천드립니다.

      데이터를 어떻게 생겼는지 볼 수 없으므로 R코두드에 대해서 제가 별로 말씀드릴 수 있는 것이 없습니다. 죄송합니다.

  18. 2019.10.16 16:05  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

  19. OGU 2019.11.11 15:23  댓글주소  수정/삭제  댓글쓰기

    올려주신 내용 너무 감사합니다!

    그런데

    for (i in 001:cnt){assign(paste0("nc_", i), read.table(paste0(road, "/", list[i]),sep = ",", header = FALSE ))}


    다음과 같은 코드로 데이터를 불러왔습니다.

    그런데 왜 nc_1,nc_2 이런 순서가 아니라 nc_1, nc10, nc_100 ..같은 순서로 불러옵니다 ㅠㅠ
    순서대로 불러오는 방법이 있을까요?

    • R Friend R_Friend 2019.11.11 15:25 신고  댓글주소  수정/삭제

      "_001", "_002", "_003" 이런식으로 네이밍할 수 있으면 순서대로 불러올수 있을것 같습니다.

    • OGU 2019.11.11 16:57  댓글주소  수정/삭제

      for (i in 001:cnt) 처럼 001부터 순서를 부여했음에도 불구하고 1,10,100,,, 같은 순서를 부여합니다 ㅠㅠ

    • R Friend R_Friend 2019.11.11 17:02 신고  댓글주소  수정/삭제

      cnt 를 정의한 코드를 볼 수 있을까요? for loop문이 작동하는 방식을 봐야할거 같아요.

    • OGU 2019.11.11 17:07  댓글주소  수정/삭제

      road<-c("C:/Users/go/Desktop/Data/r1999/0102/nc")

      list.files(road)->list

      cnt <- length(list)

      다음과 같은 코드입니다!

    • R Friend R_Friend 2019.11.11 17:10 신고  댓글주소  수정/삭제

      퇴근 후에 찬찬히 살펴볼께요. 제가 지금 업무중이라서요.

    • OGU 2019.11.11 19:35  댓글주소  수정/삭제

      네^~^ 감사합니다

    • R Friend R_Friend 2019.11.12 01:03 신고  댓글주소  수정/삭제

      안녕하세요 OGU님,

      for (i in 1:cnt) 부분은 문제가 없습니다. 따라서 assign(paste0("nc_", i) 이 부분도 문제가 없습니다.

      list <- list.files(road) 로 road 폴더 안의 파일 리스트를 만들어 놓은 list 객체에 아마도 1, 10, 100, ... 의 순서로 파일 이름이 리스트에 저장이 된 것 같습니다. 그러다보니 list[i] 로 인덱스해서 파일 이름을 가져올 때 1, 10, 100, ... 의 순서로 파일을 가져오는 것 같습니다.

      print(list) 를 해서 list 안의 파일 이름 순서가 혹시 1, 10, 100, ... 이런 순서로 되어있는게 아닌지 확인해보시기 바랍니다.

      혹시 이게 문제라면요, 원본 파일의 넘버링 구분자(끝부분?)를 "_001.txt", "_002.txt" 이런식으로 고쳐서 한번 해보실래요?

    • OGU 2019.11.12 11:08  댓글주소  수정/삭제

      확인해주셔서 감사합니다!
      하지만 원본파일이름은 001, 002, 003.. 과 같은순서로 되어있습니다 ㅠㅠ


      1. road<-c("C:/Users/go/Desktop/r1999/0102/nc")
      2. list.files(road)->list
      3. cnt <- length(list)

      4. for (i in 01:cnt){assign(paste0("nc_", i), read.table(paste0(road, "/", list[i]),sep = ",", header = FALSE ))}

      4번과정에서 데이터를 불러오면 Environment 창에 1,10,100,101,, 같은 순서로 불러집니다.


      https://www.google.com/search?q=why+r+order+files+as+1+10+100&oq=why+r+&aqs=chrome.0.69i59j69i57j69i60l3.4743j0j7&sourceid=chrome&ie=UTF-8

      여기서 첫번째 게시물과 같은 문제인것 같아요 ㅠㅠ

    • R Friend R_Friend 2019.11.12 11:57 신고  댓글주소  수정/삭제

      아, 그럼 버그인건가요? ㅠ.ㅠ

    • OGU 2019.11.14 14:42  댓글주소  수정/삭제

      앗,,ㅠㅠ 그런가요ㅠㅠ 도움주셔서 감사합니다~ㅎㅎ

  20. JHK 2019.11.30 04:59  댓글주소  수정/삭제  댓글쓰기

    안녕하세요~ 저는 파일이 처음부터 6개까지는 변수가 12개인데 7번째 파일 부터는 변수 한개가 더 추가되어있는 상태라서요! 열 수가 맞지 않는다라는 에러가 뜹니다.
    7번째 파일부터 추가되어있는 변수는 지우고 합치고 싶은데 어떻게 하면 될까요>

R 프로그래밍 중에 "target of assignment expands to non-language object" error 메시지가 발생하면 assign() 함수를 사용하여 문제를 해결할 수 있습니다.

 

"target of assignment expands to non-language object" error 가 발생하는 이유는 할당하려는 목표 객체(Target object) 를 R이 인식하지 못해 NULL 값으로 처리되어 있는 상태이기 때문입니다. 

 

아래에 이런 에러가 발생하는 간단한 예와 해결 방법을 소개해보겠습니다.

 

 

x변수는 1부터 30까지의 정수, y변수는 1부터 5까지의 정수이고, 이 두변수를 묶어서 xy라는 데이터 프레임을 만들어보겠습니다.

 

 

> x <- c(1:30) >

> y <- c(1:5)
>
> xy <- data.frame(x, y)
>
> str(xy)
'data.frame':	30 obs. of  2 variables:
 $ x: int  1 2 3 4 5 6 7 8 9 10 ...
 $ y: int  1 2 3 4 5 1 2 3 4 5 ...
>
> xy
    x y
1   1 1
2   2 2
3   3 3
4   4 4
5   5 5
6   6 1
7   7 2
8   8 3
9   9 4
10 10 5
11 11 1
12 12 2
13 13 3
14 14 4
15 15 5
16 16 1
17 17 2
18 18 3
19 19 4
20 20 5
21 21 1
22 22 2
23 23 3
24 24 4
25 25 5
26 26 1
27 27 2
28 28 3
29 29 4
30 30 5

 

 

위에서 생성한 xy 데이터프레임을 가지고, y변수 1, 2, 3, 4, 5 를 포함한 행(row) 별로 각 각 개별 데이터 프레임을 Loop 를 사용해서 만들어보겠습니다.  아래 Loop program에서 목표 객체(Target object)에 paste() 함수를 사용해서 공통 접두사(predix)로 'xy_'를 사용하고 뒤에 'i'로 loop 를 돌면서 1, 2, 3, 4, 5 를 붙여주려고 프로그램을 짰습니다만, "target of assignment expands to non-language object" 라는 오류 메시지가 떴습니다.  아래 프로그램에 대해 R은 [ paste("xy_", i, sep="") ]부분을 NULL 값으로 인식하기 때문에 이런 오류가 발생하게 됩니다.

 

 

> for (i in 1:5) {
+   paste("xy_", i, sep="") <- subset(xy, subset = (y == i))
+ }
Error in paste("xy_", i, sep = "") <- subset(xy, subset = (y == i)) : 
  target of assignment expands to non-language object

 

 

 

 

위 프로그램에서 하고자 했던 의도대로 R이 이해하고 실행을 하게끔 하려면 assign() 함수를 사용해야만 합니다.  assign() 함수를 사용할 때는 아래 처럼 '<-' 대신에 ',' 가 사용되었음에 주의하시기 바랍니다.

 

 
> for (i in 1:5) {
+   assign(paste("xy_", i, sep=""), subset(xy, subset = (y == i)))
+ }
>
> xy_1
    x y
1   1 1
6   6 1
11 11 1
16 16 1
21 21 1
26 26 1
>
> xy_2
    x y
2   2 2
7   7 2
12 12 2
17 17 2
22 22 2
27 27 2
>
> xy_3
    x y
3   3 3
8   8 3
13 13 3
18 18 3
23 23 3
28 28 3
>
> xy_4
    x y
4   4 4
9   9 4
14 14 4
19 19 4
24 24 4
29 29 4
>
> xy_5
    x y
5   5 5
10 10 5
15 15 5
20 20 5
25 25 5
30 30 5

 

 

많은 도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. 산낙지 2016.10.07 15:42  댓글주소  수정/삭제  댓글쓰기

    안녕하세요! 오늘도 포스팅 잘 보았습니다. 제가 본문을 참고해서 명령어를 실행해보았습니다. 그런데 문제가 발생해서 하나를 여쭙고 싶습니다.
    우선 저는 다음과 같은 명령어를 실행하고 싶은데요.

    pdt$birth1 <- c(1, 0, 0, 0, 0)[match(pdt$r_birth1, c(1, 2, 3, 4, 5))]
    pdt$birth2 <- c(1, 0, 0, 0, 0)[match(pdt$r_birth2, c(1, 2, 3, 4, 5))]
    pdt$birth3 <- c(1, 0, 0, 0, 0)[match(pdt$r_birth3, c(1, 2, 3, 4, 5))]

    그래서 assign을 이용해서 다음과 같은 명령어를 만들어 보았습니다.

    for (i in 1:3) {
    assign(paste("pdt$birth", i, sep=""), c(1, 0, 0, 0, 0)[match(paste("pdt$r_birth", i, sep=""), c(1, 2, 3, 4, 5))])
    }

    그런데 명령어가 오류 없이 실행은 되는데, pdt라는 객체에 birth1~3이 아예 안 생기더라고요.
    혹시 제 명령어에는 어떤 문제가 있는 건지 조언을 구할 수 있을까요? ㅠㅠ

    • R Friend R_Friend 2016.10.08 15:29 신고  댓글주소  수정/삭제

      산낙지님, 반갑습니다.

      assign() 함수 연습하는 용도라면요, birth1, birth2, birth3으로 개별 객체를 loop 연산으로 assign()으로 생성 후에 data.frame(cbind()) 하면 되겠네요.

      근데, 문의주신 형태의 output을 얻는데는 assign() 함수를 안쓰고 그냥 transform() 함수에 loop 돌리면 될거 같습니다.

한두개 정도 일회성으로 그래프 그리고 말거면 그냥 화면 캡쳐하는 프로그램 사용하거나 아니면 RStudio의 파일 내보내기를 사용하면 됩니다. 

 

한두번 분석하고 말거면 그냥 마우스로 Console 창 분석결과에 블럭 설정하고 Copy & Paste 하면 됩니다.

 

하지만, 수백개, 수천개의 그래프를 그리고 이를 파일로 저장해야 하고 자동화(사용자 정의 함수, 루프) 해야 한다거나, 분석이나 모형개발을 수백개, 수천개 해야 하고 이의 결과를 따로 저장해야 한다면 이걸 수작업으로 매번 할 수는 없는 노릇입니다.  시간도 많이 걸리고, 아무래도 사람 손이 자꾸 타다 보면 실수도 하기 마련이기 때문입니다. 

 

이에 이번 포스팅에서는

 

(1) ggplot2로 그린 그래프를 jpg 나 pdf 파일로 저장하는 방법

     : ggsave() 

 

(2) Console 창에 나타나는 분석 결과, 모형 개발 결과를 text 파일로 저장하는 방법

     : capture.output()

 

에 대해서 소개하겠습니다.  R script로 위 작업을 수행할 수 있다면 프로그래밍을 통해 자동화도 할 수 있겠지요.

 

 

예제로 사용할 데이터는 MASS 패키지 내 Cars93 데이터프레임의 고속도로연비(MPG.highway), 무게(Weight), 엔진크기(EngineSize), 마련(Horsepower), 길이(Length), 폭(Width) 등의 변수를 사용해서 선형 회귀모형을 만들고 이의 적합 결과를 text 파일로 내보내기를 해보겠습니다.

 

 
> # dataset : Cars93 dataframe,  Weight, MPG.highway variable
> library(MASS)
> str(Cars93)
'data.frame':	93 obs. of  27 variables:
 $ Manufacturer      : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ...
 $ Model             : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ...
 $ Type              : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ...
 $ Min.Price         : num  12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ...
 $ Price             : num  15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ...
 $ Max.Price         : num  18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ...
 $ MPG.city          : int  25 18 20 19 22 22 19 16 19 16 ...
 $ MPG.highway       : int  31 25 26 26 30 31 28 25 27 25 ...
 $ AirBags           : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ...
 $ DriveTrain        : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ...
 $ Cylinders         : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ...
 $ EngineSize        : num  1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ...
 $ Horsepower        : int  140 200 172 172 208 110 170 180 170 200 ...
 $ RPM               : int  6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ...
 $ Rev.per.mile      : int  2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ...
 $ Man.trans.avail   : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ...
 $ Fuel.tank.capacity: num  13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ...
 $ Passengers        : int  5 5 5 6 4 6 6 6 5 6 ...
 $ Length            : int  177 195 180 193 186 189 200 216 198 206 ...
 $ Wheelbase         : int  102 115 102 106 109 105 111 116 108 114 ...
 $ Width             : int  68 71 67 70 69 69 74 78 73 73 ...
 $ Turn.circle       : int  37 38 37 37 39 41 42 45 41 43 ...
 $ Rear.seat.room    : num  26.5 30 28 31 27 28 30.5 30.5 26.5 35 ...
 $ Luggage.room      : int  11 15 14 17 13 16 17 21 14 18 ...
 $ Weight            : int  2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ...
 $ Origin            : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ...
 $ Make              : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ...

 

 

 

 

고속도로연비(PMG.highway)와 차 무게(Weight) 간의 산포도를 ggplot으로 그리면 아래와 같이 RStudio 우측 하단의 Plot 창에 그래프가 생성됩니다.

 

 

> # Scatter Plot of Weight & MPG.highway
> library(ggplot2)
> 
> ggplot(Cars93, aes(x=Weight, y=MPG.highway)) +
+   geom_point(shape=19, size=3, colour="blue") +
+   ggtitle("Scatter Plot of Weight & MPG.highway")
 

 

 

 

 

이를 ggsave() 함수를 사용해서 jpg 파일로 저장해서 내보내기를 해보겠습니다.  pdf 파일로 저장하려면 jpg 대신에 pdf 를 사용하면 됩니다.

 

 

> # saving ggplot with jpg format file : ggsave() > ggsave(file="C:/Users/user/Documents/R/scatter_plot.jpg", # directory, filename + width=20, height=15, units=c("cm")) # width, height, units

 

 

 

 

 

단순 선형회귀모형과 다변량 선형회귀모형을 각각 적합시켜보면 아래와 같습니다.

 

 
> # linear regression modeling
> fit_1 <- lm(MPG.highway ~ Weight, Cars93)
> summary(fit_1)

Call:
lm(formula = MPG.highway ~ Weight, data = Cars93)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.6501 -1.8359 -0.0774  1.8235 11.6172 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 51.6013654  1.7355498   29.73   <2e-16 ***
Weight      -0.0073271  0.0005548  -13.21   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.139 on 91 degrees of freedom
Multiple R-squared:  0.6572,	Adjusted R-squared:  0.6534 
F-statistic: 174.4 on 1 and 91 DF,  p-value: < 2.2e-16

> 
> 
> fit_2 <- lm(MPG.highway ~ Weight + EngineSize + Horsepower + Length + Width, Cars93)
> fit_3=stepAIC(fit_2, direction="both")
Start:  AIC=210.77
MPG.highway ~ Weight + EngineSize + Horsepower + Length + Width

             Df Sum of Sq     RSS    AIC
- Horsepower  1      1.38  789.67 208.93
- EngineSize  1      2.62  790.91 209.07
- Width       1      7.03  795.33 209.59
<none>                     788.30 210.77
- Length      1     44.23  832.53 213.84
- Weight      1    562.35 1350.65 258.84

Step:  AIC=208.93
MPG.highway ~ Weight + EngineSize + Length + Width

             Df Sum of Sq     RSS    AIC
- EngineSize  1      1.62  791.29 207.12
- Width       1      7.95  797.62 207.86
<none>                     789.67 208.93
+ Horsepower  1      1.38  788.30 210.77
- Length      1     48.74  838.41 212.50
- Weight      1    699.19 1488.87 265.90

Step:  AIC=207.12
MPG.highway ~ Weight + Length + Width

             Df Sum of Sq     RSS    AIC
- Width       1     13.71  805.00 206.72
<none>                     791.29 207.12
+ EngineSize  1      1.62  789.67 208.93
+ Horsepower  1      0.38  790.91 209.07
- Length      1     52.31  843.61 211.07
- Weight      1    749.41 1540.70 267.09

Step:  AIC=206.72
MPG.highway ~ Weight + Length

             Df Sum of Sq     RSS    AIC
<none>                     805.00 206.72
+ Width       1     13.71  791.29 207.12
+ EngineSize  1      7.38  797.62 207.86
+ Horsepower  1      0.21  804.79 208.69
- Length      1     91.62  896.62 214.74
- Weight      1   1039.48 1844.48 281.82
> summary(fit_3)

Call:
lm(formula = MPG.highway ~ Weight + Length, data = Cars93)

Residuals:
    Min      1Q  Median      3Q     Max 
-7.0988 -1.8630 -0.2093  1.4199 11.3613 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) 37.5217809  4.6998055   7.984 4.41e-12 ***
Weight      -0.0096328  0.0008936 -10.780  < 2e-16 ***
Length       0.1155263  0.0360972   3.200   0.0019 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.991 on 90 degrees of freedom
Multiple R-squared:  0.6922,	Adjusted R-squared:  0.6854 
F-statistic: 101.2 on 2 and 90 DF,  p-value: < 2.2e-16

 

 

 

 

이를 capture.output() 함수를 사용하여 text 파일로 내보내서 차곡 차곡 쌓아가면서 저장하는 방법은 아래와 같습니다.  결과 text 파일을 화면캡채해서 같이 올립니다. 

 

> 
> # capture.output()
> # (1) Simple Linear Regression Model (y=MPG.highway, x=Weight)
> cat("\n", 
+     "\n",
+     "==============================================================", "\n", 
+     " [ Simple Linear Regression Model (y=MPG.highway, x=Weight)]  ", "\n", 
+     "==============================================================", "\n", 
+     file="C:/Users/user/Documents/R/lm_MPG_highway.txt", append = TRUE)
> 
> capture.output(summary(fit_1), 
+                file="C:/Users/user/Documents/R/lm_MPG_highway.txt", append = TRUE)
> 
> 
> # (2) Multivariate Linear Regression Model (y=MPG.highway, x1~x5)
> cat("\n", 
+     "\n",
+     "===============================================================", "\n", 
+     " [ Multivariate Linear Regression Model (y=MPG.highway, x1~x5)]  ", "\n", 
+     "===============================================================", "\n", 
+     file="C:/Users/user/Documents/R/lm_MPG_highway.txt", append = TRUE)
> 
> capture.output(summary(fit_3), 
+                file="C:/Users/user/Documents/R/lm_MPG_highway.txt", append = TRUE)

 

 

 

 

 

 

 

 

노가다 하기 싫다면 프로그래밍하고 자동화하는 것이 정답이지요. 

위의 작업을 반복해야 한다면 사용자 정의 함수를 덧붙여서 파일 경로 끝 부분에 파일 이름 부분을 paste() 함수를 써서 사용자 정의 함수에 입력한 값으로 매 루프 돌때 마다 바꿔치기 될 수 있도록 프로그래밍을 해주면 되겠습니다.

 

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요

R분석을 하다 보면 데이터 전처리 라든지 그래프 그리기, 혹은 모형 개발/ update 등을 하는데 있어 반복 작업을 하는 경우가 있습니다. 

 

이때 대상 데이터셋이라든지 변수, 혹은 조건 등을 조금씩 바꿔가면서 반복 작업을 (반)자동화 하고 싶을 때 유용하게 사용할 수 있는 것이 사용자 정의 함수 (User Defined Function) 입니다. 

 

만약 사용자 정의 함수를 사용하지 않는다면 특정 부분만 바뀌고 나머지는 동일한 프로그램이 매우 길고 복잡하고 산만하게 늘어세울 수 밖에 없게 됩니다.  반면 사용자 정의 함수를 사용하면 사용자 정의 함수 정의 후에 바뀌는 부분만 깔끔하게 사용자 정의 함수의 입력란에 바꿔서 한줄 입력하고 실행하면 끝입니다.  반복작업이 있다 싶으면 손과 발의 노가다를 줄이고 작업/분석 시간을 줄이는 방법, 프로그래밍을 간결하고 깔끔하게 짜는 방법으로 사용자 정의 함수를 사용할 여지가 있는지 살펴볼 필요가 있겠습니다.

 

 

 

 

 

사용자 정의 함수는

 

 

function_name <- function( arg1, arg2, ... ) {

                                                           expression

                                                           return( object)

                                                         }

 

 

의 형식을 따릅니다.

 

몇 가지 예을 들어서 설명해보겠습니다.

 

 

1) 평균(mean), 표준편차(standard deviation), min, max 계산 사용자 정의 함수
    (User defined function of statistics for continuous variable)

 

 

> # 평균, 표준편차, min, max 계산 > > stat_function <- function(x) { + x_mean = mean(x) + x_sd = sd(x) + x_min = min(x) + x_max = max(x) + x_summary = list(x_mean=x_mean, x_sd=x_sd, x_min=x_min, x_max=x_max) + return(x_summary) + } > > stat_function(x = Cars93$MPG.highway) $x_mean [1] 29.08602 $x_sd [1] 5.331726 $x_min [1] 20 $x_max [1] 50 > # summary() 함수와 비교 > summary(Cars93$MPG.highway) Min. 1st Qu. Median Mean 3rd Qu. Max. 20.00 26.00 28.00 29.09 31.00 50.00

 

 

 

 

2) 산점도 그래프 그리기 사용자 정의 함수 (User defined function of scatter plot)

 

> # 산점도 그래프 그리기 함수 (scatter plot)
> plot_function <- function(dataset, x, y, title) {
+   attach(dataset)
+   plot(y ~ x, dataset, type="p", 
+        main = title)
+   detach(dataset)

 

 

> plot_function(dataset=Cars93, x=MPG.highway, y=Weight, title="Scatter Plot of MPG.highway & Weight")

 

 

> plot_function(dataset=Cars93, x=Price, y=Horsepower, title="Scatter Plot of Price & Horsepower")
 

 

 

 

위의 기초통계량은 summary() 함수를 사용하면 되고 산포도도 plot() 함수를 쓰는 것과 별 차이가 없어보여서 사용자 정의 함수를 쓰는 것이 뭐가 매력적인지 잘 이해가 안갈 수도 있을 것 같습니다.  하지만 만약 기초 통계량을 뽑아서 txt 파일로 외부로 내보내기를 하고, x 변수를 바꿔가면서 loop를 돌려서 반복적으로 기초 통계량을 뽑고 이것을 계속 txt 파일로 외부로 내보내기를 하되, 앞서 내보냈던 파일에 계속 append 를 해가면서 결과값을 저장한다고 할때는 위의 사용자 정의 함수를 사용하는 것이 정답입니다. 

 

그래프도 변수명의 일부분을 바꿔가면서 그래프를 그리고 싶을 때는 paste() 함수를 적절히 사용하면 사용자 정의 함수를 더욱 강력하게 사용할 수 있게 됩니다.  응용하기 나름이고, 사용 가능한 경우가 무궁무진한데요, 이번 포스팅에서는 사용자 정의 함수의 기본 뼈대에 대해서만 간략히 살펴 보았습니다.

 

참고로, 사용자 정의 함수를 정의할 때 아래처럼 function(x, y, ...) 의 파란색 생략부호 점을 입력하면 나중에 사용자 정의 함수에서 정의하지 않았던 부가적인 옵션들을 추가로 덧붙여서 사용할 수 있어서 유연성이 높아지는 효과가 있습니다.

function_name <- function(x, y, ...) {

expresstion

return(object)

}

 

많은 도움 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

Posted by R Friend R_Friend

댓글을 달아 주세요

  1. kjh 2016.09.16 17:15  댓글주소  수정/삭제  댓글쓰기

    연습차 팩터로 변환하는 함수를 만들어보려고 하는데요

    factorize <- function(a, b){
    i <- which(names(b)==a)
    b[i] <- as.factor(b[i])
    }
    일단 오류가 발생하는 3번째줄을 [[i]]로 두군데 다 바꿔주면 된다는 사실을
    다른 카페에서 알게되긴 했는데요,(as.factor가 데이터프레임에만 되는건가요?)
    일단 함수가 오류없이 만들어지긴 해도, 의도했던 팩터화는 되지가 않더라구요.
    factorize("cyl", mtcars)를 해봐도 팩터화가 안되는데, 그 이유가 궁금합니다..


    그리고 함수를 사용할 때
    factorize("cyl", mtcars) 처럼 써야 하는데
    a위치에 들어가는 것에 따옴표를 안붙여도 되게 하려면 어떻게 해야 할까요

    • R Friend R_Friend 2017.07.06 00:19 신고  댓글주소  수정/삭제

      kjh님, 댓글을 이제서야 봤네요. ^^;

      아래처럼 해보시기 바랍니다.

      factorize <- function(var, df){
      i <- which(names(df) %in% var)
      df[,i] <- lapply(df[,i], factor)
      return(df)
      }

      mtcars <- factorize(c("cyl", "gear"), mtcars)
      str(mtcars)

      > str(mtcars)
      'data.frame': 32 obs. of 11 variables:
      $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
      $ cyl : Factor w/ 3 levels "4","6","8": 2 2 1 2 3 2 3 1 1 2 ...
      $ disp: num 160 160 108 258 360 ...
      $ hp : num 110 110 93 110 175 105 245 62 95 123 ...
      $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
      $ wt : num 2.62 2.88 2.32 3.21 3.44 ...
      $ qsec: num 16.5 17 18.6 19.4 17 ...
      $ vs : num 0 0 1 1 0 1 0 1 1 1 ...
      $ am : num 1 1 1 0 0 0 0 0 0 0 ...
      $ gear: Factor w/ 3 levels "3","4","5": 2 2 2 1 1 1 1 2 2 2 ...
      $ carb: num 4 4 1 1 2 1 4 2 2 4 ...


      =====================
      물론 변환할 변수가 몇 개 안되면 그냥 아래처럼 하는게 편합니다.

      mtcars <- transform(mtcars, cyl = as.factor(cyl))

  2. 러투스 2017.07.05 20:58 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 포스팅 감사합니다.

 

 

R에서 특정 조건을 만족하는지의 조건을 주고 뒤에 이어지는 표현식을 반복적으로 수행하게 하는 조건 연산 프로그래밍에 대해 알아보겠습니다.

 

 

 

먼저 한개의 숫자형 값에 대해 이것이 짝수인지 홀수인지 판단하는 R program을 짜보도록 하겠습니다.

 

 

 

 


  • 하나의 값에 대한 판단인 경우 : 

    if( 조건1 ) {
              표현식 1
    } else if (조건2) {
              표현식 2
    } else {
              표현식 3
    }

  

[ 짝수, 홀수 여부 판단 프로세스 ]

 

 

(1) 하나의 논리값에 대한 판단인 경우 : if()

 

> ## 하나의 논리값에 대한 판단
> # if()
> x1 <- c(4)
> if (x1 %% 2 == 0) {
+   y1 = c("Even Number")
+   print(y1)
+ } else {
+     y1 = c("Odd Number")
+     print(y1)
+ }
[1] "Even Number"

 

 

> x2 <- c(5)
> if (x2 %% 2 == 0) {
+   y2 = "Even Number"
+   print(y2)
+ } else {
+   y2 = "Odd Number"
+   print(y2)
+ }
[1] "Odd Number"
 

위의 2개의 예에서는 x1 이 4일 때 "Even Number"라고 판단했고, x2가 5일 때 "Odd Number"라고 잘 판단하였습니다.

 

하지만, 아래의 예제처럼 두개 이상의 논리값 벡터를 사용하는 경우에는 오류가 발생하게 되며, 아래 예제에서 보면 1~5까지 숫자 중에서 제일 처음으로 나오는 1에만 아래의 조건연산 프로그램이 적용되었고 두번째부터는 적용이 안되었습니다.  이럴 때는 ifelse() 문을 사용하여야 합니다

 

> # Error
> x3 <- c(1, 2, 3, 4, 5)
> if (x3 %% 2 == 0) {
+   y3 = "Even Number"
+   print(y3)
+ } else {
+   y3 = "Odd Number"
+   print(y3)
+ }
[1] "Odd Number"
Warning message:
In if (x3%%2 == 0) { :
  the condition has length > 1 and only the first element will be used

 



 

(2) 두개 이상의 논리값 벡터에 대한 판단 : ifelse()

 

  • 두개 이상의 논리값 벡터에 대한 판단인 경우 :

    ifelse( 조건1, 표현식1, 
          ifelse( 조건2, 표현식2, 
                 ifelse( 조건3, 표현식3, 표현식4)
                 ) 
           )
  •  

    위와 동일하게 1~5의 숫자에 대해서 이번에는 ifelse() 문을 사용해서 짝수, 홀수 여부를 판단하게 하고, 이를 데이터프레임 구조로 변환해서 view해보겠습니다.

     

    > ## vector 에 대한 판단
    > # 홀수/짝수 여부 판단 : ifelse( condition, expression 1, expression 2 )
    > x <- c(1, 2, 3, 4, 5)
    > z <- ifelse(x%%2 == 0, "Even Number", "Odd Number")
    > xz <- data.frame(x, z)
    > xz
      x           z
    1 1  Odd Number
    2 2 Even Number
    3 3  Odd Number
    4 4 Even Number
    5 5  Odd Number

     

     

     


     

     

    이번에는 양수, 0, 음수 인지 여부를 판단한 후 원래 벡터와 판단하는 프로그램을 ifelse()를 사용하여 짜보도록 하겠습니다.

     

    [ 양수, 0, 음수 인지 여부 판단하는 프로세스 ]

     

     

    > # 양수, 0, 음수인지 여부 판단 : ifelse( condition, expression 1, expression 2 ) > x <- c(-2, -1, 0, 1, 2) > y <- ifelse( x > 0, "Positive", + ifelse( x == 0, "Zero", "Negative") + ) > > xy <- data.frame(x, y) > > xy x y 1 -2 Negative 2 -1 Negative 3 0 Zero 4 1 Positive 5 2 Positive 

     

     


     

    To 산낙지님,

    제가 가족 여행다녀오느라 이제서야 집에 와서 댓글 달려고 막 하는 와중에...댓글을 삭제하셨네요. ^^;

    3가지 조건을 주어서 1, 0 혹은 yes, no 범주형 dummy 변수를 생성하는 방법은 아래를 참고하세요.

    MASS 패키지의 Cars93데이터프레임을 가지고 예를 들었습니다.

    [예제] "차종(Type)이 "Campact" 이고 & 가격(Price)이 16이하이고 & 고속도로연비(MPG.highway)가 30이상이면 1, 그 외는 모두 0인 변수 sub_yn 을 만드시오"

    ## Making dummy variable using ifelse() and transform()
    library(MASS)
    str(Cars93)
    summary(Cars93$Price)
    summary(Cars93$MPG.highway)

    Cars93 <- transform(Cars93,
                               sub_yn = ifelse(Type == c("Compact")
                                                   & Price <= 16
                                                   & MPG.highway >= 30, 1, 0))

     

     

     

     

    이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

     

     

    Posted by R Friend R_Friend

    댓글을 달아 주세요

    1. R Friend R_Friend 2016.10.03 19:03 신고  댓글주소  수정/삭제  댓글쓰기

      To 산낙지님,

      제가 가족 여행다녀오느라 이제서야 집에 와서 댓글 달려고 막 하는 와중에...댓글을 삭제하셨네요. ^^;

      3가지 조건을 주어서 1, 0 혹은 yes, no 범주형 dummy 변수를 생성하는 방법은 아래를 참고하세요.

      MASS 패키지의 Cars93데이터프레임을 가지고 예를 들었습니다.

      [예제] "차종(Type)이 "Campact" 이고 & 가격(Price)이 16이하이고 & 고속도로연비(MPG.highway)가 30이상이면 1, 그 외는 모두 0인 변수 sub_yn 을 만드시오"

      ## Making dummy variable using ifelse() and transform()
      library(MASS)
      str(Cars93)
      summary(Cars93$Price)
      summary(Cars93$MPG.highway)

      Cars93 <- transform(Cars93,
      sub_yn = ifelse(Type == c("Compact")
      & Price <= 16
      & MPG.highway >= 30, 1, 0))

      • 산낙지 2016.10.03 20:24  댓글주소  수정/삭제

        아 제가 방명록에 다시 달려고 댓글을 삭제했는데 여기에 달으셨군요 ^^; 댓글 삭제해서 죄송하고 답변 정말 감사합니다~~ 많은 참고 하겠습니다!

    2. 정현복 2017.01.06 17:56  댓글주소  수정/삭제  댓글쓰기

      a=c(1,2,NA,NA,3,5)
      b=c(2,4,5,6,7,8)
      ab=cbind(a,b)
      ab=as.data.frame(ab)
      class(ab)

      if (is.na(ab$a)=TRUE) ab$a=0 else ab$a=1
      if (is.na(ab$a)) ab$a=0 else ab$a=1
      if(ab$a==NA) {ab$a=0} else {ab$a=1}
      ab$a=ifelse(is.na(ab$a),0,1)

      안녕하세요 제가 코딩을 해보가 if문을 사용하는데 아무리 고쳐보고 책찾아보고 해도 도대체 왜 않되는지 모르겠습니다 맨마지막 코딩은 비로서 실행이 되는데 저거는 실행이 된다면 그러면 위에 if문들은 왜 않되는건지 도저히 이해가 않됩니다 도와주세요 ㅠㅠ

    3. 정현복 2017.01.09 23:54  댓글주소  수정/삭제  댓글쓰기

      글쓴이분 정말감사해요
      근데 1가지 정말 해결해주실수 잇나요
      데이터 테이블인데 지금
      gender변수안에 m f 가 팩터로 들어가 있는데 저는 그 변수값을 m이면 1 f면 0으로 넣고싶은데 어떻게 해야 할까요 이것도 찾아보고 저것도 찾아보고 3시간넘게 사투중인데 공모전 준비하는데 ㅠㅠ
      너무 힘들어요 아진짜 ㅠㅠㅠ제발 도와주세요 ㅠㅠ

      • R Friend R_Friend 2017.01.10 00:33 신고  댓글주소  수정/삭제

        정현복님, transform()과 elif() 함수가지고 간단한 예제 R script 짜보았습니다. 참고하세요.

        # making an example dataframe
        cust_id <- c(1, 2, 3, 4, 5, 6)
        gender <- c("F", "M", "F", "F", "M", "F")
        age <- c(23, 28, 42, 34, 45, 36)

        cust_df <- data.frame(cust_id, gender, age)

        # data structure check
        str(cust_df)

        # transforming by ifelse() function
        cust_df <- transform(cust_df, gender_new = ifelse(gender == "M", 1, 0))

        cust_df

        # 참고 포스팅
        http://rfriend.tistory.com/38