2012-10-23 53 views
4

这是一个由两部分组成的问题,包括一个直接问题和一个更一般的问题。使用熊猫TimeSeries在特定时间戳后选择第一个索引

我有一只熊猫TimeSeries,ts。 要知道一段时间后的第一个值。我可以做到这一点,

ts.ix[ts[datetime(2012,1,1,15,0,0):].first_valid_index()] 

a)有没有更好,更笨拙的方式来做到这一点?

b)来自C,在处理这些有点不透明时,我有一定的恐惧症,可能是可变的,但通常不是,可能是懒惰,但并不总是类型。所以要清楚,当我做

ts[datetime(2012,1,1,15,0,0):].first_valid_index() 

TS [日期时间(2012,1,1,15,0,0):]是一个pandas.TimeSeries异议的权利?我可能会改变它。

这是否意味着,无论我什么时候分片,都会在内存中分配一个ts的副本?这是否意味着这种无害的代码行可以实际触发一个千兆字节的TimeSeries的副本来获取索引值?

或者他们可能会奇迹般地共享内存,如果其中一个对象发生了变异,就会完成一个懒惰的拷贝?但是,那么,你怎么知道哪些特定的操作触发了一个副本?也许不会切片,但如何重命名列?在文档中似乎没有这样说。那会麻烦你吗?是否应该打扰我,还是应该学会不用担心,并且使用探查器来解决问题?

回答

9

一些设置:

In [1]: import numpy as np 
In [2]: import pandas as pd 
In [3]: from datetime import datetime 
In [4]: dates = [datetime(2011, 1, 2), datetime(2011, 1, 5), datetime(2011, 1, 7), datetime(2011, 1, 8), datetime(2011, 1, 10), datetime(2011, 1, 12)] 

In [5]: ts = pd.Series(np.random.randn(6), index=dates) 

In [6]: ts 
Out[6]: 
2011-01-02 -0.412335 
2011-01-05 -0.809092 
2011-01-07 -0.442320 
2011-01-08 -0.337281 
2011-01-10 0.522765 
2011-01-12 1.559876 

好的,现在要回答你的第一个问题,a)是的,有根据你的意图,较笨重的方式。这是非常简单的:

In [9]: ts[datetime(2011, 1, 8):] 
Out[9]: 
2011-01-08 -0.337281 
2011-01-10 0.522765 
2011-01-12 1.559876 

这是一个包含您选择的日期之后的所有值。第二个问题

In [10]: ts[datetime(2011, 1, 8):][0] 
Out[10]: -0.33728079849770815 

,(B) - 这种类型的索引是原来的一片,就像其他numpy的数组:您可以选择只是第一个,因为你想,通过。这不是原件的副本。看到这个问题,或许多类似: Bug or feature: cloning a numpy array w/ slicing

为了演示,让我们修改切片:

In [21]: ts2 = ts[datetime(2011, 1, 8):] 
In [23]: ts2[0] = 99 

这改变了原来的时间序列对象TS,TS2以来是一个切片,而不是一个副本。

In [24]: ts 
Out[24]: 
2011-01-02 -0.412335 
2011-01-05 -0.809092 
2011-01-07 -0.442320 
2011-01-08 99.000000 
2011-01-10  0.522765 
2011-01-12  1.559876 

如果你想一个副本,你可以(一般)使用的复制方法,或者(在这种情况下)使用截断:

In [25]: ts3 = ts.truncate(before='2011-01-08') 

In [26]: ts3 
Out[26]: 
2011-01-08 99.000000 
2011-01-10  0.522765 
2011-01-12  1.559876 

改变这个副本不会改变原来的。

In [27]: ts3[1] = 99 

In [28]: ts3 
Out[28]: 
2011-01-08 99.000000 
2011-01-10 99.000000 
2011-01-12  1.559876 

In [29]: ts    #The january 10th value will be unchanged. 
Out[29]: 
2011-01-02 -0.412335 
2011-01-05 -0.809092 
2011-01-07 -0.442320 
2011-01-08 99.000000 
2011-01-10  0.522765 
2011-01-12  1.559876 

这个例子直接来自Wes的“用于数据分析的Python”。一探究竟。这很棒。

+0

在不同的数据集上使用你的命令'ts [datetime(2011,1,8):]',我得到错误:'TypeError:'模块'对象不可调用' – Andreuccio

+0

@Andreuccio检查你的datetime import ,它应该是'从datetime import datetime'。虽然我的答案可能需要更新,如果这是改变。 – Aman

0

我不知道熊猫,一般的回答:

可以重载在蟒蛇什么,他们必须这样做,有。如果您在课堂上定义了一个特殊的方法__getitem__,那么当您使用obj[key]obj[start:stop](在前一种情况下仅使用键作为参数,后者使用特殊的slice object)时会调用该方法。然后你可以返回任何你想要的东西。

下面是一个例子来说明__getitem__是如何工作的:

class Foo(object): 
    def __getitem__(self, k): 
     if isinstance(k, slice): 
      return k.start + k.stop # properties of the slice object 
     else: 
      return k 

这给了你:

>>> f = range.Foo() 
>>> f[42] 
42 
>>> f[23:42] 
65 

我认为在你的榜样,该__getitem__方法返回一些特殊的对象,其中包含日期时间对象加上对原始对象的引用。随后,当调用方法或类似的方法时,该特殊对象可以稍后使用该信息来获取所需的信息。 (它甚至不需要修改原始对象,就像你提出的问题一样。)

TL; DR:学会不担心:-)

增加:我好奇,所以我实现了你自己上面所描述的行为的一个小例子:

class FilterableList(list): 
    def __init__(self, *args): 
     list.__init__(self, *args) 
     self.filter = FilterProxy(self) 

class FilterProxy(object): 
    def __init__(self, parent): 
     self.parent = parent 

    def __getitem__(self, sl): 
     if isinstance(sl, slice): 
      return Filter(self.parent, sl) 

class Filter(object): 
    def __init__(self, parent, sl): 
     self.parent = parent 
     self.sl = sl 

    def eval(self): 
     return [e for e in self.parent if self.sl.start <= e <= self.sl.stop] 


>>> l = FilterableList([4,5,6,7]) 
>>> f = l.filter[6:10] 
>>> f.eval() 
[6, 7] 
>>> l.append(8) 
>>> f.eval() 
[6, 7, 8]