Wer mit Pandas Schleifen schreibt, macht meist was falsch

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
Benutzeravatar
Dennis89
User
Beiträge: 1152
Registriert: Freitag 11. Dezember 2020, 15:13

Guten Abend,

das Zitat aus der Überschrift habe ich hier nun schon oft in verschiedenen Varianten gefunden.
Das würde ich jetzt gerne zur Übung anwenden wollen, ich finde in der rießen Pandas-Doku leider nicht wo nach ich suche.

Ich will mir jedes Datum ausgeben lassen, dass die Zahlen des Jahres enthält. Für das Jahr 2001 wäre die erste Ausgabe "02.01", dann der "20.01" und so weiter bis das Jahr durch ist und dann das nächste Jahr.
Das ist eine allgemeine Übungsaufgabe auf die ich zufällig gestoßen bin und das will ich gerne mit Pandas machen.

So habe ich mal meinen Bereich:

Code: Alles auswählen

from datetime import date
import pandas as pd


YEAR_START = 2001
YEAR_END = 2022


def main():
    date_range = pd.date_range(date(YEAR_START, 1, 1), date(YEAR_END, 1, 1))


if __name__ == "__main__":
    main()
Ohne Pandas würde ich jetzt darüber iterieren und abfragen ob der Tage und das Monat im Jahr vorkommt. Dazu würde ich "in" verwenden und mir je nach dem das Datum dann ausgeben lassen. Aber das geht ja mit Pandas dann bestimmt wesentlich kürzer und einfacher.(?)

Hättet ihr mir da vielleicht die Stichwörter, nach denen ich in der Doku suchen muss? Da komme ich mir noch etwas arg verloren vor.

Danke und Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
__blackjack__
User
Beiträge: 13068
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Dennis89: Ich weiss nicht ob ich das mit Pandas lösen wollen würde. Geht ``in`` denn überhaupt? Die 0 muss ja zweimal drin vorkommen, das lässt sich mit ``in`` doch gar nicht prüfen.

Wenn man die Menge der Tage in dem Jahr generiert und filtert, hat man 365 bzw. 366 Werte die man überprüfen muss. Wenn man stattdessen die Permutationen von 2, 0, 0, 1 generiert und testet ob das jeweils ein gültiger Tag in dem Jahr ist, braucht man nur 24 Werte überprüfen.

Code: Alles auswählen

#!/usr/bin/env python3
from datetime import date as Date
from functools import partial
from itertools import permutations
from pprint import pprint

from more_itertools import map_except


def create_date(year, digits):
    return Date(year, int(digits[2] + digits[3]), int(digits[0] + digits[1]))


def main():
    year = 2001
    pprint(
        sorted(
            set(
                map_except(
                    partial(create_date, year),
                    permutations(str(year)),
                    ValueError,
                )
            )
        )
    )


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Testen des Datums kann man natürlich auch mit pandas machen:

Code: Alles auswählen

from itertools import permutations
import pandas

year = "2021"
series = pandas.Series(f'{year}-{a}{b}-{c}{d}' for a,b,c,d in permutations(year))
series = pandas.to_datetime(series, errors='coerce', format='%Y-%m-%d')
dates = series[~series.isna()]
Natürlich kann man auch mit Deinem Ansatz starten:

Code: Alles auswählen

dates = pandas.Series(pandas.date_range(date(2001, 1, 1), date(2021, 1, 1)))
monthdays = date_range.dt.strftime('%d%m').apply(sorted)
years = date_range.dt.strftime('%Y').apply(sorted)
dates = dates[monthdays == years]
Benutzeravatar
Dennis89
User
Beiträge: 1152
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

@__blackjack__ auf die Idee wäre ich gar nicht gekommen, vielen Dank dafür. Ich habe die Aufgabe gelesen und da war für mich die Vorgehensweise, so wie ich sie beschrieben hatte, fest im Kopf. Ich dachte dass das vielleicht eine schöne Aufgabe für Pandas wäre.

@Sirius3 vielen Dank, soetwas habe ich gesucht. Testen konnte ich es zwar noch nicht, aber das kommt dann gegen später. Auf jeden Fall weis ich jetzt, nach was ich in der Doku suchen muss.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
snafu
User
Beiträge: 6737
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Aufbauend auf dem pandas-Code:

Code: Alles auswählen

from itertools import permutations, starmap
import pandas as pd

def get_date_strings(year):
    candidates = set(permutations(str(year)))
    month_day_pairs = starmap("{}{}-{}{}".format, sorted(candidates))
    return map(f"{year}-{{}}".format, month_day_pairs)

def get_date_series(year):
    series = pd.Series(get_date_strings(year))
    dates = pd.to_datetime(series, errors="coerce", format="%Y-%m-%d")
    return dates[~dates.isna()]

print(get_date_series(2021))
Diese Variante kommt ohne Schleifen aus (auf Python-Ebene natürlich), hat keine Duplikate mehr drin und liefert die Datumsangaben in sortierter Reihenfolge.
Benutzeravatar
snafu
User
Beiträge: 6737
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und hier etwas näher an BlackJacks Vorschlag angelehnt, wodurch keine Date-Strings mehr gebaut werden müssen:

Code: Alles auswählen

from functools import partial
from itertools import permutations
import pandas as pd

def get_date_tuple(year, digits):
    month = int(digits[0] + digits[1])
    day = int(digits[2] + digits[3])
    return (year, month, day)

def get_candidates(year):
    candidates = set(permutations(str(year)))
    date_tuples = map(partial(get_date_tuple, year), sorted(candidates))
    return dict(zip(["year", "month", "day"], zip(*date_tuples)))

def get_date_series(year):
    dates = pd.to_datetime(get_candidates(year), errors="coerce")
    return dates[~dates.isna()]

print(get_date_series(2021))
Benutzeravatar
Dennis89
User
Beiträge: 1152
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo snafu,

auch dir vielen Dank für die Beispiele.

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Antworten