2016-05-29 3 views
5

У меня есть dataframes с индексами DateTime разных типов (могут быть еженедельные, ежемесячные, годовые данные). Я хочу создать столбцы, которые являются отстающими значениями других столбцов. Я получаю их импортированные из электронной таблицы, я не генерирую индекс datetime внутри python.Pythonic способ запаздывания индексированных по дате столбцов

Я изо всех сил пытаюсь найти «питонический» способ сделать это. Я полагаю, что если я буду использовать возможности Datetime Pandas, запаздывание может быть более устойчивым в случае странных или исключительных данных.

Я сделал игрушечный пример, который, кажется, работает, но он не работает на моем примере с реальным миром.

Пример игрушка, которая работает правильно (делает новый столбец, который имеет «Foo» значение предыдущего месяца)

rng = pd.date_range('2012-01-01', '2013-1-01', freq="M") 
toy2 = pd.DataFrame(pd.Series(np.random.randint(0, 50, len(rng)), index=rng, name="foo")) 

      foo 
2012-01-31 4 
2012-02-29 2 
2012-03-31 27 
2012-04-30 7 
2012-05-31 44 
2012-06-30 22 
2012-07-31 16 
2012-08-31 18 
2012-09-30 35 
2012-10-31 35 
2012-11-30 16 
2012-12-31 32 

toy2['lag_foo']= toy2['foo'].shift(1,'m') 

    foo lag_foo 
2012-01-31 4 NaN 
2012-02-29 2 4.0 
2012-03-31 27 2.0 
2012-04-30 7 27.0 
2012-05-31 44 7.0 
2012-06-30 22 44.0 
2012-07-31 16 22.0 
2012-08-31 18 16.0 
2012-09-30 35 18.0 
2012-10-31 35 35.0 
2012-11-30 16 35.0 
2012-12-31 32 16.0 

Но когда я запускаю это на моей реальной жизни, например, он терпит неудачу с:

ValueError: cannot reindex from a duplicate axis

print type(toy) 
print toy.columns 
print toy['IPE m2'][0:5] 

<class 'pandas.core.frame.DataFrame'> 
Index([u'IPE m2'], dtype='object') 
Date 
2016-04-30 43.29 
2016-03-31 40.44 
2016-02-29 34.17 
2016-01-31 32.47 
2015-12-31 39.35 
Name: IPE m2, dtype: float64 

исключение следа:

ValueError        Traceback (most recent call last) 
<ipython-input-170-9cb57a2ed681> in <module>() 
----> 1 toy['prev_1m']= toy['IPE m2'].shift(1,'m') 

C:\Users\mds\Anaconda2\lib\site-packages\pandas\core\frame.pyc in __setitem__(self, key, value) 
    2355   else: 
    2356    # set column 
-> 2357    self._set_item(key, value) 
    2358 
    2359  def _setitem_slice(self, key, value): 

C:\Users\mds\Anaconda2\lib\site-packages\pandas\core\frame.pyc in _set_item(self, key, value) 
    2421 
    2422   self._ensure_valid_index(value) 
-> 2423   value = self._sanitize_column(key, value) 
    2424   NDFrame._set_item(self, key, value) 
    2425 

C:\Users\mds\Anaconda2\lib\site-packages\pandas\core\frame.pyc in _sanitize_column(self, key, value) 
    2555 
    2556   if isinstance(value, Series): 
-> 2557    value = reindexer(value) 
    2558 
    2559   elif isinstance(value, DataFrame): 

C:\Users\mds\Anaconda2\lib\site-packages\pandas\core\frame.pyc in reindexer(value) 
    2547      # duplicate axis 
    2548      if not value.index.is_unique: 
-> 2549       raise e 
    2550 
    2551      # other 

ValueError: cannot reindex from a duplicate axis 

Кажется, что мне не хватает какой-то тонкости индексов Dandetime Pandas, я думаю. Плюс я даже не уверен, что это идеальный способ сделать это. единственное, что я мог заподозрить, что нерабочими toy.index не имеет ни как частота, в то время как рабочий toy2, например, имеет свой набор частот, как «M»

toy.index 
DatetimeIndex(['2016-04-30', '2016-03-31', '2016-02-29', '2016-01-31', 
       '2015-12-31', '2015-11-30', '2015-10-31', '2015-09-30', 
       '2015-08-31', '2015-07-31', 
       ... 
         'NaT',  'NaT',  'NaT',  'NaT', 
         'NaT',  'NaT',  'NaT',  'NaT', 
         'NaT',  'NaT'], 
       dtype='datetime64[ns]', name=u'Date', length=142, freq=None) 


toy2.index 
DatetimeIndex(['2012-01-31', '2012-02-29', '2012-03-31', '2012-04-30', 
       '2012-05-31', '2012-06-30', '2012-07-31', '2012-08-31', 
       '2012-09-30', '2012-10-31', '2012-11-30', '2012-12-31'], 
       dtype='datetime64[ns]', freq='M') 
In [ ]: 

======== ===================

Я выбросил Ната

toy = toy.dropna() 

toy['prev_1m']= toy['IPE m2'].shift(1,'m') 

и я получаю результаты, которые я хотел. Тем не менее, я получаю предупреждение:

C:\Users\mds\Anaconda2\lib\site-packages\ipykernel\__main__.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame. 
Try using .loc[row_indexer,col_indexer] = value instead 

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy 
    if __name__ == '__main__': 

====

этот способ присвоения подавляет предупреждения:

toy.loc[:,'prev_1m2']= toy['IPE m2'].shift(1,'m') 

ответ

2

Существует еще одна проблема - в индексе многих NaT в toyDataFrame , поэтому index имеет значения дубликатов. (Может быть, некоторые даты и времени продублированы тоже.)

Пример:

import pandas as pd 
import numpy as np 

rng = pd.date_range('2012-01-01', '2013-1-01', freq="M") 
toy2 = pd.DataFrame(pd.Series(np.random.randint(0, 50, len(rng)), index=rng, name="foo")) 

df = pd.DataFrame({'foo': [10,30,19]}, index=[np.nan, np.nan, np.nan]) 
print (df) 
    foo 
NaN 10 
NaN 30 
NaN 19 

toy2 = pd.concat([toy2, df]) 
print (toy2) 
      foo 
2012-01-31 18 
2012-02-29 34 
2012-03-31 43 
2012-04-30 17 
2012-05-31 45 
2012-06-30 8 
2012-07-31 36 
2012-08-31 26 
2012-09-30 5 
2012-10-31 18 
2012-11-30 39 
2012-12-31 3 
NaT   10 
NaT   30 
NaT   19 

toy2['lag_foo']= toy2['foo'].shift(1,'m') 
print (toy2) 

ValueError: cannot reindex from a duplicate axis

Одним из возможных решений может быть опускаем параметр freq=m:

toy2['lag_foo']= toy2['foo'].shift(1) 
print (toy2) 
      foo lag_foo 
2012-01-31 21  NaN 
2012-02-29 13  21.0 
2012-03-31 41  13.0 
2012-04-30 38  41.0 
2012-05-31 15  38.0 
2012-06-30 41  15.0 
2012-07-31 30  41.0 
2012-08-31 18  30.0 
2012-09-30 12  18.0 
2012-10-31 35  12.0 
2012-11-30 23  35.0 
2012-12-31 7  23.0 
NaT   10  7.0 
NaT   30  10.0 
NaT   19  30.0 

Если нужно удалить все записи с NaN (NaT) в index, использовать notnull с boolean indexing:

print (toy2) 
      foo 
2012-01-31 41 
2012-02-29 15 
2012-03-31 8 
2012-04-30 2 
2012-05-31 16 
2012-06-30 43 
2012-07-31 2 
2012-08-31 15 
2012-09-30 3 
2012-10-31 46 
2012-11-30 34 
2012-12-31 36 
NaT   10 
NaT   30 
NaT   19 

toy2 = toy2[pd.notnull(toy2.index)] 

toy2['lag_foo']= toy2['foo'].shift(1, 'm') 
print (toy2) 
      foo lag_foo 
2012-01-31 41  NaN 
2012-02-29 15  41.0 
2012-03-31 8  15.0 
2012-04-30 2  8.0 
2012-05-31 16  2.0 
2012-06-30 43  16.0 
2012-07-31 2  43.0 
2012-08-31 15  2.0 
2012-09-30 3  15.0 
2012-10-31 46  3.0 
2012-11-30 34  46.0 
2012-12-31 36  34.0 
+0

Я использовал dropna(), чтобы выбросить NaT, и он работает, однако он дает некоторые предупреждения. Добавили добавление к исходному вопросу. – user3556757

+1

Вы уверены, что вам нужна «dropna»? Он удаляет все строки, где находится «NaN» в столбцах. Если да, используйте 'copy':' toy = toy.dropna(). Copy() 'Если вам нужно удалить записи с индексом' NaN', используйте 'toy = toy [ pd.notnull (toy.index)] '. – jezrael