I have to make a grade calculator in r which can convert numerical grades into letter grades. Here is the code I came up with:
numGrades<-(c(66,02,99,59,82))
for(i in 1:length(numGrades)) {
if (numGrades[i]>=90){
print("A")
} else if (numGrades[i]>=80){
print("B")
} else if (numGrades[i]>=70){
print("C")
} else if (numGrades[i]>=60){
print("D")
} else {
print("F")}
}
I can't find a way to integrate the cat or print(c()) functions so that it prints on one line rather than getting:
[1] "D"`
[1] "F"`
[1] "A"
[1] "F"
[1] "B"
If anyone has any ideas it would be greatly appreciated!
I would simply use paste to join all elements of a 'graded' list. Hope this helps.
numGrades = graded = (c(66,02,99,59,82))
for(i in 1:length(numGrades)) {
if (numGrades[i]>=90){
graded[i] = "A"
} else if (numGrades[i]>=80){
graded[i] = "B"
} else if (numGrades[i]>=70){
graded[i] = "C"
} else if (numGrades[i]>=60){
graded[i] = "E"
} else {
graded[i] = "F"}
}
print(paste(graded))
This gives:
> print(paste(graded))
[1] "E" "F" "A" "F" "B"
why the cat is not working?
numGrades<-(c(66,02,99,59,82))
for(i in 1:length(numGrades)) {
if (numGrades[i]>=90){
cat("A ")
} else if (numGrades[i]>=80){
cat("B ")
} else if (numGrades[i]>=70){
cat("C ")
} else if (numGrades[i]>=60){
cat("D ")
} else {
cat("F ")}
}
With many tasks in R it’s better to do this using vectorised functions rather than loops. Here’s two ways of doing what you want, one using base R and the other dplyr::case_when. Note that cut returns a factor but you can always use as.character.
numGrades <- c(66,02,99,59,82)
letGrades <- cut(
numGrades,
breaks = c(-Inf, 6:9, Inf) * 10,
labels = LETTERS[c(6, 4:1)],
right = FALSE
)
letGrades
library(dplyr)
letGrades <- case_when(
numGrades >= 90 ~ "A",
numGrades >= 80 ~ "B",
numGrades >= 70 ~ "C",
numGrades >= 60 ~ "D",
TRUE ~ "F"
)
letGrades
Just for the record, there's no need to use a for loop, you can use a nested ifelse
> graded2 <- ifelse(numGrades>=90, "A",
ifelse(numGrades >= 80 & numGrades < 90, "B",
ifelse(numGrades >= 70 & numGrades < 80, "C",
ifelse(numGrades >= 60 & numGrades < 70, "E", "F"))))
> graded2
[1] "E" "F" "A" "F" "B"
Related
I'm student trying to learn R... and have spent hours trying to figure this out but have so far failed. maybe I'm going about it the wrong way, or don't know something basic.
I have data with student number, and module results - the results are in numeric form, and I want to change the result to the grade - A, B, C etc. I have managed to create a loop that will print the grade but can't figure out how to put it in the dataframe.
The dataset I have is quite big, so I have created some dummy data for the example below, the code runs, and doesn't give me any errors but it doesn't replace the number with the letter grade:
`Result <- c(50,67,89,77,65,66,70,73,69,80)
for (i in Result){
if (i < 16.67) {
print ("G+")
i <- "G+"
} else if (i < 26.67) {
print ("F+")
i <- "F+"
} else if (i < 36.67) {
print ("E+")
i <- "E+"
} else if (i < 40) {
print ("D-")
i <- "D+"
}else if (i < 43.33) {
print ("D")
i <- "D"
}else if (i < 46.67) {
print ("D+")
i <- "D+"
}else if (i < 50) {
print ("C-")
i <- "C-"
}else if (i < 53.33) {
print ("D")
i <- "D"
}else if (i < 56.67) {
print ("D+")
i <- "D+"
}else if (i < 60) {
print ("B-")
i <- "B-"
}else if (i < 63.33) {
print ("B")
i <- "B"
}else if (i < 66.67) {
print ("B+")
i <- "B+"
}else if (i < 70) {
print ("A-")
i <- "A-"
}else if (i < 73.33) {
print ("A")
i <- "A"
}else if (i < 100) {
print ("A+")
i <- "A+"
}
}
# result: [1] "D"
[1] "A-"
[1] "A+"
[1] "A+"
[1] "B+"
[1] "B+"
[1] "A"
[1] "A"
[1] "A-"
[1] "A+"` `
Any advice would be greatly appreciated.
many thanks,
El.
Put your example data in a data.frame:
df <- data.frame( result = c(50,67,89,77,65,66,70,73,69,80) )
Then use cut() to get the grades in a new column of that data.frame:
df$grade <- cut(df$result,
breaks = c(0, 16.67, 26.67, 36.67, 40, 43.33, 46.67, 50, 53.33, 56.67, 60, 63.33, 66.67, 70, 73.33, 100),
labels = c("G+", "F+", "E+", "D-", "D", "D+", "C-", "C", "C+", "B-", "B", "B+", "A-", "A", "A+"))
Print the result to check:
df
result grade
1 50 C-
2 67 A-
3 89 A+
4 77 A+
5 65 B+
6 66 B+
7 70 A-
8 73 A
9 69 A-
10 80 A+
Notice that (1) it's better to save the results into a data.frame than to simply print them, and (2) many things can be done better/quicker in R if you don't loop; instead use R's vectorized functions (like cut!).
I created this small example. I want to print some values, for example, B for NA values using the if else statement.
x = c(1,7,NA, 3, NA, NA)
for(i in 1: length(x)){
y = x[i]
if(y == 1){
print("A")
}
else if(y == 'NA'){
print("B")
}
else{
print("C")
}
}
I am getting an error message Error in if (y == 1) { : missing value where TRUE/FALSE needed Why can't I print B for NA values? How to use NA within the if else statement?
The issue is also that == with NA returns NA and not FALSE. Make sure to add a condition to check NA as well. Also, y == 'NA' should be replaced with is.na(y)
for(i in 1:length(x)){
y = x[i]
if(y == 1 & !is.na(y)){
print("A")
}
else if(is.na(y)){
print("B")
}
else{
print("C")
}
}
-output
[1] "A"
[1] "C"
[1] "B"
[1] "C"
[1] "B"
[1] "B"
Or this can be done in a vectorized way
c("C", "B", "A")[1 + is.na(x) + 2 *(x %in% 1)]
#[1] "A" "C" "B" "C" "B" "B"
To avoid repetition, ensure that the first block checks for NA:
x = c(1,7,NA, 3, NA, NA)
for(i in 1: length(x)){
y = x[i]
if(is.na(y)){
print("B")
}
else if(y == 1){
print("A")
}
else{
print("C")
}
}
[1] "A"
[1] "C"
[1] "B"
[1] "C"
[1] "B"
[1] "B"
You can use vectorized way using case_when or nested ifelse -
dplyr::case_when(is.na(x) ~ 'B',
x == 1 ~ 'A',
TRUE ~ 'C')
#[1] "A" "C" "B" "C" "B" "B"
could anyone help me while results are not getting displayed here below
col_name <- c("A", "B", "C", "D")
i <- 1
while (i < length(col_name)) {
col_name[i]
i = i+1
}
Expected output
"A"
"B"
"C"
It needs a print
while (i < length(col_name)) {
print(col_name[i])
i = i+1
}
#[1] "A"
#[1] "B"
#[1] "C"
If we need to store the output, initialize an object and update
out <- c()
i <- 1
while (i < length(col_name)) { out <- c(out, col_name[i]); i = i+1}
out
#[1] "A" "B" "C"
I'm trying to cut numbers into categories to create a new column. Basically, trying to create a letter grade ("A", "B", "C", "D", "F") from scores.
I have reproduced a similar data frame to the one I'm having trouble with in the following code.
df <- tibble(score = rnorm(20:100, n = 150))
The code I wrote to add the grade column looks like this:
df_with_grade <- df %>%
mutate(Grade = if (score >= 90) {
"A"
} else if (score >= 80){
"B"
} else if (score >= 70){
"C"
} else if (score >= 60){
"D"
} else {
"F"
}
)
The code executes with a warning:
Warning messages:
1: In if (score >= 90) { :
the condition has length > 1 and only the first element will be used
2: In if (score >= 80) { :
the condition has length > 1 and only the first element will be used
3: In if (score >= 70) { :
the condition has length > 1 and only the first element will be used
4: In if (score >= 60) { :
the condition has length > 1 and only the first element will be used
The result is, all scores are assigned an "F"
How about
cut(df$score,breaks=c(0,6:10)*10,labels=rev(LETTERS[c(1:4,6)]))
? rev(LETTERS[c(1:4,6)]) might be too clever and doesn't save that many characters over c("F","D","C","B","A") ...
as suggested in the comments you can use case_when:
df_with_grade <- df %>%
mutate(Grade = case_when(score >= 90 ~ "A",
score >= 80 ~ "B",
score >= 70 ~ "C",
score >= 60 ~ "D",
TRUE ~ "F"))
You cannot use ifelse, it only works with binary conditions. Use cut like below,
df$Grade = cut(df$score,
breaks=c(0,60,70,80,90,100),
label=c("F","D","C","B","A"),
include.lowest =TRUE)
Just to show you can use ifelse.
df_with_grade <- df %>%
mutate(Grade =
ifelse(score>= 90, "A",
ifelse(score>=80, "B",
ifelse(score>=70, "C",
ifelse(score>=60, "D",
"F"))))
)
I've created a vector "numGrades" of 100 random numbers to represent values within a grading system. I need to write a "for" loop that takes segments of numerical grades and returns a vector of corresponding letter grades i.e.: 90+ = "A", 80-89 = "B", 70-79 = "C", 60-69 = "D", 0-59 = "F". I want to be able to run numGrades to return the corresponding letter grade for example: numGrades = [72, 65, 93] returning = ["C", "D", "A"] with the loop handling vectors of any length. This is what I have tried so far individually. All of these loops have returned warnings:
set.seed(43)
numGrades <- sample(0:100, 100, replace=FALSE)
for (i in numGrades )
if(91 <= numGrades[i]) {
numGrades[i] <- "A"
} else if (80 <= numGrades[i] & numGrades[i] <= 90) {
numGrades[i] =="B"
} else if (70 <= numGrades[i] & numGrades[i] <= 79) {
numGrades[i] =="C"
} else if (60 <= numGrades[i] & numGrades[i] <= 69) {
numGrades[i] =="D"
} else if (0 <= numGrades[i] & numGrades[i] <= 59) {
numGrades[i] =="F"
}
It's saying:
Error in if (91 <= numGrades[i]) { :
missing value where TRUE/FALSE needed
New Edit (Returns for grades >= 91 only):
numGrades <- (0:100)
for (i in 1:length(numGrades ))
if(91 <= numGrades[i]) {
numGrades[i] <- "A"
} else if (80 <= numGrades[i] & numGrades[i] <= 90) {
numGrades[i] =="B"
} else if (70 <= numGrades[i] & numGrades[i] <= 79) {
numGrades[i] =="C"
} else if (60 <= numGrades[i] & numGrades[i] <= 69) {
numGrades[i] =="D"
} else if (0 <= numGrades[i] & numGrades[i] <= 59) {
numGrades[i] =="F"
}
Working Rough Draft
ltrGrades <- (0:100)
numGrades <- character(length(ltrGrades))
for (i in 1:length(ltrGrades ))
if(any(ltrGrades[i] == 91:100)) {
numGrades[i] <- "A"
} else if (any(ltrGrades[i] == 80:90)) {
numGrades[i] <- "B"
} else if (any(ltrGrades[i] == 70:79)) {
numGrades[i] <- "C"
} else if (any(ltrGrades[i] == 60:69)) {
numGrades[i] <- "D"
} else if (any(ltrGrades[i] == 0:59)) {
numGrades[i] <- "F"
}
There are some fundamental R syntax problems with your code.
(numGrades[i]) c(80:90))
(numGrades[i]) >=80 && <=89)
(numGrades[i]) ==80:89 )
Several alternatives, most inefficient:
(80 <= numGrades[i] & numGrades[i] < 90) # the most basic
(dplyr::between(numGrades[i], 80, 90)) # if dplyr is loaded
(data.table::between(numGrades[i], 80, 90)) # if data.table is available
(numGrades[i] %in% 80:89) # works only if all grades are perfectly integral
(any(numGrades[i] == 80:89)) # ditto
Why are they wrong?
(numGrades[i]) c(80:90)), because there is not operator
(numGrades[i]) >=80 && <=89), R does not infer them as you suggest, every time you do an (in)equality test you need to specify both the LHS and RHS for each one; similarly, unlikely many languages, R does not "chain" them, so (80 <= numGrades[i] <= 89) will not work
(numGrades[i]) ==80:89 ) is getting closer, but if/else statements require a single comparison; in this case, you are comparing one number with a sequence (range) of 10, so the reply from this is length 10. It must be length 1.
Bottom line, though, is that you do not need a loop.
# set.seed(43)
numGrades <- sample(0:100, 100, replace=FALSE)
cut(numGrades, c(-1, 60, 70, 80, 90, 101), labels=c("F","D","C","B","A"))
# [1] F A F D F F D F F C F F F F F B F A F F C A F F F F A F A F C C C B F
# [36] F F F B A F F F A B B C D F F F B D F F B D D B F F F F F F D F A F F
# [71] F F F F B F D F A F F F F F A B F F F C F F F D D C C F F F
# Levels: F D C B A
or if you don't like or understand what a factor is, then
as.character(cut(numGrades, c(-1, 60, 70, 80, 90, 101), labels=c("F","D","C","B","A")))
# [1] "F" "A" "F" "D" "F" "F" "D" "F" "F" "C" "F" "F" "F" "F" "F" "B" "F"
# [18] "A" "F" "F" "C" "A" "F" "F" "F" "F" "A" "F" "A" "F" "C" "C" "C" "B"
# [35] "F" "F" "F" "F" "B" "A" "F" "F" "F" "A" "B" "B" "C" "D" "F" "F" "F"
# [52] "B" "D" "F" "F" "B" "D" "D" "B" "F" "F" "F" "F" "F" "F" "D" "F" "A"
# [69] "F" "F" "F" "F" "F" "F" "B" "F" "D" "F" "A" "F" "F" "F" "F" "F" "A"
# [86] "B" "F" "F" "F" "C" "F" "F" "F" "D" "D" "C" "C" "F" "F" "F"
EDIT
I just noticed something else about your code.
When you start, numGrades is either numeric or integer. However, since it is a vector, the first time you assign a letter to one of its elements, the entire vector is converted to a character vector. The second pass through the for loop will try to compare a number with a string, which will not do a numeric comparison, try 8 < "75" for why this will fail.
As a workaround for this:
ltrGrades <- character(length(numGrades))
for (i in 1:length(numGrades ))
if(91 <= numGrades[i]) {
ltrGrades[i] <- "A"
} else if (80 <= numGrades[i] & numGrades[i] <= 90) {
ltrGrades[i] <- "B"
} else if (70 <= numGrades[i] & numGrades[i] <= 79) {
ltrGrades[i] <- "C"
} else if (60 <= numGrades[i] & numGrades[i] <= 69) {
ltrGrades[i] <- "D"
} else if (0 <= numGrades[i] & numGrades[i] <= 59) {
ltrGrades[i] <- "F"
}