Suppose I have the following lists:
brice_grades <- list(math = c(90, 64, 100), reading = c(55, 71, 50),
science = c(100, 100, 98))
talia_grades <- list(math = c(70, 64, 70), reading = c(100, 80, 50),
science = c(100, 92, 98))
annie_grades <- list(math = c(14, 64, 50), reading = c(90, 71, 99),
science = c(88, 70, 98))
I am trying to create a function, report_card, that I can run through the sapply function such that I return a matrix with average grades for each class, unless the grade is below a 70, in which case it says "fail" rather than the grade.
Here is what I mean:
brice_grades talia_grades annie_grades
math 84.7 fail fail
reading fail 76.7 86.7
science 99.3 96.7 85.3
I'm new to creating functions in R so I'm a little tripped up. I'm not sure if I should be using sapply given that all of my output is not the same class ("fail" is a string and the means are numeric obviously). Additionally, it gets a little weird for me writing this function using vectors contained within lists.
CodePudding user response:
If you're happy with a character matrix output, a double loop with sapply
should take care of it.
failfun <- function(x) {m <- mean(x); if(m < 70) "fail" else round(m,1)}
sapply(mget(c("brice_grades", "talia_grades", "annie_grades")), \(x) sapply(x, failfun))
# brice_grades talia_grades annie_grades
#math "84.7" "fail" "fail"
#reading "fail" "76.7" "86.7"
#science "99.3" "96.7" "85.3"
CodePudding user response:
Another option is to turn your lists into a data frame and use tidyverse
for the rest:
library(tidyverse)
# All into a data frame
df <-
mget(ls(pattern = "_grades")) |>
imap(~ .x |> as_tibble() |> mutate(person = .y)) |>
bind_rows()
# Inspired by thelatemail
failfun <- function(x) {
m <- mean(x)
if (m < 70) "fail" else as.character(round(m, 1))
}
# Do the calculations
df <-
df |>
group_by(person) |>
summarise(across(everything(), ~ failfun(.))) |>
pivot_longer(-person) |>
pivot_wider(person)
df
Output:
# A tibble: 3 × 4
person math reading science
<chr> <chr> <chr> <chr>
1 annie_grades fail 86.7 85.3
2 brice_grades 84.7 fail 99.3
3 talia_grades fail 76.7 96.7
CodePudding user response:
You can use transpose
from purrr
package
purrr::transpose(list(brice_grades , talia_grades , annie_grades))
|> sapply(\(x) sapply(x ,
\(x) if(mean(x) < 70) "fail" else round(mean(x) , 1))) -> avg
row.names(avg) <- c("brice_grades" , "talia_grades" , "annie_grades")
avg <- t(avg)
- output
brice_grades talia_grades annie_grades
math "84.7" "fail" "fail"
reading "fail" "76.7" "86.7"
science "99.3" "96.7" "85.3"