2016-12-01 6 views
2

Ich habe einen Datenrahmen von Mitarbeiter Gehaltsdaten (Probe wie folgt), in dem ‚Datum‘ bezieht sich auf, wenn das Gehalt des Mitarbeiters wirksam geworden:Pandas groupby + Resampling/TimeGrouper für Veränderungen über Monate von Anfang

Employee Date  Salary 
PersonA  1/1/2016 $50000 
PersonB  3/5/2014 $65000 
PersonB  3/1/2015 $75000 
PersonB  3/1/2016 $100000 
PersonC  5/15/2010 $75000 
PersonC  6/3/2011 $100000 
PersonC  3/10/2012 $110000 
PersonC  9/5/2012 $130000 
PersonC  3/1/2013 $150000 
PersonC  3/1/2014 $200000 

In dieser Beispiel: PersonA startete in diesem Jahr bei $ 50.000 und PersonC ist seit einiger Zeit bei der Firma und hat seit seinem Start am 15.05.2010 mehrere Erhöhungen erhalten.

Ich brauche die Date Spalte Months from Start, auf einen einzelnen Mitarbeiter Basis zu konvertieren, wo Months from Start in Schritten von m Monate sein wird (von mir angegeben). Zum Beispiel für personB, unter der Annahme, m=12, wäre das Ergebnis:

Employee Months From Start Salary 
PersonB  0     $65000 
PersonB  12     $65000 
PersonB  24     $75000 

Dies bedeutet, dass im Monat 0 (Arbeitsbeginn), personB ein Gehalt von $ 65.000 hatte; 12 Monate später betrug sein Gehalt $ 65.000, und 24 Monate später betrug sein Gehalt $ 75.000. Beachten Sie, dass das nächste Inkrement (36 Monate) NICHT auf dem transformierten Datenrahmen für PersonB erscheinen würde, da diese Dauer die Dauer der Beschäftigung von PersonB überschreitet (es wäre in der Zukunft).

Beachten Sie noch einmal, dass ich m zu jedem Monatsinkrement anpassen kann. Wenn ich Schritten von 6 Monaten (m=6) wollte, wäre das Ergebnis:

Employee Months From Start Salary 
PersonB  0     $65000 
PersonB  6     $65000 
PersonB  12     $65000 
PersonB  18     $75000 
PersonB  24     $100000 
PersonB  30     $100000 

Als letzten Schritt, würde ich auch Gehalt des Mitarbeiters ab heute sind gerne auf dem transformierten Datenrahmen. Mit personB wieder und m=6 unter der Annahme, bedeutet dies, dass die Ergebnisse sein würden:

Employee Months From Start Salary 
PersonB  0     $65000 
PersonB  6     $65000 
PersonB  12     $65000 
PersonB  18     $75000 
PersonB  24     $100000 
PersonB  30     $100000 
PersonB  32.92    $100000 <--added (today is 32.92 months from start) 

Frage: gibt es eine programmatische Art und Weise (ich nehme an zumindest über einen Link: groupby, resample oder TimeGrouper) zu erreichen, der oben beschriebene gewünschte Datenrahmen?

Hinweis: Sie können davon ausgehen, dass alle Mitarbeiter aktiv sind (das Unternehmen nicht verlassen haben).

Antwort

0

Vielen Dank an die bereitgestellten Antworten. Leider sind alle Antworten ein wenig "aus" und haben das Ziel nicht ganz erreicht. Ich habe dann zwei for Loops in List Comprehensions verschachtelt, um das Ziel zu erreichen.

1

können Sie verwenden, um die groupby und merge Funktionalitäten von DataFrames

>>> import pandas as pd 
>>> df = pd.DataFrame([['PersonC','5/15/2010',75000],['PersonC','7/3/2011',100000],['PersonB','3/5/2014',65000],['PersonB','3/1/2015',75000],['PersonB','3/1/2016',100000]],columns=['Employee','Date','Salary']) 
>>> df['Date']= pd.to_datetime(df['Date']) 
>>> df 
    Employee  Date Salary 
0 PersonC 2010-05-15 75000 
1 PersonC 2011-07-03 100000 
2 PersonB 2014-03-05 65000 
3 PersonB 2015-03-01 75000 
4 PersonB 2016-03-01 100000 
>>> satrt_date = df.groupby('Employee')['Date'].min().to_frame().rename(columns={'Date':'Start Date'}) 
>>> satrt_date['Employee'] = satrt_date.index 
>>> df = df.merge(satrt_date,how='left', on= 'Employee') 
>>> df['Months From Start'] = df['Date']-df['Start Date'] 
>>> df['Months From Start'] = df['Months From Start'].apply(lambda x: x.days) 
>>> df['Months From Start']= df['Months From Start'].apply(lambda x: (x/30) - (x/30)%6) 
>>> df 
    Employee  Date Salary Start Date Months From Start 
0 PersonC 2010-05-15 75000 2010-05-15     0 
1 PersonC 2011-07-03 100000 2010-05-15     12 
2 PersonB 2014-03-05 65000 2014-03-05     0 
3 PersonB 2015-03-01 75000 2014-03-05     12 
4 PersonB 2016-03-01 100000 2014-03-05     24 

Hier können Sie 6 mit einer variablen m genannt ersetzen können und beliebige Werte, um es

1

OK zuweisen, so dass für den ersten Teil des antwort Ich würde so etwas tun ...

import numpy as np 
import pandas as pd 

df = pd.DataFrame({ 
    'Employee': ['PersonA', 'PersonB', 'PersonB', 'PersonB', 'PersonC', 'PersonC', 'PersonC', 'PersonC', 'PersonC', 'PersonC'], 
    'Date': ['1/1/2016', '3/5/2014', '3/1/2015', '3/1/2016', '5/15/2010', '6/3/2011', '3/10/2012', '9/5/2012', '3/1/2013', '3/1/2014'], 
    'Salary': [50000 , 65000 , 75000 , 100000 , 75000 , 100000 , 110000 , 130000 , 150000 , 200000] 
}) 

df.Date = pd.to_datetime(df.Date) 

m = 6 
emp_groups = df.groupby('Employee') 
df['months_from_start'] = df.Date - emp_groups.Date.transform(min) 
df.months_from_start = df.months_from_start.dt.days/30 // m * m 

m kann sein, was immer Sie wollen. Ich berechne die Tage zwischen dem min Datum dann durch die ungefähre Anzahl von Tagen in einem Monat dividiert und dann ein bisschen Integer-Division zu "runden" auf die Fenstergröße, die Sie wollen.

Dies wird Ihnen so etwas wie dieses ...

 Date Employee Salary months_from_start 
0 2016-01-01 PersonA 50000     0 
1 2014-03-05 PersonB 65000     0 
2 2015-03-01 PersonB 75000     12 
3 2016-03-01 PersonB 100000     24 
4 2010-05-15 PersonC 75000     0 
5 2011-06-03 PersonC 100000     12 
6 2012-03-10 PersonC 110000     18 
7 2012-09-05 PersonC 130000     24 
8 2013-03-01 PersonC 150000     30 
9 2014-03-01 PersonC 200000     42 

Der zweite Teil ist ein wenig kompliziert.Ich würde eine neue df und concat zur ersten ...

last_date_df = emp_groups.last() 
last_date_df.months_from_start = (last_date_df.Date - emp_groups.first().Date).dt.days/30 
last_date_df.reset_index(inplace=True) 

pd.concat([df, last_date_df], axis=0) 

Sie bekommen ...

 Date Employee Salary months_from_start 
0 2016-01-01 PersonA 50000   0.000000 
1 2014-03-05 PersonB 65000   0.000000 
2 2015-03-01 PersonB 75000   12.000000 
3 2016-03-01 PersonB 100000   24.000000 
4 2010-05-15 PersonC 75000   0.000000 
5 2011-06-03 PersonC 100000   12.000000 
6 2012-03-10 PersonC 110000   18.000000 
7 2012-09-05 PersonC 130000   24.000000 
8 2013-03-01 PersonC 150000   30.000000 
9 2014-03-01 PersonC 200000   42.000000 
0 2016-01-01 PersonA 50000   0.000000 
1 2016-03-01 PersonB 100000   24.233333 
2 2014-03-01 PersonC 200000   46.200000 
2

erstellen Sie können group_by kombinieren und neu berechnen, es zu tun. Um ressample zu verwenden, müssen Sie das Datum als Index haben.

df.index = pd.to_datetime(df.Date) 
df.drop('Date',axis = 1, inplace = True) 

Dann:

df.groupby('Employee').resample('6m').pad() 

In diesem Fall verwende ich 6 Monate Perioden. Beachten Sie, dass es den letzten Tag eines jeden Monats bekommen wird, ich hoffe, es wird kein Problem sein. Dann haben Sie:

Employee Date  Salary 
0 PersonA 2016-01-31 $50000 
1 PersonB 2014-03-31 $65000 
2 PersonB 2014-09-30 $65000 
3 PersonB 2015-03-31 $75000 
4 PersonB 2015-09-30 $75000 
5 PersonB 2016-03-31 $100000 
6 PersonC 2010-05-31 $75000 
7 PersonC 2010-11-30 $75000 
8 PersonC 2011-05-31 $75000 
9 PersonC 2011-11-30 $100000 
10 PersonC 2012-05-31 $110000 
11 PersonC 2012-11-30 $130000 
12 PersonC 2013-05-31 $150000 
13 PersonC 2013-11-30 $150000 
14 PersonC 2014-05-31 $200000 

Jetzt können Sie die „Monate seit Beginn der“ Spalte (cumcount Funktion überprüft die Reihenfolge, in der jede Zeile innerhalb ihrer Gruppe erscheint) erstellen. Denken Sie daran, es mit der Anzahl der Monate multiplizieren, die Sie für jeden Zeitraum (in diesem Fall 6) verwenden:

df['Months since started'] = df.groupby('Employee').cumcount()*6 

    Employee Date  Salary  Months since started 
0 PersonA 2016-01-31 $50000     0 
1 PersonB 2014-03-31 $65000     0 
2 PersonB 2014-09-30 $65000     6 
3 PersonB 2015-03-31 $75000     12 
4 PersonB 2015-09-30 $75000     18 
5 PersonB 2016-03-31 $100000     24 
6 PersonC 2010-05-31 $75000     0 
7 PersonC 2010-11-30 $75000     6 
8 PersonC 2011-05-31 $75000     12 
9 PersonC 2011-11-30 $100000     18 
10 PersonC 2012-05-31 $110000     24 
11 PersonC 2012-11-30 $130000     30 
12 PersonC 2013-05-31 $150000     36 
13 PersonC 2013-11-30 $150000     42 
14 PersonC 2014-05-31 $200000     48 

Ich hoffe, es hat geholfen!

+0

Danke für die Hinweise. Ein Problem, auf das ich gestoßen bin, ist, dass einige Angestellte im vollen Datensatz am selben Tag ihre Gehälter in Kraft setzen. Das Setzen von "index" auf "df.Date" verstößt also gegen die eindeutige Indexanforderung, die "resample" anscheinend will (ich erhalte diese) Fehler: 'ValueError: kann von einer doppelten Achse nicht neu indizieren'). Lass es mich wissen, wenn du Ideen hast. – NickBraunagel