Vergleich von Datumsangaben in verschiedenen Zeilen

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
de_crank
User
Beiträge: 5
Registriert: Dienstag 22. August 2023, 15:10

Hallo,
ich habe eine große Datenmenge, hier ist einmal ein Beispiel:

Code: Alles auswählen

| Seriennummer | Equipmentnummer || date1 | date2   |
| -------- | -------- || -------- | --------   |
| Value1   | Value2   ||2010-12-06| 2014-03-14 |
| Value1   | Value2   ||2014-04-13| 2017-10-01 |
| Value1   | Value2   ||2017-11-01|            |
| Value3   | Value4   ||2010-12-06| 2011-03-14 |
| Value3   | Value4   ||2014-04-13| 2017-10-01 |
In diesem Dataset hatte ich schon die Spalte Seriennummer und die Spalte Equipmentnummer gegrouped und gefilter mit folgender Anweisung. Danke an @b__blackjack__ nochmal für die Hilfe damals!

Code: Alles auswählen

grouping_data = data[["Seriennummer ", "Equipmentnummer "]]
    group_ids = (
        (grouping_data != grouping_data.shift()).any(axis="columns").cumsum()
    )
    result = data.loc[
        group_ids.groupby(group_ids)
        .filter(lambda group: len(group) >= 2)
        .index
    ]
Mit dem oben genannten Beispiel möchte ich für jede Gruppe (also wo die Seriennnummer die Gleiche ist) folgendes machen:
1. Kriterium: (2014-04-13) - (2014-03-14) <70
2. Kriterium (2014-03-14) - (2010-12-06) >900.
In diesem Schema soll verglichen werden, also dementsprechend dann auch (2017-11-01) - (2017-10-01).
Nur wenn diese Kriterien erfüllt sind, möchte ich die ganze Zeile beibehalten. Zur Info: die 70 und die 900 sollen Tage darstellen.

Der Output soll also wie folgt aussehen:

Code: Alles auswählen

| Seriennummer | Equipmentnummer || date1 | date2   |
| -------- | -------- || -------- | --------   |
| Value1   | Value2   ||2010-12-06| 2014-03-14 |
| Value1   | Value2   ||2014-04-13| 2017-10-01 |
| Value1   | Value2   ||2017-11-01|            |
Ich habe bereits eine Lösung gefunden, welche aber alles andere als elegant ist. Diese besteht aus über viele Woche zusammgewürfelten Code, welche die gewollte Aufgabe verrichtet, jedoch für Lernzwecke ich gerne eine saubere Lösung aufschreiben möchte.

Ich hatte mich gefragt, ob mein Vorhaben auch ohne Schleifen realisiert werden kann, sondern mit pandas-Anweisung wie in meinem ersten Code. Über jegliche Tipps und Anregungen würde ich mich freuen:

Hier mein Code mit Kommentaren:

Code: Alles auswählen

import pandas as pd
import numpy as np


data = pd.DataFrame([['Value1', 'Value2','2010-12-06' , '2014-03-14'],
 ['Value1', 'Value2','2014-04-13', '2017-10-01'], 
['Value1', 'Value2','2017-11-01',''],
['Value3', 'Value4', '2010-12-06', '2011-03-14'], 
['Value3', 'Value4', '2014-04-13', '2017-10-01']], columns=['Seriennummer', 'Equipmentnummer', 'date1', 'date2'])

grouping_data = data[["Seriennummer", "Equipmentnummer"]]
group_ids = (
     (grouping_data != grouping_data.shift()).any(axis="columns").cumsum()
)
 
result = data.loc[
     group_ids.groupby(group_ids)
     .filter(lambda group: len(group) >= 2)
     .index    
]

#Hinzufügen von 'value_pair' als Hilfszeile
#Die Daten für jede Seriennummer wird in eine Zeile geschrieben
result['value_pair'] = (result[['date1','date2']]).values.tolist()

data_grouped = result.groupby(['Seriennummer','Equipmentnummer'])['value_pair'].apply(list).reset_index()
data_grouped = pd.concat([data_grouped.iloc[-1:], data_grouped.iloc[:-1]], axis=0).reset_index(drop=True)

#Um aus dem Array ne Liste zu machen und somit besser draufzugreifen können
data_grouped['value_pair'] = data_grouped[['value_pair']].applymap(np.concatenate)

#Definition der beiden Kriterien
A, B, C, D, E =1, 2, 3, 4, 5

#Abstand zwischen den Tagen horizontal nebeneinander: >900 Tage
#Abstand zwischen den Tagen horizontal nebeneinander: <70 Tage
def diff(lst, gap1=70, gap2=900):
    ats, bts, cts, dts, ets = lst[A-1], lst[B-1], lst[C-1], lst[D-1], None

    if len(lst) > 4:
        ets = lst[E-1]
    
    try:
        condition1 = (cts - bts).days < gap1 if (cts and bts) else False
        condition2 = (bts - ats).days > gap2 if (bts and ats) else False

        if len(lst) > 4:
            condition3 = (ets - dts).days < gap1 if (ets and dts) else False
            condition4 = (dts - cts).days > gap2 if (dts and cts) else False
            return (condition1 and condition2) and (condition3 and condition4)
        else:
            return condition1 and condition2
    except IndexError:
        return False
#Anwenden der beiden Kriterien
m = [diff(list(map(lambda x: pd.to_datetime(x), lst))) for lst in data_grouped["value_pair"]]

out = data_grouped.loc[m]

#convert to datetime
def convert_to_datetime(value_list):
    # Convert None to NaT for datetime compatibility
    value_list = [pd.NaT if v is None else pd.to_datetime(v) for v in value_list]
    return value_list

#.copy da es sonst nicht funktioniert; hier hab ich den leichten Weg genommen
out = out.copy()
out['value_pair'] = out['value_pair'].apply(convert_to_datetime)

#convert to string
def to_str1(df1):
    df1 = [None if v is pd.NaT else pd.to_datetime(v).strftime('%Y-%m-%d') for v in df1]
    return df1

out['value_pair'] = out['value_pair'].apply(to_str1)

out['value_pair'] = out['value_pair'].apply(lambda x: [x[i:i+2] for i in range(0, len(x), 2)] if x is not None else None)
out = out.explode('value_pair').reset_index(drop=True)


# Spalte 'value_pair' in 'date1' und 'date2' aufteilen
out[['date1', 'date2']] = pd.DataFrame(out['value_pair'].tolist(), index=out.index)

# Die Spalte 'value_pair' entfernen
out = out.drop('value_pair', axis=1)
#Zusammemfügen des ursprünglichen dataframes (alle Spalten werden benötigt) mit den gewollten Zeilen aus out
df3 = data.merge(out, how="inner")

print(out)
Antworten