我有一个带有两元素分层索引(“month”和“item_id”)的pandas数据帧。每行代表特定月份的特定项目,并且包含多个感兴趣的数值度量的列。细节是无关紧要的,所以我们只是说我们在这里有X列。
我的问题源于这样一个事实:物品在他们观察的月份中有所不同,这可能是也可能不是连续的。我需要计算所有项目中X的平均值,对于该项目进行观察的第1个,第2个,第......个月。
换句话说,我的结果中的第一行应该是数据框中每个项目的第一行的所有项目的平均值,第二行结果应该是所有项目的平均值。对该项目的第二次观察,依此类推。
换句话说,如果我们要为每个项目获取所有按日期排序的行并从i = 1,2,...,n索引它们,我需要所有项目的平均 。也就是说,我希望所有项目中每个项目的第一次观察的平均值,所有项目的第二次观察的平均值,等等。
我怎样才能做到最好?我不能使用现有的日期索引,所以我需要向数据框添加另一个索引(类似于我在上一段中描述的那样),或者我是唯一可以迭代每个项目的行并保持运行平均值的方法?这可行,但不会充分利用大熊猫的力量。
添加一些示例数据:
item_id date X DUMMY_ROWS
20 2010-11-01 16759 0
2010-12-01 16961 1
2011-01-01 17126 2
2011-02-01 17255 3
2011-03-01 17400 4
2011-04-01 17551 5
21 2007-09-01 4 6
2007-10-01 5 7
2007-11-01 6 8
2007-12-01 10 9
22 2006-05-01 10 10
2006-07-01 13 11
23 2006-05-01 2 12
24 2008-01-01 2 13
2008-02-01 9 14
2008-03-01 18 15
2008-04-01 19 16
2008-05-01 23 17
2008-06-01 32 18
为了便于说明,我添加了数据中不存在的虚拟行列。我所描述的操作将有效地给出行0,6,10,12和13的平均值(每个项目的第一个观察值),然后是行1,7,11和15的平均值(第二个观察值为每个项目,不包括项目23,因为它只有一个观察),依此类推。
答案 0 :(得分:3)
一个选项是重置索引然后按ID分组。
df_new = df.reset_index()
df_new.groupby(['item_id']).X.agg(np.mean)
这会使您的原始df保持不变,并为每个项目ID获取所有月份的平均值。
对于你的更新问题(顺便说一句很好的例子)我认为方法是添加一个“item_sequence_id”我已经在具有相似数据的路径中完成了这个。
df.sort(['item_id', 'date'], inplace = True)
def sequence_id(item):
item['seq_id'] = range(0,len(item)-1,1)
return item
df_with_seq_id = df.groupby(['item_id']).apply(sequence_id)
df_with_seq_id.groupby(['seq_id']).agg(np.mean)
这里的想法是seq_id
允许您识别每item_id
个数据点的位置,为项目分配非唯一seq_id
值将允许您分组跨多个项目。我之前使用过的上下文涉及用户在会话中首先执行某些操作。使用此ID结构,我可以识别用户采取的所有第一,第二,第三等操作,无论其绝对时间和用户ID如何。
希望这更像是你想要的。
答案 1 :(得分:0)
这是我最终想到的替代方法(假设我们不关心用于计算均值的实际日期)。回想一下@cwharland提出的方法:
def sequence_id(item):
item['seq'] = range(0,len(item),1)
return item
shrinkWithSeqID_old = df.groupby(level='item_id').apply(sequence_id)
在数据帧的10,000行子集上测试:
%timeit -n10 dfWithSeqID_old = shrink.groupby(level='item_id').apply(sequence_id)
10 loops, best of 3: 301 ms per loop
事实证明,我们可以通过记住pandas的默认行为(即没有指定索引列)来为数据帧生成数字索引来简化事物,数据帧的编号从0到n(帧中的行数)。我们可以像这样利用它:
dfWithSeqID_new = df.groupby(level='item_id').apply(lambda x: x.reset_index(drop=True))
输出的唯一区别是我们有一个新的,未标记的数字索引,其内容与上一个答案中使用的'seq'列相同,但它几乎快了4倍(我无法比较完整的1300万行数据帧,因为第一种方法导致内存错误):
%timeit -n10 dfWithSeqID_new = df.groupby(level='item_id').apply(lambda x: x.reset_index(drop=True))
10 loops, best of 3: 77.2 ms per loop
计算原始问题中的平均值仅略有不同。原始方法是:
dfWithSeqID_old.groupby( 'SEQ')。AGG(np.mean)。头()
但是现在我们只需要说明我们使用新的未标记索引而不是'seq'列的事实:
dfWithSeqID_new.mean(level=1).head()
结果是一样的。