How do I increase or decrease an ordered factor's level? (Make a factor equal to the very next level)

1.1k views Asked by At

I have 2 lists of ordered factors A and B, both contain thousands of items and have the same ordinal scale of about 30 levels. I want to find out how many items in A are equal to or within one level above or below the item at the same location in B

If this scale was numeric I would convert the ordered factors to numeric values and then do something like the following:

table(A==B || A==(B+1) || A==(B-1))

However, of course, '+' is not meaningful for factors. So what do I do? I could write a giant nested if statement or I could also change my ordinal scale to numbers according to their level only so that I can convert my ordered factors to numeric... but these seem like roundabout (and lengthy) solutions to something I'd assume is easy: How do I increase or decrease an ordered factor's level?

If I have

X<-ordered("b",levels=c("a","b","c"))

How do I make X[1] equal "c" by incrementing its current level?

Sidenote: Of course, in the first example above I'd need to account for factors that are on the lower and upper end of the scale, but I think (hope) this is something easy enough to figure out once my question has been answered.

3

There are 3 answers

0
Frikster On BEST ANSWER

Figured out a way of doing it. Someone actually posted a very useful answer that helped me figure this out, but has suddenly and unhelpfully/sadly taken it down. I actually wanted to give him/her the credit.

For the example in my OP, if you do the following you get an ordered factor where X[1] becomes "c"

 ordered(sapply(X,function(i){levels(i)[which(levels(i) == X[1]) + 1][1]}),levels=c("a","b","c"))

You can then of course modify this code to alter by how many levels a particular element in an ordered factor is changed by and also modify this code to alter which element in an ordered factor is being changed.

0
AudioBubble On

You should be able to modify this example to get the output you want

set.seed(7)
df <- data.frame(A=sample(paste('A',seq(1:5),sep=''),5000,replace=TRUE),
                 B=sample(paste('A',seq(1:5),sep=''),5000,replace=TRUE))
table(df$A)
table(df$B)
fa=factor(levels(df$A))

ia=0
for (a in fa) {
  ia=ia+1
  cat('for factor',a,'\n')
  na = sum(df$A==a)
  nb = sum(df$B==a)
  cat('  df$A has',na,'\n')
  cat('  df$B has',nb,'\n')
  nbm1 = -1
  if (ia>1) {
    am1 <- fa[ia-1]
    nbm1 = sum(df$B==am1)
    cat('  df$B has',as.character(am1),', ',nbm1,'\n')
  }
  nbp1 = -1
  if (ia<length(fa)) {
    ap1 <- fa[ia+1]
    nbp1 = sum(df$B==ap1)
    cat('  df$B has',as.character(ap1),', ',nbp1,'\n')
  }
  if (na == nbm1) {
    cat('    df$A[a] has same number as df$B[a-1]\n')
  } else {
    cat('    df$A[a] does not have the same number as df$B[a-1]\n')
  }
  if (na == nbp1) {
    cat('    df$A[a] has same number as df$B[a+1]\n')
  } else {
    cat('    df$A[a] does not have the same number as df$B[a+1]\n')
  }
}
0
Wolfgang On

I came along a similar problem and wrote a function that allows you to shift the levels of an ordered factor up and downwards.

  fct_shift_ord <- function(x, increment = 1, cap = TRUE, .fun = `+`){
    x_nlevel <- nlevels(x)
    x_lables <- levels(x)

  # apply function .fun to the numeric of the ordered vector
  erg <-.fun(as.numeric(x), increment)

  # cap to 1 and x_nlevel if the increment was larger than the original range of the factor levels
  if (cap) {
    erg[erg<1] <- 1
    erg[erg>x_nlevel] <- x_nlevel
  }
  ordered(erg, levels = 1:x_nlevel, labels = x_lables)
  }

so should work

table(A==B || A==fct_shift_ord(B,1) || A==fct_shift_ord(B,-1))