data.table의 열 클래스 변환


118

data.table 사용에 문제가 있습니다. 열 클래스를 어떻게 변환합니까? 다음은 간단한 예입니다. data.frame을 사용하면 변환하는 데 문제가 없습니다. data.table을 사용하면 방법을 모르겠습니다.

df <- data.frame(ID=c(rep("A", 5), rep("B",5)), Quarter=c(1:5, 1:5), value=rnorm(10))
#One way: http://stackoverflow.com/questions/2851015/r-convert-data-frame-columns-from-factors-to-characters
df <- data.frame(lapply(df, as.character), stringsAsFactors=FALSE)
#Another way
df[, "value"] <- as.numeric(df[, "value"])

library(data.table)
dt <- data.table(ID=c(rep("A", 5), rep("B",5)), Quarter=c(1:5, 1:5), value=rnorm(10))
dt <- data.table(lapply(dt, as.character), stringsAsFactors=FALSE) 
#Error in rep("", ncol(xi)) : invalid 'times' argument
#Produces error, does data.table not have the option stringsAsFactors?
dt[, "ID", with=FALSE] <- as.character(dt[, "ID", with=FALSE]) 
#Produces error: Error in `[<-.data.table`(`*tmp*`, , "ID", with = FALSE, value = "c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2)") : 
#unused argument(s) (with = FALSE)

여기서 분명한 것이 놓치고 있습니까?

Matthew의 게시물로 인한 업데이트 : 이전에 이전 버전을 사용했지만 1.6.6 (지금 사용중인 버전)으로 업데이트 한 후에도 여전히 오류가 발생합니다.

업데이트 2 : "인자"클래스의 모든 열을 "문자"열로 변환하고 싶지만 어떤 열이 어떤 클래스에 속하는지 미리 모른다고 가정 해 보겠습니다. data.frame을 사용하여 다음을 수행 할 수 있습니다.

classes <- as.character(sapply(df, class))
colClasses <- which(classes=="factor")
df[, colClasses] <- sapply(df[, colClasses], as.character)

data.table과 비슷한 일을 할 수 있습니까?

업데이트 3 :

sessionInfo () R 버전 2.13.1 (2011-07-08) 플랫폼 : x86_64-pc-mingw32 / x64 (64 비트)

locale:
[1] C

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] data.table_1.6.6

loaded via a namespace (and not attached):
[1] tools_2.13.1

의 "["연산자 인수 data.table방법은 그들이보다 다른data.frame
IRTFM

1
대신 실제 오류를 붙여 넣으십시오 #Produces error. 어쨌든 +1. 오류가 발생하지 않습니다. 어떤 버전이 있습니까? 하지만이 영역에는 문제가 있지만 이전에 제기 된 문제이므로 FR # 1224FR # 1493 이 우선적으로 처리됩니다. 하지만 Andrie의 대답이 가장 좋은 방법입니다.
Matt Dowle

내 질문에 누락 된 @MatthewDowle 죄송합니다. 내 게시물을 업데이트했습니다.
Christoph_J

1
@Christoph_J 감사합니다. 그 invalid times argument오류 에 대해 확신 합니까? 나를 위해 잘 작동합니다. 어떤 버전이 있습니까?
Matt Dowle 2011 년

sessionInfo ()로 게시물을 업데이트했습니다. 그러나 오늘은 내 작업 기계에서 확인했습니다. 어제 내 가정용 컴퓨터 (Ubuntu)에서 동일한 오류가 발생했습니다. R을 업데이트하고 문제가 여전히 있는지 확인합니다.
Christoph_J

답변:


104

단일 열의 경우 :

dtnew <- dt[, Quarter:=as.character(Quarter)]
str(dtnew)

Classes ‘data.table’ and 'data.frame':  10 obs. of  3 variables:
 $ ID     : Factor w/ 2 levels "A","B": 1 1 1 1 1 2 2 2 2 2
 $ Quarter: chr  "1" "2" "3" "4" ...
 $ value  : num  -0.838 0.146 -1.059 -1.197 0.282 ...

lapply및 사용 as.character:

dtnew <- dt[, lapply(.SD, as.character), by=ID]
str(dtnew)

Classes ‘data.table’ and 'data.frame':  10 obs. of  3 variables:
 $ ID     : Factor w/ 2 levels "A","B": 1 1 1 1 1 2 2 2 2 2
 $ Quarter: chr  "1" "2" "3" "4" ...
 $ value  : chr  "1.487145280568" "-0.827845218358881" "0.028977182770002" "1.35392750102305" ...

2
@Christoph_J (실제 문제) 어려움을 겪고있는 그룹화 명령을 보여주세요. 간단한 것을 놓쳤을 수도 있습니다. 열 클래스를 변환하려는 이유는 무엇입니까?
Matt Dowle 2011 년

1
@Christoph_J data.tables를 조작하는 데 어려움을 겪고 있다면 단순히 일시적으로 data.frame으로 변환하고 데이터 정리를 수행 한 다음 다시 data.tables로 변환하는 것이 어떻습니까?
Andrie

17
모든 열 대신 열의 하위 집합에 대해이 작업을 수행하는 관용적 방법은 무엇입니까? convcols열의 문자형 벡터 를 정의했습니다 . R dt[,lapply(.SD,as.numeric),.SDcols=convcols]dt[,convcols:=lapply(.SD,as.numeric),.SDcols=convcols]거의 동결시키는 동안 거의 즉각적 이므로 내가 잘못하고 있다고 생각합니다. 감사합니다
Frank

4
@Frank 아래 Geneorama의 답변에 대한 Matt Dowle의 의견을 참조하십시오 ( stackoverflow.com/questions/7813578/… ); 도움이되었고 관용적이었습니다. [start quote] 또 다른 쉬운 방법은 set()예를 들어 for (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))[end quote] 를 사용하는 것입니다.
swihart

4
by = ID 옵션을 사용하는 이유는 무엇입니까?
skan

48

이 시도

DT <- data.table(X1 = c("a", "b"), X2 = c(1,2), X3 = c("hello", "you"))
changeCols <- colnames(DT)[which(as.vector(DT[,lapply(.SD, class)]) == "character")]

DT[,(changeCols):= lapply(.SD, as.factor), .SDcols = changeCols]

7
이제 Filter함수를 사용하여 열을 식별 할 수 있습니다 . 예 : changeCols<- names(Filter(is.character, DT))
David Leal

1
IMO 이것은 내가 선택한 대답에 준 이유 때문에 더 나은 대답입니다.
James Hirschorn

1
또는 더 간결하게 : changeCols <- names(DT)[sapply(DT, is.character)].
sindri_baldur

8

Geneorama의 답변 ( https://stackoverflow.com/a/20808945/4241780 )에 Matt Dowle의 의견을 올리면 더 명확하게 (권장대로) 사용할 수 있습니다 for(...)set(...).


library(data.table)

DT = data.table(a = LETTERS[c(3L,1:3)], b = 4:7, c = letters[1:4])
DT1 <- copy(DT)
names_factors <- c("a", "c")

for(col in names_factors)
  set(DT, j = col, value = as.factor(DT[[col]]))

sapply(DT, class)
#>         a         b         c 
#>  "factor" "integer"  "factor"

reprex 패키지 (v0.3.0)에 의해 2020-02-12에 생성됨

자세한 내용은 https://stackoverflow.com/a/33000778/4241780 에서 Matt의 다른 의견을 참조하십시오 .

편집하다.

Espen 및에서 언급했듯이 "열 이름 (문자) 또는 숫자 (정수)는 열이 이미 존재하는 경우 값을 할당 할 열 이름" help(set)j수 있습니다. 그래서 names_factors <- c(1L, 3L)또한 작동합니다.


names_factors여기 에있는 내용을 추가 할 수 있습니다 . 나는 그것을에서 가져온 추측 stackoverflow.com/a/20808945/1666063 는 그래서 names_factors = c('fac1', 'fac2')이 경우 - 그것은 또한 예를 들어 1 열 번호가 될 수 names.But 열이며, 모든 열을 변환하는 것을 NcoI (DT)
에스 펜 Riskedal

@EspenRiskedal 감사합니다. 게시물을 더 명확하게 수정했습니다.
JWilliman

2

이것은 나쁜 방법입니다! 나는 다른 이상한 문제를 해결하는 경우에만이 답변을 남겨 둡니다. 이러한 더 나은 방법은 아마도 부분적으로는 새로운 data.table 버전의 결과 일 것입니다. 따라서 이렇게 어려운 방법으로 문서화하는 것이 좋습니다. 또한 이것은 구문에 대한 멋진 구문 예제입니다 eval substitute.

library(data.table)
dt <- data.table(ID = c(rep("A", 5), rep("B",5)), 
                 fac1 = c(1:5, 1:5), 
                 fac2 = c(1:5, 1:5) * 2, 
                 val1 = rnorm(10),
                 val2 = rnorm(10))

names_factors = c('fac1', 'fac2')
names_values = c('val1', 'val2')

for (col in names_factors){
  e = substitute(X := as.factor(X), list(X = as.symbol(col)))
  dt[ , eval(e)]
}
for (col in names_values){
  e = substitute(X := as.numeric(X), list(X = as.symbol(col)))
  dt[ , eval(e)]
}

str(dt)

당신에게주는

Classes ‘data.table’ and 'data.frame':  10 obs. of  5 variables:
 $ ID  : chr  "A" "A" "A" "A" ...
 $ fac1: Factor w/ 5 levels "1","2","3","4",..: 1 2 3 4 5 1 2 3 4 5
 $ fac2: Factor w/ 5 levels "2","4","6","8",..: 1 2 3 4 5 1 2 3 4 5
 $ val1: num  0.0459 2.0113 0.5186 -0.8348 -0.2185 ...
 $ val2: num  -0.0688 0.6544 0.267 -0.1322 -0.4893 ...
 - attr(*, ".internal.selfref")=<externalptr> 

42
또 다른 및 쉬운 방법은 사용하는 것입니다 set()for (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))
매트 Dowle

1
내 대답은 모든 버전에 대해 한 줄로 이것을 수행한다고 생각합니다. set그래도 더 적절한 지 확실하지 않습니다 .
Ben Rollert 2014-04-23

1
for(...)set(...)여기 에 더 많은 정보 : stackoverflow.com/a/33000778/403310
Matt Dowle

1
@skan 좋은 질문입니다. 이전에 요청한 내용을 찾을 수없는 경우 새로운 질문을하십시오. 미래에 다른 사람들을 돕습니다.
Matt Dowle

1
내가 그것을 어떻게이 @skan입니다 : github.com/geneorama/geneorama/blob/master/R/...
geneorama

0

몇 가지 접근 방식을 시도했습니다.

# BY {dplyr}
data.table(ID      = c(rep("A", 5), rep("B",5)), 
           Quarter = c(1:5, 1:5), 
           value   = rnorm(10)) -> df1
df1 %<>% dplyr::mutate(ID      = as.factor(ID),
                       Quarter = as.character(Quarter))
# check classes
dplyr::glimpse(df1)
# Observations: 10
# Variables: 3
# $ ID      (fctr) A, A, A, A, A, B, B, B, B, B
# $ Quarter (chr) "1", "2", "3", "4", "5", "1", "2", "3", "4", "5"
# $ value   (dbl) -0.07676732, 0.25376110, 2.47192852, 0.84929175, -0.13567312,  -0.94224435, 0.80213218, -0.89652819...

또는 기타

# from list to data.table using data.table::setDT
list(ID      = as.factor(c(rep("A", 5), rep("B",5))), 
     Quarter = as.character(c(1:5, 1:5)), 
     value   = rnorm(10)) %>% setDT(list.df) -> df2
class(df2)
# [1] "data.table" "data.frame"

0

이 작업을 수행하는보다 일반적이고 안전한 방법을 제공합니다.

".." <- function (x) 
{
  stopifnot(inherits(x, "character"))
  stopifnot(length(x) == 1)
  get(x, parent.frame(4))
}


set_colclass <- function(x, class){
  stopifnot(all(class %in% c("integer", "numeric", "double","factor","character")))
  for(i in intersect(names(class), names(x))){
    f <- get(paste0("as.", class[i]))
    x[, (..("i")):=..("f")(get(..("i")))]
  }
  invisible(x)
}

이 함수 ..는 data.table 범위에서 변수를 얻도록합니다. set_colclass는 cols의 클래스를 설정합니다. 다음과 같이 사용할 수 있습니다.

dt <- data.table(i=1:3,f=3:1)
set_colclass(dt, c(i="character"))
class(dt$i)

-1

data.table에 열 이름 목록이있는 경우 do 클래스를 변경하려고합니다.

convert_to_character <- c("Quarter", "value")

dt[, convert_to_character] <- dt[, lapply(.SD, as.character), .SDcols = convert_to_character]

이 답변은 본질적으로 아래 @Nera의 답변의 잘못된 버전입니다. 그냥 할 dt[, c(convert_to_character) := lapply(.SD, as.character), .SDcols=convert_to_character]오히려 느린 data.frame 할당을 사용하는 것보다, 참조로 할당합니다.
altabq

-3

시험:

dt <- data.table(A = c(1:5), 
                 B= c(11:15))

x <- ncol(dt)

for(i in 1:x) 
{
     dt[[i]] <- as.character(dt[[i]])
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.