From 721ac22a7c5bd22aa09d30de47fef6abdd853c51 Mon Sep 17 00:00:00 2001 From: Benjamin Mako Hill Date: Mon, 5 Sep 2011 22:35:58 -0400 Subject: [PATCH] committed initial version to git --- .gitignore | 1 + README | 53 ++++++++++++++++++++++++ ping_pong_ball_jar.R | 56 ++++++++++++++++++++++++++ ping_pong_ball_jar_simulation.R | 67 +++++++++++++++++++++++++++++++ simulation_unweighted_random.png | Bin 0 -> 7358 bytes simulation_weighted.png | Bin 0 -> 6818 bytes 6 files changed, 177 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 ping_pong_ball_jar.R create mode 100644 ping_pong_ball_jar_simulation.R create mode 100644 simulation_unweighted_random.png create mode 100644 simulation_weighted.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4e5f6c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..3f23564 --- /dev/null +++ b/README @@ -0,0 +1,53 @@ +The following R program was designed to (mostly) randomly select +students to call for questions in a class room environment. It was +created in Harvard Graduate School of Graduate Education's S-290 to +replace a physical ping-pong ball jar. + +It takes a list of options (e.g., students), selects one at random and +returns it, and then adjusts the weights so that the selected option +is relatively less likely to be selected in subsequent draws. Every +student can be called on at any time. + +It is particularly well suited to classes in which a reasonably small +number of students will be called on a small number of times. Over +even a small number of classes, a very balanced list is likely. + +The software was written by Benjamin Mako Hill and is +released into the public domain. + +Usage +-------- + +The first time you the program this you'll need to: + + Modify ping_pong_ball_jar.R in a text editor to include the list of + options or students that you want. There are comments in the file + which should help provide some direction. + + Save the modified version for later if you'll be doing this again. + +Each time you use it, you'll need to: + + Paste the contents of ping_pong_ball_jar.R into a running session of R. + +Every time you want to select an option, you'll need to: + + Type "get.ping.pong.ball()" and then within R. + + As a shortcut, you can usually just type once or twice to see + the last command if you're going to be running it repeatedly. + +Files +--------- + +ping_pong_ball_jar.R + + Contains the R necessary to run the program. You'll need to at least + modify the list of options. + +ping_pong_ball_jar_simulation.R +simulation_unweighted_random.png +simulation_weighted.png + + Code and output from simulations that show the number of questions + asked to each participant in a large number of simulated classes. diff --git a/ping_pong_ball_jar.R b/ping_pong_ball_jar.R new file mode 100644 index 0000000..f5fe990 --- /dev/null +++ b/ping_pong_ball_jar.R @@ -0,0 +1,56 @@ +# S-290 PingPong Ball +# +# The following R program was designed for use in cold calling in a +# class room environment. It takes a list of options, selects one at +# random and returns it, and then adjusts the weights so that the +# selected option is relatively less likely to be selected in the next +# draw. +# +# The software was written by Benjamin Mako Hill and +# is released into the public domain. + +# replace this line with a list of the names from which we want to sample +member.names <- c("Alonso", "Alejandro", "Andres", "Becky", + "Deborah", "Lauren", "Mako", "Nikhit", "North", + "Steve") + +# set the default weight: after being selected the likelihood of the +# selected value being chosen will be reduced to 1 over this value +pp.weight <- 2 + +# create a variable which we'll use to keep track of who has been selected +reset.weights <- function () { + w <- rep(1, length(member.names)) + names(w) <- member.names + assign("w", w, envir=.GlobalEnv) +} + +# only run this if you want to create new we +if (!exists("w")) reset.weights() + +get.ping.pong.ball <- function () { + # create the "jar" according to the weights + weighted.names <- c(sapply(member.names, + function (x) {rep(x, w[x])}), recursive=TRUE) + # select something out of it + selected <- sample(weighted.names, 1) + + # adjust the weights for the next run based on what was selected + w[!names(w) == selected] <- w[!names(w) == selected] * pp.weight + + # if we can reduce the weights by a lowest common denom, do it + if (all((w %% pp.weight) == 0)) w <- w / pp.weight + + # save the variable in the global namespace so we can return to it next time + assign("w", w, envir=.GlobalEnv) + + # clean up the output and return the value + names(selected) <- NULL + return(selected) +} + +# run this function to get a person selected +get.ping.pong.ball() + +# run the following function to reset the weights +reset.weights() diff --git a/ping_pong_ball_jar_simulation.R b/ping_pong_ball_jar_simulation.R new file mode 100644 index 0000000..b25ce20 --- /dev/null +++ b/ping_pong_ball_jar_simulation.R @@ -0,0 +1,67 @@ +## simluation +############################################################ + +pingpong <- get.ping.pong.ball +reset.weights() + +# create a non-weighted function to compare +pingpong.nonweighted <- function () { + return(sample(member.names, 1)) +} + +# function to run a simulation and generate 1000 classes +run.simulation <- function (pingpong.fun, questions=10) { + + d <- c() + # run the comand 1000 times + for (i in seq(1,1000)) { + + all.prev <- c() + reset.weights() + for (j in seq(1,questions)) { + selected <- pingpong.fun() + all.prev <- append(all.prev, selected) + } + + d <- append(d, table(all.prev)) + + missing.names <- names(w)[sapply(names(w), + function (x) {!x %in% names(table(all.prev))})] + d.tmp <- rep(0, length(missing.names)) + names(d.tmp) <- missing.names + + d <- append(d, d.tmp) + } + + total.nums <- sapply(names(w), + function (x) {sum(d[names(d) == x])}) + + cat("Number of total selections:\n") + print(total.nums) + cat(paste("SD:", sd(total.nums), "\n")) + + return(d) +} + +d.nw <- run.simulation(pingpong.nonweighted, 15) +d.w <- run.simulation(pingpong, 15) + +library(ggplot2) + +graph.sim.output <- function (d, label.text="") { + qplot(as.factor(names(table(d))), as.integer(table(d)), geom="bar") + + scale_x_discrete("Number of questions asked to participant") + + scale_y_continuous("Number of occurances") + + opts(title = paste(label.text, "- 1000 classes of 15 questions")) +} + +p.weighted <- graph.sim.output(d.w, "weighted") +p.nonweighted <- graph.sim.output(d.nw, "non-weighted") + +png("simulation_weighted.png") +print(p.weighted) +dev.off() + +png("simulation_unweighted_random.png") +p.nonweighted +dev.off() diff --git a/simulation_unweighted_random.png b/simulation_unweighted_random.png new file mode 100644 index 0000000000000000000000000000000000000000..f9b6e83ae2f4eb8ff9033af7d02c09e165ee076e GIT binary patch literal 7358 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 literal 0 HcmV?d00001 diff --git a/simulation_weighted.png b/simulation_weighted.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9cf6dd9a560c6436d471d2ef6ca21229ca82fa GIT binary patch literal 6818 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