/ / data.table perde l'ordine dei fattori dopo rbind, R (possibile bug) - r, ordinamento, ordine, data.table

data.table perde l'ordine dei fattori dopo rbind, R (possibile errore) - r, ordinamento, ordine, data.table

quando rbinddue data.table con i fattori ordinati, l'ordine sembra essere perso:

dtb1 = data.table(id = factor(c("a", "b"), levels = c("a", "c", "b"), ordered=T), key="id")
dtb2 = data.table(id = factor(c("c"), levels = c("a", "c", "b"), ordered=T), key="id")
test = rbind(dtb1, dtb2)
is.ordered(test$id)
#[1] FALSE

Qualche idea, idea?

risposte:

7 per risposta № 1

data.table fa un po 'di gioco di gambe che significa questo data.table:::.rbind.data.table si chiama quando rbind è chiamato su oggetti compresi data.tables. .rbind.data.table utilizza le accelerazioni associate a rbindlist, con un po 'di controllo extra per corrispondere per nome ecc.

.rbind.data.table tratta con colonne di fattori usando c combinarli (mantenendo quindi l'attributo livelli)

# the relevant code is
l = lapply(seq_along(allargs[[1L]]), function(i) do.call("c",
lapply(allargs, "[[", i)))

In base R utilizzando c in questo modo non mantiene l'attributo "ordinato", non restituisce nemmeno un fattore!

Ad esempio (in base R)

f <- factor(1:2, levels = 2:1, ordered=TRUE)
g <- factor(1:2, levels = 2:1, ordered=TRUE)
# it isn"t ordered!
is.ordered(c(f,g))
# [1] FALSE
# no suprise as it isn"t even a factor!
is.factor(c(f,g))
# [1] FALSE

però data.table ha un metodo S3 c.factor, che viene utilizzato per garantire che venga restituito un fattore e che i livelli vengano mantenuti. Sfortunatamente questo metodo non mantiene l'attributo ordinato.

getAnywhere("c.factor")
# A single object matching ‘c.factor’ was found
# It was found in the following places
#   namespace:data.table
# with value
#
# function (...)
# {
#     args <- list(...)
#     for (i in seq_along(args)) if (!is.factor(args[[i]]))
#         args[[i]] = as.factor(args[[i]])
#     newlevels = unique(unlist(lapply(args, levels), recursive = TRUE,
#         use.names = TRUE))
#     ind <- fastorder(list(newlevels))
#     newlevels <- newlevels[ind]
#     nm <- names(unlist(args, recursive = TRUE, use.names = TRUE))
#     ans = unlist(lapply(args, function(x) {
#         m = match(levels(x), newlevels)
#         m[as.integer(x)]
#     }))
structure(ans, levels = newlevels, names = nm, class = "factor")
}
<bytecode: 0x073f7f70>
<environment: namespace:data.table

Quindi sì, questo è un bug. Ora è segnalato come # 5019.


1 per risposta № 2

Come di versione 1.8.11 data.table unirà i fattori ordinati per ottenere risultati ordered se esiste un ordine globale, e si lamenterà e determinerà un fattore se non esiste:

DT1 = data.table(ordered("a", levels = c("a","b","c")))
DT2 = data.table(ordered("a", levels = c("a","d","b")))

rbind(DT1, DT2)$V1
#[1] a a
#Levels: a < d < b < c

DT3 = data.table(ordered("a", levels = c("b","a","c")))
rbind(DT1, DT3)$V1
#[1] a a
#Levels: a b c
#Warning message:
#In rbindlist(lapply(seq_along(allargs), function(x) { :
#  ordered factor levels cannot be combined, going to convert to simple factor instead

Per contrasto, ecco cosa fa R di base:

rbind(data.frame(DT1), data.frame(DT2))$V1
#[1] a a
#Levels: a < b < c < d
# Notice that the resulting order does not respect the suborder for DT2

rbind(data.frame(DT1), data.frame(DT3))$V1
#[1] a a
#Levels: a < b < c
# Again, suborders are not respected and new order is created

-1 per risposta № 3

Ho incontrato lo stesso problema dopo rbind, basta riassegnare il livello ordinato per la colonna.

test$id <- factor(test$id, levels = letters, ordered = T)

È meglio definire fattore dopo rbind