Преобразование оценки в оценку в R - PullRequest
1 голос
/ 01 апреля 2019

У меня очень простая проблема. Предположим, я поставил оценку 100 студентам так:

set.seed(1234)
Marks <- rnorm(100, 55, 10)

z <- runif(100)
Gender <- ifelse(z < 0.5, "M", "F")

#Creating Data frame
Df <- data.frame(SNo = 1:100, Marks, Gender)
head(Df)

Теперь мне нужно дать оценку студентам, но критерии оценки для мужчин и женщин различны. Критерии оценки:

Grade Criteria

Мне удалось решить эту проблему, но я не нашел свой метод очень привлекательным. Я пытался так:

#1 Method
Grade = ifelse(Df$Gender == "M", cut(Df$Marks, breaks = c(0, 35, 45, 55, 101), labels = FALSE), 
                        cut(Df$Marks, breaks = c(0, 40, 50, 60, 101), labels = FALSE)) 
Grade <- as.character(factor(Grade, labels = LETTERS[4:1]))

#2. Method
Gradef <- function(x, cp = c(35, 45, 55)) {
  ifelse(x < cp[1], "D", ifelse(x < cp[2], "C", ifelse(x < cp[3], "B", "A")))
}

Grade2 <- ifelse(Df$Gender == "M", Gradef(Df$Marks), Gradef(Df$Marks, c(40, 50, 60)))
sum(Grade == Grade2)  #both method give same grade

Df$Grade <- Grade

Может кто-нибудь предложить мне лучший метод для решения той же проблемы? Я не хочу использовать какой-либо внешний пакет в R.

Спасибо

Ответы [ 3 ]

1 голос
/ 01 апреля 2019

Учитывая ваше определение эффективности - меньше строк кода, я думаю, это то, что вы ищете, используя ваш метод 1, мы просто устраняем необходимость во втором бите:

Grade = ifelse(Df$Gender == "M", as.vector(cut(Df$Marks, breaks = c(0, 34, 45, 56, 101), labels = c("D", "C", "B", "A"))), 
           as.vector(cut(Df$Marks, breaks = c(0, 39, 50, 61, 101), labels = c("D", "C", "B", "A"))))

> head(Grade)
[1] "C" "B" "A" "D" "B" "A"

Итак, нужна одна строка кода.

Примечание. Вы можете сделать код более гибким, заменив каждый фрагмент кода, например,

labs <- c("D", "C", "B", "A")

И, поместив переменную labs в код, теперь вы можете просто изменить один бит вашего кода вверху, а затем повторно использовать свои функции для различных систем оценивания и т. Д. *

Используемый код:

set.seed(1234)
Marks <- rnorm(100, 55, 10)
z <- runif(100)
Gender <- ifelse(z < 0.5, "M", "F")
Df <- data.frame(SNo = 1:100, Marks, Gender)
1 голос
/ 01 апреля 2019

Использование Cut с меткой - это трюк, который я бы использовал, что-то очень похожее @ hector-haffenden выше. Этот более шаг за шагом, хотя.

set.seed(1234)
#Marks <- rnorm(100, 55, 10)

Marks <- 1:100  #for verification 


z <- runif(100)
Gender <- ifelse(z < 0.5, "M", "F")

#Creating Data frame
Df <- data.frame(SNo = 1:100, Marks, Gender)
head(Df)

cutsF<- cut(Df$Marks,breaks = c(0,35,45,55,100),labels = c('D','C','B','A') , right=F )
cutsM<- cut(Df$Marks,breaks = c(0,40,50,60,100),labels = c('D','C','B','A') , right=F )

Df$Grades= ifelse(Df$Gender=='F' , as.character(cutsF)  ,as.character(cutsM ) )

# For sake of Verification : 
Df$CutsF=cutsF
Df$cutsM= cutsM

head(Df ,20)

Редактировать: я отредактировал код и заменил include.lowest на right=False. Это закрывает группы слева и удовлетворяет условию менее 35. Тем не менее, это не будет работать для 55/60. Возможно, вам придется использовать 54 и 59 вместо этого.

1 голос
/ 01 апреля 2019
mylist = list(F = c(35, 45, 55), M = c(40, 50, 60))
grades = c("D", "C", "B", "A")
Df$Grade = grades[1 + sapply(1:NROW(Df), function(i)
    findInterval(Df$Marks[i], mylist[[Df$Gender[i]]]))]
head(Df, 10)
#   SNo    Marks Gender Grade
#1    1 42.92934      F     C
#2    2 57.77429      F     A
#3    3 65.84441      M     A
#4    4 31.54302      F     D
#5    5 59.29125      F     A
#6    6 60.06056      F     A
#7    7 49.25260      M     C
#8    8 49.53368      M     C
#9    9 49.35548      M     C
#10  10 46.09962      F     B
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...