Панды конвертируют ежеквартально в ежедневные, сохраняя при этом разные тикеры.

Я работаю над получением финансовых данных, в которых одни отформатированы ежеквартально, а другие - ежедневно. Моя модель будет нуждаться во всем этом ежедневно, поэтому мне нужно, чтобы одно и то же квартальное значение повторялось каждый день. Я использовал этот публикацию стека и пытался адаптировать код к моим данным.

Вот мой заголовок кадра данных:

  date        ticker   value
0 31/03/1980  ECB/RA6  1.0
1 30/06/1980  ECB/RA6  4.0
2 30/09/1980  ECB/RA6  2.0
3 30/12/1980  ECB/RA6  3.0
4 31/03/1981  ECB/RA6  2.0

Вот мой желаемый результат:

   date        ticker   value
0  01/01/1980  ECB/RA6  1.0
1  02/01/1980  ECB/RA6  1.0
2  03/01/1980  ECB/RA6  1.0
3  04/01/1980  ECB/RA6  1.0
4  05/01/1980  ECB/RA6  1.0
.  .           .        .
.  .           .        .
.  .           .        .
91 01/04/1980  ECB/RA6  4.0

И мой код:

df['date'] = pd.to_datetime(df['date'], format='%d/%m/%Y')
df = df.pivot(index='date', columns='ticker')
start_date = df.index.min() - pd.DateOffset(day=1)
end_date = df.index.max() + pd.DateOffset(day=31)
dates = pd.date_range(start_date, end_date, freq='d')
dates.name = 'date'
df = df.reindex(dates, method='ffill')

df = df.stack('ticker')
df = df.sortlevel(level=1)
df = df.reset_index()

Теперь я понимаю, в чем проблема, но я считаю, что это не должно быть проблемой. Я запускаю следующий код из @Pierre D (после удаления дубликатов):

df = df.set_index('date')  # assuming 'date' is a proper Timestamp
df.index = df.index.to_period('Q')  # turn index into PeriodIndex('Q')
df = df.set_index('ticker', append=True).squeeze()
df2 = df[df.duplicated( keep = False)]

Я получаю следующий вывод для df2:

                value   value2
date    ticker      
1997Q2  AAPL    46850   NaN
1997Q3  AAPL    46850   NaN
2003Q1  MSFT    10137   19/12/2003
2003Q2  MSFT    10137   19/12/2003

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

df.unstack()

Я получаю следующую ошибку: ValueError: Индекс содержит повторяющиеся записи, не может изменить форму

Заранее спасибо всем!


person t.pellegrom    schedule 17.12.2020    source источник
comment
У вас должно быть несколько тикеров в одном фрейме данных?   -  person Jakob L    schedule 18.12.2020
comment
Можете ли вы создать отдельный столбец для каждого тикера? Таким образом, вы можете индексировать по дате.   -  person Jakob L    schedule 18.12.2020
comment
@JakobL Да, в моем столбце тикеров более 600 тикеров. Кроме того, чтобы вопрос лучше читался, я пропустил другие столбцы. Итак, помимо столбца значений есть и другие столбцы. Я не уверен, было бы разумно/выполнимо подойти к этому с этой точки зрения.   -  person t.pellegrom    schedule 18.12.2020


Ответы (1)


Обновить

Вопрос был изменен, чтобы указать, что помимо 'value' были и другие столбцы, и из некоторых комментариев я понял, что расширение может быть проблемой (примечание: мы обычно без проблем обрабатываем аналогичные временные ряды с тысячами столбцов).

Итак, вот еще один прием. Он выполняет тот же начальный шаг по преобразованию предполагаемого 'date' в то, чем он является на самом деле: ежеквартальный Period. Но затем он применяет способ повторной выборки time в мультииндексе (time, key) по группам key. На этот вопрос есть несколько ответов StackOverflow, например этот.

Все вместе (с примером):

# setup for example
txt = """  date        ticker   value   value2
0 31/03/1980  ECB/RA6  1.0  NA
1 30/06/1980  another  4.0  NA
2 30/09/1980  ECB/RA6  2.0  19/12/2003
3 30/12/1980  ECB/RA6  3.0  19/12/2003
4 31/03/1981  ECB/RA6  2.0  19/12/2003
"""
df = pd.read_csv(io.StringIO(re.sub(r' +', '\t', txt)),
                 sep='\t', index_col=[0],
                 parse_dates=['date', 'value2'])

# set date as index and convert to quarterly periods
df = df.set_index('date')
df.index = df.index.to_period('Q')

# and now the new resample method (here monthly,
# but change to 'D' for daily)
df = df.groupby('ticker').resample('M').ffill()

Затем вы можете .reset_index(), если хотите, или просто оставить все как есть. Вот результат без сброса индекса:

>>> df
                  ticker  value     value2
ticker  date                              
ECB/RA6 1980-03  ECB/RA6    1.0        NaT
        1980-04  ECB/RA6    1.0        NaT
        1980-05  ECB/RA6    1.0        NaT
        1980-06  ECB/RA6    1.0        NaT
        1980-07  ECB/RA6    1.0        NaT
        1980-08  ECB/RA6    1.0        NaT
        1980-09  ECB/RA6    2.0 2003-12-19
        1980-10  ECB/RA6    2.0 2003-12-19
        1980-11  ECB/RA6    2.0 2003-12-19
        1980-12  ECB/RA6    3.0 2003-12-19
        1981-01  ECB/RA6    3.0 2003-12-19
        1981-02  ECB/RA6    3.0 2003-12-19
        1981-03  ECB/RA6    2.0 2003-12-19
another 1980-06  another    4.0        NaT

Исходный ответ

Вот что я бы сделал: сначала установите date в качестве индекса и преобразуйте его в PeriodIndex, затем сделайте df широким, поместив каждый тикер в столбец. Затем просто пересэмплируйте:

df = df.set_index('date')  # assuming 'date' is a proper Timestamp
df.index = df.index.to_period('Q')  # turn index into PeriodIndex('Q')
df = df.set_index('ticker', append=True).squeeze().unstack()  # make wide: 1 col per ticker
df.resample('D').ffill()  # resample to daily, repeating the values

Исход:

             value
ticker     ECB/RA6
date              
1980-01-01     1.0
1980-01-02     1.0
1980-01-03     1.0
1980-01-04     1.0
1980-01-05     1.0
...            ...
1981-03-27     2.0
1981-03-28     2.0
1981-03-29     2.0
1981-03-30     2.0
1981-03-31     2.0

Возможно, будет легче проверить результат, если вместо этого вы выполните повторную выборку по месяцам:

df.resample('M').ffill()  # resample to daily, repeating the values

# out:
ticker   ECB/RA6
date            
1980-01      1.0
1980-02      1.0
1980-03      1.0
1980-04      4.0
1980-05      4.0
1980-06      4.0
1980-07      2.0
1980-08      2.0
1980-09      2.0
1980-10      3.0
1980-11      3.0
1980-12      3.0
1981-01      2.0
1981-02      2.0
1981-03      2.0

Кстати, полезно посмотреть, что происходит с отсутствующими данными:

# with input df as:
        date   ticker  value
0 1980-03-31  ECB/RA6    1.0
1 1980-06-30  another    4.0
2 1980-09-30  ECB/RA6    2.0

# output:
ticker   ECB/RA6  another
date                     
1980-01      1.0      NaN
1980-02      1.0      NaN
1980-03      1.0      NaN
1980-04      NaN      4.0
1980-05      NaN      4.0
1980-06      NaN      4.0
1980-07      2.0      NaN
1980-08      2.0      NaN
1980-09      2.0      NaN

Последнее замечание: конечно, вы можете снова сложить результат, если хотите получить его в виде высокой и тонкой таблицы (и даже сбросить индекс, если хотите):

print(df.resample('M').ffill().stack().reset_index())

# out:
      date   ticker    0
0  1980-01  ECB/RA6  1.0
1  1980-02  ECB/RA6  1.0
2  1980-03  ECB/RA6  1.0
3  1980-04  another  4.0
4  1980-05  another  4.0
5  1980-06  another  4.0
6  1980-07  ECB/RA6  2.0
7  1980-08  ECB/RA6  2.0
8  1980-09  ECB/RA6  2.0
person Pierre D    schedule 17.12.2020
comment
Когда я запускаю ваш код, я получаю точно такое же сообщение об ошибке, как и при запуске моего собственного кода: ValueError: Index содержит повторяющиеся записи, не может изменить форму. Эта ошибка возникает в третьей строке вашего кода: df = df.set_index('ticker', append=True).squeeze().unstack(). Я считаю, что это вызвано тем, что в столбце даты есть дубликаты. - person t.pellegrom; 18.12.2020
comment
Вы имеете в виду, что df.groupby(['date', 'ticker']).size().max() > 1'? Повторяющиеся даты (для разных тикеров) вполне ожидаемы и обрабатываются нормально, но не повторяющиеся значения кортежа (date, ticker). Если они есть, вам нужно решить, как обрабатывать значения (если они разные). Одной быстрой первой проверкой является df.drop_duplicates() на случай, если некоторые целые строки повторяются. - person Pierre D; 18.12.2020
comment
Вы были правы, что в данных для кортежа (date,ticker) были дубликаты. Однако, когда я использую df = df.drop_duplicates(['date','ticker']) и получаю df.groupby(['date', 'ticker']).size().max() = 1, я все равно получаю ту же ошибку в части unstack() кода. - person t.pellegrom; 19.12.2020
comment
Я отредактировал вопрос и указал, что конкретно вызывает эту ошибку. Похоже, проблема заключается в том, что дублируются не дата и тикер (индексный кортеж), а остальные значения фрейма данных. Я не понимаю, почему эти значения должны отличаться на unstack(). - person t.pellegrom; 19.12.2020
comment
хорошо, поскольку наличие нескольких столбцов меняет вопрос, я адаптировал ответ. Пожалуйста, посмотрите, работает ли это для вас. - person Pierre D; 19.12.2020