Выбор первого индекса после определенной отметки времени с помощью pandas TimeSeries

Это вопрос из двух частей, с ближайшим вопросом и более общим.

У меня есть pandas 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 [datetime (2012,1,1,15,0,0):] - это объект pandas.TimeSeries? И я мог бы мутировать его.

Означает ли это, что всякий раз, когда я беру кусочек, в памяти выделяется копия ts? Означает ли это, что эта безобидная строка кода может фактически вызвать копию гигабайта TimeSeries, чтобы получить значение индекса?

Или, может быть, они волшебным образом обмениваются памятью, и ленивая копия выполняется, если один из объектов мутирует, например? Но тогда, как вы знаете, какие конкретные операции запускают копию? Может быть, не нарезка, но как насчет переименования столбцов? Похоже, в документации это не говорится. Это беспокоит тебя? Должно ли это беспокоить меня или я должен просто научиться не беспокоиться и ловить проблемы с профилировщиком?

2 ответа

Некоторая настройка:

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

Хорошо, теперь, чтобы ответить на ваш первый вопрос: а) да, есть меньше clunky пути, в зависимости от вашего намерения. Это довольно просто:

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. Это НЕ копия оригинала. См. Этот вопрос или много похожих: Ошибка или функция: клонирование массива numpy 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

Если вам нужна копия, вы можете (в общем) использовать метод копирования или (в этом случае) использовать 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

Этот пример прямо из "Python для анализа данных" Wes. Проверьте это. Это здорово.


Мне не известно panda, общий ответ:

Вы можете перегружать что-либо в python, и они, должно быть, сделали это там. Если вы определяете специальный класс __getitem__ в своем классе, он вызывается при использовании obj[key] или 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__ возвращает некоторый специальный объект, который содержит объекты datetime плюс ссылку на исходный объект 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]

licensed under cc by-sa 3.0 with attribution.