2016-11-25 2 views
6

Dies ist ein Beispiel des Datenrahmens mit arbeite ich:Pandas zählen aufeinander folgende Datum Beobachtungen innerhalb groupby Objekts

d = { 
'item_number':['bdsm1000', 'bdsm1000', 'bdsm1000', 'ZZRWB18','ZZRWB18', 'ZZRWB18', 'ZZRWB18', 'ZZHP1427BLK', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1427', 'ZZHP1414', 'ZZHP1414', 'ZZHP1414', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115WNTR', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE', 'WRM115SCFRE'], 
'Comp_ID':[2454, 2454, 2454, 1395, 1395, 1395, 1395, 3378, 1266941, 660867, 43978, 1266941, 660867, 43978, 1266941, 660867, 43978, 1266941, 660867, 43978, 43978, 43978, 43978, 1197347907, 70745, 4737, 1197347907, 4737, 1197347907, 70745, 4737, 1197347907, 70745, 4737, 1197347907, 4737, 1197487704, 1197347907, 70745, 23872, 4737, 1197347907, 4737, 1197487704, 1197347907, 23872, 4737, 1197487704, 1197347907, 70745], 
'date':['2016-11-22', '2016-11-20', '2016-11-19', '2016-11-22', '2016-11-20', '2016-11-19', '2016-11-18', '2016-11-22', '2016-11-22', '2016-11-22', '2016-11-22', '2016-11-20', '2016-11-20', '2016-11-20', '2016-11-19', '2016-11-19', '2016-11-19', '2016-11-18', '2016-11-18', '2016-11-18', '2016-11-22', '2016-11-20', '2016-11-19', '2016-11-22', '2016-11-22', '2016-11-22', '2016-11-21', '2016-11-21', '2016-11-20', '2016-11-20', '2016-11-20', '2016-11-19', '2016-11-19', '2016-11-19', '2016-11-18', '2016-11-18', '2016-11-22', '2016-11-22', '2016-11-22', '2016-11-22', '2016-11-22', '2016-11-21', '2016-11-21', '2016-11-20', '2016-11-20', '2016-11-20', '2016-11-20', '2016-11-19', '2016-11-19', '2016-11-19']} 

df = pd.DataFrame(data=d) 
df.date = pd.to_datetime(df.date) 

Ich mag würde aufeinanderfolgende Beobachtungen von 2016.11.22 beginnen zu zählen, dass es gruppiert nach Comp_ID und Elementnummer.

Im Wesentlichen, was ich zu tun versuche, ist zu zählen, wie viele Tage in Folge gibt es eine Beobachtung aus dem heutigen Datum für jede Comp_ID und Elementnummer gezählt. (Dieses Beispiel wurde am 22. Nov. zusammengestellt.) Aufeinanderfolgende Beobachtungen, die vor dem heutigen Tag beobachtet wurden, sind nicht relevant. Nur Sequenzen wie heute ... gestern ... vorgestern ... und so weiter sind relevant.

Ich habe dies auf ein kleineres Beispiel zu arbeiten, aber es scheint, auf einem größeren Datensatz zu stolpern.

Hier ist der Code für das kleinere Beispiel. Ich muss die aufeinanderfolgenden Daten mit Beobachtungen über Tausende von Verkäufern/Artikeln finden. Aus irgendeinem Grund funktionierte der folgende Code nicht für den größeren Datensatz.

d = {'item_number':['KIN005','KIN005','KIN005','KIN005','KIN005','A789B','A789B','A789B','G123H','G123H','G123H'], 
'Comp_ID':['1395','1395','1395','1395','1395','7787','7787','7787','1395','1395','1395'], 
'date':['2016-11-22','2016-11-21','2016-11-20','2016-11-14','2016-11-13','2016-11-22','2016-11-21','2016-11-12','2016-11-22','2016-11-21','2016-11-08']} 

df = pd.DataFrame(data=d) 
df.date = pd.to_datetime(df.date) 
d = pd.Timedelta(1, 'D') 

df = df.sort_values(['item_number','date','Comp_ID'],ascending=False) 

g = df.groupby(['Comp_ID','item_number']) 
sequence = g['date'].apply(lambda x: x.diff().fillna(0).abs().le(d)).reset_index() 
sequence.set_index('index',inplace=True) 
test = df.join(sequence) 
test.columns = ['Comp_ID','date','item_number','consecutive'] 
g = test.groupby(['Comp_ID','item_number']) 
g['consecutive'].apply(lambda x: x.idxmin() - x.idxmax()) 

Dies wird das gewünschte Ergebnis für die kleineren Daten-Set:

Comp_ID item_number 
1395  G123H   2 
     KIN005   3 
7787  KIN005   2 
Name: consecutive, dtype: int64 
+0

, der die erste sku geändert bdsm1000? lol gut gespielt –

Antwort

2

Zuerst werde ich vorschlagen, dass wir eine Reihe von Terminen, die jeweils 1 Tag weniger als der Stand der Ausbeute ...

import datetime 
import pandas as pd 

def gen_prior_date(start_date): 
    yield start_date 
    while True: 
     start_date -= datetime.timedelta(days=1) 
     yield start_date 

...

>>> start_date = datetime.date(2016, 11, 22) 
>>> back_in_time = gen_prior_date(start_date) 
>>> next(back_in_time) 
datetime.date(2016, 11, 22) 
>>> next(back_in_time) 
datetime.date(2016, 11, 21) 

Nun müssen wir eine Funktion, die wir zu jeder Gruppe anwenden können ...

def count_consec_dates(dates, start_date): 
    dates = pd.to_datetime(dates.values).date 
    dates_set = set(dates) # O(1) vs O(n) lookup times 
    back_in_time = gen_prior_date(start_date) 

    tally = 0 
    while next(back_in_time) in dates_set: # jump out on first miss 
     tally += 1 
    return tally 

Der Rest ist einfach ...

>>> small_data = {'item_number': ['KIN005','KIN005','KIN005','KIN005','KIN005','A789B','A789B','A789B','G123H','G123H','G123H'], 
...    'Comp_ID': ['1395','1395','1395','1395','1395','7787','7787','7787','1395','1395','1395'], 
...    'date': ['2016-11-22','2016-11-21','2016-11-20','2016-11-14','2016-11-13','2016-11-22','2016-11-21','2016-11-12','2016-11-22','2016-11-21','2016-11-08']} 
>>> small_df = pd.DataFrame(data=small_data) 
>>> start_date = datetime.date(2016, 11, 22) 
>>> groups = small_df.groupby(['Comp_ID', 'item_number']).date 
>>> groups.apply(lambda x: count_consec_dates(x, start_date)) 
Comp_ID item_number 
1395  G123H   2 
     KIN005   3 
7787  A789B   2 
+0

Ich war in der Lage, den Datensatz, mit dem Sie gearbeitet haben, zu arbeiten, aber die Frage war auf den größeren Datensatz ausgerichtet. Der eigentliche Datensatz, mit dem ich arbeite, hat Tausende von Verkäufern und Artikeln über einen Monat hinweg. –

+0

Funktioniert es nicht mit diesen Daten? Ich verstehe nicht warum nicht. –

+0

Ist Platz oder Laufzeit ein Problem? Ich habe den Platz mit dieser Antwort verlassen, aber ich habe versucht, teure Operationen wie Join oder Sort zu vermeiden. –

3

Sie können es auf diese Weise tun:

today = pd.to_datetime('2016-11-22') 

# sort DF by `date` (descending)  
x = df.sort_values('date', ascending=0) 
g = x.groupby(['Comp_ID','item_number']) 
# compare the # of days to `today` with a consecutive day# in each group 
x[(today - x['date']).dt.days == g.cumcount()].groupby(['Comp_ID','item_number']).size() 

Ergebnis:

Comp_ID item_number 
1395  G123H   2 
     KIN005   3 
7787  A789B   2 
dtype: int64 

PS dank @DataSwede's for faster diff calculation!

Erläuterung:

In [124]: x[(today - x['date']).dt.days == g.cumcount()] \ 
      .sort_values(['Comp_ID','item_number','date'], ascending=[1,1,0]) 
Out[124]: 
    Comp_ID  date item_number 
8 1395 2016-11-22  G123H 
9 1395 2016-11-21  G123H 
0 1395 2016-11-22  KIN005 
1 1395 2016-11-21  KIN005 
2 1395 2016-11-20  KIN005 
5 7787 2016-11-22  A789B 
6 7787 2016-11-21  A789B 
+1

Gibt es einen Grund, warum Sie bei der Berechnung der Datumsdifferenz die apply-Methode über die groupby verwenden? Ich bekomme einen leichten Leistungszuwachs, und die gleiche Ausgabe durch Berechnung der Diff mit dem Dataframe - 'x ['diff'] = (heute - x ['Datum']). Dt.days', wollte aber sehen Wenn es einen Grund gibt, warum die Anwendung "Anwenden" eine bessere Option ist. – DataSwede

+0

@DataSwede, ja, guter Fang, danke! In dem Fall, wenn die Differenz zwischen einem konstanten Datum ('heute') und der Serie berechnet wird, wird es richtig funktionieren. Zuerst habe ich versucht, 'diff' in jeder Gruppe zu berechnen und habe mich an diese langsame Variante gehalten ... – MaxU

+0

@MaxU Ich habe mit der Lösung, die ich benutzt habe, einige Fehler bekommen und bin zu dieser gewechselt und es funktioniert perfekt! Ich bin ein bisschen Anfänger, wenn Sie also ein bisschen mehr von einer schrittweisen Erklärung hinzufügen könnten, was passiert, wenn Sie die Anzahl der Tage mit dieser Gruppe vergleichen, dann wäre das wirklich gut! würde gerne lernen, was passiert –

Verwandte Themen