Почему мой цикл Python функционирует, возвращая NaN?

#python #pandas #loops #nan

#python #pandas #циклы #nan

Вопрос:

У меня есть этот цикл ниже, который извлекает информацию о нескольких акциях из API и сохраняет данные в формате словаря вместе с другими тикерами в stocks .

 d = {}
m = {}
for x in stocks:
    d[x] = pdr.get_data_yahoo(x, start= window, end=today)
    d[x]['Growth'] = d[x]['Adj Close'].resample('M').ffill().pct_change()
    d[x]['Date'] = d[x].index
    m[x] = data.get_quote_yahoo(x)['marketCap']
    
sp500 = pdr.get_data_yahoo(spy, start= window, end=today)
sp500 = sp500['Adj Close'].resample('M').ffill().pct_change()
  

Код выполняется нормально, за исключением одной строки: d[x]['Growth'] = d[x]['Adj Close'].resample('M').ffill().pct_change()

В приведенной выше строке кода определяется скорость возврата, используя месяц в качестве частоты. Поскольку я пытаюсь найти эту переменную для всех запасов, я добавил ее в цикл выше. Когда я запускаю код, я получаю кучу NaN для значений строк d[x]['Growth'] , остальная часть кода работает нормально.

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

Я приступаю d[x]['Growth'] = d[x]['Adj Close'].resample('M').ffill().pct_change() к работе, когда это не в цикле.

Проверьте изображение ниже: Код здесь работает нормально

Что не так с тем, как я структурировал цикл? Кажется, не могу разобраться в этом, ищу какую-то помощь.

РЕДАКТИРОВАТЬ: В соответствии с комментарием @RichieV, если кто-нибудь хочет запустить код для репликации моего блокнота, скопируйте и вставьте это.

 import yfinance as yf
from datetime import date
import numpy as np
import pandas as pd
from pandas_datareader import data
from pandas_datareader import data as pdr

stocks = ['INTU','FTNT','MANH']
spy = 'SPY'
years = -3

def rolling_window52w(d, years):
    try:
        return d.replace(year = d.year   years)
    except ValueError:
        return d   (date(d.year   years, 1, 1) - date(d.year, 1, 1))

window_obj = rolling_window52w(date.today(), years)
today_obj = date.today()    
    
window = str(rolling_window52w(date.today(), years))
today = str(date.today())
#d = date.today() - timedelta(days=1)

yf.pdr_override()

d = {}
m = {}
for x in stocks:
    d[x] = pdr.get_data_yahoo(x, start= window, end=today)
    d[x]['Growth'] = d[x]['Adj Close'].resample('M').ffill().pct_change()
    d[x]['Date'] = d[x].index
    m[x] = data.get_quote_yahoo(x)['marketCap']
    
sp500 = pdr.get_data_yahoo(spy, start= window, end=today)
sp500 = sp500['Adj Close'].resample('M').ffill().pct_change()
  

Комментарии:

1. Когда вы запускаете его вне цикла, обратите внимание, что индекс по-прежнему содержит даты только в конце каждого месяца. Если вы присвоите это значение исходному df, оно будет соответствовать строкам, основанным на этом индексе. Все остальные строки будут иметь nans

2. Пожалуйста, всегда включайте весь соответствующий код, чтобы другие могли воспроизвести ваш результат… вам не хватает импорта get_data_yahoo и назначения stocks , window и сегодня … не заставляйте других работать дополнительно, если этого можно избежать

3. @RichieV Я добавил свой блокнот в исходное сообщение. Спасибо, что напомнили мне тоже.

Ответ №1:

Обратите внимание, что при вызове .resample('M') pandas создается новая серия с индексом конца месяца в качестве индекса.

Даже если вы пытаетесь исправить это с помощью вызова .ffill() , передискретизированный ряд не имеет NaNs для заполнения (как показано в вашей второй таблице изображений). Ваш пример вне цикла не имеет эффекта при вызове прямого заполнения, поскольку он содержит только строки на конец месяца.

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

Заменить:

 d[x]['Growth'] = d[x]['Adj Close'].resample('M').ffill().pct_change()
  

С:

 d[x]['Growth'] = d[x]['Adj Close'].resample('M').pct_change()
d[x]['Growth'] = d[x]['Growth'].bfill()
  

Комментарии:

1. Спасибо за ваши предложения, я понимаю, что происходит с моим циклом сейчас. После тестирования вашего предложения я получил сообщение об ошибке: AttributeError: 'DatetimeIndexResampler' object has no attribute 'pct_change' Пытаюсь найти альтернативу сейчас, пока безуспешно.

Ответ №2:

Ваш индекс ( Date ) имеет выборку за день, то есть у вас есть чтение за день. Когда вы делаете

 df['Adj Close'].resample('M').ffill()
  

вы выполняете повторную выборку с интервалом в месяц и ffill будете пересылать недостающие Adj Close значения для выбранных интервалов (в месяц). Таким образом, вы получаете серию, индекс которой выбирается за месяц (freq= ‘M’). Когда вы присваиваете эти значения с помощью

 df['Growth'] = df['Adj Close'].resample('M').ffill()
  

[LHS = RHS]

Поскольку индекс df является выборочным с частотой ‘D’, но ваша правая часть выбирается с ‘M’, недостающие значения левой части будут записаны как NaNs

Пример:

 index = pd.date_range('1/21/2000', periods=15, freq='D')
df  = pd.DataFrame({ 'Adj Close':  np.random.randn(len(index))}, index=index)
df['Growth'] = df['Adj Close'].resample('M').ffill()
print (df)
  

Вывод:

             Adj Close   Growth
2000-01-21  -1.709513      NaN
2000-01-22  -0.133944      NaN
2000-01-23  -0.110338      NaN
2000-01-24  -1.753118      NaN
2000-01-25  -1.023976      NaN
2000-01-26   0.078570      NaN
2000-01-27  -0.219072      NaN
2000-01-28   1.394198      NaN
2000-01-29  -0.693795      NaN
2000-01-30  -0.690569      NaN
2000-01-31   0.740330  0.74033
2000-02-01   0.033305      NaN
2000-02-02  -0.770007      NaN
2000-02-03  -0.862028      NaN
2000-02-04   2.346838      NaN
  

Ответ №3:

РЕДАКТИРОВАТЬ Найдено лучшее решение для добавления столбцов индикаторов с несколькими таймфреймами к моим минутным данным о запасах OHCLV, как показано ниже. Красивый однострочный

 bars['min20ma20'] = bars.closes.resample('20min').last().rolling(window=20).mean().reindex(bars.index, method='ffill')
  

ОРИГИНАЛЬНЫЙ ОТВЕТ НИЖЕ

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

 bars = bars.resample("20min").last()    
bars['min20ma20s'] = bars.closes.rolling(window=20).mean()
  

Если у вас есть другие столбцы в вашем фрейме данных, о которых вы заботитесь, вы также захотите объединить их во время повторной выборки. т. Е. Столбец «объем» для типичных данных запаса. Для этого вы можете использовать приведенный ниже код:

 bars = bars.resample("20min").agg({
    'opens': 'first',
    'highs': 'max',
    'lows': 'min',
    'closes': 'last',
    'volumes': 'sum',
    'todays_volumes': 'last',
    'vwaps': 'last'
})
  

Мой окончательный полный рабочий код:

 data = {
    "ev": "AM",             # Event Type ( A = Second Agg, AM = Minute Agg )
    "sym": "MSFT",          # Symbol Ticker
    "v": 10204,             # Tick Volume
    "av": 200304,           # Accumulated Volume ( Today )
    "op": 114.04,           # Today's official opening price
    "vw": 114.4040,         # VWAP (Volume Weighted Average Price)
    "o": 114.11,            # Tick Open Price
    "c": 114.14,            # Tick Close Price
    "h": 114.19,            # Tick High Price
    "l": 114.09,            # Tick Low Price
    "a": 114.1314,          # Tick Average / VWAP Price
    "s": 1536036818784,     # Tick Start Timestamp ( Unix MS )
    "e": 1536036818784,     # Tick End Timestamp ( Unix MS )
}

bars = pd.DataFrame.from_records(
    [{
        'times': data['e'],
        'symbols': data['sym'],
        'opens': data['o'],
        'highs': data['h'],
        'lows': data['l'],
        'closes': data['c'],
        'volumes': data['v'],
        'todays_volumes': data['av'],
        'vwaps': data['vw']
    }]
)
bars['times'] = pd.to_datetime(
    bars['times'],
    unit='ms'
).dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
bars.index = bars['times']

# Compile and prepare incoming data
indicator = None
for _ in range(500):
    data['e']  = (1 * 60 * 1000)
    bar_df = pd.DataFrame.from_records(
        [{
            'times': data['e'],
            'symbols': data['sym'],
            'opens': data['o'],
            'highs': data['h'],
            'lows': data['l'],
            'closes': data['c'],
            'volumes': data['v'],
            'todays_volumes': data['av'],
            'vwaps': data['vw']
        }]
    )
    bar_df['times'] = pd.to_datetime(
        bar_df['times'],
        unit='ms'
    ).dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
    bar_df.index = bar_df['times']
    bars = bars.append(bar_df)

    # Calculate 20min 20 period moving average after each min bar received
    bars = bars.resample("20min").agg({
        'opens': 'first',
        'highs': 'max',
        'lows': 'min',
        'closes': 'last',
        'volumes': 'sum',
        'todays_volumes': 'last',
        'vwaps': 'last'
    })
    bars['min20ma20s'] = bars.closes.rolling(window=20).mean()
  

Вывод заключительных строк

введите описание изображения здесь