Distanz-Funktions-Übung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
csap2109
User
Beiträge: 2
Registriert: Montag 9. November 2015, 11:44

Hi meine lieben Python-Freunde,
ich habe eine Hausübung aufbekommen, habe jedoch einen Fehler in meinem Script, den ich nicht finde.

Hier die Aufgabenstellung:

Die Datei “TrackUniSeegrube.txt” enthält GPS-Koordinaten (UTM / WGS84). Die Koordinaten markieren den Weg von der Uni bis zur Hungerburg/Nordkettenbahn. Schreibe ein Programm, das aus den Koordinaten die Länge des zurückgelegten Weges berechnet!
Am Ende soll ein neues File ausgegeben werden, welches für jede Koordinatenzeile eine zusätzliche Spalte “Zurueckgelegte Distanz” beinhaltet.

Code: Alles auswählen

Mein Script schaut folgendermaßen aus:
# Uebung 1

import math

# Definition der Funktion
def Distance(x1,y1,x2,y2):
    floatDist = math.sqrt(math.pow(x2-x1,2) + math.pow(y2-y1,2))
    return Dist


# Definition der Variablen
fobj=open("TrackUniSeegrube.txt", "r")

Names=[]

xs=[]
ys=[]
zs=[]

i=0
for element in fobj:
    if i != 0:
        element=element.strip()
        element=element.split("\t")
        Name=element[0]
        x=float(element[1])
        y=float(element[2])
        z=float(element[3])

        Names.append(Name)
        xs.append(x)
        ys.append(y)
        zs.append(z)

        
    i+=1
fobj.close()

##print Names
##print xs
##print ys
##print zs


x1=xs[0]
y1=ys[0]
x2=[]
y2=[]
Zurueckgelegte Distanz=[]

for element in xs[1:]:
    x2.append(element)

for element in ys[1:]:
    y2.append(element)

#Funktion wird ausgefuehrt
for element in x2:
    for element in y2:
        Dist = Distance(x1,y1,x2,y2)
        Zurueckgelegte Distanz.append(Dist)
print Zurueckgelegte Distanz



#Neues File mit Distanz-Spalte
fobj_out = open("Distanz.txt", "w")


#Neue Spalte "Zurueckgelegte Distanz"
for element in Names:
    fobj_out.write(Names,xs,ys,zs,Zurueckgelegte Distanz)
    
fobj_out.close()

Als Fehlermeldung kommt folgendes:
Traceback (most recent call last):
File "/afs/zid1.uibk.ac.at/home/csap/csap2109/PYTHON/Übungen4_Neu_Schmid.py", line 60, in <module>
Dist = Distance(x1,y1,x2,y2)
File "/afs/zid1.uibk.ac.at/home/csap/csap2109/PYTHON/Übungen4_Neu_Schmid.py", line 7, in Distance
Dist = math.sqrt(math.pow(x2-x1,2) + math.pow(y2-y1,2))
TypeError: unsupported operand type(s) for -: 'list' and 'float'


Ich bin dankbar für jede Antwort & bitte habt Rücksicht mit meinen Programmierkenntnissen..bin Anfänger!

Lg Hannes
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Willkommen im Forum und zu Python!

Schau dir mal Zeilen 59 - 61 an. Alle drei sind falsch. Da ich aber nicht durchblicke wie deine Koordinaten eigentlich gespeichert werden, kann ich dir nicht sagen, wie es eigentlich heissen sollte.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Noch als Ergänzung zu cofis Hinweis: in Zeile 61 übergibst Du der Funktion Distance mit x2 und y2 Listen und keine floats.
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Code: Alles auswählen

def Distance(x1,y1,x2,y2):
    floatDist = [math.sqrt(math.pow(x2i-x1i,2) + math.pow(y2i-y1i,2)) for x1i,y1i,x2i,y2i in zip(x1,y1,x2,y2)]
    return floatDist
x1, x2, y1, y2 sind Listen, deshalb musst Du zum Rechnen auf die Elemente (x1i,y1i,x2i,y2i) zugreifen. Dein Code würde funktionieren wenn es Numpy-Arrays wären. Numpy-Arrays darfst Du aber wahrscheinlich nicht benutzen, oder? Damit ließe sich die Aufgabe in weniger als 10 Zeilen lösen. Das Lesen und Schreiben der Datei und die Rechnung wären damit Einzeiler.

Wie schaltet man eigentlich die Python Code-Formatierung ein?
a fool with a tool is still a fool, www.magben.de, YouTube
Benutzeravatar
MagBen
User
Beiträge: 799
Registriert: Freitag 6. Juni 2014, 05:56
Wohnort: Bremen
Kontaktdaten:

Ich seh gerade es ist nicht die Funktion Distance falsch, sondern der Aufruf (Zeile 59 bis 61):

Code: Alles auswählen

for x2i in x2:
    for y2i in y2:
        Dist = Distance(x1,y1,x2i,y2i)
a fool with a tool is still a fool, www.magben.de, YouTube
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@csap2109: mich wundert, dass nicht schon in Zeile 50 ein SyntaxError kommt. Die Zahlen einer Zeile gehören zusammen, sollten also auch nicht in unterschiedlichen Listen gespeichert werden. Dann hast Du auch nicht das Problem, wie Du über mehrere Liste iterieren solltest.
BlackJack

@csap2109: Konkrete Grunddatentypen haben in Namen nichts zu suchen. Namen sollten beschreiben was der Wert im Programm bedeutet, nicht was für ein konkreter Typ das ist, denn Typen können sich ändern oder es können auch verschiedene Typen für einen Wert funktionieren solange sich dessen Werte so wie erwartet verhalten. Namen die Typen enthalten sind dann gegebenfalls falsch und schränken die Erwartungen/Möglichkeiten unnötig ein. Fast überall wo ein `float` möglich ist, wird man auch ein `Decimal` oder ein `Fractions` oder auch ein `int` verwenden/übergeben können. Wenn der Namen dann aber `float` als Bestandteil enthält ist das bestenfalls irreführend.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst und durch folgendes Idiom aufgerufen wird:

Code: Alles auswählen

if __name__ == '__main__':
    main()
Dann kann man das Modul importieren ohne dass das Hauptprogramm gleich losläuft, um Beispiesweise die `Distance()`-Funktion mal interaktiv in einer Python-Shell auszuprobieren. Dann fällt nämlich sofort ein Fehler in der Funktion auf.

Insgesamt scheinst Du das überhaupt nicht laufen lassen denn der Quelltext enthält Syntaxfehler und die `write()`-Methode von Dateiobjekten hätte sich bei dem gezeigten Quelltext auch beschwert. Du hast das insgesamt zu viel Quelltext der offensichtlich nicht einmal ausprobiert wurde. So entwickelt man keine Programme. Man schreibt nicht fast 100 Zeilen ohne zu testen ob die einzelnen Programmschritte das tun was sie sollen denn es macht keinen Sinn einen Programmschritt zu programmieren bevor der vorhergehende nicht funktioniert. Das führt zu Programmen bei denen man am Ende frustrierend in viel Code einzelne Fehler sucht, und ab und zu feststellt das Folgeschritte nicht so funktionieren wie man dachte weil Schritte davor andere Daten/Strukturen erzeugen als man erwartet. Und dann muss man Sachen um- oder sogar neuschreiben. Arbeit die man sich hätte sparen können. Konkret ist das beispielsweise die Organisation von zusammengehörigen Daten in ”parallelen” Listen in dem jetzigen Programm. Ein Name und die Koordinaten gehören als Datensatz zusammen und damit auch in einer Struktur zusammengefasst gespeichert. Wenn man das nicht macht wird der Code der das verarbeiten muss umfangreicher und fehleranfälliger weil man erst die Daten auf verschiedene Listen aufteilt, dann zur Verarbeitung und am Ende zur Ausgabe die zusammengehörigen Daten aus den verschiedenen Listen jedes mal wieder zusammenführen muss.

Anstelle des manuell geführten Zählers `i` in der ersten Schleife währe es einfacher die erste Zeile vor der Schleife zu lesen und einfach zu ignorieren als für jede Zeile den Zähler zu aktualisieren der nur bei der allerersten Zeile überhaupt eine Rolle spielt. Sollte man tatsächlich für jede Zeile einen Zähler-/Indexwert benötigen, gibt es die `enumerate()`-Funktion, dann muss man das nicht manuell machen.

Für das öffnen und schliessen von Dateien könntest Du mal einen Blick auf die ``with``-Anweisung werfen.

Das gesamte Programm kann man sinnvoll auf mehr Funktionen verteilen.
csap2109
User
Beiträge: 2
Registriert: Montag 9. November 2015, 11:44

Vielen Dank an Alle zunächst einmal.
Sicherlich ist es einfacher zu lösen, aber in der Uni haben wir schon bis Zeile 43 gearbeitet und es hat geklappt. Deswegen die Listen usw.
Den Text nach dem Fehler hab ich eig. immer auskommeniert aber schon mal als hingeschrieben, um meine Gedanken zu speichern

Was muss ich denn konkret ändern, dass ich die listen xs und ys als x2 und y2 in der Funktion verwenden kann?
BlackJack

@csap2109: Python lernen müsstest Du konkret. Sorry aber Du fragst hier gerade danach dass Dir jemand den Code schreibt den Du schreiben solltest. Und das mit der Aufteilung auf die Listen ist wirklich konzeptionell falsch und nicht einfach nur ein bisschen schwieriger. Wenn Du das so machst dann musst Du Probleme lösen die Du Dir selber zusätzlich geschaffen hast. Das löst man am besten dadurch dass man sich das Problem gar nicht erst schafft und nicht dadurch das man andere nach Workarounds fragt.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

csap2109 hat geschrieben:Was muss ich denn konkret ändern, dass ich die listen xs und ys als x2 und y2 in der Funktion verwenden kann?
In der Funktion verwendest Du keine Listen, sondern die Inhalte aus den Listen. Auch sind zwei Listen, über die parallel iteriert wird, unschön. Hier ein kleines Code-Schnipsel als Beispiel:

Code: Alles auswählen

positions = zip(xs, ys)
previous_position = None
distances = []

for position in positions:
    if not previous_position:
        previous_position = position
        continue
    x1, y1 = previous_position
    x2, y2 = position
    distance = Distance(x1, y1, x2, y2)
    distances.append(distance)
    previous_position = position
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Ich werfe mal zwei Funktionen und deren Anwendung in den Raum:

Code: Alles auswählen

def polyline(ps):
    """Creates a polyline from a sequence of points.
    
    >>> list( polyline([(1, 2), (2, 3), (3, 4)]) )
    [((1, 2), (2, 3)), ((2, 3), (3, 4))]
    """
    return zip(ps[:-1], ps[1:])


def length(line):
    """Returns the length of the given line.
    
    >>> round( length([(0, 0), (4, 2)]), 6 )
    4.472136
    """
    (ax, ay), (bx, by) = line
    return math.sqrt((ax - bx) ** 2 + (ay - by) ** 2)

POINTS = [(-4, -4), (4, -4), (4, 4), (-4, 4), (-4, -4)]
sum(map(length, polyline(POINTS)))
Hier kann man sehr schön sehen, wie eine passende Datenstruktur die Problemlösung in wenige Zeilen eindampfen kann.

EDIT: Das Code-Highlighting scheint Leerzeichen in den Docstrings zu "vergessen".
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
BlackJack

Ungetestet:

Code: Alles auswählen

#!/usr/bin/python
# coding: utf8
from __future__ import absolute_import, division, print_function
import csv
from itertools import izip, tee
from math import sqrt

DELIMITER = '\t'


class Position(object):

    FIELDNAMES = ['Name', 'x', 'y', 'z', 'Zurueckgelegte Distanz']

    def __init__(self, name, x, y, z):
        self.name = name
        self.x = x
        self.y = y
        self.z = z
        self.distance = 0.0

    def distance_to(self, other):
        return sqrt((self.x - other.x)**2 + (self.y - other.y)**2)

    def as_row(self):
        return [
            self.name, str(self.x), str(self.y), str(self.y), str(self.distance)
        ]

    @classmethod
    def from_row(cls, row):
        name, x, y, z = row
        return cls(name, float(x), float(y), float(z))


def pairwise(iterable):
    iterable_a, iterable_b = tee(iterable)
    next(iterable_b)
    return izip(iterable_a, iterable_b)


def load_positions_from_csv(filename):
    with open(filename, 'rb') as input_file:
        reader = csv.reader(input_file, delimiter=DELIMITER)
        next(reader)  # Skip header.
        for row in reader:
            yield Position.from_row(row)


def update_distance(positions):
    position_b = None
    for position_a, position_b in pairwise(positions):
        position_b.distance = (
            position_a.distace + position_a.distance_to(position_b)
        )
        yield position_a
    if position_b is not None:
        yield position_b


def save_positions_to_csv(filename, positions):
    with open(filename) as out_file:
        writer = csv.writer(out_file, delimiter=DELIMITER)
        writer.writerow(Position.FIELDNAMES)
        writer.writerows(position.as_row() for position in positions)


def main():
    save_positions_to_csv(
        'Distanz.txt',
        update_distance(load_positions_from_csv('TrackUniSeegrube.txt'))
    )


if __name__ == '__main__':
    main()
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@csap2109: dürft / sollt ihr die Koordinaten in 2D (also der Ebene) berechnen? Da es sich um Geokoordinaten handelt, nimmt man üblicherweise zumindest die "Semiversus" (englisch: Haversine) Formel, weil das genauer ist.

Und in welcher Form liegen die Koords vor? Die UTM-Notation müsstest du erst in Dezimalgrad umrechnen, damit du damit "normal" rechnen kannst.

Gruß, noisefloor
BlackJack

Bezüglich dem was noisefloor schrieb: Ich ging davon aus das das UTM-Koordinaten sind die in der gleichen Zone liegen, also somit einfach als Meter von einem Nullpunkt aus gesehen aufgefasst werden können.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Weitere Ergänzung: UTM ist kartesisch und damit einfach zu handhaben, wenn man in der gleichen Zone bleibt.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Antworten