Juli Paz
Juli Paz

Reputation: 1

SensoMineR panelperf error in contrasts , contrasts can only be applied to factors with 2 or more levels

I am trying to run the following code using the panelperf function from the SensoMineR package:

panelperf<-panelperf(data,formul="~Product+Panelist+rep+Product:Panelist+Product:rep+Panelist:rep", firstvar=4)

My data frame consists on: column 1: panelists, filled with names of panelists column 2: product: filled with the names of the products column 3: rep , the replicate of the product measured column 4 till the end: variables that were measured (light fruit, dark fruit, alcohol, sourness, etc) All my variables are dbl

But I get the following error when running the function: Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : contrasts can be applied only to factors with 2 or more levels In addition: Warning messages: 1: In xtfrm.data.frame(x) : cannot xtfrm data frames 2: In xtfrm.data.frame(x) : cannot xtfrm data frames 3: In xtfrm.data.frame(x) : cannot xtfrm data frames

sapply(lapply(data, unique), length)

   Product     Panelist          rep   Lightfruit    Darkfruit    Applepear       Citrus     DryFruit        Nutty   Vegetables       Earthy       Floral 
        13           12            2           87           83           72           67           76           69           76           67           66 
     Woddy          hgt     Chemical    Chocolate        Honey       Cheesy      Alcohol Overallaroma   Astringent         Sour          Hot    Viscocity 
        62           64           80           57           65           69           86           85           88           85           85           83 
     Sweet       Bitter 
        87           86 
So none of the variables has only 2 levels as the error suggests
I have been reading answers about this error, but either the answers dont apply to my case or, as a non very experienced R user, I do not follow what they suggest. 

I would appreciate your help a lot!
Thank you!

And let me know if you need more information!

Upvotes: 0

Views: 70

Answers (1)

akrun
akrun

Reputation: 887118

The code to check the factor levels can be

i1 <- sapply(data, \(x) is.factor(x) && nlevels(x) > 1)
i2 <- !sapply(data, is.factor)
data1 <- data[i1|i2]

Here is an example that shows the issue - with the data having nlevels for factor > 1, it works

library(SensoMineR)
data(chocolates)
res <- panelperf(sensochoc, firstvar = 5, formul = "~Product+Panelist+
    Session+Product:Panelist+Session:Product+Panelist:Session")
> str(res)
List of 4
 $ p.value    : num [1:14, 1:6] 8.85e-14 6.44e-08 1.75e-28 3.74e-40 1.18e-22 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:14] "CocoaA" "MilkA" "CocoaF" "MilkF" ...
  .. ..$ : chr [1:6] "Product         " "Panelist        " "Session         " "Product:Panelist" ...
 $ variability: num [1:14, 1:6] 0.139 0.103 0.397 0.532 0.267 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:14] "CocoaA" "MilkA" "CocoaF" "MilkF" ...
  .. ..$ : chr [1:6] "Product         " "Panelist        " "Session         " "Product:Panelist" ...
 $ res        : num [1:14, 1] 1.87 1.89 1.41 1.47 1.63 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:14] "CocoaA" "MilkA" "CocoaF" "MilkF" ...
  .. ..$ : chr "stdev residual"
 $ r2         : num [1:14, 1] 0.673 0.761 0.846 0.882 0.862 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:14] "CocoaA" "MilkA" "CocoaF" "MilkF" ...
  .. ..$ : chr "r2"
 > str(sensochoc)
    'data.frame':   348 obs. of  18 variables:
     $ Panelist   : Factor w/ 29 levels "1","2","3","4",..: 1 1 1 1 1 1 2 2 2 2 ...
     $ Session    : Factor w/ 2 levels "1","2": 1 1 1 1 1 1 1 1 1 1 ...
     $ Rank       : Factor w/ 6 levels "1","2","3","4",..: 1 6 3 5 2 4 1 4 3 5 ...
     $ Product    : Factor w/ 6 levels "choc1","choc2",..: 6 3 2 1 4 5 4 3 6 2 ...
     $ CocoaA     : int  7 6 8 7 8 7 6 4 5 5 ...
     $ MilkA      : int  6 7 6 8 5 5 1 2 1 2 ...
     $ CocoaF     : int  6 2 5 8 4 3 8 3 8 8 ...
     $ MilkF      : int  5 7 4 3 4 5 1 4 1 1 ...
     $ Caramel    : int  5 8 7 3 4 6 0 0 0 0 ...
     $ Vanilla    : int  3 4 4 2 4 2 0 0 0 0 ...
     $ Sweetness  : int  7 7 5 4 5 5 1 5 1 0 ...
     $ Acidity    : int  2 2 5 7 6 4 0 0 0 0 ...
     $ Bitterness : int  4 2 6 8 6 7 3 0 3 6 ...
     $ Astringency: int  5 3 6 6 4 4 0 0 0 0 ...
     $ Crunchy    : int  8 3 7 3 6 6 8 4 6 8 ...
     $ Melting    : int  3 8 5 2 3 6 5 8 2 2 ...
     $ Sticky     : int  4 6 4 3 7 4 0 3 1 4 ...
     $ Granular   : int  3 5 3 5 3 7 0 1 1 0 ...

If we change the 'Session' level 2 to NA (which have only 2 levels), it shows the error

levels(sensochoc$Session)[2] <- NA

res1 <- panelperf(sensochoc, firstvar = 5, formul = "~Product+Panelist+
        Session+Product:Panelist+Session:Product+Panelist:Session")
 Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) : 
   contrasts can be applied only to factors with 2 or more levels   

With OP's code, it still shows the data to be having more than 1 level because unique returns NA as well if present and thus the length will include the NA element, here it is 2 in total

> sapply(lapply(sensochoc, unique), length)
   Panelist     Session        Rank     Product      CocoaA       MilkA      CocoaF       MilkF     Caramel     Vanilla   Sweetness     Acidity 
         29           2           6           6          11          11          11          11          11          10          11          11 
 Bitterness Astringency     Crunchy     Melting      Sticky    Granular 
         11          11          11          11          11          11 

where as with the specific code in this post, nlevels remove the NA an return only the count of non-NA levels

i1 <- sapply(sensochoc, \(x) is.factor(x) && nlevels(x) > 1)
i2 <- !sapply(sensochoc, is.factor)
names(sensochoc)[i1]
[1] "Panelist" "Rank"     "Product" 
names(sensochoc)[sapply(sensochoc, is.factor)]
[1] "Panelist" "Session"  "Rank"     "Product" 

Session is omitted. We may need to change the formula to omit the terms that have Session

Upvotes: 0

Related Questions