Sortieren unter Formatbeibehaltung

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
Aurora
User
Beiträge: 2
Registriert: Montag 15. Mai 2017, 10:22

Liebes Forum,

ich schreibe gerade meine Abschlussarbeit in der theoretischen Chemie und bin dadurch auch mit Python konfrontiert worden. Ich möchte nun ein Skript schreiben, dass mir eine Datei nach einer bestimmten Wert sortiert wobei die Formatierung erhalten bleiben soll...
Anschaulicher: In der Datei sind einige hundert Atome aufgelistet mit ihren xyz-Koordinaten in folgendem speziellen Format:
HETATM *[4 Leerzeichen]* 1 Pt '*[17 Leerzeichen]* 0.00000 *[3LZ]* 9.86843 *[3LZ]* 19.86842 *[3LZ]* Pt *[3LZ]* 1*[3LZ]* 1 *[3LZ]* 0.00000
HETATM 2 Pt 0.65790 0.65790 19.21053 Pt 1 1 0.00000

Nun möchte ich diese aufsteigend nach der z-Koordinate sortieren ...

Ich bin so weit, dass ich die Zeilen einzeln in eine Liste schreiben kann - dann häng ich aber daran, dass ich jetzt ja nicht einzeln die z-Koordinate aufrufen kann und dann die Listen sortieren. Sollte ich dann für jede Zeile eine Unterliste setzen? - dann könnte ich die Unterlisten nach der z-Koordinate sortieren, oder?

Ich bin für jede Anregung und Hilfe dankbar, weil mir einfach komplett der Überblick fehlt... Deswegen Danke jetzt schonmal!

Viele Grüße :wink:
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@Aurora: Wenn die z-Koordinate in allen Zeilen stets das sechste Element ist und alle Inhalte einer Zeile durch Whitespaces getrennt sind, dann geht das etwa so:

Code: Alles auswählen

def sorter(line):
    return float(line.split()[5])

with open('atoms.txt') as fobj:
    lines = fobj.readlines()
lines.sort(key=sorter)
print(lines)
Das ist natürlich nur ein Code-Fragment und nicht stabil, falls der Inhalt der Datei nicht exakt so ist, wie erwartet, aber vielleicht gibt es Dir ja die richtige Idee.
Aurora
User
Beiträge: 2
Registriert: Montag 15. Mai 2017, 10:22

@kbr:
Vielen, vielen Dank! Deine Antwort hat mir echt geholfen und ich konnte den Code jetzt zum Laufen bringen.

Code: Alles auswählen

def sorter(line):
    return float(line.split()[5])

l_geo = []
with open('fort.90.221') as fobj:
    for line in fobj:		
        if 'HETATM' in line:
            l_geo.append(line)

fobj.close()
l_geo.sort(key=sorter)

output = open('test_new','w')
for entry in l_geo:
    output.write(entry)
output.write('END\n')
output.close()    
:wink:
Zuletzt geändert von Anonymous am Dienstag 16. Mai 2017, 12:48, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@Aurora: Noch ein paar Anmerkungen zum Quelltext:

Ich würde der Funktion einen besseren Namen geben der dem Leser vermittelt was dort tatsächlich passiert. Also beispielsweise `get_z_coordinate()`. Sonst muss der Leser wissen was diese ”magische” 5 als Index bedeutet.

Falls das `l` in `l_geo` für Liste stehen sollte: Gewöhn Dir das am besten gar nicht erst an Grunddatentypen in Namen zu schreiben. Falls es für etwas anderes steht: Auch Abkürzungen sind schlecht, eben aus dem Grund das man sie nicht, oder missverstehen kann.

Wenn man ``with`` verwendet, braucht man die Datei nicht mehr noch mal extra schliessen. Genau deswegen verwendet man ``with`` ja. Und das sollte man auch — warum also bei der Eingabedatei, aber dann nicht mehr bei der Ausgabedatei?

Die Zeilen könnte man mit einer „list comprehension“ (LC) in einem Ausdruck einlesen und filtern. Wenn man aus der LC einen Generatorausdruck macht könnte man mit `sorted()` sogar das sortieren noch im gleichen Ausdruck erledigen.

Beim schreiben kann man sich die Schleife sparen.

Ungetestet:

Code: Alles auswählen

def get_z_coordinate(line):
    return float(line.split()[5])


def main():
    with open('fort.90.221') as lines:
        output_lines = sorted(
            line for line in lines if 'HETATM' in line, key=get_z_coordinate
        )
    
    output_lines.append('END\n')

    with open('test_new', 'w') as output_file:
        output_file.writelines(output_lines)


if __name__ == '__main__':
    main()
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

BlackJack hat geschrieben:Die Zeilen könnte man mit einer „list comprehension“ (LC) in einem Ausdruck einlesen und filtern. Wenn man aus der LC einen Generatorausdruck macht könnte man mit `sorted()` sogar das sortieren noch im gleichen Ausdruck erledigen.
Nicht schlecht — das geht (gleichfalls ungetestet) auch noch etwas kompakter:

Code: Alles auswählen

def get_z_coordinate(line):
    return float(line.split()[5])
 
 
def main():
    with open('fort.90.221') as lines, open('test_new', 'w') as output_file:
        output_file.writelines(sorted(
            line for line in lines if 'HETATM' in line, key=get_z_coordinate
        ))
        output_file.write('END\n')

 
if __name__ == '__main__':
    main()
Antworten