Durchlaufen einer Reihe von Daten in Python

Lesezeit: 8 Minuten

Benutzer-Avatar
ShawnMilo

Ich habe den folgenden Code, um dies zu tun, aber wie kann ich es besser machen? Im Moment denke ich, dass es besser ist als verschachtelte Schleifen, aber es fängt an, Perl-Einzeiler zu werden, wenn Sie einen Generator in einem Listenverständnis haben.

day_count = (end_date - start_date).days + 1
for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]:
    print strftime("%Y-%m-%d", single_date.timetuple())

Anmerkungen

  • Ich benutze das eigentlich nicht zum Drucken. Das ist nur zu Demozwecken.
  • Das start_date und end_date Variablen sind datetime.date Objekte, da ich die Zeitstempel nicht benötige. (Sie werden verwendet, um einen Bericht zu erstellen).

Beispielausgabe

Für ein Startdatum von 2009-05-30 und ein Enddatum von 2009-06-09:

2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09

  • Nur um darauf hinzuweisen: Ich glaube nicht, dass es einen Unterschied zwischen ‘time.strftime(“%Y-%m-%d”, single_date.timetuple())’ und dem kürzeren ‘single_date.strftime(“%Y-% m-%d”)’. Die meisten Antworten scheinen den längeren Stil zu kopieren.

    – Mu Geist

    21. September 2010 um 13:20 Uhr

  • Wow, diese Antworten sind viel zu kompliziert. Versuchen Sie Folgendes: stackoverflow.com/questions/7274267/…

    – Gringo Suave

    19. September 2012 um 19:47 Uhr


  • @GringoSuave: Was ist kompliziert an Sean Cavanaghs Antwort?

    – jfs

    24. April 2015 um 0:30 Uhr

  • Anwendung: Cheat auf GitHub Streaks: stackoverflow.com/questions/20099235/…

    – Ciro Santilli Путлер Капут 六四事

    6. Dezember 2015 um 9:47 Uhr

  • @GringoSuave Dieser Link ist ein Duplikat von: stackoverflow.com/questions/1060279/…

    – Blairg23

    19. April 2017 um 17:52 Uhr

Benutzer-Avatar
Ber

Warum gibt es zwei verschachtelte Iterationen? Für mich erzeugt es die gleiche Liste von Daten mit nur einer Iteration:

for single_date in (start_date + timedelta(n) for n in range(day_count)):
    print ...

Und es wird keine Liste gespeichert, es wird nur über einen Generator iteriert. Auch das “if” im Generator scheint unnötig zu sein.

Schließlich sollte eine lineare Sequenz nur einen Iterator benötigen, nicht zwei.

Update nach Diskussion mit John Machin:

Die vielleicht eleganteste Lösung ist die Verwendung einer Generatorfunktion, um die Iteration über den Datumsbereich vollständig auszublenden/zu abstrahieren:

from datetime import date, timedelta

def daterange(start_date, end_date):
    for n in range(int((end_date - start_date).days)):
        yield start_date + timedelta(n)

start_date = date(2013, 1, 1)
end_date = date(2015, 6, 2)
for single_date in daterange(start_date, end_date):
    print(single_date.strftime("%Y-%m-%d"))

NB: Für die Konsistenz mit dem eingebauten range() Funktion stoppt diese Iteration Vor Erreichen der end_date. Verwenden Sie also für die inklusive Iteration am nächsten Tag, wie Sie es mit tun würden range().

  • @ John Machin: Okay. Ich bevorzuge jedoch eine Iteration über While-Schleifen mit expliziter Inkrementierung eines Zählers oder Werts. Das Interationsmuster ist pythonischer (zumindest aus meiner persönlichen Sicht) und auch allgemeiner, da es ermöglicht, eine Iteration auszudrücken, während die Details, wie diese Iteration durchgeführt wird, verborgen werden.

    – Ber

    30. Juni 2009 um 9:57 Uhr

  • @Ber: Ich mag es überhaupt nicht; es ist DOPPELT schlecht. Sie hatten BEREITS eine Iteration! Indem Sie die beanstandeten Konstrukte in einen Generator packen, haben Sie noch mehr Ausführungsaufwand hinzugefügt und die Aufmerksamkeit des Benutzers auf etwas anderes gelenkt, um den Code und/oder die Dokumentation Ihres Dreizeilers zu lesen. -2

    – John Machin

    30. Juni 2009 um 10:36 Uhr

  • @John Machin: Ich bin anderer Meinung. Es geht nicht darum, die Anzahl der Zeilen auf das absolute Minimum zu reduzieren. Schließlich sprechen wir hier nicht von Perl. Außerdem macht mein Code nur eine Iteration (so funktioniert der Generator, aber ich denke, das wissen Sie). *** Mein Punkt ist das Abstrahieren von Konzepten für die Wiederverwendung und selbsterklärenden Code. Ich behaupte, dass dies weitaus lohnender ist, als einen möglichst kurzen Code zu haben.

    – Ber

    30. Juni 2009 um 10:58 Uhr

  • Wenn Sie auf Knappheit aus sind, können Sie einen Generatorausdruck verwenden: (start_date + datetime.timedelta(n) for n in range((end_date - start_date).days))

    – Markieren Sie Lösegeld

    3. April 2012 um 19:35 Uhr

  • for n in range(int ((end_date - start_date).days+1)): Damit das Enddatum enthalten ist

    – Vamshi G

    30. Januar 2018 um 14:42 Uhr


Benutzer-Avatar
Sean Cavanagh

Das ist vielleicht deutlicher:

from datetime import date, timedelta

start_date = date(2019, 1, 1)
end_date = date(2020, 1, 1)
delta = timedelta(days=1)
while start_date <= end_date:
    print(start_date.strftime("%Y-%m-%d"))
    start_date += delta

  • Sehr klar und kurz, funktioniert aber nicht gut, wenn Sie Continue verwenden möchten

    – rslite

    2. Juni 2017 um 2:51 Uhr

Benutzer-Avatar
nosklo

Verwenden Sie die dateutil Bibliothek:

from datetime import date
from dateutil.rrule import rrule, DAILY

a = date(2009, 5, 30)
b = date(2009, 6, 9)

for dt in rrule(DAILY, dtstart=a, until=b):
    print dt.strftime("%Y-%m-%d")

Diese Python-Bibliothek hat viele weitere erweiterte Funktionen, von denen einige sehr nützlich sind, wie z relative deltas – und wird als einzelne Datei (Modul) implementiert, die einfach in ein Projekt eingebunden werden kann.

  • Beachten Sie, dass das letzte Datum in der for-Schleife hier ist inklusive von until während das endgültige Datum der daterange Methode in Bers Antwort ist exklusiv von end_date.

    – Ninjakannon

    23. Mai 2015 um 16:33 Uhr


  • modernere Dokumente dateutil.readthedocs.io/en/stable/rrule.html

    – qwr

    27. Dezember 2019 um 0:33 Uhr

Benutzer-Avatar
fantastisch

Pandas eignet sich im Allgemeinen hervorragend für Zeitreihen und bietet direkte Unterstützung für Datumsbereiche.

import pandas as pd
daterange = pd.date_range(start_date, end_date)

Sie können dann den Datumsbereich durchlaufen, um das Datum zu drucken:

for single_date in daterange:
    print (single_date.strftime("%Y-%m-%d"))

Es hat auch viele Optionen, um das Leben einfacher zu machen. Wenn Sie beispielsweise nur Wochentage möchten, tauschen Sie einfach bdate_range aus. Sehen http://pandas.pydata.org/pandas-docs/stable/timeseries.html#generating-ranges-of-timestamps

Die Stärke von Pandas sind wirklich seine Datenrahmen, die vektorisierte Operationen (ähnlich wie numpy) unterstützen, die Operationen über große Datenmengen hinweg sehr schnell und einfach machen.

EDIT: Sie könnten die for-Schleife auch komplett überspringen und direkt drucken, was einfacher und effizienter ist:

print(daterange)

import datetime

def daterange(start, stop, step=datetime.timedelta(days=1), inclusive=False):
  # inclusive=False to behave like range by default
  if step.days > 0:
    while start < stop:
      yield start
      start = start + step
      # not +=! don't modify object passed in if it's mutable
      # since this function is not restricted to
      # only types from datetime module
  elif step.days < 0:
    while start > stop:
      yield start
      start = start + step
  if inclusive and start == stop:
    yield start

# ...

for date in daterange(start_date, end_date, inclusive=True):
  print strftime("%Y-%m-%d", date.timetuple())

Diese Funktion leistet mehr als Sie unbedingt benötigen, indem sie negative Schritte usw. unterstützt. Solange Sie Ihre Bereichslogik ausklammern, benötigen Sie die separate Funktion nicht day_count und vor allem wird der Code leichter lesbar, wenn Sie die Funktion von mehreren Stellen aus aufrufen.

  • Danke, umbenannt, um den Parametern des Bereichs besser zu entsprechen, vergessen, den Körper zu ändern.

    Roger Pate

    29. Juni 2009 um 20:56 Uhr

  • +1 … aber da Sie zulassen, dass der Schritt ein Zeitdelta ist, sollten Sie ihn entweder (a) dateTIMErange() nennen und Schritte von zB timedelta(hours=12) und timedelta(hours=36) richtig funktionieren lassen oder ( b) Schritte abfangen, die keine ganze Zahl von Tagen sind, oder (c) dem Anrufer den Ärger ersparen und den Schritt als Anzahl von Tagen anstelle eines Zeitdeltas ausdrücken.

    – John Machin

    30. Juni 2009 um 11:57 Uhr

  • Jedes timedelta sollte bereits funktionieren, aber ich habe datetime_range und date_range zu meiner persönlichen Schrottsammlung hinzugefügt, nachdem ich dies geschrieben habe, wegen (a). Ich bin mir nicht sicher, ob sich eine andere Funktion für (c) lohnt, der häufigste Fall von Tagen = 1 ist bereits erledigt, und die Übergabe eines expliziten Zeitdeltas vermeidet Verwirrung. Vielleicht ist es am besten, es irgendwo hochzuladen: bitbucket.org/kniht/scraps/src/tip/python/gen_range.py

    Roger Pate

    30. Juni 2009 um 20:49 Uhr

  • Damit dies an anderen Inkrementen als Tagen funktioniert, sollten Sie mit step.total_seconds() und nicht mit step.days prüfen

    – Amohr

    11. Mai 2016 um 5:37 Uhr

Benutzer-Avatar
Patrick

Dies ist die menschenlesbarste Lösung, die ich mir vorstellen kann.

import datetime

def daterange(start, end, step=datetime.timedelta(1)):
    curr = start
    while curr < end:
        yield curr
        curr += step

  • Danke, umbenannt, um den Parametern des Bereichs besser zu entsprechen, vergessen, den Körper zu ändern.

    Roger Pate

    29. Juni 2009 um 20:56 Uhr

  • +1 … aber da Sie zulassen, dass der Schritt ein Zeitdelta ist, sollten Sie ihn entweder (a) dateTIMErange() nennen und Schritte von zB timedelta(hours=12) und timedelta(hours=36) richtig funktionieren lassen oder ( b) Schritte abfangen, die keine ganze Zahl von Tagen sind, oder (c) dem Anrufer den Ärger ersparen und den Schritt als Anzahl von Tagen anstelle eines Zeitdeltas ausdrücken.

    – John Machin

    30. Juni 2009 um 11:57 Uhr

  • Jedes timedelta sollte bereits funktionieren, aber ich habe datetime_range und date_range zu meiner persönlichen Schrottsammlung hinzugefügt, nachdem ich dies geschrieben habe, wegen (a). Ich bin mir nicht sicher, ob sich eine andere Funktion für (c) lohnt, der häufigste Fall von Tagen = 1 ist bereits erledigt, und die Übergabe eines expliziten Zeitdeltas vermeidet Verwirrung. Vielleicht ist es am besten, es irgendwo hochzuladen: bitbucket.org/kniht/scraps/src/tip/python/gen_range.py

    Roger Pate

    30. Juni 2009 um 20:49 Uhr

  • Damit dies an anderen Inkrementen als Tagen funktioniert, sollten Sie mit step.total_seconds() und nicht mit step.days prüfen

    – Amohr

    11. Mai 2016 um 5:37 Uhr

Numpys arange Funktion kann auf Daten angewendet werden:

import numpy as np
from datetime import datetime, timedelta
d0 = datetime(2009, 1,1)
d1 = datetime(2010, 1,1)
dt = timedelta(days = 1)
dates = np.arange(d0, d1, dt).astype(datetime)

Die Verwendung von astype ist von umzuwandeln numpy.datetime64 zu einer Reihe von datetime.datetime Objekte.

  • Super schlanke Bauweise! Die letzte Zeile funktioniert bei mir mit dates = np.arange(d0, d1, dt).astype(datetime.datetime)

    – pyano

    9. August 2017 um 7:11 Uhr

  • +1 für die Veröffentlichung einer generischen Einzeiler-Lösung, die ein beliebiges Zeitdelta zulässt, anstelle eines festen gerundeten Schritts wie stündlich/minütlich/… .

    – F.Raab

    29. August 2018 um 16:39 Uhr

1115160cookie-checkDurchlaufen einer Reihe von Daten in Python

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy