2017-10-21 52 views
2

Que mon exemple soit beaucoup grand, mon code est ici:Comment obtenir la dernière date dans un intervalle personnalisé? - Pandas

import pandas as pd 
import numpy as np 
import io 
t = """ 
name  date 
a  2005-08-31 
a  2005-09-20 
a  2005-11-12 
a  2005-12-31 
a  2006-03-31 
a  2006-06-25 
a  2006-07-23 
a  2006-09-28 
a  2006-12-21 
a  2006-12-27 
a  2007-07-23 
a  2007-09-21 
a  2007-03-15 
a  2008-04-12 
a  2008-06-21 
a  2008-06-11 
b  2005-08-31 
b  2005-09-23 
b  2005-11-12 
b  2005-12-31 
b  2006-03-31 
b  2006-06-25 
b  2006-07-23 
b  2006-09-28 
b  2006-12-21 
b  2006-12-27 
b  2007-07-23 
b  2007-09-21 
b  2007-03-15 
b  2008-04-12 
b  2008-06-21 
b  2008-06-11 
""" 
data=pd.read_csv(io.StringIO(t),delimiter='  ')#5 space here 
data 

Ce que je veux faire est de trouver le tout dernier jour de l'année quelle année commence 2005-7-1) et à la fin 2006-06-30, commencer 2006-7-1 et fin 2007-6-30 ... et ainsi de suite. Et mon résultat attendu est ici:

name  date 
a  2006-06-25 #the last day of the 2005/7/01 -2006/06/31 
a  2007-03-15 #the last day of the 2006/7/01 -2007/06/31 
a  2008-06-21 #the last day of the 2007/7/01 -2008/06/31 
b  2006-06-25 #the last day of the 2005/7/01 -2006/06/31 
b  2007-03-15 #the last day of the 2006/7/01 -2007/06/31 
b  2008-06-21 #the last day of the 2007/7/01 -2008/06/31 

Comment résoudre ce problème? Je pense que je devrais utiliser le custom

+0

est-ce est long à lire? – ileadall42

+2

Note: Septembre a seulement 30 jours, donc il y a quelques mauvaises données là-dedans. –

+0

@AndyHayden Mon dieu, merci de le signaler! – ileadall42

Répondre

5

Vous pouvez le faire sans l'aide d'un seul rollback groupby:

In [11]: data.date = pd.to_datetime(data.date, format="%Y-%m-%d") 

In [12]: df.groupby(["name", pd.Grouper(key="date", freq="AS-JUL")])["date"].max() 
Out[12]: 
name date 
a  2005-07-01 2006-06-25 
     2006-07-01 2007-03-15 
     2007-07-01 2008-06-21 
b  2005-07-01 2006-06-25 
     2006-07-01 2007-03-15 
     2007-07-01 2008-06-21 
Name: date, dtype: datetime64[ns] 
+0

J'utilisais le freq et le grouper tout le temps je ne sais pas qu'il peut aussi prendre des offsets. Super +1. Je me souviendrai de celui-ci – Dark

+0

Ajoutant comme une autre réponse, comme c'est assez différent (beaucoup plus court)! –

+1

@Bharathshetty c'est le point principal du Mérou! :) –

4

Eh bien, cela semble être un moyen magique!
La fréquence est "AS-JUL" (qui est la fréquence de démarrage de l'année, à partir de Juillet).

Nous allons d'abord prendre le début de chaque mois (puisque vous avez quelques mauvaises dates là-dedans, nous allons les ignorer), mais la chose essentielle est que nous devons à être datetime plutôt que de chaîne:

In [11]: pd.to_datetime(data.date.str[:7], format="%Y-%m") # to beginning of month 
Out[11]: 
0 2005-08-01 
1 2005-09-01 
2 2005-11-01 
3 2005-12-01 
... 

In [12]: df.date = pd.to_datetime(data.date.str[:7], format="%Y-%m") 

maintenant vient ici le magic:

In [13]: from pandas.tseries.frequencies import to_offset 

In [14]: df.date.map(to_offset("AS-JUL").rollback) 
Out[14]: 
0 2005-07-01 
1 2005-07-01 
2 2005-07-01 
3 2005-07-01 
4 2005-07-01 
5 2005-07-01 
6 2006-07-01 
7 2006-07-01 
8 2006-07-01 
9 2006-07-01 
10 2007-07-01 
11 2007-07-01 
12 2006-07-01 
13 2007-07-01 
14 2007-07-01 
15 2007-07-01 
16 2005-07-01 
17 2005-07-01 
18 2005-07-01 
19 2005-07-01 
20 2005-07-01 
21 2005-07-01 
22 2006-07-01 
23 2006-07-01 
24 2006-07-01 
25 2006-07-01 
26 2007-07-01 
27 2007-07-01 
28 2006-07-01 
29 2007-07-01 
30 2007-07-01 
31 2007-07-01 
Name: date, dtype: datetime64[ns] 

Nous avons créé un décalage par rapport à "AS-JUL" et fit rouler en arrière (ce qui signifie étage).
Note: Pour une raison quelconque, nous ne pouvons pas utiliser dt.floor ...


D'accord, mal interprété cette partie, vous voulez la dernière date d'enregistrement pour chaque groupe dans chaque période, les dates corrigées, la dernière partie est juste une groupby:

In [21]: data.date = pd.to_datetime(data.date, format="%Y-%m-%d") 

In [22]: data["period_start"] = data.date.map(to_offset("AS-JUL").rollback).dt.normalize() 

In [23]: data.groupby(["name", "period_start"])["date"].max() 
Out[23]: 
name period_start 
a  2005-07-01  2006-06-25 
     2006-07-01  2007-03-15 
     2007-07-01  2008-06-21 
b  2005-07-01  2006-06-25 
     2006-07-01  2007-03-15 
     2007-07-01  2008-06-21 
Name: date, dtype: datetime64[ns] 
+0

Eh bien ceux-ci sont vraiment nouveau pour moi – Dark

+0

@Bharathshetty nouveau à moi aussi, astuce très bien (la "magie" est de la [réponse liée] (https://stackoverflow.com/a/45963946/1240268)). Pour les autres offsets, voir: https://stackoverflow.com/a/35339226/1240268. –

+0

J'ai adoré la solution mais la réponse que l'OP recherche est différente. Il veut trouver le dernier jour de travail dans l'intervalle je pense. Nous avons besoin de grouper ensuite vérifier la date maximale en créant un index d'intervalle je pense que – Dark

3

de la belle fonction to_offset @Andy a suggéré que nous pouvons faire

from pandas.tseries.frequencies import to_offset 
new = data.groupby('name').apply(lambda x : x.groupby(x['date'].map(to_offset("AS-JUL"))).max()) 
 
      name  date 
name date      
a 2006-07-01 a 2006-06-25 
    2007-07-01 a 2007-03-15 
    2008-07-01 a 2008-06-21 
b 2006-07-01 b 2006-06-25 
    2007-07-01 b 2007-03-15 
    2008-07-01 b 2008-06-21 
+1

Vous faites tous les deux bien mais je peux juste accepter un, ainsi j'accepterai le plus rapide Andy, mais vous remercie également beaucoup! – ileadall42

+0

Même je me sentirai mal si vous n'acceptez pas sa solution. – Dark

3

En utilisant IntervalIndex (DF est votre DataFrame)

idx=pd.IntervalIndex.from_arrays(pd.date_range(start='2005-07-01',freq='12MS',periods=12),pd.date_range(start='2006-06-30',freq='12M',periods=12),closed='both') 
df=pd.DataFrame({'G':list(range(len(idx)))},index=idx) 
DF.date=pd.to_datetime(DF.date) 
DF['G']=df.loc[DF.date].values 
DF.sort_values(['name','date']).drop_duplicates(['name','G'],keep='last') 

Out[19]: 
    name  date G 
5  a 2006-06-25 0 
12 a 2007-03-15 1 
14 a 2008-06-21 2 
21 b 2006-06-25 0 
28 b 2007-03-15 1 
30 b 2008-06-21 2 
+0

Je dois dire que c'est très facile à comprendre! Merci à vous. – ileadall42

+0

Je sais que c'est ma première approche J'ai utilisé 'beg = np.array ([['{}/7/01'.format (i),' {}/6/30'format (i + 1) ] pour i dans la plage (2005,2010)]) '' index = pd.IntervalIndex.from_arrays (pd.to_datetime (beg [:, 0]), pd.to_datetime (beg [:, 1])) '. Le vôtre est meilleur – Dark

+0

@Tangfeifan Yw ~ :-) – Wen