将具有看不见的字符串值的新记录附加到数据框时,看不见的因子级别会导致警告并导致 NA
Posted
技术标签:
【中文标题】将具有看不见的字符串值的新记录附加到数据框时,看不见的因子级别会导致警告并导致 NA【英文标题】:Unseen factor levels when appending new records with unseen string values to a dataframe, cause Warning and result in NA 【发布时间】:2010-12-10 14:32:25 【问题描述】:我有一个数据框(14.5K 行 x 15 列),其中包含从 2001 年到 2007 年的结算数据。
我将新的 2008 年数据附加到它:alltime <- rbind(alltime,all2008)
不幸的是,这会产生警告:
> Warning message:
In `[<-.factor`(`*tmp*`, ri, value = c(NA, NA, NA, NA, NA, NA, NA, :
invalid factor level, NAs generated
我的猜测是,有一些新患者的姓名不在之前的数据框中,因此它不知道给这些患者提供什么级别。在“推荐医生”列中出现类似的新名字。
解决办法是什么?
【问题讨论】:
这很奇怪。因素不应该导致这种情况,在rbind
的帮助中声明:“因素的水平根据需要扩大”(R-2.9.2)。也许您可以准确检查是哪一列导致了这种情况?
Marek 真是太棒了!警告信息吓坏了我。阅读您的评论后,我回去探索我的数据。所有新数据似乎都在那里,并且已添加其他级别。在这个阶段,我可以把它作为一个应该被忽略的警告——这是一个危险的习惯(从那时起,一个人必须在他们的头中保存一个数据库,以便认真对待警告而不是忽略警告)。如何确定警告消息的来源?
“R 导入数据并自动计算出什么是数字和什么不是数字的方式(从而使其成为一个因素)......” 参见 read.csv(..., stringsAsFactors=FALSE
和 @ 987654326@。 SO上有很多问题。
“警告”表示警告,而不是错误。您可以使用 str() 或 table(..., useNA='ifany') 检查结果因子是否正确。最好给出一个可重现的例子(你可以在
类似问题Convert data.frame columns from factors to characters
【参考方案1】:
这可能是由于两个data.frames
中的类型不匹配造成的。
首先检查类型(类)。为了诊断目的,这样做:
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)
)
我希望有一行看起来像:
alltime all2008 new2old old2new
... ... ... ... ...
some_column "factor" "numeric" "factor" "character"
... ... ... ... ...
如果是,那么说明:
rbind
不检查类型匹配。如果您分析rbind.data.frame
代码,那么您可以看到第一个参数初始化了输出类型。如果第一个 data.frame 类型是一个因子,则输出 data.frame 列是因子,级别为unique(c(levels(x1),levels(x2)))
。但是当第二个 data.frame 列不是因素时,levels(x2)
是NULL
,所以级别不会扩展。
说明你的输出数据有误!有NA
's 而不是真值
我想:
-
您使用另一个 R/RODBC 版本创建旧数据,因此使用不同的方法创建类型(不同的设置 - 可能是小数分隔符)
有问题的列中有 NULL 或某些特定数据,例如。有人更改数据库下的列。
解决方案:
查找错误的列并找出其错误并已修复的原因。消除原因而不是症状。
【讨论】:
耶。你是对的。在一个数据框中,列的类是一个因素,而在另一个数据框中,它是一个数字。这把事情搞得一团糟。我将数字转换为因子,一切正常。感谢您的指导。还有一些其他的差异。例如,因素-字符差异并没有把事情搞砸。 你对因子字符是正确的,在代码的某处我发现这个组合的级别将是unique(c(levels(x1),x2))
。一件事:组合因素 - 字符导致一个因素,组合字符 - 因素到字符。所以当类型匹配时会更好。【参考方案2】:
一种“简单”的方法是在导入文本数据时根本不将字符串设置为因素。
注意read.table,csv,...
函数采用stringsAsFactors
参数,默认设置为TRUE
。您可以在导入数据时将其设置为FALSE
,并rbind
-ing 数据。
如果您想将列设置为最后的一个因素,您也可以这样做。
例如:
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)
【讨论】:
【参考方案3】:1) 创建将 stringsAsFactor 设置为 FALSE 的数据框。这应该可以解决因素问题
2) 之后不要使用 rbind - 如果数据框为空,它会弄乱列名。只需这样做:
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
【讨论】:
【参考方案4】:按照上一个答案中的建议,将列读取为字符并转换为rbind
之后的因子。
SQLFetch
(我假设 RODBC)还有 stringsAsFactors
或 as.is
参数来控制字符的转换。
允许的值与 read.table
相同,例如 as.is=TRUE
或某个列号。
【讨论】:
【参考方案5】:我在类型不匹配方面遇到了同样的问题,尤其是在因素方面。我不得不将两个原本兼容的数据集粘合在一起。
我的解决方案是将两个数据框中的因子转换为“字符”。然后它就像一个魅力:-)
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)
如果您想查看运行的两个数据帧中的类型(更改 var 名称):
cbind("orig"=sapply(allSurveyData, class),
"merge" = sapply(curSurveyDataMerge, class),
"eq"=sapply(allSurveyData, class) == sapply(curSurveyDataMerge, class)
)
【讨论】:
mydf[sapply(mydf, is.factor)] <- lapply(mydf[sapply(mydf, is.factor)], as.character)
似乎是一种更简单的方法。【参考方案6】:
创建数据框时,您可以选择将字符串列设为因子 (stringsAsFactors=T
),或将它们保留为字符串。
对于你的情况,不要让你的字符串列因素。将它们保留为字符串,然后追加工作正常。如果您最终需要它们成为因子,请先将所有插入和追加作为字符串,然后最后将它们转换为因子。
如果您将字符串列设为因子,然后附加包含未见值的行,则会在每个新的未见因子级别上得到您提到的错误,并且该值将被替换为 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>
所以不要让你的字符串列因素。将它们保留为字符串,然后追加工作正常:
> 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
更改默认行为:
options(stringsAsFactors=F)
将单个列转换为字符串或因子
df$col <- as.character(df$col)
df$col <- as.factor(df$col)
【讨论】:
【参考方案7】:这里有一个函数来获取 2 个数据帧的公共行名并执行 rbind,我们基本上找到作为因子的字段,添加新因子然后执行 rbind。这应该解决任何因素问题:
rbindCommonCols
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
【讨论】:
以上是关于将具有看不见的字符串值的新记录附加到数据框时,看不见的因子级别会导致警告并导致 NA的主要内容,如果未能解决你的问题,请参考以下文章