以下是我使用的所有变量:
str(ad.train)
$ Date : Factor w/ 427 levels "2012-03-24","2012-03-29",..: 4 7 12 14 19 21 24 29 31 34 ...
$ Team : Factor w/ 18 levels "Adelaide","Brisbane Lions",..: 1 1 1 1 1 1 1 1 1 1 ...
$ Season : int 2012 2012 2012 2012 2012 2012 2012 2012 2012 2012 ...
$ Round : Factor w/ 28 levels "EF","GF","PF",..: 5 16 21 22 23 24 25 26 27 6 ...
$ Score : int 137 82 84 96 110 99 122 124 49 111 ...
$ Margin : int 69 18 -56 46 19 5 50 69 -26 29 ...
$ WinLoss : Factor w/ 2 levels "0","1": 2 2 1 2 2 2 2 2 1 2 ...
$ Opposition : Factor w/ 18 levels "Adelaide","Brisbane Lions",..: 8 18 10 9 13 16 7 3 4 6 ...
$ Venue : Factor w/ 19 levels "Adelaide Oval",..: 4 7 10 7 7 13 7 6 7 15 ...
$ Disposals : int 406 360 304 370 359 362 365 345 324 351 ...
$ Kicks : int 252 215 170 225 221 218 224 230 205 215 ...
$ Marks : int 109 102 52 41 95 78 93 110 69 85 ...
$ Handballs : int 154 145 134 145 138 144 141 115 119 136 ...
$ Goals : int 19 11 12 13 16 15 19 19 6 17 ...
$ Behinds : int 19 14 9 16 11 6 7 9 12 6 ...
$ Hitouts : int 42 41 34 47 45 70 48 54 46 34 ...
$ Tackles : int 73 53 51 76 65 63 65 67 77 58 ...
$ Rebound50s : int 28 34 23 24 32 48 39 31 34 29 ...
$ Inside50s : int 73 49 49 56 61 45 47 50 49 48 ...
$ Clearances : int 39 33 38 52 37 43 43 48 37 52 ...
$ Clangers : int 47 38 44 62 49 46 32 24 31 41 ...
$ FreesFor : int 15 14 15 18 17 15 19 14 18 20 ...
$ ContendedPossessions: int 152 141 149 192 138 164 148 151 160 155 ...
$ ContestedMarks : int 10 16 11 3 12 12 17 14 15 11 ...
$ MarksInside50 : int 16 13 10 8 12 9 14 13 6 12 ...
$ OnePercenters : int 42 54 30 58 24 56 32 53 50 57 ...
$ Bounces : int 1 6 4 4 1 7 11 14 0 4 ...
$ GoalAssists : int 15 6 9 10 9 12 13 14 5 14 ...
这是我想要拟合的glm:
ad.glm.all <- glm(WinLoss ~ factor(Team) + Season + Round + Score + Margin + Opposition + Venue + Disposals + Kicks + Marks + Handballs + Goals + Behinds + Hitouts + Tackles + Rebound50s + Inside50s+ Clearances+ Clangers+ FreesFor + ContendedPossessions + ContestedMarks + MarksInside50 + OnePercenters + Bounces+GoalAssists,
data = ad.train, family = binomial(logit))
我知道这是一个很多变量(计划是通过向前变量选择来减少),但即使知道这是一个很多变量,他们要么是整型或因子;正如我所理解的,事情应该只与glm一起工作。然而,每次我试图符合这个模型时,我都会得到:
Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels
在我看来,由于某种原因,R没有把因子变量当作因子变量对待
即使是像这样简单的事情:
ad.glm.test <- glm(WinLoss ~ factor(Team), data = ad.train, family = binomial(logit))
是不是工作!(相同的错误消息)
其中:
ad.glm.test <- glm(WinLoss ~ Clearances, data = ad.train, family = binomial(logit))
一定行!
这是怎么回事?为什么我不能把这些因子变量拟合到我的glm中?
3条答案
按热度按时间jfewjypa1#
简介
但在现实中,这个简单的事实很容易被掩盖,因为实际用于模型拟合的数据可能与您传入的数据有很大不同。当您的数据中包含
NA
、您对数据进行了子集化、因子具有未使用的水平、或者你已经转换了你的变量并在某处得到了NaN
。You are rarely in this ideal situation where a single-level factor can be spotted fromstr(your_data_frame)
directly. StackOverflow上关于这个错误的许多问题是不可重现的,因此人们的建议可能会起作用,也可能不起作用。虽然现在已经有118 posts针对这个问题,但是用户还是找不到一个适应性的解决方案,以至于这个问题被一次又一次的提出,这个答案就是我的尝试,想一劳永逸的解决这个问题,或者至少提供一个合理的指导。这个答案信息量很大,我先做一个快速总结。
我为您定义了3个辅助函数:一米三米一,一米四米一,一米五米一。
我建议你用下面的方法来使用它们。
1.运行
NA_preproc
以获得更完整的用例;1.运行你的模型,如果你得到一个"对比错误",使用
debug_contr_error2
进行调试。大部分的答案都会一步一步地告诉你这些函数是如何定义的以及为什么定义的。跳过这些开发过程可能没有坏处,但是不要跳过"可复制的案例研究和讨论"中的章节。
修改答案
original answer在OP上运行得很好,并且已经成功地帮助了一些其他人。但是它在其他地方失败了,因为缺乏适应性。看看问题中
str(ad.train)
的输出。OP的变量是数字或因子;没有字符。最初的答案是针对这种情况的。如果你有字符变量,尽管它们在lm
和glm
拟合过程中会被强制为因子,但代码不会报告它们,因为它们没有作为因子提供,所以is.factor
会错过它们。在本扩展中,我将使最初的答案更具适应性。假设
dat
是传递给lm
或glm
的数据集,如果你没有这样一个数据框,也就是说,你所有的变量都分散在全局环境中,你需要将它们聚集到一个数据框中,下面的方法可能不是最好的,但它是有效的。如果你已经使用了
lm
或glm
的subset
参数,那么从一个显式的子集开始:如果您从
subset
automatically removes incomplete cases开始已经执行了步骤0,则可以跳过此步骤。数据框列通常是一个原子向量,其 * mode * 如下所示:"逻辑"、"数字"、"复杂"、"字符"、"原始"。对于回归,不同模式的变量被不同地处理。
一个逻辑变量是很棘手的,它可以被当作一个伪变量(
1
对应TRUE
;0
forFALSE
),因此是一个"数值",或者可以强制为两级因子。这完全取决于model.matrix
是否认为从模型公式的规范中必须强制为"至因子"。为简单起见,我们可以这样理解:它总是被强制为因子,但是应用对比的结果可能以相同的模型矩阵结束,就好像它被直接作为哑元处理一样。有些人可能想知道为什么不包括"integer"。因为整数向量,如
1:4
,有一个"numeric"模式(尝试mode(1:4)
)。数据框列也可以是具有"AsIs"类的矩阵,但此类矩阵必须具有"numeric"模式。
我们的检查是在以下情况下产生错误
并继续将"逻辑"和"字符"转换为"因子"类的"数字"。
请注意,如果数据框列已经是因子变量,则它不会包含在
ind1
中,因为因子变量具有"数值"模式(尝试mode(factor(letters[1:4]))
)。对于从第2步转换而来的因子变量,即那些由
ind1
索引的因子变量,我们不会有未使用的因子水平。然而,随dat
而来的因子变量可能有未使用的水平(通常是第0步和第1步的结果),我们需要删除其中任何可能未使用的水平。现在,我们可以看到
lm
或glm
实际使用了哪些因子水平以及使用了多少因子水平:为了简化您的工作,我将这些步骤封装到一个函数
debug_contr_error
中。dat
是通过data
参数传递给lm
或glm
的 Dataframe ;subset_vec
是通过subset
参数传递给lm
或glm
的索引向量。nlevels
(列表)给出所有因子变量的因子水平数;levels
(向量)给出所有因子变量的水平。如果没有完整的案例或没有因子变量要汇总,该函数将生成警告。
这是一个构造好的小例子。
很好,我们看到了一个错误。现在我的
debug_contr_error
暴露了f2
最终只有一个级别。注意,原始的简短答案在这里是没有希望的,因为
f2
是作为字符变量而不是因子变量提供的。现在我们来看一个矩阵变量为
x
的示例。请注意,没有水平的因子变量也会导致"对比错误"。您可能会想知道0水平因子是怎么可能的。
nlevels(factor(character(0)))
。如果您没有完整的案例,则在这里您将以0级因子结束。最后让我们看看
f2
是逻辑变量的情况。我们的调试器会预测一个"对比度错误",但它真的会发生吗?
否,至少此操作未失败(the
NA
coefficient is due to the rank-deficiency of the model; don't worry):我很难举出一个错误的例子,但也没有必要,实际上我们不用调试器来预测;当我们真的遇到错误时我们就用它在这种情况下,调试器可以定位出错的因子变量。
也许有些人会说逻辑变量和虚拟变量没有什么不同,但请尝试下面这个简单的例子:这取决于你的公式。
使用
lm
的"model.frame"
方法实现更灵活我们还建议您浏览一下R: how to debug "factor has new levels" error for linear model and prediction,它解释了
lm
和glm
在数据集上的作用。您会明白上面列出的步骤0到4只是试图模拟这样的内部过程。请记住,实际用于模型拟合的数据可能与您传递的数据有很大不同。我们的步骤与这种内部处理并不完全一致。为了进行比较,您可以通过在
lm
和glm
中使用method = "model.frame"
来检索内部处理的结果。在前面构造的小型示例dat
中尝试此操作,其中f2
是字符变量。实际上,
model.frame
只会执行步骤0和步骤1。它还会删除数据集中提供的变量,但不会删除模型公式中的变量。因此,模型框架中的行和列可能比您提供给lm
和glm
的少。在我们的步骤2中执行的类型强制是由后面的model.matrix
执行的,可能会产生"对比错误"。首先获取这个内部模型帧,然后将其传递给
debug_contr_error
(这样它实质上只执行步骤2到4)有几个优点。模型公式中的变量可以进行变换,
model.frame
记录的是变换后的变量而不是原来的变量,注意即使原来的变量没有NA
,变换后的变量也可以有。考虑到这些好处,我编写了另一个函数来 Package
model.frame
和debug_contr_error
。form
是您的模型公式;dat
是通过data
参数传递给lm
或glm
的数据集;subset_vec
是通过subset
参数传递给lm
或glm
的索引向量。mf
( Dataframe )给出模型帧(删除"terms"属性);nlevels
(列表)给出了所有因子变量的因子水平数;levels
(向量)给出所有因子变量的水平。尝试前面的
log
转换示例。也可以尝试
subset_vec
。每组的模型拟合和NA作为因子水平
您需要
1.通过分组变量分割 Dataframe (参见
?split.data.frame
);1.应用
debug_contr_error2
逐个处理这些 Dataframe (lapply
函数有助于完成此循环)。Some also told me that they can not use
na.omit
on their data, because it will end up too few rows to do anything sensible.这个可以放宽,实际上是NA_integer_
和NA_real_
需要省略,但是NA_character_
可以保留:只需添加NA
作为因子水平。要实现这一点,您需要循环数据框中的变量:x
已经是因子**,并且anyNA(x)
是TRUE
**,则执行x <- addNA(x)
。"and"很重要。如果x
没有NA
,则addNA(x)
将添加未使用的<NA>
水平。x
是字符,执行x <- factor(x, exclude = NULL)
将其强制为因子。exclude = NULL
将保留<NA>
作为级别。x
是"逻辑的"、"数字的"、"原始的"或"复杂的",则不应改变任何东西。NA
仅仅是NA
。droplevels
或na.omit
不会删除<NA>
因子水平,并且对于构建模型矩阵有效,请检查以下示例。将
NA
作为水平添加到因子/字符中后,您的数据集可能会突然拥有更多完整的事例。然后您可以运行模型。如果仍然出现"对比错误",请使用debug_contr_error2
查看发生了什么。为了方便起见,我为这个
NA
预处理编写了一个函数。dat
是您的完整数据集。可重现的案例研究和讨论
下面是专门为可重现的案例研究而选择的,因为我刚刚用这里创建的三个帮助函数回答了这些问题。
lm
还有一些其他StackOverflow用户解决的高质量线程:
此答案旨在调试模型拟合过程中的“对比错误”。但是,使用
predict
进行预测时也会出现此错误。predict.lm
或predict.glm
不会出现此行为,但某些包中的预测方法会出现此行为。以下是StackOverflow上的一些相关线程。还要注意,这个答案的原理是基于
lm
和glm
. These two functions are a coding standard for many model fitting routines的原理,但可能不是所有的模型拟合例程都有类似的行为。例如,下面的帮助函数是否真的有用,我看起来并不清楚。虽然有点离题,但知道有时“对比错误”仅仅来自编写错误的代码片段仍然是有用的。在下面的示例中,OP将其变量的名称而不是其值传递给
lm
。由于名称是单值字符,因此稍后将其强制为单级别因子并导致错误。contrasts<-
(*tmp*
, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels调试后如何解决此错误?
在实践中,人们想知道如何解决这个问题,无论是在统计层面还是在编程层面。
如果您在完整的数据集上拟合模型,那么可能没有统计解决方案,除非您可以插补缺失值或收集更多数据。因此,您可以简单地转向编码解决方案来删除有问题的变量。
debug_contr_error2
返回nlevels
,这有助于您轻松地定位它们。用向量1替换它们(如在How to do a GLM when "contrasts can be applied only to factors with 2 or more levels"?中所解释的),并且让lm
或glm
处理所得到的秩亏。如果您在子集上拟合模型,则可能存在统计解决方案。
按组拟合模型不一定需要按组拆分数据集并拟合独立模型。以下内容可能会让您有一个大致的概念:
如果您确实显式拆分数据,则很容易出现“对比错误”,因此必须按组调整模型公式(即,您需要动态生成模型公式)。更简单的解决方案是跳过为此组构建模型。
也可以将数据集随机划分为训练子集和测试子集,这样就可以进行交叉验证,R: how to debug "factor has new levels" error for linear model and prediction简要提到了这一点,最好进行分层抽样,以确保训练部分的模型估计和测试部分的预测都成功。
kg7wmglp2#
也许作为一个非常快的步骤,一个是验证你确实至少有两个因素。我发现的快速方法是:
zaq34kh63#
根据我十分钟前的经验,这种情况可能发生在有多个类别但有很多NA的情况下,以Kaggle Houseprice Dataset为例,如果加载数据并运行简单的回归,
你会得到同样的错误。我也试过测试每个因素的水平数,但没有一个说它少于2个水平。
所以过了很长一段时间,我使用
summary(train.df)
查看每个列的细节,并删除了一些,它终于工作:并删除其中任何一个,回归无法再次运行相同的错误(我已经自己测试过)。
另一种调试大量NA错误的方法是,用列的最常见属性替换每个NA。注意下面的方法cannotdebug,其中NA是列的模式,我建议手动删除这些列或替换这些列,而不是像这样应用一个函数来处理整个数据集:
而上述属性通常有1400+个NA和10个有用的值,所以你可能想删除这些垃圾属性,即使它们有3或4个级别。我想一个计算每列中有多少个NA的函数会有所帮助。