Pandas按月份和年份分组
我有一个这样的数据表:
Date abc xyz
01-Jun-13 100 200
03-Jun-13 -20 50
15-Aug-13 40 -5
20-Jan-14 25 15
21-Feb-14 60 80
我需要按年和月来整理这些数据。也就是说,要把2013年1月、2013年2月、2013年3月等的数据分在一起。
我会用整理好的数据来画一个图,显示每年每月的abc和xyz之间的关系。
我试过很多种组合的方法,比如用groupby和sum,但就是没法搞定。请问我该怎么做呢?
6 个回答
有些回答是把Date
当作索引来用,而不是当作一列(这样做没有问题)。
不过,如果你把日期存储在一列里(而不是索引),记得要访问这一列的dt
属性。也就是说:
# First make sure `Date` is a datetime column
df['Date'] = pd.to_datetime(
arg=df['Date'],
format='%d-%b-%y' # Assuming dd-Mon-yy format
)
# Group by year and month
df.groupby(
[
df['Date'].dt.year,
df['Date'].dt.month
]
).sum()
你也可以通过创建一个包含年份和月份的字符串列来实现,方法如下:
df['date'] = df.index
df['year-month'] = df['date'].apply(lambda x: str(x.year) + ' ' + str(x.month))
grouped = df.groupby('year-month')
不过这样做在你遍历这些组的时候,顺序就不会被保留,比如:
for name, group in grouped:
print(name)
这会得到:
2007 11
2007 12
2008 1
2008 10
2008 11
2008 12
2008 2
2008 3
2008 4
2008 5
2008 6
2008 7
2008 8
2008 9
2009 1
2009 10
所以,如果你想保留顺序,就必须按照@Q-man上面提到的方法来做:
grouped = df.groupby([df.index.year, df.index.month])
这样在上面的循环中就能保留顺序:
(2007, 11)
(2007, 12)
(2008, 1)
(2008, 2)
(2008, 3)
(2008, 4)
(2008, 5)
(2008, 6)
(2008, 7)
(2008, 8)
(2008, 9)
(2008, 10)
有很多方法可以做到这一点。
我创建了一个数据框,来展示不同的过滤数据的技巧。
df = pd.DataFrame({'Date': ['01-Jun-13', '03-Jun-13', '15-Aug-13', '20-Jan-14', '21-Feb-14'], 'abc': [100, -20, 40, 25, 60], 'xyz': [200, 50,-5, 15, 80] })
我把月份、年份和日期分开了,并且把月份和年份也分开了,正如你所说的那样。
def getMonth(s): return s.split("-")[1] def getDay(s): return s.split("-")[0] def getYear(s): return s.split("-")[2] def getYearMonth(s): return s.split("-")[1] + "-" + s.split("-")[2]
我创建了新的列:
year
(年份)、month
(月份)、day
(日期)和yearMonth
(年月)。在你的情况下,你需要其中一个。你可以用两列'year','month'
来分组,或者用一列yearMonth
来分组。df['year'] = df['Date'].apply(lambda x: getYear(x)) df['month'] = df['Date'].apply(lambda x: getMonth(x)) df['day'] = df['Date'].apply(lambda x: getDay(x)) df['YearMonth'] = df['Date'].apply(lambda x: getYearMonth(x))
输出:
Date abc xyz year month day YearMonth 0 01-Jun-13 100 200 13 Jun 01 Jun-13 1 03-Jun-13 -20 50 13 Jun 03 Jun-13 2 15-Aug-13 40 -5 13 Aug 15 Aug-13 3 20-Jan-14 25 15 14 Jan 20 Jan-14 4 21-Feb-14 60 80 14 Feb 21 Feb-14
你可以通过groupby(..)来查看不同的分组。
在这个例子中,我们是按两列分组的:
for key, g in df.groupby(['year', 'month']): print key, g
输出:
('13', 'Jun') Date abc xyz year month day YearMonth 0 01-Jun-13 100 200 13 Jun 01 Jun-13 1 03-Jun-13 -20 50 13 Jun 03 Jun-13 ('13', 'Aug') Date abc xyz year month day YearMonth 2 15-Aug-13 40 -5 13 Aug 15 Aug-13 ('14', 'Jan') Date abc xyz year month day YearMonth 3 20-Jan-14 25 15 14 Jan 20 Jan-14 ('14', 'Feb') Date abc xyz year month day YearMonth
在这个例子中,我们是按一列分组的:
for key, g in df.groupby(['YearMonth']): print key, g
输出:
Jun-13 Date abc xyz year month day YearMonth 0 01-Jun-13 100 200 13 Jun 01 Jun-13 1 03-Jun-13 -20 50 13 Jun 03 Jun-13 Aug-13 Date abc xyz year month day YearMonth 2 15-Aug-13 40 -5 13 Aug 15 Aug-13 Jan-14 Date abc xyz year month day YearMonth 3 20-Jan-14 25 15 14 Jan 20 Jan-14 Feb-14 Date abc xyz year month day YearMonth 4 21-Feb-14 60 80 14 Feb 21 Feb-14
如果你想访问特定的项目,可以使用
get_group
。print df.groupby(['YearMonth']).get_group('Jun-13')
输出:
Date abc xyz year month day YearMonth 0 01-Jun-13 100 200 13 Jun 01 Jun-13 1 03-Jun-13 -20 50 13 Jun 03 Jun-13
和
get_group
类似。这个小技巧可以帮助你过滤值并获取分组后的值。这也会得到相同的结果。
print df[df['YearMonth']=='Jun-13']
输出:
Date abc xyz year month day YearMonth 0 01-Jun-13 100 200 13 Jun 01 Jun-13 1 03-Jun-13 -20 50 13 Jun 03 Jun-13
你可以在
Jun-13
期间选择abc
或xyz
的值。print df[df['YearMonth']=='Jun-13'].abc.values print df[df['YearMonth']=='Jun-13'].xyz.values
输出:
[100 -20] #abc values [200 50] #xyz values
你可以用这个方法来查看你已经分类为“年-月”的日期,并在其上应用条件,以获取相关数据。
for x in set(df.YearMonth): print df[df['YearMonth']==x].abc.values print df[df['YearMonth']==x].xyz.values
我还建议你查看这个答案。
保持简单:
GB = DF.groupby([(DF.index.year), (DF.index.month)]).sum()
给你,
print(GB)
abc xyz
2013 6 80 250
8 40 -5
2014 1 25 15
2 60 80
然后你可以像要求的那样绘图,使用,
GB.plot('abc', 'xyz', kind='scatter')
你可以使用 resample 或者 Grouper
(其实它的底层是用 resample 的)。
首先要确保你的日期时间列确实是日期时间格式(可以用 pd.to_datetime
来转换)。如果它是 DatetimeIndex,那就更简单了:
In [11]: df1
Out[11]:
abc xyz
Date
2013-06-01 100 200
2013-06-03 -20 50
2013-08-15 40 -5
2014-01-20 25 15
2014-02-21 60 80
In [12]: g = df1.groupby(pd.Grouper(freq="M")) # DataFrameGroupBy (grouped by Month)
In [13]: g.sum()
Out[13]:
abc xyz
Date
2013-06-30 80 250
2013-07-31 NaN NaN
2013-08-31 40 -5
2013-09-30 NaN NaN
2013-10-31 NaN NaN
2013-11-30 NaN NaN
2013-12-31 NaN NaN
2014-01-31 25 15
2014-02-28 60 80
In [14]: df1.resample("M", how='sum') # the same
Out[14]:
abc xyz
Date
2013-06-30 40 125
2013-07-31 NaN NaN
2013-08-31 40 -5
2013-09-30 NaN NaN
2013-10-31 NaN NaN
2013-11-30 NaN NaN
2013-12-31 NaN NaN
2014-01-31 25 15
2014-02-28 60 80
注意:之前 pd.Grouper(freq="M")
是写成 pd.TimeGrouper("M")
的,但后者从 0.21 版本开始就不推荐使用了。
我原以为下面的代码可以正常工作,但实际上并不行(可能是因为 as_index
没有被尊重?我不太确定)。我把这个放在这里是为了让大家了解一下。
如果它是一个列(必须是 datetime64 类型的列!就像我说的,记得用 to_datetime
转换),你可以使用 PeriodIndex:
In [21]: df
Out[21]:
Date abc xyz
0 2013-06-01 100 200
1 2013-06-03 -20 50
2 2013-08-15 40 -5
3 2014-01-20 25 15
4 2014-02-21 60 80
In [22]: pd.DatetimeIndex(df.Date).to_period("M") # old way
Out[22]:
<class 'pandas.tseries.period.PeriodIndex'>
[2013-06, ..., 2014-02]
Length: 5, Freq: M
In [23]: per = df.Date.dt.to_period("M") # new way to get the same
In [24]: g = df.groupby(per)
In [25]: g.sum() # dang not quite what we want (doesn't fill in the gaps)
Out[25]:
abc xyz
2013-06 80 250
2013-08 40 -5
2014-01 25 15
2014-02 60 80
为了得到想要的结果,我们需要重新索引...