Расчет с несколькими столбцами при условии наличия данных - PullRequest
3 голосов
/ 20 июня 2019

Я пытаюсь решить, как рассчитать взвешенную оценку для каждого класса каждый месяц.

В каждом классе учится несколько учеников, и вес (вклад) балла учащегося меняется во времени.

Для включения в расчет учащийся должен иметь как баллы, так и вес.

Я немного растерялся, и ни один из использованных мною подходов не сработал.

 Student Class Jan_18_score Feb_18_score Jan_18_Weight Feb_18_Weight
    Adam   1       3             2           150           153 
    Char   1       5             7           30            60
    Fred   1       -7            8           NA            80
    Greg   1       2             NA          80            40
    Ed     2       1             2           60            80
    Mick   2       NA            6           80            30
    Dave   3       5             NA          40            25
    Nick   3       8             8           12            45  
    Tim    3       -2            7           23            40 
    George 3       5             3           65            NA
    Tom    3       NA            8           78            50

Общая цель - рассчитать взвешенную оценку для каждого класса каждый месяц.

Взяв в качестве примера класс 1 (первые 4 строки) и взглянув на январь_18.

- Наблюдения за Адамом, Чаром и Грегом действительны, поскольку они имеют как баллы, так и веса.Их оценки и веса должны быть включены - у Фреда нет Jan_18_weight, поэтому и его Jan_18_score и Jan_18_weight исключаются из расчета.

Затем должны произойти следующие вычисления:

= [(3 * 150) + (5 * 30) + (2 * 80)] / [150 + 30 + 80] = 2,92307

Этот расчет будет повторяться для каждого класса и каждого месяца.

Выводится новый кадр данных, подобный следующему:

  Class Jan_18_Weight_Score Feb_18_Weight_Score 
    1            2.92307           etc
    2             etc              etc
    3             etc              etc

Есть много столбцов и много строк.

Любая помощь приветствуется.

Ответы [ 2 ]

2 голосов
/ 20 июня 2019

Вот способ с tidyverse. Основной трюк состоит в том, чтобы заменить NA на 0 в столбцах «весов», а затем использовать weighted.mean() на na.rm = T для игнорирования NA баллов. Для этого вы можете собрать оценки и веса в один столбец, а затем сгруппировать по Class и month_abb (вычисляемое поле для группировки), а затем использовать weighted.mean().

df %>%
  mutate_at(vars(ends_with("Weight")), ~replace_na(., 0)) %>% 
  gather(month, value, -Student, -Class) %>% 
  group_by(Class, month_abb = paste0(substr(month, 1, 3), "_Weight_Score")) %>%
  summarize(
    weight_score = weighted.mean(value[grepl("score", month)], value[grepl("Weight", month)], na.rm = T)
  ) %>% 
  ungroup() %>%
  spread(month_abb, weight_score)

# A tibble: 3 x 3
  Class Feb_Weight_Score Jan_Weight_Score
  <int>            <dbl>            <dbl>
1     1             4.66             2.92
2     2             3.09             1   
3     3             7.70             4.11

Данные -

df <- structure(list(Student = c("Adam", "Char", "Fred", "Greg", "Ed", 
"Mick", "Dave", "Nick", "Tim", "George", "Tom"), Class = c(1L, 
1L, 1L, 1L, 2L, 2L, 3L, 3L, 3L, 3L, 3L), Jan_18_score = c(3L, 
5L, -7L, 2L, 1L, NA, 5L, 8L, -2L, 5L, NA), Feb_18_score = c(2L, 
7L, 8L, NA, 2L, 6L, NA, 8L, 7L, 3L, 8L), Jan_18_Weight = c(150L, 
30L, NA, 80L, 60L, 80L, 40L, 12L, 23L, 65L, 78L), Feb_18_Weight = c(153L, 
60L, 80L, 40L, 80L, 30L, 25L, 45L, 40L, NA, 50L)), class = "data.frame", row.names = c(NA, 
-11L))
1 голос
/ 20 июня 2019

Может быть, это можно решить гораздо лучшим способом, но здесь есть один вариант Base R, в котором мы выполняем агрегацию дважды, а затем объединяем результаты.

#Separate score and weight columns
score_cols <- grep("score$", names(df))
weight_cols <- grep("Weight$", names(df))

#Replace NA's in corresponding score and weight columns to 0
inds <- is.na(df[score_cols]) | is.na(df[weight_cols]) 
df[score_cols][inds] <- 0
df[weight_cols][inds] <- 0


#Find sum of weight columns for each class
df1 <- aggregate(.~Class, cbind(df["Class"], df[weight_cols]), sum)

#find sum of multiplication of score and weight columns for each class
df2 <- aggregate(.~Class, cbind(df["Class"], df[score_cols] * df[weight_cols]), sum)

#Get the ratio between two dataframes.
cbind(df1[1], df2[-1]/df1[-1])

#  Class Jan_18_score Feb_18_score
#1     1         2.92         4.66
#2     2         1.00         3.09
#3     3         4.11         7.70
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...