Detecting whether strings arise in a specific order

Viewed 576

I'd like to count the number of times my students could state 5 specific words AND subset/filter for the students' responses in which the words were in the correct order. The correct order= green, yellow, orange, red, black. All the data is in lower case and has no punctuation:

#     Student responses
Id    Data$Colors
1     green yellow orange red black
2     yellow green orange red black
3     red violet pink black
4     purple green orange red black
5     blue pink yellow scarlet   

The output I'm aiming for is:

#   Student responses
Id  Data$Colors                                Data$Count   Data$CorrOrder
1   green yellow orange red black              5            TRUE
2   yellow green orange red blacks             4            FALSE
3   red violet pink black                      2            TRUE
4   purple green orange red black              4            TRUE
5   blue pink yellow brown                     1            NA
6   green yellow orange red very red black     4*           TRUE

-1 point for repetition. I've been able to get the count column by doing this

Data <- c("\\bgreen\\b", "\\byellow\\b", "\\borange\\b", "\\bred\\b", "\\bblack\\b")

Data$Count<- str_count(Data$Colors, paste(Data, collapse = '|'))

However, this doesn't subtract for repeated correct colors like Id 6.

Anyone know how I could generate Data$CorrOrder?

2 Answers

Using tidyverse we can get Count and CorrOrder separately. To get Count we first split Colors on space and create separate_rows for each color so that it is easy to compare values. We then count how many of unique Colors are present in each Id out of the total all_colors and since we want to give -1 for each repeated value we subtract it by number of duplicated value in each group giving us the total score.

all_colors <- c("green", "yellow", "orange", "red", "black")
library(tidyverse)

df1 <- df %>%
      left_join(df %>%
                 separate_rows(Colors, sep = "\\s+") %>%
                 group_by(Id) %>%
                 summarise(count = max(sum(all_colors %in% unique(Colors)) - 
                                   sum(duplicated(Colors)), 0)))

To get correct order we again separate colors into different rows keep only the colors from all_colors and remove duplicates and check if the order in which the color appears are always increasing and assign logical TRUE/FALSE value accordingly.

df1 %>%
   left_join(df1 %>%
              separate_rows(Colors, sep = "\\s+") %>%
              group_by(Id) %>%
              filter(Colors %in% all_colors & !duplicated(Colors)) %>%
              summarise(new = if (n() == 1) NA 
              else all(diff(na.omit(match(Colors, all_colors))) > 0)))

#  Id                                 Colors CorOrder Count
#1  1          green yellow orange red black     TRUE     5
#2  2         yellow green orange red blacks    FALSE     4
#3  3                  red violet pink black     TRUE     2
#4  4          purple green orange red black     TRUE     4
#5  5                 blue pink yellow brown       NA     1
#6  6 green yellow orange red very red black     TRUE     4

As a start, if you treat the values as an ordered factor, you can check if they are sorted, without sorting, using is.unsorted:

colorder <- c("green", "yellow", "orange", "red", "black")

spl <- lapply(strsplit(dat$Colors, "\\s+"), ordered, levels=colorder)
cnt <- sapply(spl, function(x) length(unique(na.omit(x))) - sum(tabulate(x) > 1) )
cnt
#[1] 5 4 2 4 1 4
out <- !sapply(spl, is.unsorted, na.rm=TRUE)
out[cnt == 1] <- NA
out
#[1]  TRUE FALSE  TRUE  TRUE    NA  TRUE
Related