如何按R中的个人按数值对排名列表进行加权

f2uvfpb9  于 2023-03-15  发布在  其他
关注(0)|答案(6)|浏览(164)

在R中,我想根据人们的等级偏好以及他们的绩效来分配项目。假设我有5个项目和3个人。在这种情况下,这三个人都想要A计划,因为这是他们最喜欢的计划,但安娜应该得到,因为她的成绩最好。现在她出局了,詹姆斯和比利都在争夺B计划,而比利应该得到B计划,因为他It“我有一个更好的绩效指标。我怎么能在R中做到这一点呢?我将在现实中有更多的项目和人员。

Project Rank Person Performance 
A        1   Billy   95
B        2   Billy   95
C        3   Billy   95
D        4   Billy   95
E        5   Billy   95
A        1   Anna    97
B        2   Anna    97
C        3   Anna    97
D        5   Anna    97
E        4   Anna    97
A        1   James   92
B        2   James   92
C        4   James   92
D        3   James   92
E        5   James   92
vngu2lb8

vngu2lb81#

这是一个purrr::reduce的迭代解决方案,这种方法还可以显示每个人选择的项目的排名,例如James选择了排名第三的项目D

library(dplyr)
library(purrr)

df %>%
  arrange(desc(Performance), Rank) %>%
  split(.$Person) %>% # group_split(Person)
  reduce(~ bind_rows(.x, head(anti_join(.y, .x, by = 'Project'), n = 1)),
         .init = tibble(Project = character(0)))

# # A tibble: 3 × 4
#   Project  Rank Person Performance
#   <chr>   <int> <chr>        <int>
# 1 A           1 Anna            97
# 2 B           2 Billy           95
# 3 D           3 James           92

1.按PerformanceRank排列数据,使Performance高者优先选择项目。
1.按Person将数据框拆分为列表。

  • 从高Performance迭代到低:
  • 轮到Anna时,她有最高优先级选择她排名第一的项目A
  • Billy的回合中,他的第一个排名项目A已经被Anna选中,所以从他的项目列表中删除那个选项。Billy原来选择了他的第二个排名项目B
  • 轮到James时,他的前两个项目AB已被其他人选中,因此请从他的项目列表中删除这些选项。James只能选择他的第三个项目D
piwo6bdm

piwo6bdm2#

这里有一个for循环的方法。首先,根据性能分组(如果几个性能相同,则按人员分组)。第一个应该是性能最好的一个。
然后,在一个for循环中,迭代地选择具有最低等级的Project,并为其他个体移除该Project。

l <- split(df, list(-df$Performance, df$Person), drop = TRUE)
choice = setNames(character(length(l)), unique(df$Person[order(-df$Performance)]))
for(i in seq_along(l)){
  tmp <- l[[i]]
  choice[i] <- tmp$Project[which.min(tmp$Rank)]
  l <- lapply(l, \(x) subset(x, x$Project != choice[i]))
}  
choice
# Anna Billy James 
#  "A"   "B"   "D"
gt0wga4j

gt0wga4j3#

  • base* 中的方法可能如下所示:

1.创建独特的向量的人排序他们的表现。
1.按人员拆分项目,并按排名排序。
1.使用Reduce,从绩效最高的人员开始,将(剩余的)最高排名的项目分配给每个人员。

#Get Persons ordered by Performance
P <- which(!duplicated(DF$Person))
P <- DF$Person[P[order(DF$Performance[P], decreasing = TRUE)]]

#Split by person and order by Rank
. <- lapply(split(DF[c("Project", "Rank")], DF$Person), \(x) x[[1]][x[[2]]])

#Get highest ranked (remaining) project per Person
setNames(Reduce(\(x, y) {c(x, y[!y %in% x][1])}, .[P[-1]], .[[P[1]]][1]), P)
# Anna Billy James 
#  "A"   "B"   "D"

数据:

DF <- read.table(header=TRUE, text="Project Rank Person Performance 
A        1   Billy   95
B        2   Billy   95
C        3   Billy   95
D        4   Billy   95
E        5   Billy   95
A        1   Anna    97
B        2   Anna    97
C        3   Anna    97
D        5   Anna    97
E        4   Anna    97
A        1   James   92
B        2   James   92
C        4   James   92
D        3   James   92
E        5   James   92")

基准

library(dplyr)
library(purrr)
library(data.table)

bench::mark(check=FALSE,
Maël = {l <- DF %>% 
  mutate(perf_rank = dense_rank(-Performance)) %>% 
  group_split(perf_rank, Person)

choice = setNames(character(length(l)), unique(DF$Person[order(-DF$Performance)]))
for(i in seq_along(l)){
  tmp <- l[[i]]
  choice[i] <- tmp$Project[which.min(tmp$Rank)]
  l <- lapply(l, \(x) subset(x, x$Project != choice[i]))
}  
choice},
"Darren Tsai" = {DF %>%
  arrange(desc(Performance), Rank) %>%
  split(.$Person) %>% # group_split(Person)
  reduce(~ bind_rows(.x, head(anti_join(.y, .x, by = 'Project'), n = 1)),
         .init = tibble(Project = character(0)))},
IceCreamToucan = {df <- as.data.table(DF)
setkey(df, Person, Rank)
person <- df[order(-Performance), unique(Person)]
project <- Reduce(
  \(project, person) c(project, df[person, setdiff(Project, project)[1]]), 
  person, init = character())
df[data.table(Person = person, Project = project), on = .(Person, Project)]},
GKi = {P <- which(!duplicated(DF$Person))
P <- DF$Person[P[order(DF$Performance[P], decreasing = TRUE)]]
. <- lapply(split(DF[c("Project", "Rank")], DF$Person), \(x) x[[1]][x[[2]]])
setNames(Reduce(\(A, B) {c(A, B[!B %in% A][1])}, .[P[-1]], .[[P[1]]][1]), P)}
)

结果

expression          min   median itr/se…¹ mem_al…² gc/se…³ n_itr  n_gc total…⁴
  <bch:expr>     <bch:tm> <bch:tm>    <dbl> <bch:by>   <dbl> <int> <dbl> <bch:t>
1 Maël            13.84ms   14.4ms     69.1  53.22KB    14.8    28     6   405ms
2 Darren Tsai      6.52ms   6.74ms    148.    3.99KB    11.0    67     5   453ms
3 IceCreamToucan   3.64ms   3.78ms    263.  385.87KB    10.7   123     5   468ms
4 GKi             332.2µs  353.5µs   2772.   23.35KB    14.7  1323     7   477ms

在本例中,GKi1是最快的,比第二个IceCreamToucan快大约10倍。Darren Tsai分配的内存量最低。

k97glaaz

k97glaaz4#

使用数据.table

library(data.table)
setDT(df)

setkey(df, Person, Rank)
person <- df[order(-Performance), unique(Person)]
project <- Reduce(
  \(project, person) c(project, df[person, setdiff(Project, project)[1]]), 
  person, init = character())
df[data.table(Person = person, Project = project), on = .(Person, Project)]

#> Key: <Person, Rank>
#>    Project  Rank Person Performance
#>     <char> <int> <char>       <int>
#> 1:       A     1   Anna          97
#> 2:       B     2  Billy          95
#> 3:       D     3  James          92
yhqotfr8

yhqotfr85#

上面描述的问题是stable matching problem的一个特例。(至少)有两个R包可以解决这个问题。乍一看,matchingR的文档看起来更容易理解一些,作者声称已经使用它来解决大约30,000个匹配候选项的问题,所以对于这个应用程序来说性能应该是不错的。
还要注意,这些包中使用的算法也将处理工人具有不同项目偏好和/或绩效依赖于项目的一般情况。

rkkpypqq

rkkpypqq6#

经过一番搜索,clue::solve_LSAP似乎可以在这里使用。
首先,我们在人员和项目之间建立一个“得分”/“成本”矩阵;这里,我们可以使用一个分数集“performance * rank”(DF复制自GKi答案):

mat = xtabs(Performance * Rank ~ Person + Project, DF)
mat
#       Project
#Person    A   B   C   D   E
#  Anna   97 194 291 485 388
#  Billy  95 190 285 380 475
#  James  92 184 368 276 460

然后,我们将人员分配到项目中,以使总“成本”最小化:

library(clue)
x = solve_LSAP(mat, maximum = FALSE)
x
#Optimal assignment:
#1 => 1, 2 => 2, 3 => 4
data.frame(pers = rownames(mat), proj = colnames(mat)[x])
#   pers proj
#1  Anna    A
#2 Billy    B
#3 James    D

相关问题