Datei(Namen) nach Datum sortieren...

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
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Hi "Pythons",

irgendwie stehe ich auf´m Schlauch...

Ich habe die Dateinamen in einem Verzeichnis per glob in eine Liste eingelesen - leider haut die Sortierung nicht hin:
nn_1.230.tif
nn_10.349.tif
nn_4.498.tif

sollte eigentlich
nn_1.230.tif
nn_4.498.tif
nn_10.349.tif

lauten - hmmm... dachte, ich nehme einfach das Erstellungstatum und lese/sortiere danach.

Leider habe ich nix gefunden - wie würde man das bei einem Dateiviewer machen, um nach Datum zu sortieren ??
gruss x-herbert
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi x-herbert,

das erste ist asciimässig richtig sortiert. Du kannst bei der Methode sort von Listen auch eine eigene cmp-Funktion angeben, nach der dann sortiert wird.


Gruß

Dookie
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

ich hab mal für Deinen speziellen fall was zusammengewurschtelt:

Code: Alles auswählen

dateiliste.sort(lambda x,y: cmp(map(int,x[3:-4].split(".")),  map(int,y[3:-4].split("."))))
Gruß

Dookie
joerg
User
Beiträge: 188
Registriert: Samstag 17. August 2002, 17:48
Wohnort: Berlin
Kontaktdaten:

x-herbert hat geschrieben:Leider habe ich nix gefunden - wie würde man das bei einem Dateiviewer machen, um nach Datum zu sortieren ??
Bei den Dateinamen müßtest Du erst Zahlen im String finden und die vergleichen, der reiner Stringvergleich bringt hier nicht das von dir gewünschte.

Zu den Erstellungszeiten:

Prinzipiell kannst Du der Methode sort() einer Liste auch eine Vergleichsfunktion mitgeben, die kann dann z.B. os.path.getmtime() benutzen.

Um zu vermeiden, daß diese Funktion aber sehr oft gerufen wird, kann man es auch alternativ so machen:

>>> import glob, os
>>> files = glob.glob("*")
>>> tmp = [(os.path.getmtime(f), f) for f in files]
>>> tmp.sort()
>>> files = [f for t,f in tmp]

Genauso könntest Du auch nach Dateigröße etc. sortieren.

Jetzt hagelt es bestimmt Optimierungsvorschläge.... duck!

Jörg
"Sie sind nicht berechtigt, unrechtmäßige Kopien dieses Datenträgers zu erstellen." - Microsoft-Weisheit auf einer CD von MS-VisualC++-6.0
Benutzeravatar
strogon14
User
Beiträge: 58
Registriert: Sonntag 23. Februar 2003, 19:34
Wohnort: Köln
Kontaktdaten:

joerg hat geschrieben: Jetzt hagelt es bestimmt Optimierungsvorschläge.... duck!
Nö, wieso. Deine Lösung ist das typische Decorate/Sort/Undecorate Pattern!

Geht nun mal nicht schneller (und einfacher schon mal gar nicht ;-).

Chris
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Hi,

danke für die Hinweise....

Bezüglich der Sortierung von Datei(Namen) nach verschiedenen Eigenschaften hatte ich ein kleines Script find.py gefunden - darin wurde nach Erstellungsdatum wie folgt sortiert:

filelist= map( lambda f: (os.stat(f)[ST_MTIME], f), filelist )
gruss x-herbert
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

NACHTRAG:

kommt natürlich noch´n filelist.sort() dazu...

Ich bin erstaunt, dass solche "natürlichen" Sortierungen noch nicht enthalten sind, wie ich sie z.B. von PHP kenne (natsort()). Für 2.3.x habe ich diesbezüglich bei sourceforge was gefunden [url]http://py-stablesort.sourceforge.net/[/url]
gruss x-herbert
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo!
x-herbert hat geschrieben:Ich bin erstaunt, dass solche "natürlichen" Sortierungen noch nicht enthalten sind, wie ich sie z.B. von PHP kenne (natsort()).
Was man so 'natürlich' nennt ;-). Ist ein nettes Feature, für das man bei PHP sicherlich häufiger Verwendung hat. PHP hat ja einige *nat*-Funktionen. Die in Python würd' ich nicht haben wollen. Das wird dann doch arg überfrachtet.
x-herbert hat geschrieben:Für 2.3.x habe ich diesbezüglich bei sourceforge was gefunden http://py-stablesort.sourceforge.net/
Soweit ich das sehe, geht es da nur um das sog. stabile Sortieren. Die sort()-Funktion älterer Python-Vesionen sortiert nämlich nicht stabil, die derzeitige Version tut es; aber bei zukünftigen Versionen kann das schon wieder anders ausehen, denn das stabile Sortieren ist keine garantierte Eigenschaft von sort().

Jan
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Hi,

über eine Lambda-Routine ist sicher einer der besseren Wege
z.B.

Code: Alles auswählen

dateiliste = ['nn_1.230.tif','nn_1.0543.tif','nn_12.2543.tif','nn_4.498.tif','nn_10.349.tif','askfj_aaf_a123_0.349.tif']

dateiliste.sort(lambda x,y: cmp(float(x.split("_")[-1].replace(".tif","")),float(y.split("_")[-1].replace(".tif",""))))

print dateiliste
ergibt
['askfj_aaf_a123_0.349.tif', 'nn_1.0543.tif', 'nn_1.230.tif', 'nn_4.498.tif', 'nn_10.349.tif', 'nn_12.2543.tif']

die Dateinamen haben die allgemeine Form
"irgendwas_zahl.tif" wobei Dezimaltrenner der Zahl ein Punkt ist

was ich bei Dookie nicht durchschaue, warum die map-Funktion eingefügt wurde??
gruss x-herbert
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo!
x-herbert hat geschrieben:

Code: Alles auswählen

dateiliste.sort(lambda x,y: cmp(float(x.split("_")[-1].replace(".tif","")),float(y.split("_")[-1].replace(".tif",""))))
Man sollte allerdings bedenken, dass die lambda-Funktion u.U. ziemlich häufig aufgerufen wird. 10x erfolgt bei Deinem Beispiel der Aufruf, also 20x split(), replace() und Konvertierung nach float.
Bei einer Handvoll Dateien sicherlich kein Problem. Ansonsten ist joergs Weg über die Tupel-Liste sicherlich performanter. Und dass das auch noch "Decorate/Sort/Undecorate Pattern" heißt, muss ich mir unbedingt merken ;-)

Jan
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

mit dem map mache ich aus den Zahlenstrings Integerwerte, die sich dann natürlich sortieren lassen.
Falls die Möglichkeit besteht, die Dateien vor dem verarbeiten umzubenennen, würde ich sie gleich mit Dateinamen in der Form "YYYY-MM-DD-hh-mm-ss.tif" dbei steht YYYY für das Jahr, MM für den Monat u.s.w.. Speichern so können sie einfach nach Datum/Uhrzeit sortiert werden.

Gruß

Dookie
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Voges hat geschrieben:Soweit ich das sehe, geht es da nur um das sog. stabile Sortieren. Die sort()-Funktion älterer Python-Vesionen sortiert nämlich nicht stabil, die derzeitige Version tut es; aber bei zukünftigen Versionen kann das schon wieder anders ausehen, denn das stabile Sortieren ist keine garantierte Eigenschaft von sort().
Was macht denn den Unterschied? Dort stand, dass es um Geschwindigkeit und Arbeitsspeicher geht, aber wonach entscheidet sich, welche Variante gefahren wird?
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo Milan!
Milan hat geschrieben:Was macht denn den Unterschied? Dort stand, dass es um Geschwindigkeit und Arbeitsspeicher geht, aber wonach entscheidet sich, welche Variante gefahren wird?
Beim 'stabilen' Sortieren ist sichergestellt, dass gleiche Elemente nicht getauscht werden. Beispiel: Sortieren nach Namen. Zuerst sortiert man nach Vornamen und dann mit einem zweiten sort()-Aufruf nach Nachnamen. Wurde 'stabil' sortiert, ist die Reihenfolge der Vornamen innerhalb der Müllers und Lehmanns unverändert, was ja wünschenswert ist. Das ist natürlich ein Zusatz-Feature, das es nicht umsonst gibt.

Zum Thema Sortieren gibt das Cookbook[1] ja auch Einiges her.
Zudem fand ich eben Folgendes:
http://www.python.org/dev/summary/2003- ... t-sort-guy

Jan

[1] http://aspn.activestate.com/ASPN/Cookbo ... =Searching
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Also....

"back to the roots":

Ich arbeite mit einem Simulationsprogramm (CFD) welches mir zu jedem Zeitschritt ein Hardcopy meines Strömungszustandes ausgibt/abspeichert.

Der Dateiname ist so angelegt, dass ich einen allemeinenen Dateinamen vergeben kann, an dem mit einem Underline getrennt der Zeitschritt angehängt wird + ein Punkt + Extension.

Da das Programm mit englischer Notation arbeitet, sind die Zahlen der Zeitschritte mit einem Punkt getrennt.

Also:
meindateiname_0.0.tif
meindateiname_0.001.tif
meindateiname_0.002.tif
meindateiname_0.003.tif
meindateiname_0.045.tif

Die Zeitabstände sind nicht immer äquidistant.


Die Sortierung nach dem Erstellungsdatum war nur ein Workaround, da ich die andere Sortierung auf anhieb nicht hinbekommen habe.

Daher ist die Lösung nicht das, was ich eigentlich suchte.

Die Frage steht daher, ob es "elegantere" bzw. performantere Alternativen zu meinem letzten "Lambda" gibt.

Die Datenmenge liegt etwa zwischen 1000 und 6000 Dateien - mit der Liste werden anschließend die einzelnen Dateien "angefasst" und zu einem AVI "verbastelt".

Danke nochmal für die Tipps!
gruss x-herbert
Voges
User
Beiträge: 564
Registriert: Dienstag 6. August 2002, 14:52
Wohnort: Region Hannover

Hallo!
x-herbert hat geschrieben:"back to the roots"!
Ich gehe mal davon aus, dass der allg. Teil des Dateinamens immer gleich und bekannt ist (für letztes muss man ja vorab nur eine der Dateien auswerten). Dann ist nämlich schonmal die Anfangsposition des Zeitwertes klar, die Endposition sowieso.

Code: Alles auswählen

iStart = len("meindateiname_")
dateien = ["meindateiname_0.0.tif","meindateiname_0.001.tif"]
liste = [(float(datei[iStart:-4]),datei) for datei in dateien]
liste.sort()
liste = [b for a,b in liste]
Also im Prinzip genau das, was bereits im bisherigen Thread schon erwähnt und verwendet wurde. Ob man da noch was optimieren kann? Zur not müsste man ein Modul auf C-Basis schreiben, das scanf bzw. sscanf verwendet (wird es sicherlich schon irgenwo geben).

Jan
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

COOL!

Danke für den Tipp.
gruss x-herbert
Benutzeravatar
__blackjack__
User
Beiträge: 14002
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mal nach etwas mehr als 20 Jahren das Problem in die Gegenwart geholt. Ausgangspunkt:

Code: Alles auswählen

    dateinamen = [
        "nn_1.230.tif",
        "nn_1.0543.tif",
        "nn_12.2543.tif",
        "nn_4.498.tif",
        "nn_10.349.tif",
        "askfj_aaf_a123_0.349.tif",
    ]
    dateinamen.sort(
        lambda x, y: cmp(
            float(x.split("_")[-1].replace(".tif", "")),
            float(y.split("_")[-1].replace(".tif", "")),
        )
    )
    print(dateinamen)
Das läuft unter einem aktuellen Python nicht mehr, da `sort()` dieses Argument nicht mehr unterstützt und es die `cmp()`-Funktion nicht mehr gibt. Letztere kann man einfach durch eine Subtraktion der beiden Zahlen ersetzen (unter der Voraussetzung das in den Dateinamen weder "inf" noch "nan" an der Stelle vorkommt), und `functools` enthält eine Funktion um aus einer `cmp`-Funktion eine `key`-Funktion zu machen. Also die minimalen Änderungen um das zu portieren sind:

Code: Alles auswählen

from functools import cmp_to_key


def main():
    dateinamen = [
        "nn_1.230.tif",
        "nn_1.0543.tif",
        "nn_12.2543.tif",
        "nn_4.498.tif",
        "nn_10.349.tif",
        "askfj_aaf_a123_0.349.tif",
    ]
    dateinamen.sort(
        key=cmp_to_key(
            lambda dateiname_a, dateiname_b: (
                float(dateiname_a.split("_")[-1].replace(".tif", ""))
                - float(dateiname_b.split("_")[-1].replace(".tif", ""))
            )
        )
    )
    print(dateinamen)


if __name__ == "__main__":
    main()
Das ist natürlich ein bisschen unsinnig, weil der ``lambda``-Ausdruck im Grunde schon den Teilausdruck enthält, den man für eine `key`-Funktion braucht und das ganze so effizient macht wie das „decorate/sort/undecorate“-Vorgehen:

Code: Alles auswählen

    dateinamen.sort(
        key=lambda dateiname: float(
            dateiname.split("_")[-1].replace(".tif", "")
        )
    )
Da die Dateinamen ursprünglich aus einem `glob.glob()`-Aufruf kamen, hätte man da heute eher `Path`-Objekte und würde die Dateiendung weniger falsch loswerden können. Ausserdem gibt es `rparttition()` statt mit `split()` potentiell mehr als zwei Teile zu erzeugen.

Was man bei solchen Sortierungen auch immer bedenken sollte, ist was passieren soll wenn die Werte gleich sind. Man nimmt in der Regel deshalb den Originalwert mit in den Sortierschlüssel mit auf, als letztes Kriterium um immer eine eindeutige Sortierreihenfolge zu haben.

Code: Alles auswählen

#!/usr/bin/env python3
from pathlib import Path


def main():
    pfade = list(
        map(
            Path,
            [
                "nn_1.230.tif",
                "nn_1.0543.tif",
                "nn_12.2543.tif",
                "nn_4.498.tif",
                "nn_10.349.tif",
                "askfj_aaf_a123_0.349.tif",
            ],
        )
    )
    pfade.sort(key=lambda pfad: (float(pfad.stem.rpartition("_")[-1]), pfad))
    print(pfade)


if __name__ == "__main__":
    main()
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Antworten