更新:从版本0.20.0开始,pandas cut / qcut处理日期字段。有关详情,请参阅What's New。
pd.cut和pd.qcut现在支持datetime64和timedelta64 dtypes(GH14714,GH14798)
原始问题: Pandas cut和qcut函数非常适合用于数据透视表等的“bucketing”连续数据,但我看不到一种简单的方法来获取日期时间轴混合。令人沮丧,因为大熊猫在所有与时间有关的东西中都是如此之大!
这是一个简单的例子:
def randomDates(size, start=134e7, end=137e7):
return np.array(np.random.randint(start, end, size), dtype='datetime64[s]')
df = pd.DataFrame({'ship' : randomDates(10), 'recd' : randomDates(10),
'qty' : np.random.randint(0,10,10), 'price' : 100*np.random.random(10)})
df
price qty recd ship
0 14.723510 3 2012-11-30 19:32:27 2013-03-08 23:10:12
1 53.535143 2 2012-07-25 14:26:45 2012-10-01 11:06:39
2 85.278743 7 2012-12-07 22:24:20 2013-02-26 10:23:20
3 35.940935 8 2013-04-18 13:49:43 2013-03-29 21:19:26
4 54.218896 8 2013-01-03 09:00:15 2012-08-08 12:50:41
5 61.404931 9 2013-02-10 19:36:54 2013-02-23 13:14:42
6 28.917693 1 2012-12-13 02:56:40 2012-09-08 21:14:45
7 88.440408 8 2013-04-04 22:54:55 2012-07-31 18:11:35
8 77.329931 7 2012-11-23 00:49:26 2012-12-09 19:27:40
9 46.540859 5 2013-03-13 11:37:59 2013-03-17 20:09:09
要按价格或数量组合,我可以使用cut / qcut来存储它们:
df.groupby([pd.cut(df['qty'], bins=[0,1,5,10]), pd.qcut(df['price'],q=3)]).count()
price qty recd ship
qty price
(0, 1] [14.724, 46.541] 1 1 1 1
(1, 5] [14.724, 46.541] 2 2 2 2
(46.541, 61.405] 1 1 1 1
(5, 10] [14.724, 46.541] 1 1 1 1
(46.541, 61.405] 2 2 2 2
(61.405, 88.44] 3 3 3 3
但我看不出用我的'recd'或'ship'日期字段做同样事情的简单方法。例如,生成一个类似的计数表,按照(比如)每月的recd和ship数据列进行细分。看起来像resample()已经将所有机制都用到了句号中,但我无法弄清楚如何在这里应用它。 'date cut'中的桶(或级别)将等同于pandas.PeriodIndex,然后我想用df ['recd']的每个值标记它落入的时间段?
所以我正在寻找的输出类型如下:
ship recv count
2011-01 2011-01 1
2011-02 3
... ...
2011-02 2011-01 2
2011-02 6
... ... ...
更一般地说,我希望能够在输出中混合和匹配连续或分类变量。想象一下,df还包含一个带有红色/黄色/绿色值的“状态”列,然后我想根据状态,价格桶,发货和再生桶来总结计数,所以:
ship recv price status count
2011-01 2011-01 [0-10) green 1
red 4
[10-20) yellow 2
... ... ...
2011-02 [0-10) yellow 3
... ... ... ...
作为一个额外的问题,修改上面的groupby()结果只包含一个名为'count'的输出列的最简单方法是什么?
答案 0 :(得分:5)
这是使用pandas.PeriodIndex的解决方案(警告:PeriodIndex没有
似乎支持带有多个>的时间规则1,如'4M')。我认为
您的奖金问题的答案是.size()
。
In [49]: df.groupby([pd.PeriodIndex(df.recd, freq='Q'),
....: pd.PeriodIndex(df.ship, freq='Q'),
....: pd.cut(df['qty'], bins=[0,5,10]),
....: pd.qcut(df['price'],q=2),
....: ]).size()
Out[49]:
qty price
2012Q2 2013Q1 (0, 5] [2, 5] 1
2012Q3 2013Q1 (5, 10] [2, 5] 1
2012Q4 2012Q3 (5, 10] [2, 5] 1
2013Q1 (0, 5] [2, 5] 1
(5, 10] [2, 5] 1
2013Q1 2012Q3 (0, 5] (5, 8] 1
2013Q1 (5, 10] (5, 8] 2
2013Q2 2012Q4 (0, 5] (5, 8] 1
2013Q2 (0, 5] [2, 5] 1
答案 1 :(得分:4)
只需要设置您要重新采样的字段的索引,这里有一些例子
In [36]: df.set_index('recd').resample('1M',how='sum')
Out[36]:
price qty
recd
2012-07-31 64.151194 9
2012-08-31 93.476665 7
2012-09-30 94.193027 7
2012-10-31 NaN NaN
2012-11-30 NaN NaN
2012-12-31 12.353405 6
2013-01-31 NaN NaN
2013-02-28 129.586697 7
2013-03-31 NaN NaN
2013-04-30 NaN NaN
2013-05-31 211.979583 13
In [37]: df.set_index('recd').resample('1M',how='count')
Out[37]:
2012-07-31 price 1
qty 1
ship 1
2012-08-31 price 1
qty 1
ship 1
2012-09-30 price 2
qty 2
ship 2
2012-10-31 price 0
qty 0
ship 0
2012-11-30 price 0
qty 0
ship 0
2012-12-31 price 1
qty 1
ship 1
2013-01-31 price 0
qty 0
ship 0
2013-02-28 price 2
qty 2
ship 2
2013-03-31 price 0
qty 0
ship 0
2013-04-30 price 0
qty 0
ship 0
2013-05-31 price 3
qty 3
ship 3
dtype: int64
答案 2 :(得分:1)
我提出了一个依赖于datetime64 [ns]的底层存储格式的想法。如果你像这样定义dcut()
def dcut(dts, freq='d', right=True):
hi = pd.Period(dts.max(), freq=freq) + 1 # get first period past end of data
periods = pd.PeriodIndex(start=dts.min(), end=hi, freq=freq)
# get a list of integer bin boundaries representing ns-since-epoch
# note the extra period gives us the extra right-hand bin boundary we need
bounds = np.array(periods.to_timestamp(how='start'), dtype='int')
# bin our time field as integers
cut = pd.cut(np.array(dts, dtype='int'), bins=bounds, right=right)
# relabel the bins using the periods, omitting the extra one at the end
cut.levels = periods[:-1].format()
return cut
然后我们可以做我想做的事:
df.groupby([dcut(df.recd, freq='m', right=False),dcut(df.ship, freq='m', right=False)]).count()
获得:
price qty recd ship
2012-07 2012-10 1 1 1 1
2012-11 2012-12 1 1 1 1
2013-03 1 1 1 1
2012-12 2012-09 1 1 1 1
2013-02 1 1 1 1
2013-01 2012-08 1 1 1 1
2013-02 2013-02 1 1 1 1
2013-03 2013-03 1 1 1 1
2013-04 2012-07 1 1 1 1
2013-03 1 1 1 1
我猜你可以类似地定义dqcut(),它首先将每个日期时间值“舍入”到表示其包含周期开始的整数(以指定的频率),然后使用qcut()在这些边界中进行选择。或者首先对原始整数值执行qcut()并根据您选择的频率对结果二进制数进行舍入?
奖金问题上没有欢乐吗? :)
答案 3 :(得分:0)
如何使用Series
并将您感兴趣的DataFrame
部分放入其中,然后在系列对象上调用cut
?
price_series = pd.Series(df.price.tolist(), index=df.recd)
然后
pd.qcut(price_series, q=3)
等等。 (虽然我认为@ Jeff的答案是最好的)