R-Programmierung, zeilenweise Datenrahmenberechnung mit benutzerdefiniertem Skript (für jedes i) zur Lösung des „Brückenspiels“
Ich habe einen Datenrahmen, der "Brückenspiele" angibt (jede Zeile ist ein unabhängiges Spiel). Siehe ein minimales Beispiel mit 4 Spielen unten:
start <- list(c("10","15","5"), c("5") ,c("11","6"),c("6","11"))
end <- list(c("7","17","11"), c("10"), c("8","12"),c("8","12"))
ascending <- c("+","-","+","-")
position <- c(11,6,9,8)
desired_output <- c(5,5,"disqualified",3)
bridge_game <- data.frame(start = I(start), end = I(end), ascending = ascending, position = position, desired_output = desired_output)
bridge_game
Wie funktioniert das Bridge-Spiel? Kandidaten auf der ganzen Welt nehmen an einer Bridge-Game-Challenge teil und wir haben die Daten jedes Bridge-Spiels in einem Datenrahmen gesammelt. Jede Brücke besteht aus nummerierten Holztafeln (positive ganze Zahlen, die nicht unbedingt bei 1 beginnen müssen) und "Lücken" gebrochener Paneele. Der Kandidat kann wählen, von welcher Seite der Brücke er seinen Spaziergang beginnt (aufsteigend = die Nummerierung des Panels nimmt mit fortschreitendem Gang zu; absteigend = die Nummerierung des Panels nimmt mit fortschreitendem Gang ab).
Eine Grafik zum besseren Verständnis des Brückenspiels finden Sie hier (beispielhaft für die 1. Zeile im Datenrahmen): Klicken Sie hier
Für jedes Brückenspiel (= Zeile im Datenrahmen) haben wir folgende Informationen (= Spalten):
- bridge_game $ start : alle Startpositionen von Flächen ganzer Holztafeln (zufällige Reihenfolge)
- bridge_game $ end : alle Endpositionen von Flächen ganzer Holztafeln (zufällige Reihenfolge)
- bridge_game $ ascending : Gehen Sie in aufsteigender (+) oder absteigender (-) Reihenfolge der Felder über die Brücke
- bridge_game $ position : Der Kandidat landete auf dem angegebenen Feld
Was ist die Herausforderung? Ich muss ein Skript schreiben, das ich zeilenweise über den gesamten Datenrahmen ausführen kann, um die folgende Ausgabe zu erhalten:
- bridge_game $ wanted_output : Testet , ob der Kandidat in den Fluss gefallen ist (landete an einem kaputten Panel und ist "disqualifiziert"). Und wenn er nicht disqualifiziert wird, muss ich die Anzahl der gesamten Holztafeln berechnen, die vom Lauf des Kandidaten abgedeckt werden (zerbrochene Paneele zählen nicht).
Wichtig ist , dass es für die Arbeit eines beliebigen Anzahl i von ganzen Flächen von Holzplatten.
Um genauer zu sein, gebe ich eine schrittweise Anleitung, wie das angeforderte R-Skript unten funktionieren soll:
0) gelöst
a) Konvertieren Sie die Liste der Zeichen in eine numerische Liste für die Spalten bridge_game $ start und bridge_game $ end.
b) Berechnen Sie i (die Anzahl der Flächen ganzer Holzplatten; i geht von 1 bis i = max für jede Reihe) und sortieren Sie die Start- und Endpositionen, um die richtigen Start- und Endwerte für jedes i zu erhalten .
1) Testen Sie, ob sich die Position an einer defekten Platte befindet: Ende (i = 1 bis max-1)> Position> Start (i = 2 bis max) -> wenn WAHR für eines der getesteten Paare -> "disqualifiziert"
2) Wenn nein, testen Sie, in welchem Bereich ganzer Paneele die angegebene Position liegt ( i = n ): Start (i = 1 bis max) <= Position <= Ende (i = 1 bis max) -> wenn TRUE zurückgeben i (= n)
3)
a) Wenden Sie diese Formel an (wenn die Richtung "+" aufsteigt und n = 1 ist): Ausgabe = Position - Start (i = 1) + 1
b) Wenden Sie diese Formel an (wenn die Richtung "-" abfällt und n = i max): Ausgabe = Ende (i = max) - Position + 1
c) Wenden Sie diese Formel an (wenn die Richtung "+" aufsteigt und n> 1 ist): Ausgabe = Position - Start (i = 1) + 1 - (Start (i = 2 bis n) - Ende (i = 1 bis n-) 1) - 1x [n-1])
d) Wenden Sie diese Formel an (wenn die Richtung "-" abfällt und n <i max): Ausgabe = Ende (i = max) - Position + 1 - (Start (i = n + 1 bis max) - Ende (i = n) bis max-1) - 1x [i = max - n])
Ich hoffe, ich habe die Mathematik genau dort verstanden. Um die korrekte Ausgabe zu überprüfen, habe ich im Datenrahmen "bridge_game" eine Spalte "gewünschte Ausgabe" erstellt.
Danke für Ihre Hilfe!
Antworten
Es scheint, ich habe eine einfachere Lösung für Schritt 3. Die Funktion npanels
erstellt einen Vektor aus den Panel-Nummern und bestimmt die Position des Stopps des Spielers darin. Wenn die Bewegungsrichtung positiv ist ( ascending
variabel ist "+"
), ist dies die gewünschte Lösung. Wenn sie negativ ist, wird der gewünschte Wert basierend auf der Länge dieses Vektors berechnet.
start <- list(c(5,10,15), c(5) ,c(6,11),c(6,11))
end <- list(c(7,11,17), c(10), c(8,12),c(8,12))
position <- c(11,6,9,8)
ascending <- c("+","-","+","-")
game <- data.frame(start = I(start), end = I(end), position = position, ascending = ascending)
npanels <- function (data) {
v <- unlist(Map(":",
unlist(data[["start"]]),
unlist(data[["end"]])))
p <- which(v == data[["position"]])
l <- length(v)
b <- 1+l-p
d <- data[["ascending"]]
n <- ifelse(d == "+", p, b)
n <- if(is.na(n)) "disqualified" else n
return(n)
}
game$solution <- apply(game, 1, npanels)
game
Sie haben dieses Problem überkompliziert. Betrachten Sie die folgende Implementierung
parse_pos <- function(x) sort(as.integer(x))
construct_bridge <- function(starts, ends) {
starts <- parse_pos(starts); ends <- parse_pos(ends)
bridge <- logical(tail(ends, 1L))
whole_panels <- sequence(ends - starts + 1L, starts)
bridge[whole_panels] <- TRUE
bridge
}
count_steps <- function(bridge, direction, stop_pos) {
if (isFALSE(bridge[[stop_pos]]))
return("disqualified")
start_pos = c("+" = 1L, "-" = length(bridge))[[direction]]
sum(bridge[start_pos:stop_pos])
}
play_games <- function(starts, ends, direction, stop_pos) {
mapply(function(s, e, d, sp) {
bridge <- construct_bridge(s, e)
count_steps(bridge, d, sp)
}, starts, ends, direction, stop_pos)
}
Ausgabe
> with(bridge_game, play_games(start, end, ascending, position))
[1] "5" "5" "disqualified" "3"
Der Schlüssel hier ist, dass wir einen logischen Vektor verwenden können, um eine Brücke darzustellen, bei der ein kaputtes / ganzes Panel durch F
/ indiziert ist T
. Dann testen wir einfach, ob sich die Stoppposition auf einem ganzen Panel befindet oder nicht. Geben Sie die Summe der Panels von der Start- bis zur Endposition zurück, wenn dies der Fall ist (defekte Panels wirken sich nicht auf die Summe aus, da sie nur Nullen sind) oder andernfalls "disqualifiziert".
Dies könnte Ihnen das liefern, was Sie für Ihren dritten Schritt benötigen. Ich habe die Funktion von Ihrem anderen Beitrag geändert .
Zuerst würde prüfen, ob n
(oder region
) ist NA
. Wenn ja, gab es keine Übereinstimmung position
zwischen start
und end
.
Andernfalls können Sie 2x2 Kombinationen von if
else
Betrachten ascending
und einschließen n
. Die Gleichungen verwenden eine ähnliche Extraktion von Werten aus x
. Bemerkenswert ist, dass Sie zu sum
den Werten wollen, bei denen es einen Bereich von Indizes gibt (z. B. wenn Sie "Start (i = 2 bis n)" sagen, möchten Sie zu sum
den Werten, wie z. B. sum(start[2:n])
).
Beachten Sie, dass dies Ihre Gleichung direkt in Code übersetzt, wie es gewünscht schien. Es gibt jedoch einfachere Alternativen, die auf der in den anderen Antworten beschriebenen Logik basieren.
start <- list(c(5,10,15), c(5) ,c(6,11),c(6,11))
end <- list(c(7,11,17), c(10), c(8,12),c(8,12))
ascending <- c("+","-","+","-")
imax <- c(3,1,2,2)
position <- c(11,6,9,8)
example <- data.frame(start = I(start), end = I(end), ascending = ascending, imax = imax, position = position)
my_fun <- function(x) {
n <- NA
out <- NA
start <- as.numeric(unlist(x[["start"]]))
end <- as.numeric(unlist(x[["end"]]))
for (i in 1:x[["imax"]]) {
if (between(x[["position"]], start[i], end[i])) n <- i
}
if (!is.na(n)) {
if (x[["ascending"]] == "+") {
if (n == 1) {
out <- x[["position"]] - start[1] + 1
} else if (n > 1) {
out <- x[["position"]] - start[1] + 1 - (sum(start[2:n]) - sum(end[1:(n-1)]) - (n - 1))
}
} else if (x[["ascending"]] == "-") {
if (n == x[["imax"]]) {
out <- end[x[["imax"]]] - x[["position"]] + 1
} else if (n < x[["imax"]]) {
out <- end[x[["imax"]]] - x[["position"]] + 1 - (sum(start[(n+1):x[["imax"]]]) - sum(end[n:(x[["imax"]] - 1)]) - (x[["imax"]] - n))
}
}
}
out
}
example$desired_output <- apply(example, 1, my_fun)
Ausgabe
start end ascending imax position desired_output
1 5, 10, 15 7, 11, 17 + 3 11 5
2 5 10 - 1 6 5
3 6, 11 8, 12 + 2 9 NA
4 6, 11 8, 12 - 2 8 3
Aktualisieren:
Schritt 0) ist erledigt:
#Change to numeric
bridge_game$start <- lapply(bridge_game$start, as.numeric)
bridge_game$end <- lapply(bridge_game$end, as.numeric)
#Calculate number of tracts of whole wooden panels
bridge_game$tracts <- lapply(bridge_game$start, length)
#Sort start and end positions
bridge_game$start <- lapply(bridge_game$start, sort)
bridge_game$end <- lapply(bridge_game$end, sort)
#Calculate number of tracts of whole wooden panels
bridge_game$tracts <- lapply(bridge_game$start, length)
Kämpfen ab Schritt 1) weiter ...