it-swarm.com.de

Liste der Monate zwischen Intervallen in Python erstellen

Ich möchte eine Python-Liste erstellen, die alle Monate zwischen zwei Datumsangaben enthält, wobei die Eingabe und Ausgabe wie folgt formatiert sind:

date1 = "2014-10-10"  # input start date
date2 = "2016-01-07"  # input end date
month_list = ['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16']  # output
20
Birlla
>>> from datetime import datetime, timedelta
>>> from collections import OrderedDict
>>> dates = ["2014-10-10", "2016-01-07"]
>>> start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
>>> OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()
['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16']

Update: etwas Erklärung, wie in einem Kommentar angefordert. Hierbei gibt es drei Probleme: Parsen der Datumsangaben in entsprechende Datenstrukturen (strptime); Ermitteln des Datumsbereichs unter Berücksichtigung der beiden Extreme und des Schritts (einen Monat); Formatieren der Ausgabedaten (strftime). Der Typ datetime überlastet den Subtraktionsoperator, sodass end - start sinnvoll ist. Das Ergebnis ist ein timedelta-Objekt, das die Differenz zwischen den beiden Datumsangaben darstellt, und das .days-Attribut erhält diese Differenz in Tagen. Es gibt kein .months-Attribut. Wir iterieren also jeweils einen Tag und konvertieren die Datumsangaben in das gewünschte Ausgabeformat. Dies führt zu vielen Duplikaten, die OrderedDict entfernt, während die Elemente in der richtigen Reihenfolge bleiben.

Dies ist nun einfach und prägnant, da das datetime-Modul die gesamte Arbeit erledigen kann, es ist jedoch auch ineffizient und ineffizient. Wir rufen viele Methoden für jeden Tag an, während wir nur Monate ausgeben müssen. Wenn die Leistung kein Problem darstellt, ist der obige Code in Ordnung. Ansonsten müssen wir etwas mehr arbeiten. Vergleichen wir die obige Implementierung mit einer effizienteren:

from datetime import datetime, timedelta
from collections import OrderedDict

dates = ["2014-10-10", "2016-01-07"]

def monthlist_short(dates):
    start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
    return OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()

def monthlist_fast(dates):
    start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
    total_months = lambda dt: dt.month + 12 * dt.year
    mlist = []
    for tot_m in xrange(total_months(start)-1, total_months(end)):
        y, m = divmod(tot_m, 12)
        mlist.append(datetime(y, m+1, 1).strftime("%b-%y"))
    return mlist

assert monthlist_fast(dates) == monthlist_short(dates)

if __== "__main__":
    from timeit import Timer
    for func in "monthlist_short", "monthlist_fast":
        print func, Timer("%s(dates)" % func, "from __main__ import dates, %s" % func).timeit(1000)

Auf meinem Laptop bekomme ich folgende Ausgabe:

monthlist_short 2.3209939003
monthlist_fast 0.0774540901184

Die prägnante Implementierung ist etwa 30-mal langsamer, daher würde ich sie in zeitkritischen Anwendungen nicht empfehlen :)

27
simleo

Ich habe mit Pandas einen sehr kurzen Weg gefunden, dies zu tun, falls es jemandem helfen sollte:


UPDATE: Ich habe es mit diesem Beitrag :) auf einen Einzeiler gebracht. 

pd.date_range('2014-10-10','2016-01-07', 
              freq='MS').strftime("%Y-%b").tolist()

ALTE ANTWORT:

daterange = pd.date_range('2014-10-10','2016-01-07' , freq='1M') 
daterange = daterange.union([daterange[-1] + 1])  
daterange = [d.strftime('%y-%b') for d in daterange]

Die zweite Zeile verhindert, dass das letzte Datum aus der Liste entfernt wird.

28
atkat12

Sie müssen Calendar und Datetime verwenden. 

import calendar
from datetime import *
date1 = datetime.strptime("2014-10-10", "%Y-%m-%d")
date2 = datetime.strptime("2016-01-07", "%Y-%m-%d")
months_str = calendar.month_name
months = []
while date1 < date2:
    month = date1.month
    year  = date1.year
    month_str = months_str[month][0:3]
    months.append("{0}-{1}".format(month_str,str(year)[-2:]))
    next_month = month+1 if month != 12 else 1
    next_year = year + 1 if next_month == 1 else year
    date1 = date1.replace( month = next_month, year= next_year)

print months

Dieser Code wird zurückgegeben 

['Oct-14', 'Nov-14', 'Dec-14', 'Jan-14', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-15']
5
Luis González

Mit Pandas können Sie einen Liner wie folgt haben:

import pandas as pd

date1 = "2014-10-10"  # input start date
date2 = "2016-01-07"  # input end date

month_list = [i.strftime("%b-%y") for i in pd.date_range(start=date1, end=date2, freq='MS')]
4
tmsss

Wenn Sie daran interessiert sind, Ihre Daten im Format Python) zu speichern, können Sie versuchen, to_pydatetime() zu verwenden.

import pandas as pd
from datetime import datetime

datemin = datetime(2010, 1, 1)
datemax = datetime(2019, 12, 31)

# First day of month
pd.date_range(datemin, datemax, freq='MS').to_pydatetime().tolist()

# Last day of month
pd.date_range(datemin, datemax, freq='M').to_pydatetime().tolist()

Liste der Versatz-Aliase

1
Nunya

Im Folgenden finden Sie meinen Ansatz für dieses Problem mithilfe von split und einfachen modulo-basierten iterationen, ohne ein spezielles Modul zu importieren.

date1 = "2014-10-10"
date2 = "2016-01-07"

y0 = int( date1.split('-')[0] ) # 2014
y1 = int( date2.split('-')[0] ) # 2016

m0 = int( date1.split('-')[1] ) - 1 # 10-1 --> 9 because will be used for indexing
m1 = int( date2.split('-')[1] ) - 1 # 01-1 --> 0 because will be used for indexing

months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
result = []
start = m0
for y in range(y0, y1+1):
    for m in range(start,12):
        result.append( str( months[m  % 12])+'-'+str(y) )
        if y == y1 and (m % 12) == m1:
            break
    start = 0

print result

$ python dates.py  

['Oct-2014', 'Nov-2014', 'Dec-2014', 'Jan-2015', 'Feb-2015', 'Mar-2015', 'Apr-2015', 'May-2015', 'Jun-2015', 'Jul-2015', 'Aug-2015', 'Sep-2015', 'Oct-2015', 'Nov-2015', 'Dec-2015', 'Jan-2016']
1
funk

Nachdem ich zuvor ähnliche Sachen gemacht hatte, versuchte ich, dies zu lösen. Die Verwendung unterschiedlicher Komponenten ist flexibler und ermöglicht Ihnen das Mischen und Anpassen für unterschiedliche Anwendungsfälle. Sie können auf diese Weise auch leichter getestet werden, wie Sie den Doctests in iterate_months entnehmen können.

Außerdem empfehle ich, datetime.date-Objekte für Ihre Eingabe zu verwenden, da Sie damit mehr erreichen können. Dazu müssen Sie Ihre Eingabezeichenfolge zunächst analysieren, dies ist jedoch sehr einfach.

Parsen der Datums-Zeichenfolgen

def datify(date):
    if isinstance(date, datetime.date):
        return date
    Elif isinstance(date, datetime.datetime):
        return date.date()
    else:
        # taken from simleo's answer
        return datetime.strptime(date, "%Y-%m-%d")

Zuerst durchlaufen wir die Monate

import datetime


def iterate_months(start_date, end_date):
    """Iterate monthly between two given dates.

    Emitted will be the first day of each month.

    >>> list(iterate_months(datetime.date(1999, 11, 1),
    ...                     datetime.date(2000, 2, 1)))
    [datetime.date(1999, 11, 1), datetime.date(1999, 12, 1),\
 datetime.date(2000, 1, 1), datetime.date(2000, 2, 1)]

    """
    assert isinstance(start_date, datetime.date)
    assert isinstance(end_date, datetime.date)
    assert start_date < end_date

    year = start_date.year
    month = start_date.month
    while True:
        current = datetime.date(year, month, 1)
        yield current
        if current.month == end_date.month and current.year == end_date.year:
            break
        else:
            month = ((month + 1) % 12) or 12
            if month == 1:
                year += 1


if __== '__main__':
    import doctest
    doctest.testmod()

Um Ihre Daten zu formatieren, verwenden Sie so etwas

def format_month(date):
    return date.strftime(r"%b-%y")

Alles zusammenstellen

start = datify("2014-10-10")
end = datify("2016-01-07")

for entry in iterate_months(start, end):
    print format_month(entry)

Oder speichern Sie es als Liste:

result = list(iterate_months(start, end))
1
pi.

Hier ist meine Lösung mit einem einfachen Listenverständnis, das range verwendet, um zu wissen, wo Monate beginnen und enden müssen

from datetime import datetime as dt
sd = dt.strptime('2014-10-10', "%Y-%m-%d") 
ed = dt.strptime('2016-01-07', "%Y-%m-%d") 

lst = [dt.strptime('%2.2d-%2.2d' % (y, m), '%Y-%m').strftime('%b-%y') \
       for y in xrange(sd.year, ed.year+1) \
       for m in xrange(sd.month if y==sd.year else 1, ed.month+1 if y == ed.year else 13)]

print lst

produziert

['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16']
0
Pynchia