どこかで (どこだったか思い出せないが)、因子は実際には data.table 内の文字ベクトルよりも効率的ではないと読んだような気がする。これは本当だろうか? 因子を使用して data.table 内のさまざまなベクトルを格納し続けるべきかどうか検討していた。近似テストではobject.size
そうではないことが示されているようだ。
chars <- data.table(a = sample(letters, 1e5, TRUE)) # chars (not really)
string <- data.table(a = sample(state.name, 1e5, TRUE)) # strings
fact <- data.table(a = factor(sample(letters, 1e5, TRUE))) # factor
int <- data.table(a = sample(1:26, 1e5, TRUE)) # int
mbs <- function(...) {
ns <- sapply(match.call(expand.dots=TRUE)[-1L], deparse)
vals <- mget(ns, .GlobalEnv)
cat('Sizes:\n',
paste('\t', ns, ':', round(sapply(vals, object.size)/1024/1024, 3), 'MB\n'))
}
## Get approximate sizes?
mbs(chars, string, fact, int)
# Sizes:
# chars : 0.765 MB
# string : 0.766 MB
# fact : 0.384 MB
# int : 0.382 MB
ベストアンサー1
おそらく、data.table FAQ 2.17 を覚えているでしょう。そこには次の内容が含まれています:
stringsAsFactors は、効率化のため、data.frame ではデフォルトで TRUE ですが、data.table では FALSE です。R にグローバル文字列キャッシュが追加されたため、文字項目は単一のキャッシュされた文字列へのポインターとなり、factor に変換してもパフォーマンス上の利点はなくなりました。
(この部分は、2012 年 7 月の v1.8.2 で FAQ に追加されました。)
因子ではなく文字を使用すると、スタッキング (rbindlist) などのタスクで非常に役立ちます。2c()
つの文字ベクトルの は単なる連結ですが、c()
2 つの因子列の は 2 つの因子レベルを走査して結合する必要があるため、コーディングが難しく、実行に時間がかかります。
64 ビット マシンでの RAM 消費の違いに気付いたでしょう。因子は、integer
レベル内の項目のベクトル検索として保存されます。タイプはinteger
、64 ビット プラットフォームでも 32 ビットです。ただし、ポインター (ベクトルcharacter
) は 64 ビット マシンでは 64 ビットです。したがって、64 ビット マシンでは、文字列の列は因子列の 2 倍の RAM を使用します。32 ビットでは違いはありません。ただし、通常、このコストは、文字ベクトルで可能なより単純で高速な命令によって相殺されます。[余談: 因子はinteger
20 億を超える一意の文字列を含むことができないため、character
列にはその制限はありません。]
何をしているかによって異なりますが、操作は data.table で最適化されているためcharacter
、これをお勧めします。基本的に、ホップ (レベル) が節約され、グローバル キャッシュへのホップもまったく行わずに、ポインター値を比較するだけで、異なるテーブル内の 2 つの文字列を比較できます。
列のカーディナリティにも依存します。列が 100 万行で、100 万の一意の文字列が含まれているとします。これを因子として保存するには、レベル用の 100 万文字ベクトルと、レベルの要素を指す 100 万整数ベクトルが必要です。これは (4+8)*1e6 バイトです。一方、文字ベクトルにはレベルは必要なく、8*1e6 バイトだけです。どちらの場合も、グローバル キャッシュには 100 万の一意の文字列が同じ方法で保存されるため、いずれにせよそうなることになります。この場合、文字列は因子の場合よりも RAM を少なく使用します。RAM 使用量を計算するために使用されるメモリ ツールがこれを適切に計算していることを慎重に確認してください。