web-dev-qa-db-fra.com

Les niveaux de facteur invisibles lors de l'ajout de nouveaux enregistrements avec des valeurs de chaîne invisibles à une trame de données, provoquent un avertissement et entraînent NA

J'ai une trame de données (14,5 K lignes par 15 colonnes) contenant les données de facturation de 2001 à 2007.

J'y ajoute de nouvelles données 2008 avec: alltime <- rbind(alltime,all2008)

Malheureusement, cela génère un avertissement:

> Warning message:
In `[<-.factor`(`*tmp*`, ri, value = c(NA, NA, NA, NA, NA, NA, NA,  :
  invalid factor level, NAs generated

Je suppose qu'il y a de nouveaux patients dont les noms ne figuraient pas dans la trame de données précédente et par conséquent, ils ne sauraient pas quel niveau leur donner. De même, de nouveaux noms invisibles dans la colonne "médecin référent".

Quelle est la solution?

66
Farrel

Cela peut être dû à une incompatibilité de types dans deux data.frames.

Tout d'abord les types de contrôle (classes). À des fins de diagnostic, procédez comme suit:

new2old <- rbind( alltime, all2008 ) # this gives you a warning
old2new <- rbind( all2008, alltime ) # this should be without warning

cbind(
    alltime = sapply( alltime, class),
    all2008 = sapply( all2008, class),
    new2old = sapply( new2old, class),
    old2new = sapply( old2new, class)
)

Je m'attends à ce qu'il y ait une rangée:

            alltime  all2008   new2old  old2new
...         ...      ...       ...      ...
some_column "factor" "numeric" "factor" "character"
...         ...      ...       ...      ...

Si oui, alors explication: rbind ne vérifie pas la correspondance des types. Si vous analysez le code rbind.data.frame, Vous pouvez voir que le premier argument initialise les types de sortie. Si dans le premier type data.frame est un facteur, la colonne data.frame en sortie est un facteur avec les niveaux unique(c(levels(x1),levels(x2))). Mais quand dans la deuxième colonne data.frame n'est pas le facteur alors levels(x2) est NULL, donc les niveaux ne s'étendent pas.

Cela signifie que vos données de sortie sont incorrectes! Il y a NA au lieu de vraies valeurs

Je suppose que:

  1. vous créez vos anciennes données avec une autre version R/RODBC afin que les types soient créés avec différentes méthodes (différents paramètres - séparateur décimal peut-être)
  2. il y a des valeurs NULL ou certaines données spécifiques dans la colonne problématique, par exemple. quelqu'un change de colonne sous la base de données.

Solution:

trouver la mauvaise colonne et trouver la raison pour laquelle elle est incorrecte et corrigée. Éliminez la cause et non les symptômes.

30
Marek

Un moyen "simple" consiste à ne pas définir vos chaînes comme facteurs lors de l'importation de données texte.

Notez que les fonctions read.{table,csv,...} Prennent un paramètre stringsAsFactors, qui est par défaut défini sur TRUE. Vous pouvez définir ceci sur FALSE pendant que vous importez et rbind- vos données.

Si vous souhaitez définir la colonne comme facteur à la fin, vous pouvez également le faire.

Par exemple:

alltime <- read.table("alltime.txt", stringsAsFactors=FALSE)
all2008 <- read.table("all2008.txt", stringsAsFactors=FALSE)
alltime <- rbind(alltime, all2008)
# If you want the doctor column to be a factor, make it so:
alltime$doctor <- as.factor(alltime$doctor)
27
Steve Lianoglou

1) créez le bloc de données avec des chaînes AsFactor définies sur FAUX. Cela devrait résoudre le problème de facteur

2) ensuite n'utilisez pas rbind - cela gâche les noms de colonne si le bloc de données est vide. 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
9
Raffael

Comme suggéré dans la réponse précédente, lisez les colonnes sous forme de caractères et effectuez la conversion en facteurs après rbind. SQLFetch (je suppose RODBC ) a aussi le stringsAsFactors ou le as.is argument pour contrôler la conversion des caractères. Les valeurs autorisées sont les mêmes que pour read.table, par exemple., as.is=TRUE ou un numéro de colonne.

4
rcs

J'ai eu le même problème avec les asymétries de types, en particulier avec les facteurs. J'ai dû coller deux ensembles de données par ailleurs compatibles.

Ma solution consiste à convertir les facteurs des deux trames de données en "caractère". Alors ça marche comme un charme :-)

    convert.factors.to.strings.in.dataframe <- function(dataframe)
    {
        class.data  <- sapply(dataframe, class)
        factor.vars <- class.data[class.data == "factor"]
        for (colname in names(factor.vars))
        {
            dataframe[,colname] <- as.character(dataframe[,colname])
        }
        return (dataframe)
    }

Si vous souhaitez voir les types de vos deux cadres de données exécutés (changer les noms de var):

    cbind("orig"=sapply(allSurveyData, class), 
          "merge" = sapply(curSurveyDataMerge, class),
          "eq"=sapply(allSurveyData, class) == sapply(curSurveyDataMerge, class)
    )
3
JSawyer

Lorsque vous créez la trame de données, vous avez le choix de faire des facteurs de colonnes de chaîne (stringsAsFactors=T), ou en les conservant sous forme de chaînes.

Dans votre cas, ne faites pas de facteurs vos colonnes de chaînes. Conservez-les sous forme de chaînes, puis l'ajout fonctionne correctement. Si vous en avez besoin en fin de compte, effectuez toute l'insertion et l'ajout en tant que chaîne, puis enfin les convertir en facteur.

Si vous effectuez les facteurs de colonnes de chaîne, puis ajoutez des lignes contenant des valeurs invisibles, vous obtenez l'erreur que vous avez mentionnée à chaque nouveau niveau de facteur invisible et cette valeur est remplacée par NA ...

> df <- data.frame(patient=c('Ann','Bob','Carol'), referring_doctor=c('X','Y','X'), stringsAsFactors=T)

  patient referring_doctor
1     Ann                X
2     Bob                Y
3   Carol                X

> df <- rbind(df, c('Denise','Z'))
Warning messages:
1: In `[<-.factor`(`*tmp*`, ri, value = "Denise") :
  invalid factor level, NA generated
2: In `[<-.factor`(`*tmp*`, ri, value = "Z") :
  invalid factor level, NA generated
> df
  patient referring_doctor
1     Ann                X
2     Bob                Y
3   Carol                X
4    <NA>             <NA>

Donc ne faites pas de facteurs pour vos colonnes de chaînes. Conservez-les sous forme de chaînes, puis l'ajout fonctionne correctement:

> df <- data.frame(patient=c('Ann','Bob','Carol'), referring_doctor=c('X','Y','X'), stringsAsFactors=F)
> df <- rbind(df, c('Denise','Z'))
  patient referring_doctor
1     Ann                X
2     Bob                Y
3   Carol                X
4  Denise                Z

Pour changer le comportement par défaut:

options(stringsAsFactors=F)

Pour convertir des colonnes individuelles en/à partir d'une chaîne ou d'un facteur

df$col <- as.character(df$col)
df$col <- as.factor(df$col)
2
smci

voici une fonction pour prendre les noms de lignes communs de 2 trames de données et faire un rbind où nous trouvons essentiellement les champs qui sont des facteurs, ajouter les nouveaux facteurs puis faire le rbind. Cela devrait prendre en charge tous les problèmes de facteurs:

rbindCommonCols <-function (x, y) {

commonColNames = intersect(colnames(x), colnames(y))
x = x[,commonColNames]
y = y[,commonColNames]

colClassesX = sapply(x, class)
colClassesY = sapply(y, class)
classMatch = paste( colClassesX, colClassesY, sep = "-" )
factorColIdx = grep("factor", classMatch)

for(n in factorColIdx){ 
    x[,n] = as.factor(x[,n])
    y[,n] = as.factor(y[,n])
}

for(n in factorColIdx){ 
    x[,n] = factor(x[,n], levels = unique(c( levels(x[,n]), levels(y[,n]) )))
    y[,n] = factor(y[,n], levels = unique(c( levels(y[,n]), levels(x[,n]) )))  
} 

res = rbind(x,y)
res

}

0
trycash2