#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]))