2011-03-08 25 views
54

Estoy empezando con R y encontré un comportamiento extraño: al insertar la primera fila en un marco de datos vacío, los nombres de columna originales se pierden.R: perder nombres de columna al agregar filas a un marco de datos vacío

ejemplo:

a<-data.frame(one = numeric(0), two = numeric(0)) 
a 
#[1] one two 
#<0 rows> (or 0-length row.names) 
names(a) 
#[1] "one" "two" 
a<-rbind(a, c(5,6)) 
a 
# X5 X6 
#1 5 6 
names(a) 
#[1] "X5" "X6" 

Como se puede ver, los nombres de las columnas uno y dos fueron reemplazados por X5 X6 y .

¿Podría alguien decirme por qué sucede esto y existe una forma correcta de hacerlo sin perder los nombres de las columnas?

Una solución de escopeta sería guardar los nombres en un vector auxiliar y luego agregarlos cuando haya terminado de trabajar en el marco de datos.

Gracias

Contexto:

he creado una función que recoge algunos datos y los añade como una nueva fila a una trama de datos recibida como parámetro. Creo el marco de datos, repito a través de mis fuentes de datos, pasando el data.frame a cada llamada de función para completar con sus resultados.

Respuesta

28

Los rbind páginas de ayuda especifica que:

Para 'cbind' ('rbind'), vectores de cero longitud (incluyendo 'NULL') se ignoran a menos que el resultado sería tener cero filas (columnas), para la compatibilidad S (matrices cero medida no se producen en S3 y no se tienen en cuenta en R.)

Así que, de hecho, a se ignora en su instrucción rbind. No totalmente ignorado, al parecer, ya que como se trata de una trama de datos de la función rbind se llama como rbind.data.frame:

rbind.data.frame(c(5,6)) 
# X5 X6 
#1 5 6 

Tal vez una forma de insertar la fila podría ser:

a[nrow(a)+1,] <- c(5,6) 
a 
# one two 
#1 5 6 

pero puede haber una mejor forma de hacerlo dependiendo de tu código.

8

Solución alternativa sería:

a <- rbind(a, data.frame(one = 5, two = 6)) 

?rbind afirma que la fusión de objetos exige nombres coincidentes:

A continuación, toma las clases de los columnas de la primera trama de datos, y los partidos columnas de nombre (en lugar de por posición)

+0

Creo que en el código se ignora el 'a' 'dentro rbind', por lo que es de hecho un equivalente a' <- hoja.de.datos (uno = 5, dos = 6) '. Pero puedo estar equivocado. – juba

+0

+1 Normalmente uso este enfoque: tenga en cuenta que puede simplemente inicializar 'a' en el vector vacío:' a <- c() '. –

+0

@juba, ese puede ser el caso, porque el data.frame 'a' está vacío. –

7

Fwiw, un diseño alternativo podría tener sus funciones vectores de construcción de las dos columnas, en lugar de rbinding a una trama de datos:

ones <- c() 
twos <- c() 

Modificar los vectores en sus funciones:

ones <- append(ones, 5) 
twos <- append(twos, 6) 

Repetir según sea necesario, cree su data.frame de una vez:

a <- data.frame(one=ones, two=twos) 
+1

increíblemente útil. quizás no sea tan sucinto, pero el flujo de datos es un poco menos negro. – Andrew

+0

De hecho, una buena respuesta. Pero parece muy "no R". Al construir el data.frame primero necesita * loop * sobre todos los contenidos mientras que los operadores de fila son caballos de batalla de R. Tal vez usando la respuesta de @juba pero establezca los colnames al final: 'colnames (a) <- c (" uno "," dos ")'? – user989762

+0

El problema con este enfoque es que a menudo se requieren los nombres para hacer la extensión del marco de datos. ¿Por qué son cosas tan simples tan complicadas en r ...? – TMOTTM

7

was almost surrende anillo a este problema.

1) crear trama de datos con stringsAsFactor conjunto de FALSE o se ejecuta directamente en el próximo número

2) no utilice rbind - ni idea de por qué en la tierra que está arruinando los nombres de columna. simplemente hacerlo de esta manera:

df[nrow(df)+1,] <- c("d","gsgsgd",4)

df <- data.frame(a = character(0), b=character(0), c=numeric(0)) 

df[nrow(df)+1,] <- c("d","gsgsgd",4) 

#Warnmeldungen: 
#1: In `[<-.factor`(`*tmp*`, iseq, value = "d") : 
# invalid factor level, NAs generated 
#2: In `[<-.factor`(`*tmp*`, iseq, value = "gsgsgd") : 
# invalid factor level, NAs generated 

df <- data.frame(a = character(0), b=character(0), c=numeric(0), stringsAsFactors=F) 

df[nrow(df)+1,] <- c("d","gsgsgd",4) 

df 
# a  b c 
#1 d gsgsgd 4 
1

usted puede hacer esto:

dar una fila a la trama de datos inicial

df=data.frame(matrix(nrow=1,ncol=length(newrow)) 

añadir su nueva fila y sacar el NAS

newdf=na.omit(rbind(newrow,df)) 

pero ten cuidado con que tu newrow no tenga NA o se borrará también.

Saludos Agus

-1

En lugar de construir la hoja.de.datos con numeric(0) utilizo as.numeric(0).

a<-data.frame(one=as.numeric(0), two=as.numeric(0)) 

Esto crea una fila adicional inicial

a 
# one two 
#1 0 0 

Enlazar las filas adicionales

a<-rbind(a,c(5,6)) 
a 
# one two 
#1 0 0 
#2 5 6 

a continuación, utilizar la indexación negativa para eliminar el primer (falsa) fila

a<-a[-1,] 
a 

# one two 
#2 5 6 

Nota: se arruina th índice e (extremo izquierdo). No he descubierto cómo prevenir eso (¿alguien más?), Pero la mayoría de las veces probablemente no importe.

+0

La mayoría de las veces, probablemente sí. – TMOTTM

0

Una forma de hacer que esto funcione de forma genérica y con la menor cantidad de escritura de los nombres de las columnas es la siguiente. Este método no requiere cortar la AN o 0.

rs <- data.frame(i=numeric(), square=numeric(), cube=numeric()) 
for (i in 1:4) { 
    calc <- c(i, i^2, i^3) 
    # append calc to rs 
    names(calc) <- names(rs) 
    rs <- rbind(rs, as.list(calc)) 
} 

RS tendrá los nombres correctos

> rs 
    i square cube 
1 1  1 1 
2 2  4 8 
3 3  9 27 
4 4  16 64 
> 

Otra manera de hacer esto de forma más limpia es utilizar los datos.tabla:

> df <- data.frame(a=numeric(0), b=numeric(0)) 
> rbind(df, list(1,2)) # column names are messed up 
> X1 X2 
> 1 1 2 

> df <- data.table(a=numeric(0), b=numeric(0)) 
> rbind(df, list(1,2)) # column names are preserved 
    a b 
1: 1 2 

Observe que un data.table también es un data.frame.

> class(df) 
"data.table" "data.frame" 
0

utilizo la siguiente solución para añadir una fila a una trama de datos vacía:

d_dataset <- 
    data.frame(
    variable = character(), 
    before = numeric(), 
    after = numeric(), 
    stringsAsFactors = FALSE) 

d_dataset <- 
    rbind(
    d_dataset, 
     data.frame(
     variable = "test", 
     before = 9, 
     after = 12, 
     stringsAsFactors = FALSE)) 

print(d_dataset) 

variable before after 
1  test  9 12 

HTH.

Saludos cordiales

Georg

Cuestiones relacionadas