Pandas:按日历周分组,然后为实际日期时间绘制分组条形图
Posted
技术标签:
【中文标题】Pandas:按日历周分组,然后为实际日期时间绘制分组条形图【英文标题】:Pandas: Group by calendar-week, then plot grouped barplots for the real datetime 【发布时间】:2014-08-03 21:10:48 【问题描述】:编辑
我找到了一个很好的解决方案,并将其发布在下面作为答案。 结果将如下所示:
您可以为这个问题生成一些示例数据:
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)
导致:
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]
我想按日历周和col1
的值进行分组。像这样:
kw = lambda x: x.isocalendar()[1]
grouped = df.groupby([df['date'].map(kw), 'col1'], sort=False).agg('amount': 'sum')
导致:
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 轴上的日历周和年(日期时间)以及每个分组的 col1
一个条形。
我面临的问题是:我只有整数描述日历周(图中的 KW),但我必须以某种方式合并其上的日期才能获得按年份标记的刻度。此外,我不能只绘制分组日历周,因为我需要正确的项目顺序(kw 47、kw 48(2013 年)必须在 kw 1 的左侧(因为这是 2014 年))。
编辑
我从这里发现:
http://pandas.pydata.org/pandas-docs/stable/visualization.html#visualization-barplot 分组条需要是列而不是行。于是我想了想如何转换数据,发现了pivot
这个方法,原来是个很棒的功能。需要reset_index
将多索引转换为列。最后我用零填充NaN
s:
A = grouped.reset_index().pivot(index='date', columns='col1', values='amount').fillna(0)
将数据转换成:
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
这看起来像文档中的示例数据以分组条形绘制:
A. plot(kind='bar')
得到这个:
而我的轴有问题,因为它现在已排序(从 1 到 52),这实际上是错误的,因为在这种情况下,日历周 52 属于 2013 年......关于如何合并回来的任何想法日历周的真实日期时间并将它们用作 x 轴刻度?
【问题讨论】:
评论真的应该放在 cmets 中。我知道当没有足够的人回答问题时很容易气馁,但最好的办法是从问题中删除不相关的部分,并尝试让问题尽可能快速- 尽可能点。 那不是评论,真的。只是为用户提供的信息,对所有读者都非常有帮助...... 【参考方案1】:我认为resample('W') 是一种更好的方法 - 默认情况下,它按星期天结束的星期分组('W' 与 'W-SUN' 相同),但你可以指定任何你想要的。
在你的例子中,试试这个:
grouped = (df
.groupby('col1')
.apply(lambda g: # work on groups of col1
g.set_index('date')
[['amount']]
.resample('W').agg('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')
它应该打印您的数据表并绘制与您的相似的图,但带有“真实”日期标签:
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
【讨论】:
how
在 0.19 版之后被弃用。请改用.resample('W').agg('sum')
。【参考方案2】:
好的,我自己回答这个问题,因为我终于弄明白了。关键是不要按日历周分组(因为您会丢失有关年份的信息),而是按包含日历周和年份的字符串分组。
然后使用pivot
更改问题中提到的布局(重塑)。日期将是索引。使用reset_index()
使当前date
-index 成为一列,而不是获取一个整数范围作为索引(然后按正确的顺序绘制(最低年份/日历周是索引0,最高年份/日历周是最大整数)。
选择date
-column 作为新变量ticks
作为列表并从DataFrame 中删除该列。现在绘制条形图并简单地将 xticks 的标签设置为ticks
。 Completey 解决方案非常简单,在这里:
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)
结果:
【讨论】:
哇,非常好的、简单且干净的解决方案。我喜欢。关于 matplotlib-axisticks 没有太多麻烦 :)【参考方案3】:将一周添加到一年中的 52 次,以便“按年”对周进行排序。将勾选labels,which might be nontrivial,设置为你想要的。
你想要的是这样增加几周
nth week → (n+1)th week → (n+2)th week → etc.
但是当你有新的一年时,它会下降 51 (52 → 1
)。
要抵消这一点,请注意年份增加一。所以加上当年的增长乘以 52,总变化将是-51 + 52 = 1
。
【讨论】:
“将一周添加到一年中的 52 次”是什么意思?当我按日历周分组时,日期时间会丢失,这是我的绘图所需要的:( 又名。不要按周分组,按week + 52*year
分组。虽然要小心那第365天……
不知何故我还是不明白。但也许我不得不重新考虑一下为什么要按返回值添加年份的 52 的倍数来分组
啊,现在我明白了,您的意思是 delta_year*52,表示自开始以来的真实周数。
是的。只是年份是否从 2013 年开始并不重要,因为它只会使数字变大(而不是它们之间的差异)。【参考方案4】:
KeyError: '日期'
上述异常是以下异常的直接原因:
KeyError Traceback(最近一次调用最后一次) 在 10 kw_year = lambda x: str(x.year) + ' - ' + str(x.isocalendar()[1]) 11 分组 = df.groupby([df['date'].map(kw_year), 'col1'], sort=False, as_index=False).agg('amount': 'sum') ---> 12 A = grouped.pivot(index='date', columns='col1', values='amount').fillna(0).reset_index()
【讨论】:
以上是关于Pandas:按日历周分组,然后为实际日期时间绘制分组条形图的主要内容,如果未能解决你的问题,请参考以下文章