2016-09-26 5 views
7

Ich brauche Hilfe bei der Umwandlung meiner Daten, damit ich Transaktionsdaten lesen kann.Erstellen Sie Gruppen/Klassen basierend auf Bedingungen in den Spalten

Business Case

ich zu einer Gruppe zusammen einige verwandte Transaktionen versuchen, einige Gruppen oder Klassen von Ereignissen zu erstellen. Dieser Datensatz repräsentiert Mitarbeiter, die aus verschiedenen Absenzen herauskommen. Ich möchte eine Klasse von Blättern basierend auf jeder Transaktion erstellen, die innerhalb von 365 Tagen der Urlaubsereignisklasse fällt. Um Trends zu chartern, möchte ich die Klassen nummerieren, damit ich eine Sequenz/ein Muster erhalte.

Mein Code ermöglicht es mir zu sehen, wann das allererste Ereignis aufgetreten ist, und es kann identifizieren, wenn eine neue Klasse gestartet wird, aber es nicht jede Transaktion in eine Klasse Bucket.

Anforderungen:

  • alle Zeilen Tag auf, was Klasse lassen sie fallen in.
  • Nummer jedes Unique Leave Event. Dieses Beispiel Index unter Verwendung von 0 Einzigartige Event verlassen 2 sein würde, Index 1 wäre würde Einzigartige Event verlassen 2, Index 3 Einzigartige Event verlassen 2 und Index 4 würde Einzigartige Event verlassen 1 usw.

I hinzugefügt werden in einer Spalte für die gewünschte Ausgabe, gekennzeichnet als "gewünschte Ausgabe". Beachten Sie, dass es viel mehr Zeilen/Ereignisse pro Person geben kann; und es kann viel mehr Leute geben.

Einige Daten

import pandas as pd 

data = {'Employee ID': ["100", "100", "100","100","200","200","200","300"], 
     'Effective Date': ["2016-01-01","2015-06-05","2014-07-01","2013-01-01","2016-01-01","2015-01-01","2013-01-01","2014-01"], 
     'Desired Output': ["Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 1"]} 
df = pd.DataFrame(data, columns=['Employee ID','Effective Date','Desired Output']) 

einige Code Ich habe versucht

df['Effective Date'] = df['Effective Date'].astype('datetime64[ns]') 
df['EmplidShift'] = df['Employee ID'].shift(-1) 
df['Effdt-Shift'] = df['Effective Date'].shift(-1) 
df['Prior Row in Same Emplid Class'] = "No" 
df['Effdt Diff'] = df['Effdt-Shift'] - df['Effective Date'] 
df['Effdt Diff'] = (pd.to_timedelta(df['Effdt Diff'], unit='d') + pd.to_timedelta(1,unit='s')).astype('timedelta64[D]') 
df['Cumul. Count'] = df.groupby('Employee ID').cumcount() 


df['Groupby'] = df.groupby('Employee ID')['Cumul. Count'].transform('max') 
df['First Row Appears?'] = "" 
df['First Row Appears?'][df['Cumul. Count'] == df['Groupby']] = "First Row" 
df['Prior Row in Same Emplid Class'][ df['Employee ID'] == df['EmplidShift']] = "Yes" 

df['Prior Row in Same Emplid Class'][ df['Employee ID'] == df['EmplidShift']] = "Yes" 

df['Effdt > 1 Yr?'] = ""           
df['Effdt > 1 Yr?'][ ((df['Prior Row in Same Emplid Class'] == "Yes") & (df['Effdt Diff'] < -365)) ] = "Yes" 

df['Unique Leave Event'] = "" 
df['Unique Leave Event'][ (df['Effdt > 1 Yr?'] == "Yes") | (df['First Row Appears?'] == "First Row") ] = "Unique Leave Event" 

df 

Antwort

2

Sie können dies tun, ohne Ihren Dataframe durchlaufen oder iterieren zu müssen. Pro Wes McKinney können Sie .apply() mit einem groupBy-Objekt verwenden und eine Funktion definieren, die auf das groupby-Objekt angewendet wird. Wenn Sie dies mit .shift() (like here) verwenden, können Sie Ihr Ergebnis erhalten, ohne Schleifen zu verwenden.

Terse Beispiel:

# Group by Employee ID 
grouped = df.groupby("Employee ID") 
# Define function 
def get_unique_events(group): 
    # Convert to date and sort by date, like @Khris did 
    group["Effective Date"] = pd.to_datetime(group["Effective Date"]) 
    group = group.sort_values("Effective Date") 
    event_series = (group["Effective Date"] - group["Effective Date"].shift(1) > pd.Timedelta('365 days')).apply(lambda x: int(x)).cumsum()+1 
    return event_series 

event_df = pd.DataFrame(grouped.apply(get_unique_events).rename("Unique Event")).reset_index(level=0) 
df = pd.merge(df, event_df[['Unique Event']], left_index=True, right_index=True) 
df['Output'] = df['Unique Event'].apply(lambda x: "Unique Leave Event " + str(x)) 
df['Match'] = df['Desired Output'] == df['Output'] 

print(df) 

Output:

Employee ID Effective Date  Desired Output Unique Event \ 
3   100  2013-01-01 Unique Leave Event 1    1 
2   100  2014-07-01 Unique Leave Event 2    2 
1   100  2015-06-05 Unique Leave Event 2    2 
0   100  2016-01-01 Unique Leave Event 2    2 
6   200  2013-01-01 Unique Leave Event 1    1 
5   200  2015-01-01 Unique Leave Event 2    2 
4   200  2016-01-01 Unique Leave Event 2    2 
7   300  2014-01 Unique Leave Event 1    1 

       Output Match 
3 Unique Leave Event 1 True 
2 Unique Leave Event 2 True 
1 Unique Leave Event 2 True 
0 Unique Leave Event 2 True 
6 Unique Leave Event 1 True 
5 Unique Leave Event 2 True 
4 Unique Leave Event 2 True 
7 Unique Leave Event 1 True 

Weitere ausführliches Beispiel zur Klarheit:

import pandas as pd 

data = {'Employee ID': ["100", "100", "100","100","200","200","200","300"], 
     'Effective Date': ["2016-01-01","2015-06-05","2014-07-01","2013-01-01","2016-01-01","2015-01-01","2013-01-01","2014-01"], 
     'Desired Output': ["Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 1"]} 
df = pd.DataFrame(data, columns=['Employee ID','Effective Date','Desired Output']) 

# Group by Employee ID 
grouped = df.groupby("Employee ID") 

# Define a function to get the unique events 
def get_unique_events(group): 
    # Convert to date and sort by date, like @Khris did 
    group["Effective Date"] = pd.to_datetime(group["Effective Date"]) 
    group = group.sort_values("Effective Date") 
    # Define a series of booleans to determine whether the time between dates is over 365 days 
    # Use .shift(1) to look back one row 
    is_year = group["Effective Date"] - group["Effective Date"].shift(1) > pd.Timedelta('365 days') 
    # Convert booleans to integers (0 for False, 1 for True) 
    is_year_int = is_year.apply(lambda x: int(x))  
    # Use the cumulative sum function in pandas to get the cumulative adjustment from the first date. 
    # Add one to start the first event as 1 instead of 0 
    event_series = is_year_int.cumsum() + 1 
    return event_series 

# Run function on df and put results into a new dataframe 
# Convert Employee ID back from an index to a column with .reset_index(level=0) 
event_df = pd.DataFrame(grouped.apply(get_unique_events).rename("Unique Event")).reset_index(level=0) 

# Merge the dataframes 
df = pd.merge(df, event_df[['Unique Event']], left_index=True, right_index=True) 

# Add string to match desired format 
df['Output'] = df['Unique Event'].apply(lambda x: "Unique Leave Event " + str(x)) 

# Check to see if output matches desired output 
df['Match'] = df['Desired Output'] == df['Output'] 

print(df) 

Sie erhalten die gleiche Ausgabe:

Employee ID Effective Date  Desired Output Unique Event \ 
3   100  2013-01-01 Unique Leave Event 1    1 
2   100  2014-07-01 Unique Leave Event 2    2 
1   100  2015-06-05 Unique Leave Event 2    2 
0   100  2016-01-01 Unique Leave Event 2    2 
6   200  2013-01-01 Unique Leave Event 1    1 
5   200  2015-01-01 Unique Leave Event 2    2 
4   200  2016-01-01 Unique Leave Event 2    2 
7   300  2014-01 Unique Leave Event 1    1 

       Output Match 
3 Unique Leave Event 1 True 
2 Unique Leave Event 2 True 
1 Unique Leave Event 2 True 
0 Unique Leave Event 2 True 
6 Unique Leave Event 1 True 
5 Unique Leave Event 2 True 
4 Unique Leave Event 2 True 
7 Unique Leave Event 1 True 
+0

Das ist eine elegante Lösung. Die einzige Gefahr könnte in der "Zusammenführung" liegen, wenn OP sehr große Datenrahmen verwendet, aber aufgrund des Inhalts der Daten unwahrscheinlich ist. – Khris

3

Das ist ein bisschen klobig, aber es ergibt sich die richtige Ausgabe zumindest für Ihr kleines Beispiel:

import pandas as pd 

data = {'Employee ID': ["100", "100", "100","100","200","200","200","300"], 
     'Effective Date': ["2016-01-01","2015-06-05","2014-07-01","2013-01-01","2016-01-01","2015-01-01","2013-01-01","2014-01-01"], 
     'Desired Output': ["Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 2","Unique Leave Event 2","Unique Leave Event 1","Unique Leave Event 1"]} 
df = pd.DataFrame(data, columns=['Employee ID','Effective Date','Desired Output']) 

df["Effective Date"] = pd.to_datetime(df["Effective Date"]) 
df = df.sort_values(["Employee ID","Effective Date"]).reset_index(drop=True) 

for i,_ in df.iterrows(): 
    df.ix[0,"Result"] = "Unique Leave Event 1" 
    if i < len(df)-1: 
    if df.ix[i+1,"Employee ID"] == df.ix[i,"Employee ID"]: 
     if df.ix[i+1,"Effective Date"] - df.ix[i,"Effective Date"] > pd.Timedelta('365 days'): 
     df.ix[i+1,"Result"] = "Unique Leave Event " + str(int(df.ix[i,"Result"].split()[-1])+1) 
     else: 
     df.ix[i+1,"Result"] = df.ix[i,"Result"] 
    else: 
     df.ix[i+1,"Result"] = "Unique Leave Event 1" 

Hinweis dass dieser Code annimmt, dass die erste Zeile immer die Zeichenfolge Unique Leave Event 1 enthält.

EDIT: Einige Erklärung.

Zuerst konvertiere ich die Datumsangaben in das Datetime-Format und ordne dann den Datenrahmen neu, so dass die Daten für jede Mitarbeiter-ID aufsteigend sind.

Dann Iteriere über die Zeilen des Frames mit dem integrierten Iterator iterrows. Die _ in for i,_ ist nur ein Platzhalter für die zweite Variable, die ich nicht verwende, weil der Iterator sowohl Zeilennummern als auch Zeilen zurückgibt, ich brauche nur die Zahlen hier.

Im Iterator mache ich zeilenweise Vergleiche, deshalb fülle ich standardmäßig die erste Zeile von Hand und weise sie dann der i+1-ten Zeile zu. Ich mache das so, weil ich den Wert der ersten Zeile, aber nicht den Wert der letzten Zeile kenne. Dann vergleiche ich die i+1 -te Zeile mit der i -te Zeile innerhalb eines if -safeguard, da i+1 bei der letzten Iteration einen Indexfehler ergeben würde.

In der Schleife überprüfe ich zuerst, ob Employee ID zwischen den zwei Zeilen geändert hat. Wenn nicht, dann vergleiche ich die Daten der zwei Zeilen und schaue, ob sie mehr als 365 Tage voneinander entfernt sind. Wenn dies der Fall ist, lese ich die Zeichenkette "Unique Leave Event X" aus der i -ten Zeile, erhöhe die Zahl um eins und schreibe sie in die i+1-Zeile. Wenn die Daten näher sind, kopiere ich einfach die Zeichenfolge aus der vorherigen Zeile.

Wenn die ändert sich auf der anderen Seite schreibe ich einfach "Unique Leave Event 1" neu zu starten.

Hinweis 1: iterrows() hat keine Optionen zu setzen, so dass ich nur über eine Teilmenge nicht iterieren kann.

Hinweis 2: Iterieren Sie immer mit einem der integrierten Iteratoren und iterieren Sie nur, wenn Sie das Problem sonst nicht lösen können.

Hinweis 3: Verwenden Sie beim Zuweisen von Werten in einer Iteration immer ix, loc oder iloc.

+0

Vielen Dank! Können Sie bitte einen Kommentar abgeben, wie Sie das gemacht haben? – Christopher

+0

Hallo, und Entschuldigung für die lange Wartezeit, ich kommentieren nur hier von der Arbeit und wir hatten ein dreitägiges Wochenende. Ich werde jetzt einen Kommentar hinzufügen. – Khris

Verwandte Themen