将行分配/拆分为相等大小的组,每组最大阈值

时间:2019-02-18 19:35:00

标签: r grouping

我已经研究了一段时间了,我真的找不到能满足我需要的解决方案...

简单地说,我有一个带有两列的DF,比方说,'n'个不同的行(例如学生的姓名),和'm'个不同的主管的姓名。 “ n”可以大于或小于“ m”。

问题:

  1. 将“ n”名学生随机分配到主管的“ m”组中,以便每个主管接收其组中相同数量的学生(如果“ n”和“ m”不是倍数,则尽可能接近)

  2. 有些学生已经事先被分配给特定的主管。这就是说,有些组开始时是空的,有些已经分配了一些行。

  3. 每个组中每个组的行数限制相同,即round(n/m)

  4. 已经分配给一个组的行'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:我找不到与此特定问题相关的任何问题。如果有正确答案的人,请告诉我。

再次,谢谢。

2 个答案:

答案 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)

我认为不应为了避免循环而避免循环,在这种情况下,我认为这很好并且可以生成简单的代码,但是您可以通过更巧妙地使用数据结构来做得更好。