web-dev-qa-db-fra.com

R: perte de noms de colonnes lors de l'ajout de lignes à un cadre de données vide

Je commence tout juste avec R et j'ai rencontré un comportement étrange: lors de l'insertion de la première ligne dans un cadre de données vide, les noms de colonne d'origine sont perdus.

exemple:

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"

Comme vous pouvez le constater, les noms de colonne un et deux ont été remplacés par X5 et X6 .

Quelqu'un pourrait-il me dire pourquoi cela se produit et s'il existe un moyen approprié de le faire sans perdre les noms de colonne?

Une solution efficace consisterait à enregistrer les noms dans un vecteur auxiliaire, puis à les rajouter une fois que vous avez terminé de travailler sur le bloc de données.

Merci

Le contexte:

J'ai créé une fonction qui rassemble des données et les ajoute en tant que nouvelle ligne à une trame de données reçue en tant que paramètre . rempli avec ses résultats.

58
cdmihai

La page d'aide rbind spécifie que:

Pour ‘cbind’ (‘rbind’), des vecteurs de zéro longueur (y compris "NULL") sont ignorés sauf si le résultat aurait zéro rangée (colonnes), pour la compatibilité S. (Les matrices à extension nulle ne figurent pas dans S3 et ne sont pas ignorées dans R.)

Donc, en fait, a est ignoré dans votre instruction rbind. Il semble que ce ne soit pas totalement ignoré, car comme il s’agit d’un bloc de données, la fonction rbind est appelée rbind.data.frame:

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

Peut-être qu'une façon d'insérer la ligne pourrait être:

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

Mais il peut y avoir une meilleure façon de le faire en fonction de votre code.

33
juba

se rendait presque à cette question.

1) créer un bloc de données avec stringsAsFactor défini sur FALSE ou passer directement au numéro suivant

2) n'utilisez pas rbind - vous ne savez pas pourquoi, les noms des colonnes sont gâchés. faites-le simplement de cette façon:

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
12
Raffael

La solution serait:

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

?rbind indique que la fusion d'objets requiert des noms correspondants:

Il prend ensuite les classes de la des colonnes du premier bloc de données, et correspond aux colonnes par nom (plutôt que par position)

8
Roman Luštrik

FWIW, une conception alternative pourrait faire en sorte que vos fonctions créent des vecteurs pour les deux colonnes, au lieu de se lier à un cadre de données:

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

Modifiez les vecteurs dans vos fonctions:

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

Répétez autant de fois que nécessaire, puis créez votre nom data.frame en une fois:

a <- data.frame(one=ones, two=twos)
7
David

Voici une façon de procéder de manière générique et avec le moins de saisies de nouveau des noms de colonnes. Cette méthode ne nécessite pas de piratage de NA ou de 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 auront les noms corrects

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

Une autre façon de le faire plus proprement consiste à utiliser data.table: 

> 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

Notez qu'un data.table est aussi un data.frame.

> class(df)
"data.table" "data.frame"
2
Steve Lihn

Tu peux le faire:

donne une ligne au bloc de données initial

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

ajoutez votre nouvelle ligne et sortez le NAS

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

mais faites attention à ce que votre nouvelle génération ne possède pas de NA ou qu'il soit également effacé.

À la vôtre Agus

1
Agus camacho

J'utilise la solution suivante pour ajouter une ligne à un cadre de données vide:

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.

Sincères amitiés

Georg

0
Georg