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)

ここで明らかな何かを見逃していますか?

マシューの投稿による更新:以前に古いバージョンを使用しましたが、1.6.6(現在使用しているバージョン)に更新した後でもエラーが発生します。

更新2:「factor」クラスのすべての列を「文字」列に変換したいとしますが、どの列がどのクラスのものであるかは事前にわかりません。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は優先的に対処します。アンドリーの答えは、しかし、最良の方法です。
Matt Dowle、2011年

@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 ...

使用するlapplyas.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.framesに変換してから、データをクリーンアップしてからdata.tablesに変換し直してみませんか?
Andrie、

17
(すべてではなく)列のサブセットに対してこれを行う慣用的な方法は何ですか?convcols列の文字ベクトルを定義しました。Rがほぼフリーズしているdt[,lapply(.SD,as.numeric),.SDcols=convcols]間はほぼ瞬時なdt[,convcols:=lapply(.SD,as.numeric),.SDcols=convcols]ので、私はそれを間違っていると推測しています。ありがとう
フランク

4
@Frank以下のGeneoramaの回答に対するMatt Dowleのコメントを参照してください(stackoverflow.com/questions/7813578/…); それは私にとって十分役に立ち、慣用的でした[引用を開始]別のより簡単な方法は、set()例えばfor (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))[引用を終了] を使用することです
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、2018年

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"

2020-02-12にreprexパッケージ(v0.3.0)によって作成されました

https://stackoverflow.com/a/33000778/4241780でマットの別のコメントを参照してくださいしてください。

編集。

エスペンによって、およびに記載されているようにhelp(set)j「カラム(s)が既に存在する場合に値が割り当てられる列名(S)(文字)または数字(複数可)(整数)」であってもよいです。だから names_factors <- c(1L, 3L)また働くでしょう。


names_factorsここにあるものを追加することもできます。私はそれがstackoverflow.com/a/20808945/1666063から取得されていると思いますのでnames_factors = c('fac1', 'fac2')、この場合は-これは列名です。しかし、たとえば1; ncol(dt)のように、すべての列を変換することもできます
Espen 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]]))
Matt Dowle

1
私の答えは、すべてのバージョンで1行でこれを達成すると思います。setしかし、より適切かどうかはわかりません。
Ben Rollert、2014

1
詳細はfor(...)set(...)こちら:stackoverflow.com/a/33000778/403310
Matt Dowle 2015年

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.