Punkt-in-Polygon in R

Gepostet: , Zuletzt aktualisiert:

point_in_polygon <- function(polygon, point){
  #' Raycasting Algorithm to find out whether a point is in a given polygon.
  #' Performs the even-odd-rule Algorithm to find out whether a point is in a given polygon.
  #' This runs in O(n) where n is the number of edges of the polygon.
  #' @param polygon an array representation of the polygon where polygon[i,1] is the x Value of the i-th point and polygon[i,2] is the y Value.
  #' @param point   an array representation of the point where point[1] is its x Value and point[2] is its y Value
  #' @return whether the point is in the polygon (not on the edge, just turn < into <= and > into >= for that)
  
  # A point is in a polygon if a line from the point to infinity crosses the polygon an odd number of times
  odd = FALSE
  # For each edge (In this case for each point of the polygon and the previous one)
  i = 0
  j = nrow(polygon) - 1
  while(i < nrow(polygon) - 1){
    i = i + 1
    # If a line from the point into infinity crosses this edge
    # One point needs to be above, one below our y coordinate
    # ...and the edge doesn't cross our Y corrdinate before our x coordinate (but between our x coordinate and infinity)
    if (((polygon[i,2] > point[2]) != (polygon[j,2] > point[2])) 
        && (point[1] < ((polygon[j,1] - polygon[i,1]) * (point[2] - polygon[i,2]) / (polygon[j,2] - polygon[i,2])) + polygon[i,1])){
      # Invert odd
      odd = !odd
    }
    j = i
  }
  # If the number of crossings was odd, the point is in the polygon
  return (odd)
}

Über den Algorithmus und die Programmiersprache in diesem Snippet:

Punkt-in-Polygon Algorithmus

Das PIP-Problem (Punkt-in-Polygon) ist das Problem, zu bestimmen, ob ein Punkt ein beliebiges Polygon ist. Dies mag für ein einfaches Polygon wie ein Quadrat oder ein Dreieck trivial klingen, wird jedoch mit komplexeren Polygonen wie dem im folgenden Beispiel komplexer. In diesem Beitrag wird der gerade-ungerade Algorithmus, auch Kreuzungsnummernalgorithmus oder Jordan-Algorithmus genannt (da er mit dem Jordan-Kurvensatz bewiesen werden kann) eingeführt.

Beschreibung des Algorithmus

Das Grundprinzip hinter dem Even-Odd aka. Jordan aka. Der Kreuzzahlalgorithmus besteht darin, zu zählen, wie oft eine Linie vom betreffenden Punkt (in einer beliebigen Richtung) eine Kante des Polygons kreuzt. Diese Linie befindet sich immer außerhalb des Polygons an ihrem “Ende” im Unendlichen. Wenn sie sich also am Anfang (dem fraglichen Punkt) innerhalb des Polygons befindet, muss sie das Polygon irgendwann verlassen und eine Kante überqueren. Es kann das Polygon erneut eingeben (siehe Beispiel unten), muss jedoch immer wieder verlassen werden, wodurch die Gesamtzahl der Kreuzungen ungerade wird, wenn sich der Punkt im Polygon befindet. Das Gegenteil ist auch wahr; Wenn die Anzahl der Kreuzungen gerade ist, liegt der Punkt immer außerhalb des Polygons. Dies ist der oben erwähnte Jordan-Kurvensatz.

Der Algorithmus überprüft jede Kante des Polygons in einer Schleife, um festzustellen, ob die Linie vom Punkt bis zur Unendlichkeit diese kreuzt. Im folgenden Beispiel wird diese Linie vom Punkt bis unendlich nach rechts gezeichnet, kann jedoch eine beliebige Richtung haben.

Die Schritte sind:

  1. Für jede Kante im Polygon:
  2. Wenn die Kante die imaginäre Linie vom Punkt bis unendlich kreuzt, erhöhen Sie einen Zähler.
  3. Wenn der Zähler am Ende ungleichmäßig ist, geben Sie true zurück. Andernfalls geben Sie false zurück.

Eine einfache boolesche Variable, die jedes Mal invertiert wird, wenn eine Kreuzung gefunden wird, ist ebenfalls möglich.

Beispiel des Algorithmus

Betrachten Sie das folgende Polygon mit 8 Kanten und zwei Punkten, für die wir bestimmen möchten, ob sie sich im Polygon befinden:

Punkt im Polygon Jordan (gerade-ungerade) Algorithmus Beispiel Polygon und Punkte

Die Schritte, die der Algorithmus für dieses Polygon ausführt, um zu bestimmen, ob sich der erste (grüne) Punkt im Polygon befindet, beginnen an der ersten Kante:

  1. Grüner Punkt kreuzt Kante 8
  2. Der grüne Punkt kreuzt nicht die Kante 1
  3. Grüner Punkt kreuzt Kante 2
  4. Der grüne Punkt kreuzt nicht die Kante 3
  5. Grüner Punkt kreuzt Kante 4
  6. Grüner Punkt kreuzt Kante 5
  7. Grüner Punkt kreuzt nicht die Kante 6
  8. Der grüne Punkt kreuzt nicht die Kante 7
  9. Gesamtzahl der Überfahrten: 4
  10. Gerade Anzahl von Kantenübergängen, daher befindet sich der Punkt nicht im Polygon

Die Schritte, die der Algorithmus für dieses Polygon ausführt, um zu bestimmen, ob sich der zweite (rote) Punkt im Polygon befindet, beginnen an der ersten Kante:

  1. Der rote Punkt kreuzt nicht die Kante 8
  2. Der rote Punkt kreuzt nicht die Kante 1
  3. Der rote Punkt kreuzt nicht die Kante 2
  4. Der rote Punkt kreuzt nicht die Kante 3
  5. Roter Punkt kreuzt nicht die Kante 4
  6. Roter Punkt kreuzt Kante 5
  7. Der rote Punkt kreuzt nicht die Kante 6
  8. Der rote Punkt kreuzt nicht die Kante 7
  9. Gesamtzahl der Überfahrten: 1
  10. Ungleichmäßige Anzahl von Kantenübergängen, daher befindet sich der Punkt im Polygon

Laufzeitkomplexität des Algorithmus

Die Laufzeitkomplexität des Jordan-Algorithmus aka. Crossing Number Algorithmus aka. Der geradzahlige Algorithmus zur Lösung des Punkt-in-Polygon-Problems für einen einzelnen Punkt ist in Bezug auf die Anzahl der Kanten linear. Dies wird deutlich, wenn man sich den Code ansieht, der eine einzelne Schleife über den Kanten enthält, ohne Rekursion oder weitere Funktionsaufrufe oder Schleifen.

Formal ist die Laufzeit *O(| V |), | V |: = Anzahl der Kanten im Polygon *.

Speicherkomplexität des Algorithmus

Die Speicherkomplexität ist auch linear w.r.t. die Anzahl der Kanten, da zusätzlich zum Polygon nur Variablen mit fester Größe gespeichert werden müssen. Darüber hinaus kann der Algorithmus online implementiert werden, sodass keine vergangenen Kanten während der Schleife betrachtet werden müssen, sodass sie aus dem Speicher entfernt werden können (oder vergleichbare Maßnahmen zur Leistungsverbesserung).

R

The R Logo

R ist eine interpretierte Sprache, die erstmals 1993 veröffentlicht wurde und in den letzten Jahren erheblich an Popularität gewonnen hat. Es wird hauptsächlich für Data Mining und -science sowie für Statistiken verwendet und ist eine beliebte Sprache in Disziplinen außerhalb der Informatik, die von Biologie bis Physik reichen. R ist dynamisch typisiert und verfügt über eine der vielfältigsten Bibliotheken für Statistik, maschinelles Lernen, Data Mining usw.

<! - Ende des Auszugs ->

Anreise zu “Hello World” in R.

Das Wichtigste zuerst - hier erfahren Sie, wie Sie Ihre erste Codezeile in R ausführen können.

  1. Laden Sie die neueste Version von R von r-project.org herunter und installieren Sie sie. Sie können auch eine frühere Version herunterladen, wenn Ihr Anwendungsfall dies erfordert.
  2. Öffnen Sie ein Terminal, stellen Sie sicher, dass der Befehl R funktioniert und dass der Befehl, den Sie verwenden werden, sich auf die Version bezieht, die Sie gerade installiert haben, indem SieR --version ausführen. Wenn der Fehler “Befehl nicht gefunden” (oder ähnlich) angezeigt wird, starten Sie die Befehlszeile und, falls dies nicht hilft, Ihren Computer neu. Wenn das Problem weiterhin besteht, finden Sie hier einige hilfreiche Fragen zu StackOverflow für Windows, Mac und Linux .
  3. Sobald dies funktioniert, können Sie das folgende Snippet ausführen: print (" Hello World "). Sie haben zwei Möglichkeiten, dies auszuführen: 3.1 Führen Sie “R” in der Befehlszeile aus, fügen Sie einfach das Code-Snippet ein und drücken Sie die Eingabetaste (Drücken Sie “STRG + D” und geben Sie “n” gefolgt von der Eingabetaste ein, um das Menü zu verlassen). 3.2 Speichern Sie das Snippet in einer Datei und nennen Sie es etwas, das mit “.R” endet, z. hello_world.R und führen SieRscript hello_world.R aus. Tipp: Verwenden Sie den Befehl ls (dir in Windows), um herauszufinden, welche Dateien sich in dem Ordner befinden, in dem sich Ihre Befehlszeile gerade befindet.

Das ist es! Beachten Sie, dass das Drucken von etwas auf die Konsole nur eine einzige Zeile in R ist - diese niedrige Eintrittsbarriere und das Fehlen des erforderlichen Boilerplate-Codes machen einen großen Teil der Attraktivität von R aus.

Grundlagen in R.

Um in R implementierte Algorithmen und Technologien zu verstehen, muss man zunächst verstehen, wie grundlegende Programmierkonzepte in dieser bestimmten Sprache aussehen.

Variablen und Arithmetik

Variablen in R sind wirklich einfach. Sie müssen weder einen Datentyp deklarieren noch deklarieren, dass Sie eine Variable definieren. R weiß das implizit. R ist auch in der Lage, Objekte und ihre Eigenschaften auf verschiedene Arten einfach zu definieren.

some_value = 10
my_object <- list(my_value = 4)
attr(my_object, 'other_value') <- 3

print((some_value + my_object$my_value + attr(my_object, 'other_value'))) # Prints 17

Arrays

Das Arbeiten mit Arrays ist in R ähnlich einfach:

# Create 2 vectors of length 3
vector1 <- c(1,2,3)
vector2 <- c(4,5,6)

# Create names for rows and columns (optional)
column.names <- c("column_1","column_2","column_3")
row.names <- c("row_1","row_2")

# Concatenate the vectors (as rows) to form an array, providing dimensions and row/column names
result <- array(c(vector1,vector2), dim = c(2,3), dimnames = list(row.names, column.names))

print(result)
# Prints:
#       column_1 column_2 column_3
# row_1        1        3        5
# row_2        2        4        6

Wie diejenigen unter Ihnen, die mit anderen Programmiersprachen wie Java vertraut sind, möglicherweise bereits bemerkt haben, handelt es sich nicht um native Arrays, sondern um Listen, die wie Arrays gekleidet sind. Dies bedeutet, dass Arrays in R erheblich langsamer sind als in Programmiersprachen niedrigerer Ebene. Dies ist ein Kompromiss, den R zugunsten der Einfachheit eingeht. Es gibt jedoch Pakete, die echte Arrays implementieren, die erheblich schneller sind.

Bedingungen

Wie die meisten Programmiersprachen kann R “if-else” -Anweisungen ausführen:

value = 1
if(value==1){
   print("Value is 1")
} else if(value==2){
     print("Value is 2")
} else {
     print("Value is something else")
}

R kann auch switch-Anweisungen ausführen, obwohl sie im Gegensatz zu anderen Sprachen wie Java als Funktion implementiert sind:

x <- switch(
   1,
   "Value is 1",
   "Value is 2",
   "Value is 3"
)

print(x)

Beachten Sie, dass diese Funktion ziemlich nutzlos ist, es jedoch andere Funktionen für komplexere Anwendungsfälle gibt.

Schleifen

R unterstützt sowohl for- als auch while-Schleifen sowie break- und next-Anweisungen (vergleichbar mit continue in anderen Sprachen). Zusätzlich unterstützt R “Wiederholungsschleifen”, die mit “while (true)” - Schleifen in anderen Sprachen vergleichbar sind, aber den Code ein wenig vereinfachen.

value <- 0
repeat {
  value <- value + 1
  if(value > 10) {
    break
  }
}
print(value)

value <- 0
while (value <= 10) {
  value = value + 1
}
print(value)

value <- c("Hello","World","!")
for ( i in value) {
  print(i)
}

for(i in 1:10){
  print(i)
}

Funktionen

Funktionen in R sind einfach zu definieren und erfordern zum Guten oder Schlechten keine Angabe von Rückgabe- oder Argumenttypen. Optional kann ein Standardwert für Argumente angegeben werden:

my_func <- function (
  a = "World"
) {
  print(a)
  return("!")
}

my_func("Hello")
print(my_func())

(Dies druckt “Hallo”, “Welt” und dann ”!“)

Syntax

R erfordert die Verwendung von geschweiften Klammern ({}), um Codeblöcke in Bedingungen, Schleifen, Funktionen usw.; Dies kann zwar zu lästigen Syntaxfehlern führen, bedeutet jedoch auch, dass die Verwendung von Leerzeichen für die bevorzugte Formatierung (z. B. Einrücken von Codeteilen) den Code nicht beeinflusst.

Fortgeschrittenes Wissen in R

Für weitere Informationen hat R einen großartigen Artikel Wikipedia. Die offizielle Website ist r-project.org.