2017-10-21 69 views
2

可能我的例子是很多大,我的代码是在这里:如何获取自定义时间间隔中的最后日期? - 熊猫

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 

我想要做的是找到哪一年开始2005-7-1一年的天和最后一天),并最终2006-06-30,开始2006-7-1并结束2007-6-30 ...等等。 我的预期输出是在这里:

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 

怎样才能解决这个问题?我想我应该使用custom

+0

是是长期看? – ileadall42

+2

注意:9月只有30天,所以有一些不好的数据。 –

+0

@AndyHayden我的天啊,多亏了点! – ileadall42

回答

5

您可以使用一个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

我一直在使用freq和石斑鱼,但不知道它也可能会造成偏移。超级+1。我会记住这一个 – Dark

+0

添加另一个答案,因为它非常不同(更短)! –

+1

@Bharathshetty这是石斑鱼的主要观点! :) –

4

嗯,这看起来像一个神奇的方式!
频率为“AS-JUL”(年份开始频率,从7月开始)。

我们首先采取每个月的开始(因为你在那里有一些不好的日期,我们就忽略它们),但关键的是,我们需要的是日期时间,而不是字符串:

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") 

现在,这里是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] 

我们创建了一个偏移"AS-JUL"和滚回去(指楼)。
注:无论出于何种原因,我们不能用dt.floor ...


好吧,你要为每个组在各个时期的最新记录日期,误读这一部分与该修正的日期时,最后的部分是一个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

那么这些都是真正的新我 – Dark

+0

@Bharathshetty新的我也很巧妙的方法(“魔”是从[链接答案](https://stackoverflow.com/a/45963946/1240268))。有关其他偏移量,请参阅:https://stackoverflow.com/a/35339226/1240268。 –

+0

我很喜欢这个解决方案,但OP找的答案我认为是不同的。他想在我想的时间间隔中找到最后一个工作日。我们需要groupby然后通过创建一个区间索引来检查最大日期我认为 – Dark

3

从美丽的功能to_offset @Andy建议我们可以做

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

你们都很棒,但我只能接受一个,所以我会接受更快的Andy,但也非常感谢你! – ileadall42

+0

即使我不接受他的解决方案,我也会感觉不好。 – Dark

3

通过使用IntervalIndexDF是你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

我不得不说这很容易理解!谢谢你。 – ileadall42

+0

我你知道这是我用'BEG = np.array([[ '{}/7/01'.format(i)中,' 我的第一种方法{}/6/30'.format第(i + 1) ]对于i在范围(2005,2010)])''指数= pd.IntervalIndex.from_arrays(pd.to_datetime(BEG [:,0]),pd.to_datetime(BEG [:,1]))'。你的更好 – Dark

+0

@Tangfeifan Yw〜:-) – Wen