在R中定义线性模型时对比度出错

60

当我试图在R中定义我的线性模型时,代码如下:

lm1 <- lm(predictorvariable ~ x1+x2+x3, data=dataframe.df)

我收到了以下错误信息:

Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : 
contrasts can be applied only to factors with 2 or more levels 

有没有办法忽略或修复这个问题?有些变量是因素,有些不是。


当我尝试为(price ~ year)构建线性模型时,由于年份是分类变量而不是数值变量,导致出现了这个错误。 - duhaime
9个回答

79
如果您的自变量(右手边的变量)是一个因子或只采用一个值的字符,则会出现这种类型的错误。
例如:在R中的鸢尾花数据。
(model1 <- lm(Sepal.Length ~ Sepal.Width + Species, data=iris))

# Call:
# lm(formula = Sepal.Length ~ Sepal.Width + Species, data = iris)

# Coefficients:
#       (Intercept)        Sepal.Width  Speciesversicolor   Speciesvirginica  
#            2.2514             0.8036             1.4587             1.9468  

现在,如果您的数据只包含一种物种:

(model1 <- lm(Sepal.Length ~ Sepal.Width + Species,
              data=iris[iris$Species == "setosa", ]))
# Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : 
#   contrasts can be applied only to factors with 2 or more levels
如果变量是数值型的(例如Sepal.Width),但只有一个取值,比如说是3,那么模型会运行,但你会得到这个变量的系数值为NA,具体如下:
(model2 <-lm(Sepal.Length ~ Sepal.Width + Species,
             data=iris[iris$Sepal.Width == 3, ]))

# Call:
# lm(formula = Sepal.Length ~ Sepal.Width + Species, 
#    data = iris[iris$Sepal.Width == 3, ])

# Coefficients:
#       (Intercept)        Sepal.Width  Speciesversicolor   Speciesvirginica  
#             4.700                 NA              1.250              2.017

解决方案: 如果因变量只有一个值,那么它的变化不足以支持分析。所以,你需要删除该变量,无论其是数值型、字符型还是分类变量。

根据评论更新: 由于你知道错误仅会发生在分类/字符变量上,所以你可以仅关注这些变量,并查看这些分类变量级别的长度是1(DROP)还是大于1(NODROP)。

要查看变量是否为分类变量,请使用以下代码:

(l <- sapply(iris, function(x) is.factor(x)))
# Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
#        FALSE        FALSE        FALSE        FALSE         TRUE 

然后您可以仅获取因子变量的数据框。

m <- iris[, l]

现在,查找因子变量的级别数,如果级别数为1,则需要将其删除。

ifelse(n <- sapply(m, function(x) length(levels(x))) == 1, "DROP", "NODROP")
注意:如果因子变量的水平只有一个,那么你需要删除这个变量。

Note: 如果因子变量的水平只有一个,那么你需要删除这个变量。


好的,谢谢。我能在R中修复这个问题吗?还是需要编辑原始数据?另外,查看了数据后发现所有变量都有多个值?有没有办法知道它们具体指的是哪些变量? - REnthusiast
2
另外,如果您的变量包含“奇异”字符,则会出现相同的错误。我想这是一个漏洞。我的变量CustomerType有一个值包含“ö”,当我更改它时,错误消失了。 - ErrantBard
5
你的最后一个 ifelse 语句无效。一个变量可能有2个水平,但如果其中一个为空,你将会得到一个错误,但你的代码不会检测到这个错误。对于一个数据框 df,更好的公式是: which(sapply(df, function(x) length(unique(x))<2)) ,它会列出有问题的变量。 - Roobie Nuby

20

看起来你的预测变量之一,x1x2x3,只有一个因子水平,因此是一个常数。

看一下

lapply(dataframe.df[c("x1", "x2", "x3")], unique)

找到不同的值。


9
这个错误信息也有可能发生在数据包含NA的情况下。
在这种情况下,行为取决于默认值(详见文档),并且可能所有包含变量中列中的NA的情况都会被静默删除。因此,在没有NA的情况下限制到的因子只有一个结果,尽管该因子实际上可能有几个结果。
要解决这个错误,可以改变模型(从公式中删除问题因子)或更改数据(即完成案例)。

6
其他作者的回答已经解决了只有一个水平或NA因素的问题。
今天,当我使用“rstatix :: anova_test()”函数时,我遇到了相同的错误,但我的因素是可以的(不只有一个水平,没有NA,没有字符向量等)。 相反,我可以通过删除数据框中未包含在模型中的所有变量来修复错误。我不知道这种行为的原因,但当遇到此错误时,知道这一点也可能会有所帮助。

2
你刚刚解决了我的问题。这一定是某种错误,你会认为该函数应该能够忽略其他列。 - MonikaP
谢谢!我也遇到了这个问题,使用rstatix::anova_test()时尽管错误信息指向其他地方,但确实是这个原因。 - Michael MacAskill
这个问题似乎在最新的rstatix包(0.7.0)中得到了修复。 - ekatko1

3

Metrics和Svens的答案处理了通常情况,但是对于我们这些在非英语环境中工作的人来说,如果您的字符变量中有奇异字符(如å,ä,ö),即使您有多个因子水平,也会得到相同的结果。

Levels <- c("Pri", "För") 会导致对比度错误,而 Levels <- c("Pri", "For") 则不会。

这可能是一个错误。


1
谢谢您的建议。尽管我已经仔细检查了将多个水平传递给模型,但我的两个因子变量仍然出现错误,并且我想知道是否由于我的数据来自非英语环境所致。 但是,这些水平不包含任何奇特的字符,重新编码它们也无法解决问题。 - Joe

1
这是对@Metrics提供的答案进行了修改并由@Max Ghenis编辑的变体...
l <- sapply(iris, function(x) is.factor(x))
m <- iris[,l]

n <- sapply( m, function(x) { y <- summary(x)/length(x)
len <- length(y[y<0.005 | y>0.995])
cbind(len,t(y))} )

drop_cols_df <- data.frame(var = names(l[l]), 
                           status = ifelse(as.vector(t(n[1,]))==0,"NODROP","DROP" ),
                           level1 = as.vector(t(n[2,])),
                           level2 = as.vector(t(n[3,])))

在确定因子变量后,第二个sapply计算每个变量的每个级别/类别所占记录的百分比。然后它确定了超过99.5%或低于0.5%发生率的级别数(我的任意阈值)。
然后它继续返回每个分类变量中有效级别的数量和每个级别的发生率。
跨越阈值的零级别变量不应被删除,而其他变量应从线性模型中删除。
最后的数据框使得查看结果更加容易。由于所有因子变量都是二项式的,因此这个数据框对于这个数据集进行了硬编码。这个数据框可以很容易地变得通用。

1
当一些值的列是整数,而其他值的列是数字时,我遇到了同样的问题。将所有数字更改为整数解决了这个问题(不知道是否会影响分析)。

这并没有真正回答问题。如果您有不同的问题,可以通过点击提问来提出。如果您想在此问题获得新的答案时收到通知,可以关注此问题。一旦您拥有足够的声望,您还可以添加悬赏以吸引更多关注。- 来自审核 - Vinícius Félix

1

1
从我十分钟前的经验来看,当有多个类别但有很多NAs时,可能会出现这种情况。以 Kaggle Houseprice Dataset为例,如果您加载数据并运行简单回归分析。
train.df = read.csv('train.csv')
lm1 = lm(SalePrice ~ ., data = train.df)

您会收到相同的错误。我还尝试测试每个因子的级别数量,但没有一个说它具有少于2个级别。
cols = colnames(train.df)
for (col in cols){
  if(is.factor(train.df[[col]])){
    cat(col, ' has ', length(levels(train.df[[col]])), '\n')
  }
}

经过很长时间的使用,我使用了 summary(train.df) 来查看每列的详细信息,并删除了一些内容,最终它终于起作用了:

train.df = subset(train.df, select=-c(Id, PoolQC,Fence, MiscFeature, Alley, Utilities))
lm1 = lm(SalePrice ~ ., data = train.df)

移除其中任何一个属性,回归分析将无法再次运行并显示相同错误(我已经亲自测试过)。

而且上述属性通常有 1400 多个空值和 10 个有用的值,因此您可能希望删除这些垃圾属性,即使它们有 3 或 4 个级别。我猜测编写一个函数来计算每列中有多少个空值会有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接