Wie berechnet man „Wege zum Weißen Haus“ mit R?

12

Ich bin gerade auf diese großartige Analyse gestoßen, die sowohl interessant als auch optisch schön ist:

http://www.nytimes.com/interactive/2012/11/02/us/politics/paths-to-the-white-house.html

Ich bin gespannt, wie ein solcher "Pfadbaum" mit R erstellt werden kann. Welche Daten und Algorithmen braucht man, um einen solchen Pfadbaum zu erstellen?

Vielen Dank.

Tal Galili
quelle
Grob gesagt : Überprüfen Sie alle Kombinationen des Gewinners in jedem Zustand und fügen Sie die Ergebnisse in eine 9-dim-Binärhypertabelle ein, ordnen Sie sie basierend auf dem Informationsgewinn in einem Baum neu an und schneiden Sie die redundanten Zweige ab. 29
einfach Eh @mbq ?! ;-)
Wiedereinsetzung von Monica - G. Simpson
1
Ich denke, sie haben es tatsächlich ein wenig anders gemacht: Ordnen Sie die Zustände nach EV, und sehen Sie dann, was passiert, wenn jeder Kandidat gewinnt, und gehen Sie den Baum hinunter. Sie müssen also nicht generieren und dann abschneiden. 29
Peter Flom - Wiedereinsetzung von Monica

Antworten:

10

Es ist natürlich, eine rekursive Lösung zu verwenden.

Die Daten müssen aus einer Liste der im Spiel befindlichen Staaten, ihren Wahlstimmen und dem vermuteten Startvorteil für den linken ("blauen") Kandidaten bestehen. (Ein Wert von kommt der Reproduktion der NY Times-Grafik nahe.) Bei jedem Schritt werden die beiden Möglichkeiten (links gewinnt oder verliert) untersucht. der Vorteil wird aktualisiert; Wenn zu diesem Zeitpunkt das Ergebnis (Gewinn, Verlust oder Unentschieden) auf der Grundlage der verbleibenden Stimmen ermittelt werden kann, wird die Berechnung angehalten. Andernfalls wird es für die verbleibenden Status in der Liste rekursiv wiederholt. Somit:47

paths.compute <- function(start, options, states) {
  if (start > sum(options)) x <- list(Id="O", width=1)
  else if (start < -sum(options)) x <- list(Id="R", width=1)
  else if (length(options) == 0 && start == 0) x <- list(Id="*", width=1)
  else {
    l <- paths.compute(start+options[1], options[-1], states[-1])
    r <- paths.compute(start-options[1], options[-1], states[-1])
    x <- list(Id=states[1], L=l, R=r, width=l$width+r$width, node=TRUE)
  }
  class(x) <- "path"
  return(x)
}

states <- c("FL", "OH", "NC", "VA", "WI", "CO", "IA", "NV", "NH")
votes <- c(29, 18, 15, 13, 10, 9, 5, 6, 4)
p <- paths.compute(47, votes, states)

29=512

Bild

plot.pathwidthpaths.compute1/512

Die vertikalen Positionen der Knoten sind in einer geometrischen Reihe (mit gemeinsamem Verhältnis a) angeordnet, so dass der Abstand in den tieferen Teilen des Baums enger wird. Die Dicke der Zweige und die Größe der Blattsymbole werden ebenfalls nach Tiefe skaliert. (Dies führt zu Problemen mit den Kreissymbolen an den Blättern, da sich deren Seitenverhältnis je nach Situation ändert a. Ich habe mich nicht darum gekümmert, das zu beheben.)

paths.compute <- function(start, options, states) {
  if (start > sum(options)) x <- list(Id="O", width=1)
  else if (start < -sum(options)) x <- list(Id="R", width=1)
  else if (length(options) == 0 && start == 0) x <- list(Id="*", width=1)
  else {
    l <- paths.compute(start+options[1], options[-1], states[-1])
    r <- paths.compute(start-options[1], options[-1], states[-1])
    x <- list(Id=states[1], L=l, R=r, width=l$width+r$width, node=TRUE)
  }
  class(x) <- "path"
  return(x)
}

plot.path <- function(p, depth=0, x0=1/2, y0=1, u=0, v=1, a=.9, delta=0,
               x.offset=0.01, thickness=12, size.leaf=4, decay=0.15, ...) {
  #
  # Graphical symbols
  #
  cyan <- rgb(.25, .5, .8, .5); cyan.full <- rgb(.625, .75, .9, 1)
  magenta <- rgb(1, .7, .775, .5); magenta.full <- rgb(1, .7, .775, 1)
  gray <- rgb(.95, .9, .4, 1)
  #
  # Graphical elements: circles and connectors.
  #
  circle <- function(center, radius, n.points=60) {
    z <- (1:n.points) * 2 * pi / n.points
    t(rbind(cos(z), sin(z)) * radius + center)
  }
  connect <- function(x1, x2, veer=0.45, n=15, ...){
    x <- seq(x1[1], x1[2], length.out=5)
    y <- seq(x2[1], x2[2], length.out=5)
    y[2] = veer * y[3] + (1-veer) * y[2]
    y[4] = veer * y[3] + (1-veer) * y[4]
    s = spline(x, y, n)
    lines(s$x, s$y, ...)
  }
  #
  # Plot recursively:
  #
  scale <- exp(-decay * depth)
  if (is.null(p$node)) {
    if (p$Id=="O") {dx <- -y0; color <- cyan.full} 
    else if (p$Id=="R") {dx <- y0; color <- magenta.full}
    else {dx = 0; color <- gray}
    polygon(circle(c(x0 + dx*x.offset, y0), size.leaf*scale/100), col=color, border=NA)
    text(x0 + dx*x.offset, y0, p$Id, cex=size.leaf*scale)
  } else {  
    mid <- ((delta+p$L$width) * v + (delta+p$R$width) * u) / (p$L$width + p$R$width + 2*delta)
    connect(c(x0, (x0+u)/2), c(y0, y0 * a), lwd=thickness*scale, col=cyan, ...)
    connect(c(x0, (x0+v)/2), c(y0, y0 * a), lwd=thickness*scale, col=magenta,  ...)
    plot(p$L, depth=depth+1, x0=(x0+u)/2, y0=y0*a, u, mid, a, delta, x.offset, thickness, size.leaf, decay, ...)
    plot(p$R, depth=depth+1, x0=(x0+v)/2, y0=y0*a, mid, v, a, delta, x.offset, thickness, size.leaf, decay, ...)
  }
}

plot.grid <- function(p, y0=1, a=.9, col.text="Gray", col.line="White", ...) {
  #
  # Plot horizontal lines and identifiers.
  #
  if (!is.null(p$node)) {
    abline(h=y0, col=col.line, ...)
    text(0.025, y0*1.0125, p$Id, cex=y0, col=col.text, ...)
    plot.grid(p$L, y0=y0*a, a, col.text, col.line, ...)
    plot.grid(p$R, y0=y0*a, a, col.text, col.line, ...)
  }
}

states <- c("FL", "OH", "NC", "VA", "WI", "CO", "IA", "NV", "NH")
votes <- c(29, 18, 15, 13, 10, 9, 5, 6, 4)
p <- paths.compute(47, votes, states)

a <- 0.925
eps <- 1/26
y0 <- a^10; y1 <- 1.05

mai <- par("mai")
par(bg="White", mai=c(eps, eps, eps, eps))
plot(c(0,1), c(a^10, 1.05), type="n", xaxt="n", yaxt="n", xlab="", ylab="")
rect(-eps, y0 - eps * (y1 - y0), 1+eps, y1 + eps * (y1-y0), col="#f0f0f0", border=NA)
plot.grid(p, y0=1, a=a, col="White", col.text="#888888")
plot(p, a=a, delta=40, thickness=12, size.leaf=4, decay=0.2)
par(mai=mai)
whuber
quelle
2
Das ist eine ziemlich schöne Lösung. Und die Grafik ist beeindruckend. Es gibt auch ein partitionsPaket, das möglicherweise eine Struktur zum Auflisten der Möglichkeiten bereitgestellt hat.
DWin
Wow, Whuber, es gibt nicht genug Vs, um deine Antwort mit zu markieren!
Tal Galili