Pandas 按月份和年份分组
- 2025-03-04 08:25:00
- admin 原创
- 75
问题描述:
我有以下数据框:
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 的各种组合,但似乎什么都不起作用。我该怎么做?
解决方案 1:
您可以使用重新采样或Grouper
(在后台重新采样)。
首先确保 datetime 列确实是日期时间 (用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
为了获得期望的结果,我们必须重新索引......
解决方案 2:
保持简单:
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')
解决方案 3:
有多种方法可以做到这一点。
我创建了数据框来展示过滤数据的不同技术。
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
。此 hack 有助于过滤值并获取分组值。
这也会产生相同的结果。
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
您可以选择列表abc
或xyz
值Jun-13
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
我也建议检查一下这个答案。
解决方案 4:
您还可以通过创建包含年份和月份的字符串列来实现此目的,如下所示:
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)
解决方案 5:
一些答案使用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()
解决方案 6:
一种简单且更通用的方法是创建一个列,其中包含您想要的数据箱。对于这个问题,箱将是年份和月份。请注意,由于您正在绘制结果,因此您不需要包含没有数据的月份。
该索引将转换为日期时间索引,并将用于创建容器。该groupby
方法在处理容器时删除列,容器将成为索引中的行。可选地,索引可以在最后再次转换为日期时间索引,以允许正确排序。
import pandas as pd
idx = ['01-Jun-13', '03-Jun-13', '15-Aug-13', '20-Jan-14', '21-Feb-14']
data = {'abc': [100, -20, 40, 25, 60],
'xyz': [200, 50, -5, 15, 80]}
df = pd.DataFrame(index=idx, data=data)
df.index = pd.to_datetime(df.index, format='%d-%b-%y')
df['bin'] = df.index.strftime('%Y-%m-01')
result = df.groupby('bin').sum()
result.index = pd.to_datetime(result.index)
df
现在result
是:
In [1]: df
Out[1]:
abc xyz bin
2013-06-01 100 200 2013-06-01
2013-06-03 -20 50 2013-06-01
2013-08-15 40 -5 2013-08-01
2014-01-20 25 15 2014-01-01
2014-02-21 60 80 2014-02-01
In [2]: result
Out[2]:
abc xyz
bin
2013-06-01 80 250
2013-08-01 40 -5
2014-01-01 25 15
2014-02-01 60 80
扫码咨询,免费领取项目管理大礼包!