Как создать систему показателей для фрейма данных в R?

#r #dataframe

#r #фрейм данных

Вопрос:

Я пытаюсь создать систему показателей для значений, относящихся к системе показателей (оба приведены ниже).

 values <- data.frame(A= c(-200,-78,-100,0,-30),
                     B= c(100,0,-101,-199,-300),
                     C= c(-400,400,500,-500,250),
                     D= c(NA,NA,-1000,-1000,-1000),
                     E= c(1000,1000,1,-1000,-2000))

scorecard <- data.frame(Names = c("A","B","C","D","E"), 
                        "Score5" = c(-100,-200,-300,-400,-500), 
                        "Score3" = c(-50,-100,-150,-200,-250), 
                        "Score1" = c(-25,-50,-75,-100,-125))

values
     A    B    C     D     E
1 -200  100 -400    NA  1000
2  -78    0  400    NA  1000
3 -100 -101  500 -1000     1
4    0 -199 -500 -1000 -1000
5  -30 -300  250 -1000 -2000

scorecard
  Names Score5 Score3 Score1
1     A   -100    -50    -25
2     B   -200   -100    -50
3     C   -300   -150    -75
4     D   -400   -200   -100
5     E   -500   -250   -125
  

Для моей системы показателей, если значение:

  • является < его соответствующим Score5, ему присваивается 5
  • является> его соответствующим Score5 И < Score3, но ближе к Score5, чем к Score3, он получает 5 баллов.
  • является ли> его соответствующим Score5 И < Score3, но ближе к Score3, чем к Score5, ему присваивается 3
  • является> его соответствующим Score3 И < Score1, но ближе к Score3, чем к Score1, он получает 3
  • является> его соответствующим Score3 И < Score1, но ближе к Score1, чем к Score3, он получает 1
  • все остальные значения получают 0

Желаемый результат:

желаемый результат

Я пробовал следующее — для чего требовался упакованный xts: install.packages («xts»), но я не совсем туда попал.

 pointsfunction <- function(value)  {
  points <- c()
  for(i in names) {
    index = which(colnames(value)==i)
    data_start <- which(!is.na(value))[1]
    points[1:(data_start -1)] <- NA
    for(a in (data_start):(length(value))) {
      if(value[a] < scorecard[index, 2]) {
        points[a] <- -5
      } else {
        points[a] <- 0
      }
    }
  }
  points <- reclass(points, value)
  return(points)
}

scorecardpoints <- as.data.frame(lapply(values, pointsfunction))
  

Я получил следующую ошибку:

Ошибка в if (value[a] < scorecard[index, 2]) { : аргумент имеет нулевую длину, вызывается из: FUN(X[[i]], …)

Есть идеи?

Комментарии:

1. В своем последнем вопросе вы были уверены, что хотите lapply найти решение. Вы ограничены в использовании пакетов? Решение dplyr / tidyr или data.table здесь было бы довольно простым…

2. Совсем нет. Я все еще изучаю R, и мне было комфортно с lapply, но я очень доволен любым рабочим решением.

3. Я думаю, вы хотите dplyr и case_when() заявление

Ответ №1:

Вот dplyr решение. Мы сводим к длинному формату, присоединяемся к системе показателей, выполняем сравнения и сводим результат обратно к широкому. Я добавил столбец ID, но вы можете удалить его в конце, если хотите.

 library(dplyr)
library(tidyr)

values %>%
  mutate(id = row_number()) %>%
  pivot_longer(-id, names_to = "Names") %>%
  left_join(scorecard) %>%
  mutate(
    result = case_when(
      value < (Score5   Score3) / 2 ~ 5,
      value < (Score3   Score1) / 2 ~ 3,
      value < Score1 ~ 1,
      is.na(value) ~ NA_real_,
      TRUE ~ 0
    )
  ) %>%
  pivot_wider(id_cols = id, names_from = Names, values_from = result)
# # A tibble: 5 x 6
#      id     A     B     C     D     E
#   <int> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1     1     5     0     5    NA     0
# 2     2     5     0     0    NA     0
# 3     3     5     3     0     5     0
# 4     4     0     5     5     5     5
# 5     5     0     5     0     5     5  
  

Комментарии:

1. Когда я использовал это с другими данными, я получил эту ошибку: «Ошибка объединения, по = «Запасы»: столбец 1 должен быть назван. Запустите rlang::last_error() , чтобы увидеть, где произошла ошибка. Кроме того: предупреждающее сообщение: значения не идентифицируются однозначно; выходные данные будут содержать список-cols. * Используется values_fn = list для подавления этого предупреждения. * Используется values_fn = length для определения места возникновения дубликатов * Используется values_fn = {summary_fun} для обобщения дубликатов Ошибка: 1 компоненты ... не использовались. Мы обнаружили эти проблемные аргументы: * names_to Вы неправильно указали аргумент?»

2. Я проверил свои введенные данные, и у каждого столбца есть имя.

3. Убедитесь, что вы создаете уникальный столбец идентификаторов. Убедитесь, что перед . pivot_longer Если вам нужна дополнительная помощь, пожалуйста, найдите подмножество данных, иллюстрирующих проблему, и опубликуйте его с помощью dput() .

Ответ №2:

Значения в вашем примере values объекта не совпадают со значениями data.frame , которые вы присваиваете значениям. Например. посмотрите на 5-е значение A .

Вы могли бы использовать базовый подход R, подобный этому:

 # Look up the scorecard values for a name from the scorecard data.frame
get_scorecard_values <- function(name, card) {
  as.numeric(card[card$Names == name, c(2,3,4)])
}

# translate scorecard values into breakpoints for scoring intervals     
get_breaks <- function(x){
  c((x[1] x[2])/2, (x[2] x[3])/2, x[3])
}

# the value to assign to each scoring interval
my_scores <- c(5,3,1,0)

# given a vector of values, assign a score value to each based on
# the interval that it falls into
get_scores <- function(x, intervals, scores) {
  scores[(findInterval(x, get_breaks(intervals))   1L)]
}

# go across the list of names of variables of the values object.
# for each name, get the values and corresponding scorecard values
# and calculate the score values.
sapply(
  names(values),
  function(val, values, card, scores) {
    get_scores(
      x = values[[val]],
      intervals = get_scorecard_values(name = val, card = card),
      scores = scores
    )
  }, 
  values = values,
  card = scorecard,
  scores = my_scores
)

     A B C  D E
[1,] 5 0 5 NA 0
[2,] 5 0 0 NA 0
[3,] 5 3 0  5 0
[4,] 0 5 5  5 5
[5,] 0 5 0  5 5
  

Ответ №3:

Я использовал фрейм данных с A5 = -30. Вот базовое решение R

 scoremat <- as.matrix(scorecard[, -1L])
dimnames(scoremat) <- list(scorecard$Names, names(scorecard)[-1L])

vscore <- function(x, nm, scoremat) {
  scores <- c("Score5" = 5, "Score3" = 3, "Score1" = 1)[dimnames(score_mat)[[2L]]]
  conds <- scoremat[rep(nm, length(x)), ]
  i <- as.integer(apply(abs(x - conds), 1L, which.min))
  unname(ifelse(x > conds[, "Score1"] , 0, scores[i]))
}

dscore <- function(df, scoremat) {
  as.data.frame(vapply(
    names(df), 
    function(nm, mat) vscore(df[[nm]], nm, mat), 
    numeric(nrow(df)), 
    scoremat
  ))
}
  

Вывод

 > dscore(values, scoremat)
  A B C  D E
1 5 0 5 NA 0
2 5 0 0 NA 0
3 5 3 0  5 0
4 0 5 5  5 5
5 1 5 0  5 5
  

Сначала мы создаем матрицу оценок следующим образом

 > scoremat
  Score5 Score3 Score1
A   -100    -50    -25
B   -200   -100    -50
C   -300   -150    -75
D   -400   -200   -100
E   -500   -250   -125
  

Обратите внимание, что ваша логика упрощается до

 for any x in, for example, column A
  if x > -25 (i.e. scoremat["A", "Score1"]) then
    return 0
  else 
   calculate distance = abs(x - values in row A of scoremat)
   return the score where the minimum distance is 
  

В принципе, так vscore и работает. Сначала сопоставьте оценки

 scores <- c("Score5" = 5, "Score3" = 3, "Score1" = 1)[dimnames(score_mat)[[2L]]]
  

Затем сопоставьте и повторите строку так, чтобы в conds матрице было столько же строк, сколько и длина вектора x .

 conds <- scoremat[rep(nm, length(x)), ]
  

Затем вычислите abs(x - conds) и получите, где минимум для каждой строки. Например,

 let x = values$A

abs (  x   -           conds      )     =         distance       which.min = i
     -200       -100    -50    -25            100    150    175              1
     -150       -100    -50    -25             50    100    125              1
     -100       -100    -50    -25              0     50     75              1
        0       -100    -50    -25            100     50     25              3
      -30       -100    -50    -25             70     20      5              3
              Score5 Score3 Score1         Score5 Score3 Score1
  

Используется as.integer для преобразования отсутствия совпадений (это происходит, когда в x есть значения NA) в NA значения.

 i <- as.integer(apply(abs(x - conds), 1L, which.min))
  

Наконец, верните результаты на основе логики, показанной выше

 unname(ifelse(x > conds[, "Score1"] , 0, scores[i]))