我已经研究了一段时间了,我真的找不到能满足我需要的解决方案...
简单地说,我有一个带有两列的DF,比方说,'n'个不同的行(例如学生的姓名),和'm'个不同的主管的姓名。 “ n”可以大于或小于“ m”。
问题:
将“ n”名学生随机分配到主管的“ m”组中,以便每个主管接收其组中相同数量的学生(如果“ n”和“ m”不是倍数,则尽可能接近)
有些学生已经事先被分配给特定的主管。这就是说,有些组开始时是空的,有些已经分配了一些行。
每个组中每个组的行数限制相同,即round(n/m)
。
已经分配给一个组的行'n'不能切换到新组。
到目前为止,我一直在尝试使用dplyr排序问题,使用不同的表,为每个观察值分配索引...但是我觉得我的代码对于这种类型的问题太过复杂了,所以我想知道如果有人知道更简单的解决方案。
出于视觉目的,我将保留数据框示例。当然,我正在处理具有不同类型信息的更大数据集。但是问题是完全一样的:
我有:
Names_stud (n) Supervisors (m)
Ralph SKINNER
Michael NA
Mitch NA
Julen NA
Richard CARAPAPEL
John NA
Ramon SKINNER
Laura McGONAGALL
Paul NA
Ivy NA
Lucas NA
Mathiew NA
我想拥有的东西
Names_students Supervisor
Ralph SKINNER
Michael CARAPAPEL
Mitch SKINNER
Julen McGONAGALL
Richard CARAPAPEL
John CARAPAPEL
Ramon SKINNER
Laura McGONAGALL
Paul McGONAGALL
Ivy SKINNER
Lucas McGONAGALL
Mathiew CARAPAPEL
如此:
table(DF$Supervisors)
McGONAGALL SKINNER CARAPAPEL
4 4 4
如果'n'不是'm'的倍数,则完全可以得到与此结果最接近的结果(例如4,3、3或4,4,3 ...)。
到目前为止,我已经使用dplyr进行了很多编码,为以前分配的学生分配了索引...但是我总是被卡在某个地方,我觉得我处理它的方式效率很低。
我想知道是否有人知道解决此问题的特定解决方案。 我还检查了“拆分”包。在那里找不到任何有用的东西。
非常感谢您。如果您需要进一步说明,请询问。
PD:我找不到与此特定问题相关的任何问题。如果有正确答案的人,请告诉我。
再次,谢谢。
答案 0 :(得分:0)
我认为这可能适合您想要做的事情。 myFun只是列出了一个随机的“学生”列表,并且样本生成了一个加权均衡的“顾问”列表,其NA值约为70%。然后for循环使用表调用中值最低的顾问程序填充NA。
如果任何人都可以在没有for循环的情况下以更适合R的方式执行此操作,我将非常有兴趣看到它。
myFun <- function(n = 5000) {
a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE))
paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE))
}
students <- myFun(50)
advisors <- sample(c("TA1", "TA2", "TA3", NA), size = 50, replace = TRUE, prob = c(0.1, 0.2, 0.1, 0.7))
datFrame <- data.frame(students, advisors)
for(i in 1:length(datFrame$advisors)){
ifelse(is.na(datFrame$advisors[i]),
datFrame$advisors[i] <- names(table(datFrame$advisors))[which.min(table(datFrame$advisors))],
datFrame$advisors[i] <- datFrame$advisors[i])
}
table(datFrame$advisors)
答案 1 :(得分:0)
这是我认为比MR更有效率的另一种方法,希望同样容易理解。
这是一个分配问题(双向匹配),但是约束非常简单,您无需使用较重的算法或专用的整数编程工具就可以逃脱。
这里的策略是为那些还没有主管的学生生成作业的“右侧”,然后将这些新作业与现有作业进行行绑定。
为此,我们创建了一个足够长的主管向量循环,然后从该向量的顶部开始丢弃已经有学生的主管,以确保最终的小组保持平衡。
set.seed(1)
n <- 10
m <- 3
# Initialise our students and supervisors
students <- sample(letters, n, replace = FALSE)
supers <- sample(LETTERS, m, replace = FALSE)
# Create your dataframe and randomly assign a few supers
df <- data.frame(student = students,
super = NA, stringsAsFactors = FALSE)
df[sample(1:n, 2), "super"] <- sample(supers, 2)
# Each supervisor must be assigned to [floor(n / m), ceiling(n / m)] students
# We can ensure this by cycling through supervisors...
to_assign <- rep(supers, ceiling(n / m))
# ... but only if we skip those that have already been assigned to a student
for (super in df[!is.na(df$super), "super"]) {
to_assign <- to_assign[-match(super, to_assign)]
}
new_assignments <- df[is.na(df$super), ]
new_assignments$super <- to_assign[1:nrow(new_assignments)]
result <- rbind(df[!is.na(df$super), ], new_assignments)
我认为不应为了避免循环而避免循环,在这种情况下,我认为这很好并且可以生成简单的代码,但是您可以通过更巧妙地使用数据结构来做得更好。