From 71d13ae1d4a1e0b5a23345c08bc529b2cef79a2a Mon Sep 17 00:00:00 2001 From: avisionh Date: Fri, 1 Jan 2021 23:22:19 +0000 Subject: [PATCH 1/4] fix: Pass in list of double and character This is so function works. Also, remove legacy code in main.R of iterative preference function because it will not work with the data shape. Update link to functions script to use new folder name. --- src/functions.R | 10 ++++++---- src/main.R | 15 ++++++--------- src/test/benchmark_large_data.R | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/functions.R b/src/functions.R index ab299a8..4ccc7d7 100644 --- a/src/functions.R +++ b/src/functions.R @@ -52,7 +52,8 @@ func_sample <- function(x, n, replacement) { # ARGUMENTS: # 1. 'x' | (tibble/dataframe) Data to feed in # 2. 'limits' | (tibble/dataframe) Maximum capacity of each session -func_iterative_preferences <- function(x, limits, with_replacement) { +# TODO: Pass in columns names instead of relying on indexing which is brittle +func_iterative_preferences <- function(x, limits, with_replacement = FALSE) { # get number of people n_people <- nrow(x) @@ -61,10 +62,10 @@ func_iterative_preferences <- function(x, limits, with_replacement) { # create dummy tibble for storing output matchings <- tibble(PersonRowId = rep(x = -1, times = n_people), - SessionPreferredColumnId = rep(x = "dummy", times = n_people)) + SessionPreferredColumnId = rep(x = -1, times = n_people)) # convert limits from vector to tibble - limits <- limits[,2] %>% as.tibble() + limits <- limits[,2] %>% as_tibble() # generate vector of people and random sample from it people <- seq(from = 1, to = n_people, by = 1) @@ -90,7 +91,8 @@ func_iterative_preferences <- function(x, limits, with_replacement) { if(limits[preferred_session,] > 0) { # assign person number to session number - matchings[i, ] <- c(rownames(x)[sample_people[i]], preferred_session) + person_id = as.double(rownames(x)[sample_people[i]]) + matchings[i, ] <- list(person_id, preferred_session) # remove a place from session that's been allocated limits[preferred_session, 1] <- limits[preferred_session, 1] - 1 diff --git a/src/main.R b/src/main.R index 3b755d1..9320991 100644 --- a/src/main.R +++ b/src/main.R @@ -11,7 +11,7 @@ library(dplyr) library(tibble) # Load custom functions -source('scripts/functions.R') +source('src/functions.R') # Set seed so we can replicate our results set.seed(1) @@ -26,11 +26,11 @@ utility_delegates <- utility_delegates %>% n_delegates <- ncol(utility_delegates) m_sessions <- nrow(utility_delegates) -utility_sessions <- matrix(data = rep(x = 0, times = n_delegates*m_sessions), - nrow = n_delegates, - ncol = m_sessions) +utility_sessions <- matrix(data = rep(x = 0, times = n_delegates*m_sessions), + nrow = n_delegates, + ncol = m_sessions) utility_sessions <- utility_sessions %>% - as.tibble() %>% + as_tibble() %>% rename(`College 1` = V1, `College 2` = V2, `College 3` = V3, @@ -45,9 +45,6 @@ results_galeshapley <- galeShapley.collegeAdmissions( collegeUtils = utility_sessions, slots = c(2, 1, 1, 2) ) -# Approach 2 - Iterative Preferences -results_iterativepreference <- func_iterative_preferences(x = utility_delegates, limits = c(2, 1, 1, 2), with_replacement = FALSE) - # convert matchings to dataframe -results_iterativepreference[[1]] <- results_iterativepreference[[1]] %>% as.data.frame() + diff --git a/src/test/benchmark_large_data.R b/src/test/benchmark_large_data.R index b56346b..c6a67c5 100644 --- a/src/test/benchmark_large_data.R +++ b/src/test/benchmark_large_data.R @@ -13,7 +13,7 @@ library(dplyr) library(tibble) # Load custom functions -source('scripts/functions.R') +source('src/functions.R') # Set seed so we can replicate our results set.seed(1) From 66c9f2dc2d371876e7fc8ba5281f6aca5d543178 Mon Sep 17 00:00:00 2001 From: avisionh Date: Fri, 1 Jan 2021 23:24:52 +0000 Subject: [PATCH 2/4] chore: Rename and move files This is so they are organised more appropriately. --- src/{main.R => galeshapley.R} | 0 src/{test/benchmark_large_data.R => iterative_preference.R} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{main.R => galeshapley.R} (100%) rename src/{test/benchmark_large_data.R => iterative_preference.R} (100%) diff --git a/src/main.R b/src/galeshapley.R similarity index 100% rename from src/main.R rename to src/galeshapley.R diff --git a/src/test/benchmark_large_data.R b/src/iterative_preference.R similarity index 100% rename from src/test/benchmark_large_data.R rename to src/iterative_preference.R From a5e8899482ada3a0159572dbe28ca960e24ebcef Mon Sep 17 00:00:00 2001 From: avisionh Date: Sat, 2 Jan 2021 00:20:13 +0000 Subject: [PATCH 3/4] docs: Update README on how to run method This is so others can easily know where to go to apply this method. --- README.md | 57 +++++++++++++++++--------------------- src/iterative_preference.R | 2 +- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 88c199a..64607ed 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,40 @@ # Preference Allocation [![Travis-CI Build Status](https://travis-ci.org/avisionh/Preference-Allocation.svg?branch=master)](https://travis-ci.org/avisionh/Preference-Allocation) -### Collaborators +# Overview +preferenceallocation explores methods for solving *preference allocation*/*one-sided matching* problems. -- [Avision Ho](https://github.com/avisionh) -- [Le Duong](https://github.com/ledu1993) +Consider that we have to assign *x* people to *y* sessions. For each of these *y* sessions, +and individual person will have a preference ordering, meaning that they strictly prefer some sessions over others. +The task is to allocate these *x* people to their *y* sessions, accounting for their preferences +in such a way that the total utility of all *x* people is maximised. -# Update -- A Shiny app is being built for this problem. -- The project management and code development of this stage will be captured on [Azure DevOps](https://azure.microsoft.com/en-gb/services/devops/). - - Compared to GitHub, this has superior project management capabilities. -- Link to this can be found on the [public project page, Preference Allocation](https://avisionh.visualstudio.com/Preference%20Allocation). +## Methodology +We will tackle this problem in two ways: +1. **Gale-Shapley Algorithm |** Implementation of Alvin Roth and Lloyd Shapley's algorithm that assigns delegates to sessions in random order by accounting for both their preferences and ensuring that no two matching pairs will mutually want to switch their matches. +1. **Iterative Preference |** Implementation of a method suggested by a work experience student, Fatma Hussain, this takes chooses delegates and assigns them their n-th most preferred session provided the session is available. -*** +## Usage +To see how to use the bespoke *iterative preference* method proposed in this repo, access and run the `src/iterative_preference.R` script. -# Background -The problem we will tackle in this repository is of *preference allocation*/*one-sided matching*. +To see how to use [matchingR](https://github.com/jtilly/matchingR)'s implementation of Gale-Shapley algorithm, access and run the `src/galeshapley.R` script. -## Task -Consider that we have to assign *x* people to *y* sessions. For each of these *y* sessions, -and individual person will have a preference ordering, meaning that they strictly prefer some sessions over others. +> Note: Your data needs to be in tidy data format for *iterative preference* whereas for the Gale-Shapley algorithm, it does not. -The task is to allocate these *x* people to their *y* sessions, accounting for their preferences -in such a way that the total utility of all *x* people is maximised. +WIP Shiny app is being developed so you can enter your data and apply the iterative preference method on it to get matchings. +- https://avisionh.shinyapps.io/preference20allocation/ + +Documentation of how each method works is available in these slides: +- https://avisionh.github.io/preferenceallocation/ -## Aim/Motivation -- [x] Write an algorithm that automates the matching/mapping of one set to another set given pre-defined constraints. -- [ ] Write effective functions, include error-trapping and -handling. -- [ ] Build a Shiny app that makes this algorithm accessible to non-programmers. -- [ ] Host the Shiny app on a public domain, [shinyapp.io](https://www.shinyapps.io/). -- [ ] Demonstrate a consistent R coding and Git workflow usage. -- [ ] Implement a CI/CD pipeline to robustly and continuously test whether the code works on different operating systems (OS) using [travis-ci](https://travis-ci.org/) and [Azure DevOps](https://azure.microsoft.com/en-gb/services/devops/). -- [ ] Adopt an Agile project management approach using [Azure DevOps](https://azure.microsoft.com/en-gb/services/devops/) to capture and efficiently manage feature requests. -- [ ] Conduct user-research to continuously improve the algorithm and Shiny app. +## Getting help +If you encounter a clear bug, please fill a minimal reproducible example on [Issues](https://github.com/avisionh/preferenceallocation/issues). For questions and other discussion, please use the [Discussion](https://github.com/avisionh/preferenceallocation/discussions) channel. + +*** # Case Study -This algorithm was used in the [GSS Conference 2018](https://gss.civilservice.gov.uk/events/gss-conference-2018/) to allocate a set of 400 conference delegates to a series of talks that were taking place at the same time. +This algorithm was used in the 2018 and 2019 versions of the [GSS Conference](https://gss.civilservice.gov.uk/) to allocate a set ofvconference delegates to a series of talks that were taking place at the same time. These talks were delivered by internal government and external private sector companies. @@ -44,11 +42,8 @@ In total, there were four sessions of five simultaneous talks. As such, this alg **Note:** The rooms in which the speakers delivered their presentations were not pre-allocated. Instead, the decision to place more popular talks (based on people's preferences) in larger rooms was based on plotting the distribution of preferences for the five simultaneous talks for all delegates. -# Methodology -We will tackle this problem in two ways: -1. **Gale-Shapley Algorithm |** Implementation of Alvin Roth and Lloyd Shapley's algorithm that assigns delegates to sessions in random order by accounting for both their preferences and ensuring that no two matching pairs will mutually want to switch their matches. -1. **Iterative Preference |** Implementation of a method suggested by a work experience student, Fatma Hussain, this takes chooses delegates and assigns them their n-th most preferred session provided the session is available. -*** +## EARL 2019 +This project was presented at [Enterprise Application of the R Language (EARL) Conference](https://www.mango-solutions.com/earl-speaker-highlights-from-the-mango-team/) in 2019. ## References - [The Stable Marriage Problem and School Choice](http://www.ams.org/publicoutreach/feature-column/fc-2015-03) diff --git a/src/iterative_preference.R b/src/iterative_preference.R index c6a67c5..8e7efa6 100644 --- a/src/iterative_preference.R +++ b/src/iterative_preference.R @@ -56,7 +56,7 @@ utility_delegates <- utility_delegates %>% room_sizes <- data.frame(Room = c("Room_01","Room_02","Room_03","Room_04"), Size = c(0.2 * n_delegates, 0.3 * n_delegates ,0.1 * n_delegates, 0.4 * n_delegates)) -# Run interative preferences timed +# Run iterative preferences timed start_time <- Sys.time() results_iterativepreference <- func_iterative_preferences(x = utility_delegates, limits = room_sizes, From 61a94d0af1eb6435438ae743c938e16a410d93d4 Mon Sep 17 00:00:00 2001 From: avisionh Date: Sat, 2 Jan 2021 01:59:31 +0000 Subject: [PATCH 4/4] docs: Add badges This is to make README more pretty. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64607ed..f5c1e7e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Preference Allocation -[![Travis-CI Build Status](https://travis-ci.org/avisionh/Preference-Allocation.svg?branch=master)](https://travis-ci.org/avisionh/Preference-Allocation) +[![Travis-CI Build Status](https://travis-ci.org/avisionh/Preference-Allocation.svg?branch=master)](https://travis-ci.org/avisionh/Preference-Allocation) [![CodeFactor](https://www.codefactor.io/repository/github/avisionh/preferenceallocation/badge)](https://www.codefactor.io/repository/github/avisionh/preferenceallocation) [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](https://opensource.org/licenses/MIT) # Overview preferenceallocation explores methods for solving *preference allocation*/*one-sided matching* problems.