2013-04-10 6 views
13

Ho appena scoperto questo avviso nel mio script che era un po 'strano.rbindlist due data.tables dove uno ha un fattore e altro ha un tipo di carattere per una colonna

# Warning message: 
# In rbindlist(list(DT.1, DT.2)) : NAs introduced by coercion 

Osservazione 1: Ecco un esempio riproducibile:

require(data.table) 
DT.1 <- data.table(x = letters[1:5], y = 6:10) 
DT.2 <- data.table(x = LETTERS[1:5], y = 11:15) 

# works fine 
rbindlist(list(DT.1, DT.2)) 
#  x y 
# 1: a 6 
# 2: b 7 
# 3: c 8 
# 4: d 9 
# 5: e 10 
# 6: A 11 
# 7: B 12 
# 8: C 13 
# 9: D 14 
# 10: E 15 

Tuttavia, ora se convertire colonna x ad un factor (ordinata o meno) e fare lo stesso:

DT.1[, x := factor(x)] 
rbindlist(list(DT.1, DT.2)) 
#  x y 
# 1: a 6 
# 2: b 7 
# 3: c 8 
# 4: d 9 
# 5: e 10 
# 6: NA 11 
# 7: NA 12 
# 8: NA 13 
# 9: NA 14 
# 10: NA 15 
# Warning message: 
# In rbindlist(list(DT.1, DT.2)) : NAs introduced by coercion 

Ma rbind fa bene questo lavoro!

rbind(DT.1, DT.2) # where DT.1 has column x as factor 
# do.call(rbind, list(DT.1, DT.2)) # also works fine 
#  x y 
# 1: a 6 
# 2: b 7 
# 3: c 8 
# 4: d 9 
# 5: e 10 
# 6: A 11 
# 7: B 12 
# 8: C 13 
# 9: D 14 
# 10: E 15 

Lo stesso comportamento può essere riprodotto se colonna x è un ordered factor pure. Poiché la pagina di guida ?rbindlist dice: Same as do.call("rbind",l), but much faster., suppongo che questo non sia il comportamento desiderato?


Ecco le mie sessione:

# R version 3.0.0 (2013-04-03) 
# Platform: x86_64-apple-darwin10.8.0 (64-bit) 
# 
# locale: 
# [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8 
# 
# attached base packages: 
# [1] stats  graphics grDevices utils  datasets methods base  
# 
# other attached packages: 
# [1] data.table_1.8.8 
# 
# loaded via a namespace (and not attached): 
# [1] tools_3.0.0 

Edit:

Osservazione 2: seguito @ AnandaMahto un'altra osservazione interessante, invertendo l'ordine:

# column x in DT.1 is still a factor 
rbindlist(list(DT.2, DT.1)) 
#  x y 
# 1: A 11 
# 2: B 12 
# 3: C 13 
# 4: D 14 
# 5: E 15 
# 6: 1 6 
# 7: 2 7 
# 8: 3 8 
# 9: 4 9 
# 10: 5 10 

Qui, la colonna da DT.1 è forzata in modo invisibile a numeric.
Modifica: Giusto per chiarire, questo è lo stesso comportamento di rbind(DT2, DT1) con la colonna x di DT1 come fattore. rbind sembra mantenere la classe del primo argomento. Lascerò questa parte qui e dirò che in questo caso, questo è il comportamento desiderato poiché rbindlist è un'implementazione più veloce di rbind.

Osservazione 3: Se ora, entrambe le colonne vengono convertiti fattori:

# DT.1 column x is already a factor 
DT.2[, x := factor(x)] 
rbindlist(list(DT.1, DT.2)) 
#  x y 
# 1: a 6 
# 2: b 7 
# 3: c 8 
# 4: d 9 
# 5: e 10 
# 6: a 11 
# 7: b 12 
# 8: c 13 
# 9: d 14 
# 10: e 15 

Qui, la colonna x da DT.2 è perduto (/ sostituito con quello di DT.1). Se l'ordine viene annullato, accade esattamente l'opposto (la colonna x di DT.1 viene sostituita con quella di DT.2).

In generale, sembra esserci un problema con la gestione delle colonne factor in rbindlist.

risposta

7

UPDATE: questo bug (#2650) è stato risolto il 17 maggio 2013 nella v1.8.9


ritengo che rbindlist quando applicato a fattori viene Combinando i valori numerici dei fattori e utilizzando solo i livelli associati con il primo elemento dell'elenco.

Come in questo bug report: http://r-forge.r-project.org/tracker/index.php?func=detail&aid=2650&group_id=240&atid=975


# Temporary workaround: 

levs <- c(as.character(DT.1$x), as.character(DT.2$x)) 

DT.1[, x := factor(x, levels=levs)] 
DT.2[, x := factor(x, levels=levs)] 

rbindlist(list(DT.1, DT.2)) 

Come un altro vista di che cosa sta succedendo:

DT3 <- data.table(x=c("1st", "2nd"), y=1:2) 
DT4 <- copy(DT3) 

DT3[, x := factor(x, levels=x)] 
DT4[, x := factor(x, levels=x, labels=rev(x))] 

DT3 
DT4 

# Have a look at the difference: 
rbindlist(list(DT3, DT4))$x 
# [1] 1st 2nd 1st 2nd 
# Levels: 1st 2nd 

do.call(rbind, list(DT3, DT4))$x 
# [1] 1st 2nd 2nd 1st 
# Levels: 1st 2nd 

Edit come da commenti:

come per l'osservazione 1, ciò che sta accadendo è simile a:

x <- factor(LETTERS[1:5]) 

x[6:10] <- letters[1:5] 
x 

# Notice however, if you are assigning a value that is already present 
x[11] <- "S" # warning, since `S` is not one of the levels of x 
x[12] <- "D" # all good, since `D` *is* one of the levels of x 
+0

Ricardo, grande! grazie per aver archiviato il bug Penso che quello che spieghi sia giusto, ma parte della storia. Come in, in caso di "Osservazione 1", non sono stati combinati valori numerici. Intendo dire che si comportano in modo diverso a seconda che entrambe le colonne siano fattori e anche sull'ordine con cui li leghi. Suona bene? – Arun

+0

(+1) grazie per la soluzione temporanea. Dato il numero di operazioni che si eseguono usando "fattore", mi piacerebbe avere la priorità di questo bug urtato ... (Aspetterò un po 'prima di accettare la risposta). – Arun

+0

@Arun, per favore vedi la modifica sopra in risposta alla domanda sull'osservazione 1 –

2

rbindlist è superveloce, perché non fa il controllo di rbindfill o do.call(rbind.data.frame,...)

È possibile utilizzare una soluzione come questo per garantire che i fattori sono forzati ai personaggi.

DT.1 <- data.table(x = factor(letters[1:5]), y = 6:10) 
DT.2 <- data.table(x = LETTERS[1:5], y = 11:15) 


for(ii in seq_along(DDL)){ 
    ff <- Filter(function(x) is.factor(DDL[[ii]][[x]]), names(DDL[[ii]])) 
    for(fn in ff){ 
    set(DDL[[ii]], j = fn, value = as.character(DDL[[ii]][[fn]])) 
    } 
    } 
rbindlist(DDL) 

o (meno memoria in modo efficiente)

rbindlist(rapply(DDL, classes = 'factor', f= as.character, how = 'replace')) 
Problemi correlati