From 71d13ae1d4a1e0b5a23345c08bc529b2cef79a2a Mon Sep 17 00:00:00 2001 From: avisionh Date: Fri, 1 Jan 2021 23:22:19 +0000 Subject: [PATCH 1/8] 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/8] 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/8] 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 40a977dda0b4e7297f8ee7693773a21e4fec4e60 Mon Sep 17 00:00:00 2001 From: avisionh Date: Sat, 2 Jan 2021 01:39:39 +0000 Subject: [PATCH 4/8] fix: Update code to take new iterative preference function This is so it is adapted to tidier data format. --- .gitignore | 2 - docs/presentation.Rmd | 106 +++++++++++++++++++++++++++++------------- src/galeshapley.R | 2 +- 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index f86955f..e7d39db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ -packrat/lib*/ .Rproj.user .Rhistory report.html slides.html slides.md slides.Rpres - diff --git a/docs/presentation.Rmd b/docs/presentation.Rmd index 1391225..64341d4 100644 --- a/docs/presentation.Rmd +++ b/docs/presentation.Rmd @@ -17,6 +17,7 @@ knitr::opts_chunk$set(echo = FALSE) library(matchingR) library(readr) library(dplyr) +library(tidyr) library(tibble) library(DT) @@ -24,10 +25,31 @@ library(DT) set.seed(1) # Load data - note are moving up one directory -utility_delegates <- read_csv(file = "data/dummy_student_preferences.csv") +utility_delegates <- read_csv(file = "../data/dummy_student_preferences.csv") -# Import source code -source("scripts/main.R") +# Load functions +source("../src/functions.R") +``` + + +```{r data-prep, include=FALSE} +utility_delegates <- utility_delegates %>% + remove_rownames() %>% + column_to_rownames(var = "X1") + +# Set colleges to have no preferences +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 <- utility_sessions %>% + as_tibble() %>% + rename(`College 1` = V1, + `College 2` = V2, + `College 3` = V3, + `College 4` = V4) ``` ## Author's Note @@ -121,8 +143,7 @@ The **Iterative Preference algorithm** involves a number of rounds: - For instance, we can see that for *Person 1*, they strictly prefer *Session 2* over *Session 1*, $u_{d_1}(2) > u_{d_1}(1)$. ```{r Data: delegate preferences} -utility_delegates %>% - datatable() +datatable(data = utility_delegates) ``` *** @@ -132,8 +153,7 @@ utility_delegates %>% - We have given each session a preference of $0$ for each delegate. This is because sessions are indifferent over which delegates attend. ```{r Data: session preferences} -utility_sessions %>% - datatable() +datatable(data = utility_sessions) ``` *** @@ -145,15 +165,15 @@ utility_sessions %>% ## Analysis Approach 1 - Gale-Shapley {.smaller} In the code below, we will run the college admissions variant of the **Gale-Shapley algorithm**. -``` {r Gale-Shapley Analysis, echo = TRUE, eval = FALSE} -galeShapley.collegeAdmissions( +``` {r Gale-Shapley Analysis, echo = TRUE} +results_galeshapley <- galeShapley.collegeAdmissions( studentUtils = utility_delegates, collegeUtils = utility_sessions, slots = c(2, 1, 1, 2) ) ``` -From running the algorithm we have the following allocation of delegates to sessions. `r results_galeshapley$matched.students %>% t()`. +From running the algorithm we have the following allocation of delegates to sessions. `r t(results_galeshapley$matched.students)`.
For instance, `Person 1` and `Person 4` are allocated to `r paste0("Session ", results_galeshapley$matched.students[1,])` and `r paste0("Session ", results_galeshapley$matched.students[4,])` respectively. @@ -169,40 +189,53 @@ rownames(results_galeshapley$matched.students) <- names colnames(results_galeshapley$matched.students) <- "Session Allocated" # show allocation -results_galeshapley$matched.students %>% - t() +t(results_galeshapley$matched.students) ``` In the second output is the table of delegates' initial preferences to compare our allocations against. ```{r Gale-Shapley - delegate preferences} -# show deleagate preferences -utility_delegates %>% - datatable() +# show delegate preferences +datatable(data = utility_delegates) ``` ## Analysis Approach 2 - Iterative Preferences {.smaller} In the code below, we will run Fatma's suggested algorithm, **Iterative Preferences** against the initial preferences of delegates to check that our matchings are desirable. -``` {r Iterative Preference - implementation, echo = TRUE, eval = FALSE} -results_iterativepreference <- func_iterative_preferences( - x = utility_delegates, - limits = c(2, 1, 1, 2), - with_replacement = FALSE +Before we do so, we need to reshape our data into tidy data format. +```{r Iterative Preference - wrangle, include=FALSE} +utility_delegates <- utility_delegates %>% + rownames_to_column(var = "Session") %>% + pivot_longer(cols = -"Session", + names_to = "Person", + values_to = "Preference Score") %>% + pivot_wider(id_cols = "Person", + names_from = "Session", + values_from = "Preference Score") +room_sizes <- data.frame(Room = c("Room_01","Room_02","Room_03","Room_04"), + Size = c(2, 1 , 1, 2)) + +``` + + +``` {r Iterative Preference - implementation, echo = TRUE} +results_iterativepreference <- func_iterative_preferences(x = utility_delegates, + limits = room_sizes, + with_replacement = FALSE ) ``` -```{r (HIDDEN) Dataframe transformation, echo = FALSE, eval = TRUE} -# convert to viewable format for slides -results_iterativepreference[[1]] <- results_iterativepreference[[1]] %>% - magrittr::set_colnames(paste0("Person ", results_iterativepreference[[2]])) -rownames(results_iterativepreference[[1]]) <- c("Delegate", "Session Allocated") +```{r Iterative Preference - helper, include = FALSE} +matchings <- results_iterativepreference[[1]] +sampling <- results_iterativepreference[[2]] ``` -From running the algorithm we have the following allocation of delegates to sessions. `r results_iterativepreference[[1]][2, ]`. + + +From running the algorithm we have the following allocation of delegates to sessions. `r matchings$SessionPreferredColumnId`.
-For instance, `r results_iterativepreference[[1]] %>% select(1) %>% colnames()` and `r results_iterativepreference[[1]] %>% select(4) %>% colnames()` are allocated to `r paste0("Session ", results_iterativepreference[[1]][2, 1])` and `r paste0("Session ", results_iterativepreference[[1]][2, 4])` respectively. +For instance, `r pull(matchings[1,1])` and `r pull(matchings[4,1])` are allocated to `r paste0("Session ", pull(matchings[1,2]))` and `r paste0("Session ", pull(matchings[4,2]))` respectively.
## Algorithm's Output {.smaller} @@ -210,23 +243,32 @@ In the first output is the matched allocations from using the **Iterative Prefer ```{r Iterative Preference - allocation} # show allocation -results_iterativepreference[[1]][2, ] +matchings <- matchings %>% + rename("Session Allocated" = "SessionPreferredColumnId") %>% + mutate(PersonRowId = paste0('Person ', PersonRowId)) %>% + t() %>% + as_tibble() +colnames(matchings) <- matchings[1, ] +matchings <- matchings[-1, ] +datatable(data = matchings, + options = list(dom = 't'), + rownames = FALSE) ``` In the second output is the random order of delegates that our algorithm used. ```{r Iterative Preference - delegate sampling} # show delegate sampling -paste0("Person ", results_iterativepreference[[2]]) - +paste0("Person ", sampling) ``` In the third output is the table of delegates' initial preferences to compare our allocations against. ```{r Iterative Preference - delegate preferences} # show delegate preferences -utility_delegates %>% - datatable() +datatable(data = utility_delegates, + options = list(dom = 't'), + rownames = FALSE) ``` # Hope you enjoyed the talk! :) diff --git a/src/galeshapley.R b/src/galeshapley.R index 9320991..faf38ce 100644 --- a/src/galeshapley.R +++ b/src/galeshapley.R @@ -19,7 +19,7 @@ set.seed(1) # Load in student preferences dummy data utility_delegates <- read_csv(file = "data/dummy_student_preferences.csv") utility_delegates <- utility_delegates %>% - remove_rownames %>% + remove_rownames() %>% column_to_rownames(var = "X1") # Set colleges to have no preferences From 677eabd0bb1610ea2fcffdf3d38be716dd18fa89 Mon Sep 17 00:00:00 2001 From: avisionh Date: Sat, 2 Jan 2021 01:40:43 +0000 Subject: [PATCH 5/8] feat: Add new generated slides This is so they can be deployed to GitHub pages. --- docs/index.html | 5989 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5989 insertions(+) create mode 100644 docs/index.html diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..da801f2 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,5989 @@ + + + + Preference Allocation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+ +

+

9 June 2018

+
+
+ +

Author’s Note

+ +
    +
  • DISCLAIMER: The author is relatively new to the Gale-Shapley algorithm so would appreciate suggestions and tips.
  • +
  • The Gale-Shapley algorithm is unlikely to be the most optimal solution to the problem of allocating delegates with preferences to sessions. + +
      +
    • With additional time, the author wishes to explore an algorithm that maximises the group utility of delegates.
    • +
  • +
  • Credit to a Fatma Hussain, a work experience student who came up with the second algorithm, iterative preferences.
  • +
+ +

Preference Allocation

One-sided matching of delegates to sessions

+ +
    +
  • We have a n delegates who have preferences over m sessions they wish to go to.
  • +
  • Delegates can only go to one session at any one time.
  • +
  • Each session can only hold varying numbers of people.
  • +
+ +

Or more formally…

+ +

Defining the Problem

+ +
    +
  • Each delegate, \(d_i\) for \(i = 1, ..., n\), receives utility \(u_{d_i}(s_j)\) from being matched to session \(s_j\)
  • +
  • Each session, \(s_j\) for \(j = 1, ..., m\), receives utility \(u_{s_j}(d_i)\) = 0 from being matched to delegate \(d_i\). 1
  • +
+ +
    +
  • Each delegate, \(d_i\), has a well defined preference ordering over all sessions.
  • +
+ +
+For example, for delegate $d_i$ who has the choice of two sessions $s$ and $s'$, they prefer session $s$ over session $s'$ when $u_{d_i}(s) \ge u_{d_i}(s')$ +
+ +
    +
  • Each session, \(s_j\), can hold \(p_k\) delegates where \(k \in \{1, ..., m\}\).
  • +
+ +

+ +Note that \(i\), \(j\) and \(k\) belong to the set of natural numbers. + +

+ +

Approaches

+ +

To tackle this preference allocation problem, we will take two alternative approaches:

+ +
- Gale-Shapley algorithm
+- Iterative Preference algorithm
+ +

Background

Nobel Prize for Economics 2012

+ +
    +
  • The issue we will tackle in this project is popularised by the famous stable marriage problem.
  • +
  • At a high level, it asks the question of how a set of men and women should be matched whilst respecting each individual’s preferences.
  • +
  • Gale and Shapley first analysed this matching at an abstract, general level in 1962, and proved that for any equal number of men and women, it is always possible to solve the stable marriage problem and make all marriages stable.
  • +
  • The Nobel Prize in Economic Sciences 2012 was awared to Lloyd Shapely and Alvin Roth for extending this work in areas such as assigning new doctors to hospitals, students to colleges, and human organ transplants to recipients. 2
  • +
+ +

Key Assumptions

+ +

Key assumptions to our problem and the stable marriage problem are:

+ +
    +
  • Each man (delegate) and woman (session) has a well-defined preference ordering, meaning we know their preferences.
  • +
  • Each man (delegate) and woman (session) prefers being matched over being not matched.
  • +
+ +

Our problem differs from the stable marriage problem by:

+ +
    +
  • Sessions have no preferences over which delegates attend them. We thus have one-sided matching.
  • +
  • Stability here will be defined from the viewpoint of pareto efficiency. 3
  • +
+ +
    +
  • Each session can hold a varying number of people.
  • +
+ +

How it Works

Gale-Shapley algorithm

+ +

The Gale-Shapley algorithm for the college admissions problem involves a number of rounds:

+ +
    +
  1. Students sequentially make proposals to each off their most preferred available colleges.
  2. +
  3. A college can hold onto at most \(s\) proposals at a time, since they have \(s\) spaces for students.
  4. +
  5. A college with an open slot will provisionally accept any application it receives.
  6. +
  7. A college already holding \(s\) applications will reject any student application that it values less than the current applicants they have provisionally accepted.
  8. +
  9. A college already holding \(s\) applications will accept any student application that it values more than the current applicants they have provisionally accepted, and drop the least valued applicant they have currently provisionally accepted.
  10. +
  11. Process continues until all students are matched to colleges.
  12. +
+ +

How it Works

Iterative Preference algorithm

+ +

The Iterative Preference algorithm involves a number of rounds:

+ +
    +
  1. Pick a delegate at random and assign them to their most preferred session.
  2. +
  3. If this session is full, then assign them to their next most preferred session.
  4. +
  5. Repeat the previous step until this randomly selected delegate is allocated to a free session.
  6. +
  7. Pick another delegate at random and repeat the previous steps until they are allocated to a free session.
  8. +
  9. Process continues until all delegates have been assigned to a free session.
  10. +
+ +

Data

+ +
    +
  • For illustrative purposes, we will use dummy data to model preferences. The table below is of delegate preferences.

  • +
  • For instance, we can see that for Person 1, they strictly prefer Session 2 over Session 1, \(u_{d_1}(2) > u_{d_1}(1)\).

  • +
+ +
+ + +

+ +
    +
  • The session preferences table is the transpose of the delegate preferences table above.

  • +
  • We have given each session a preference of \(0\) for each delegate. This is because sessions are indifferent over which delegates attend.

  • +
+ +
+ + +

+ +
    +
  • Suppose that Session 1 and Session 4 can hold two delegates, whereas Session 2 and Session 3 can only hold one delegate.
  • +
  • This means that in total, all sessions can hold 6 delegates.
  • +
  • Given we have 6 delegates to allocate to sessions, we will not get any unmatched delegates to sessions.
  • +
+ +

Analysis Approach 1 - Gale-Shapley

+ +

In the code below, we will run the college admissions variant of the Gale-Shapley algorithm.

+ +
results_galeshapley <- galeShapley.collegeAdmissions(
+  studentUtils = utility_delegates,
+  collegeUtils = utility_sessions,
+  slots = c(2, 1, 1, 2)
+)
+ +

From running the algorithm we have the following allocation of delegates to sessions. 1, 4, 1, 2, 4, 3.

+ +
+For instance, `Person 1` and `Person 4` are allocated to Session 1 and Session 2 respectively. +
+ +

Algorithm’s Output

+ +

In the first output is the matched allocations from using the Gale-Shapley algorithm.

+ +
##                   Person 1 Person 2 Person 3 Person 4 Person 5 Person 6
+## Session Allocated        1        4        1        2        4        3
+ +

In the second output is the table of delegates’ initial preferences to compare our allocations against.

+ +
+ + +

Analysis Approach 2 - Iterative Preferences

+ +

In the code below, we will run Fatma’s suggested algorithm, Iterative Preferences against the initial preferences of delegates to check that our matchings are desirable.

+ +

Before we do so, we need to reshape our data into tidy data format.

+ +
results_iterativepreference <- func_iterative_preferences(x = utility_delegates, 
+                                                          limits = room_sizes, 
+                                                          with_replacement = FALSE
+)
+ +

From running the algorithm we have the following allocation of delegates to sessions. 3, 4, 2, 4, 1, 1.

+ +
+For instance, 3 and 4 are allocated to Session 3 and Session 4 respectively. +
+ +

Algorithm’s Output

+ +

In the first output is the matched allocations from using the Iterative Preferences algorithm.

+ +
+ + +

In the second output is the random order of delegates that our algorithm used.

+ +
## [1] "Person 3" "Person 2" "Person 6" "Person 4" "Person 1" "Person 5"
+ +

In the third output is the table of delegates’ initial preferences to compare our allocations against.

+ +
+ + +

Hope you enjoyed the talk! :)

+ + + + +
+ + + + + + + + + From a1cf108283cbfcada00791578ff3f9b4b6ae9390 Mon Sep 17 00:00:00 2001 From: avisionh Date: Sat, 2 Jan 2021 01:46:04 +0000 Subject: [PATCH 6/8] fix: Update report to use new folder structures This is so the code works. --- docs/report.Rmd | 50 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/docs/report.Rmd b/docs/report.Rmd index 1714773..1d71785 100644 --- a/docs/report.Rmd +++ b/docs/report.Rmd @@ -32,10 +32,30 @@ set.seed(1) options(scipen = 999) # Load data -utility_delegates <- read_csv(file = "data/dummy_student_preferences.csv") +utility_delegates <- read_csv(file = "../data/dummy_student_preferences.csv") -# Import source code -source("scripts/main.R") +# Load functions +source("../src/functions.R") +``` + +```{r data-prep, include=FALSE} +utility_delegates <- utility_delegates %>% + remove_rownames() %>% + column_to_rownames(var = "X1") + +# Set colleges to have no preferences +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 <- utility_sessions %>% + as_tibble() %>% + rename(`College 1` = V1, + `College 2` = V2, + `College 3` = V3, + `College 4` = V4) ``` # Introduction @@ -135,8 +155,7 @@ source("scripts/main.R") nce, we can see that for *Person 1*, they strictly prefer *Session 2* over *Session 1*, $u_{d_1}(2) > u_{d_1}(1)$. ```{r Data: Delegate Peferences} -utility_delegates %>% - datatable() +datatable(data = utility_delegates) ``` (@) We will also generate the preferences for sessions to delegates in the code below. Here, we have sessions as our columns and delegates as our rows. This table is the transpose of the delegate's one above. @@ -166,13 +185,15 @@ utility_sessions %>% # Section 4: Analysis (@) In the code below, we will run the college admissions variant of the **Gale-Shapley algorithm**. -``` {r Gale-Shapley Analysis, eval = FALSE} -galeShapley.collegeAdmissions(studentUtils = utility_delegates, - collegeUtils = utility_sessions, - slots = c(2, 1, 1, 2)) +``` {r Gale-Shapley Analysis, echo = TRUE} +results_galeshapley <- galeShapley.collegeAdmissions( + studentUtils = utility_delegates, + collegeUtils = utility_sessions, + slots = c(2, 1, 1, 2) +) ``` -(@) From running the algorithm we have the following allocation of delegates to sessions. `r results$matched.students %>% t()`. For instance, `Person 1` and `Person 4` are allocated to `r paste0("Session ", results$matched.students[1,])` and `r paste0("Session ", results$matched.students[4,])` respectively. +(@) From running the algorithm we have the following allocation of delegates to sessions. `r t(results_galeshapley$matched.students)`. For instance, `Person 1` and `Person 4` are allocated to `r paste0("Session ", results_galeshapley$matched.students[1,])` and `r paste0("Session ", results_galeshapley$matched.students[4,])` respectively. *** @@ -180,14 +201,13 @@ galeShapley.collegeAdmissions(studentUtils = utility_delegates, (@) In the code below, we will present the matched allocations generated by the college admissions variant of the **Gale-Shapley algorithm** we used against the initial preferences of delegates to check that our matchings are desirable, ```{r Compare Outputs with Preferences} -rownames(results$matched.students) <- names -colnames(results$matched.students) <- "Session Allocated" -results$matched.students %>% +rownames(results_galeshapley$matched.students) <- names +colnames(results_galeshapley$matched.students) <- "Session Allocated" +results_galeshapley$matched.students %>% t() %>% pander() -utility_delegates %>% - datatable() +datatable(data = utility_delegates) ``` From b34f0312e11135048fa9b6f9fc46ce5c8629d418 Mon Sep 17 00:00:00 2001 From: avisionh Date: Sat, 2 Jan 2021 01:55:40 +0000 Subject: [PATCH 7/8] chore: Change travis-ci for Shiny app build This is to check it builds correctly. Delete previous job to check generation of report. --- .travis.yml | 42 ++++++++++++++++++++---------------------- _build.sh | 3 --- _deploy.sh | 29 ----------------------------- 3 files changed, 20 insertions(+), 54 deletions(-) delete mode 100644 _build.sh delete mode 100644 _deploy.sh diff --git a/.travis.yml b/.travis.yml index d3eb51b..b05b7bd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,34 +1,32 @@ # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r language: r -sudo: required -cache: - directories: - - "$TRAVIS_BUILD_DIR/packrat/src" - - "$TRAVIS_BUILD_DIR/packrat/lib" - packages: true +r: + - release -git: - depth: 3 +sudo: required env: global: - secure: GITHUB_PAT -r_packages: - - matchingR - - readr - - dplyr - - tibble - - DT +cache: + directories: + - $HOME/.local/share/renv + - $TRAVIS_BUILD_DIR/renv/library + packages: true install: -- R -e "0" --args --bootstrap-packrat - -before_script: - - chmod +x ./_build.sh - - chmod +x ./_deploy.sh - + - Rscript -e "if (!requireNamespace('renv', quietly = TRUE)) install.packages('renv')" + - Rscript -e "renv::restore()" + script: - - ./_build.sh - - ./_deploy.sh \ No newline at end of file + - nohup R --slave --no-restore -e 'shiny::runApp(port = 3000)' & + +addons: + apt: + packages: + - libudunits2-dev + - libgdal-dev + - libgeos-dev + - libproj-dev \ No newline at end of file diff --git a/_build.sh b/_build.sh deleted file mode 100644 index 10b8674..0000000 --- a/_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env Rscript - -rmarkdown::render("index.Rmd") \ No newline at end of file diff --git a/_deploy.sh b/_deploy.sh deleted file mode 100644 index ecdbff8..0000000 --- a/_deploy.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# Configure your name and email if you have not done so -git config --global user.email ${EMAIL} -git config --global user.name ${USERNAME} - -# Ensure that the book will only be updated when the build is -# triggered from the master branch. -[ "${TRAVIS_BRANCH}" != "master" ] && exit 0 - -[ "${TRAVIS_PULL_REQUEST}" != "false" ] && exit 0 - -# Clone the repository to the book-output directory -git clone -b gh-pages \ - https://${GITHUB_PAT}@github.com/${TRAVIS_REPO_SLUG}.git \ - book-output - -# Copy locally built *.html files into -cp -r index.html book-output/ - -# Create .nojekyll file to prevent git from trying to build -# html pages with jekyll. -touch book-output/.nojekyll - -# Add the locally built files to a commit and push -cd book-output -git add . -f -git commit -m "chore: automatic build update" || true -git push origin gh-pages From 980d67aa1b9bde8c6d4286315903b9bcea9b3c97 Mon Sep 17 00:00:00 2001 From: avisionh Date: Sat, 2 Jan 2021 01:57:30 +0000 Subject: [PATCH 8/8] fix: Update app references This is so app works. --- inst/global.R | 2 +- {www => inst/www}/custom.css | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {www => inst/www}/custom.css (100%) diff --git a/inst/global.R b/inst/global.R index 062ba8e..bb4226f 100644 --- a/inst/global.R +++ b/inst/global.R @@ -26,7 +26,7 @@ library(stringr) library(ggplot2) # load external functions -source("scripts/functions.R") +source("../src/functions.R") # ----------------------------------------------------------------------- # diff --git a/www/custom.css b/inst/www/custom.css similarity index 100% rename from www/custom.css rename to inst/www/custom.css