Назначить / разбить строки на группы одинакового размера до определенного порога для каждой группы - PullRequest
0 голосов
/ 18 февраля 2019

Я работал над этим некоторое время, и я не могу найти никакого решения, которое бы выполняло то, что мне нужно ...

Проще говоря, у меня есть 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, назначая индексы ранее назначенным учащимся ... но я всегда где-то застреваю и чувствую, как я с этим справляюсь, уж неэффективно.

Мне интересно, знает ли кто-нибудь конкретное решение, чтобы справиться с этим.Я также проверил пакет «split».Не смог найти ничего полезного для этой цели.

Заранее большое спасибо.Если вам нужны дальнейшие разъяснения, пожалуйста, просто спросите.

PD: Я не смог найти ни одного связанного вопроса с этой конкретной проблемой.Если есть правильный ответ, пожалуйста, дайте мне знать.

Еще раз спасибо заранее.

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Вот еще один подход, который, я думаю, будет несколько более эффективным, чем у 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)

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

0 голосов
/ 19 февраля 2019

Я думаю, это может сработать для того, что вы хотите сделать.myFun просто составляет список случайных «учеников», а выборка генерирует неравномерно взвешенный список «советников» с ~ 70% значениями NA.Затем цикл for заполняет NA с помощью советника, который имеет наименьшее значение при вызове таблицы.

Если кто-то может сделать это более подходящим для R способом без цикла for, мне было бы действительно интереснопосмотри.

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...