Pandas: Group by calendar-week, then plot grouped barplots for the real datetime
我找到了一个很好的解决方案,并将其发布在下面作为答案。
结果将如下所示:
您可以针对此问题生成一些示例数据:
1 2 3 4 5 | codes = list('ABCDEFGH'); dates = pd.Series(pd.date_range('2013-11-01', '2014-01-31')); dates = dates.append(dates) dates.sort() df = pd.DataFrame({'amount': np.random.randint(1, 10, dates.size), 'col1': np.random.choice(codes, dates.size), 'col2': np.random.choice(codes, dates.size), 'date': dates}) |
导致:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | In [55]: df Out[55]: amount col1 col2 date 0 1 D E 2013-11-01 0 5 E B 2013-11-01 1 5 G A 2013-11-02 1 7 D H 2013-11-02 2 5 E G 2013-11-03 2 4 H G 2013-11-03 3 7 A F 2013-11-04 3 3 A A 2013-11-04 4 1 E G 2013-11-05 4 7 D C 2013-11-05 5 5 C A 2013-11-06 5 7 H F 2013-11-06 6 1 G B 2013-11-07 6 8 D A 2013-11-07 7 1 B H 2013-11-08 7 8 F H 2013-11-08 8 3 A E 2013-11-09 8 1 H D 2013-11-09 9 3 B D 2013-11-10 9 1 H G 2013-11-10 10 6 E E 2013-11-11 10 6 F E 2013-11-11 11 2 G B 2013-11-12 11 5 H H 2013-11-12 12 5 F G 2013-11-13 12 5 G B 2013-11-13 13 8 H B 2013-11-14 13 6 G F 2013-11-14 14 9 F C 2013-11-15 14 4 H A 2013-11-15 .. ... ... ... ... 77 9 A B 2014-01-17 77 7 E B 2014-01-17 78 4 F E 2014-01-18 78 6 B E 2014-01-18 79 6 A H 2014-01-19 79 3 G D 2014-01-19 80 7 E E 2014-01-20 80 6 G C 2014-01-20 81 9 H G 2014-01-21 81 9 C B 2014-01-21 82 2 D D 2014-01-22 82 7 D A 2014-01-22 83 6 G B 2014-01-23 83 1 A G 2014-01-23 84 9 B D 2014-01-24 84 7 G D 2014-01-24 85 7 A F 2014-01-25 85 9 B H 2014-01-25 86 9 C D 2014-01-26 86 5 E B 2014-01-26 87 3 C H 2014-01-27 87 7 F D 2014-01-27 88 3 D G 2014-01-28 88 4 A D 2014-01-28 89 2 F A 2014-01-29 89 8 D A 2014-01-29 90 1 A G 2014-01-30 90 6 C A 2014-01-30 91 6 H C 2014-01-31 91 2 G F 2014-01-31 [184 rows x 4 columns] |
我想按日历周和
1 2 | kw = lambda x: x.isocalendar()[1] grouped = df.groupby([df['date'].map(kw), 'col1'], sort=False).agg({'amount': 'sum'}) |
导致:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | In [58]: grouped Out[58]: amount date col1 44 D 8 E 10 G 5 H 4 45 D 15 E 1 G 1 H 9 A 13 C 5 B 4 F 8 46 E 7 G 13 H 17 B 9 F 23 47 G 14 H 4 A 40 C 7 B 16 F 13 48 D 7 E 16 G 9 H 2 A 7 C 7 B 2 ... ... 1 H 14 A 14 B 15 F 19 2 D 13 H 13 A 13 B 10 F 32 3 D 8 E 18 G 3 H 6 A 30 C 9 B 6 F 5 4 D 9 E 12 G 19 H 9 A 8 C 18 B 18 5 D 11 G 2 H 6 A 5 C 9 F 9 [87 rows x 1 columns] |
然后,我想像这样生成图:
这意味着:x轴上的日历周和年份(日期时间),每个分组的
我面临的问题是:我只有整数来描述日历周(图中的KW),但是我必须以某种方式将其上的日期合并回去,以使年份也标记为刻度。此外,我不能仅绘制日历周,因为我需要正确排列项目的顺序(kw 47,kw 48(2013年)必须在kw 1的左侧(因为这是2014年))。
编辑
我从这里想通了:
http://pandas.pydata.org/pandas-docs/stable/visualization.html#visualization-barplot分组的条需要是列而不是行。因此,我考虑了如何转换数据,并发现了
1 | A = grouped.reset_index().pivot(index='date', columns='col1', values='amount').fillna(0) |
将数据转换为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | col1 A B C D E F G H date 1 4 31 0 0 0 18 13 8 2 0 12 13 22 1 17 0 8 3 3 10 4 13 12 8 7 6 4 17 0 10 7 0 25 7 4 5 7 0 7 9 8 6 0 7 44 0 0 2 11 7 0 0 2 45 9 3 2 14 0 16 21 2 46 0 14 7 2 17 13 11 8 47 5 13 0 15 19 7 5 10 48 15 8 12 2 20 4 7 6 49 20 0 0 18 22 17 11 0 50 7 11 8 6 5 6 13 10 51 8 26 0 0 5 5 16 9 52 8 13 7 5 4 10 0 11 |
看起来像文档中的示例数据以分组的条形图所示:
1 | A. plot(kind='bar') |
得到这个:
而我现在对轴进行排序(从1-52开始)时出现问题,这实际上是错误的,因为在这种情况下,日历周52属于2013年...关于如何合并实际日期时间的任何想法日历周并将其用作x轴刻度?
我认为resample('W')是执行此操作的更好方法-默认情况下,它按在星期日结束的几周进行分组(" W"与" W-SUN"相同),但是您可以指定所需的内容。
在您的示例中,请尝试以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 | grouped = (df .groupby('col1') .apply(lambda g: # work on groups of col1 g.set_index('date') [['amount']] .resample('W', how='sum') # sum the amount field across weeks ) .unstack(level=0) # pivot the col1 index rows to columns .fillna(0) ) grouped.columns=grouped.columns.droplevel() # drop the 'col1' part of the multi-index column names print grouped grouped.plot(kind='bar') |
它将打印您的数据表并绘制与您相似的图,但带有"真实"日期标签:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | col1 A B C D E F G H date 2013-11-03 18 0 9 0 8 0 0 4 2013-11-10 4 11 0 1 16 2 15 2 2013-11-17 10 14 19 8 13 6 9 8 2013-11-24 10 13 13 0 0 13 15 10 2013-12-01 6 3 19 8 8 17 8 12 2013-12-08 5 15 5 7 12 0 11 8 2013-12-15 8 6 11 11 0 16 6 14 2013-12-22 16 3 13 8 8 11 15 0 2013-12-29 1 3 6 10 7 7 17 15 2014-01-05 12 7 10 11 6 0 1 12 2014-01-12 13 0 17 0 23 0 10 12 2014-01-19 10 9 2 3 8 1 18 3 2014-01-26 24 9 8 1 19 10 0 3 2014-02-02 1 6 16 0 0 10 8 13 |
好吧,我终于回答了这个问题。关键不是按日历周分组(因为您会丢失有关年份的信息),而是按包含日历周和年份的字符串分组。
然后使用
选择
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | codes = list('ABCDEFGH'); dates = pd.Series(pd.date_range('2013-11-01', '2014-01-31')); dates = dates.append(dates) dates.sort() df = pd.DataFrame({'amount': np.random.randint(1, 10, dates.size), 'col1': np.random.choice(codes, dates.size), 'col2': np.random.choice(codes, dates.size), 'date': dates}) kw = lambda x: x.isocalendar()[1]; kw_year = lambda x: str(x.year) + ' - ' + str(x.isocalendar()[1]) grouped = df.groupby([df['date'].map(kw_year), 'col1'], sort=False, as_index=False).agg({'amount': 'sum'}) A = grouped.pivot(index='date', columns='col1', values='amount').fillna(0).reset_index() ticks = A.date.values.tolist() del A['date'] ax = A.plot(kind='bar') ax.set_xticklabels(ticks) |
结果:
将星期加到一年中的52次,以便按"年"排序星期。将对勾标签设置回您想要的位置,这可能是不平凡的。
你想要的是像这样增加几周
1 | nth week → (n+1)th week → (n+2)th week → etc. |
但是当您有新的一年时,它下降了51(
为了弥补这一点,请注意,年份增加了一年。因此,将当年的增长乘以52即可得出总变化为