Home > Software engineering >  Sorting the values inside row in a data frame, by the order of its factor levels?
Sorting the values inside row in a data frame, by the order of its factor levels?

Time:03-20

So, I'm trying to create a tool for a tabletop roleplaying game using R Shiny, allowing players to automatically generate random ghosts. These stats are factors, with the order of "Supreme", "Good", "Moderate", "Poor", "Awful", and "Worst", in order. In order to create the ghosts, I need to take a vector of these factors 25 items long, randomize the order, then put it into a 5x5 data frame before sorting two rows from best to worst.

At the moment, the basic R code (Shiny stuff aside, since it's just complicating things and it's not the primary issue here) looks like this:

arcanaVector <- c(rep("Supreme", 3),
                  rep(c("Good", "Moderate", "Poor", "Awful"), each = 5),
                  rep("Worst", 2))
arcanaLevels <- c("Supreme", "Good", "Moderate", "Poor", "Awful", "Worst")
arcanaVector <- factor(arcanaVector, ordered = TRUE, levels = arcanaLevels)
shuffled_arcana <- sample(arcanaVector)
arcana_table <- as.data.frame(matrix(shuffled_arcana,
                                     nrow = 5, ncol = 5))
row.names(arcana_table) <- c("Presence", "Manner", "Expression", "Complexity", "Tradition")

This much of the code is working, and it's producing an output that looks sort of like this:

Row Names Col1 Col2 Col3 Col4 Col5
Presence Awful Good Poor Supreme Worst
Manner Good Awful Moderate Moderate Supreme
Expression Awful Poor Poor Awful Awful
Complexity Poor Good Worst Good Good
Tradition Supreme Moderate Moderate Moderate Poor

However, no matter what I do, I can't seem to get it to sort properly. I've tried order(), I've tried sort(), I've looked over all the code I could see on StackExchange and I couldn't find anything that helped. At best, it's done nothing; at worst, it's not only failed to sort, but stripped away the factor values so that it was just displaying the underlying numbers.

Once sorted on the Presence row, for instance, it should look something like this:

Row Names Col1 Col2 Col3 Col4 Col5
Presence Supreme Good Poor Awful Worst
Manner Good Awful Moderate Moderate Supreme
Expression Awful Poor Poor Awful Awful
Complexity Poor Good Worst Good Good
Tradition Supreme Moderate Moderate Moderate Poor

CodePudding user response:

In your code, the issue is happening at this line.

arcana_table <- as.data.frame(matrix(shuffled_arcana, nrow = 5, ncol = 5))

shuffled_arcana is a factored vector but you cannot have a factor-matrix so it changes the vector from factor to character and hence, sorting does not happen as desired.

Here's a way -

set.seed(2022)

arcanaVector <- c(rep("Supreme", 3),
                  rep(c("Good", "Moderate", "Poor", "Awful"), each = 5),
                  rep("Worst", 2))
arcanaLevels <- c("Supreme", "Good", "Moderate", "Poor", "Awful", "Worst")
shuffled_arcana <- sample(arcanaVector)
arcana_table <- matrix(shuffled_arcana,nrow = 5, ncol = 5)
row.names(arcana_table) <- c("Presence", "Manner", "Expression", "Complexity", "Tradition")

arcana_table <- apply(arcana_table, 1, function(x) sort(factor(x, arcanaLevels))) |>
  t() |>
  as.data.frame()

arcana_table

#                 V1       V2       V3       V4    V5
#Presence       Good     Good     Good     Good Awful
#Manner      Supreme Moderate     Poor    Awful Awful
#Expression  Supreme  Supreme Moderate Moderate  Poor
#Complexity Moderate Moderate     Poor     Poor Worst
#Tradition      Good     Poor    Awful    Awful Worst

If you want to change a specific row you may use -

arcana_table[1, ] <- as.character(sort(factor(arcana_table[1, ], arcanaLevels))) 

CodePudding user response:

If I understand you correctly, you want to sort the columns of the matrix by a chosen row. If so, I think you simply may do:

chosen <- 'Presence'
arcana_table[, order(factor(arcana_table[chosen, ], levels=arcanaLevels))]
#            [,1]       [,2]       [,3]       [,4]    [,5]      
# Presence   "Supreme"  "Moderate" "Poor"     "Poor"  "Poor"    
# Manner     "Awful"    "Worst"    "Good"     "Worst" "Awful"   
# Expression "Awful"    "Poor"     "Supreme"  "Poor"  "Good"    
# Complexity "Moderate" "Awful"    "Moderate" "Good"  "Good"    
# Tradition  "Moderate" "Supreme"  "Good"     "Awful" "Moderate"

chosen <- 'Manner'
arcana_table[, order(factor(arcana_table[chosen, ], levels=arcanaLevels))]
#            [,1]       [,2]       [,3]       [,4]    [,5]   
# Presence   "Poor"     "Supreme"  "Poor"     "Poor"  "Moderate"
# Manner     "Good"     "Awful"    "Awful"    "Worst" "Worst"   
# Expression "Supreme"  "Awful"    "Good"     "Poor"  "Poor"    
# Complexity "Moderate" "Moderate" "Good"     "Good"  "Awful"   
# Tradition  "Good"     "Moderate" "Moderate" "Awful" "Supreme" 

Note, that the order in a factor is given by the order how the levels are provided. ordered=TRUE argument actually further differentiates ordinal scale from nominal scale.


Data:

arcanaVector <- structure(c(1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 
4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L, 6L, 6L), .Label = c("Supreme", 
"Good", "Moderate", "Poor", "Awful", "Worst"), class = c("ordered", 
"factor"))

set.seed(42)  ## for sake of reproducibility

arcana_table <- matrix(sample(arcanaVector), nrow=5, ncol=5,
                       dimnames=list(c("Presence", "Manner", "Expression",
                                       "Complexity", "Tradition"), NULL)) 

arcanaLevels <- c("Supreme", "Good", "Moderate", "Poor", "Awful", "Worst")
  • Related