5

这是一个由两部分组成的问题,一个是直接的问题,一个是更一般的问题。

我有一个熊猫时间序列,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[datetime(2012,1,1,15,0,0):] 是 pandas.TimeSeries 对象,对吗?我可能会改变它。

这是否意味着每当我分片时,都会在内存中分配 ts 的副本?这是否意味着这行无害的代码实际上可以触发一千兆字节的 TimeSeries 的副本只是为了获取索引值?

或者,如果其中一个对象发生突变,它们可能会神奇地共享内存并完成惰性复制?但是,您如何知道哪些特定操作触发了副本?也许不是切片,但重命名列怎么样?文档中似乎没有这样说。那会麻烦你吗?它应该困扰我还是我应该学会不用担心并使用分析器发现问题?

4

2 回答 2

13

一些设置:

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

如果您确实想要一个副本,您可以(通常)使用 copy 方法,或者(在这种情况下)使用 truncate:

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 for Data Analysis”。看看这个。这很棒。

于 2012-10-23T23:16:18.527 回答
0

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

你可以在 python 中重载任何东西,他们必须在那里完成。如果你__getitem__在你的类上定义了一个特殊的方法,它会在你使用obj[key]or时被调用obj[start:stop](在前一种情况下只用键作为参数,在后一种情况下用一个特殊的slice对象)。然后,您可以返回任何您想要的东西。

下面是一个展示如何__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__方法返回一些特殊对象,其中包含日期时间对象以及对原始ts对象的引用。first_valid_index然后,当调用该方法或类似方法时,该特殊对象可以使用该信息来获取所需的信息。(它甚至不必修改原始对象,就像您提出的问题一样。)

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]
于 2012-10-23T22:54:33.180 回答