Суть:
Если столбец содержит последовательность из более чем, скажем, 5 пропущенных значений, я хотел бы удалить соответствующие индексы из этого кадра данных. Таким образом, в кадре данных, как показано ниже ...
A B
2017-01-01 -0.0053 -0.0062
2017-01-02 NaN 0.0016
2017-01-03 NaN 0.0043
2017-01-04 NaN -0.0077
2017-01-05 NaN -0.0070
2017-01-06 NaN 0.0058
2017-01-07 0.0024 -0.0074
2017-01-08 0.0018 0.0086
2017-01-09 0.0020 0.0012
2017-01-10 -0.0031 -0.0020
2017-01-11 0.0027 NaN
2017-01-12 -0.0050 NaN
2017-01-13 -0.0063 NaN
2017-01-14 0.0066 0.0095
2017-01-15 0.0039 0.0028
... Я хотел бы удалить индексы 2017-01-02
до 2017-01-06
, чтобы желаемый результат выглядел так:
A B
2017-01-01 -0.0053 -0.0062
2017-01-07 0.0024 -0.0074
2017-01-08 0.0018 0.0086
2017-01-09 0.0020 0.0012
2017-01-10 -0.0031 -0.0020
2017-01-11 0.0027 NaN
2017-01-12 -0.0050 NaN
2017-01-13 -0.0063 NaN
2017-01-14 0.0066 0.0095
2017-01-15 0.0039 0.0028
Как я могу сделать это эффективно?
Подробности:
Вот фрагмент кода для воспроизведения кадра данных:
# imports
import pandas as pd
import numpy as np
np.random.seed(1234)
# Reproducible data sample
def df_sample(rows, names):
''' Function to create data sample with random returns
Parameters
==========
rows : number of rows in the dataframe
names: list of names to represent assets
Example
=======
>>> returns(rows = 2, names = ['A', 'B'])
A B
2017-01-01 0.0027 0.0075
2017-01-02 -0.0050 -0.0024
'''
listVars= names
rng = pd.date_range('1/1/2017', periods=rows, freq='D')
df_temp = pd.DataFrame(np.random.randint(-100,100,size=(rows, len(listVars))), columns=listVars)
df_temp = df_temp.set_index(rng)
df_temp = df_temp / 10000
return df_temp
df = df_sample(15,list('AB'))
Осложнения, которые я знаю
Если бы у фрейма данных были перекрывающиеся индексы с пропущенными значениями в нескольких столбцах, например:
A B
2017-01-01 -0.0053 -0.0062
2017-01-02 NaN 0.0016
2017-01-03 NaN 0.0043
2017-01-04 NaN NaN
2017-01-05 NaN NaN
2017-01-06 NaN NaN
2017-01-07 0.0024 NaN
2017-01-08 0.0018 NaN
2017-01-09 0.0020 0.0012
2017-01-10 NaN -0.0020
... тогда, я думаю, любое решение, использующее apply
столбец за столбцом, будет отображать временный кадр данных, подобный этому ...
A B
2017-01-01 -0.0053 -0.0062
2017-01-07 0.0024 NaN
2017-01-08 0.0018 NaN
2017-01-09 0.0020 0.0012
2017-01-10 NaN -0.0020
... и затем, возможно, игнорировать исходные отсутствующие индексы для column B
с 2017-01-04
до 2017-01-08
. Это, возможно, просто то, что нужно было бы принять, хотя. Но в идеале решение должно признать, что эти индексы изначально представляют 5 последовательно пропущенных значений, а также удалить эти индексы, чтобы результирующий кадр данных выглядел следующим образом:
A B
2017-01-01 -0.0053 -0.0062
2017-01-09 0.0020 0.0012
2017-01-10 NaN -0.0020
(А как насчет последнего NaN? Я бы просто fill forward
. Но, сделав то же самое с каждым пропущенным значением, можно было бы уйти далеко.)
Так что я предполагаю, что это потенциально гораздо более сложная проблема, чем я изначально подозревал (и, возможно, это также является причиной того, что функция pandas.DataFrame.dropna
не имеет конкретного аргумента для нее).
Что я пробовал:
1. pandas.DataFrame.dropna
Я думал, что аргумент thresh
будет способом использовать pandas.DataFrame.dropna , но в соответствии с документами этот аргумент устанавливает порог для существующих вместо пропущено значения:
thresh: int, по умолчанию None
int value: требуется много значений, отличных от NA
2. Определение и поиск закономерностей столбца nan по столбцу
Ниже приведено возможное решение на основе предложенных ответов здесь . Тем не менее, вам необходимо определить, что вы ищете 5 и только 5 пропущенных значений в последовательности. Чтобы завершить решение, мне также нужно найти объединение индексов по всем спискам, которые представляют индексы отсутствующих последовательностей для всех столбцов, а затем установить подкадр данных, соответствующий этому.
Спасибо за любые другие предложения!
Вот все, что нужно для легкого копирования:
import pandas as pd
import numpy as np
np.random.seed(1234)
# Reproducible data sample
def df_sample(rows, names):
''' Function to create data sample with random returns
Parameters
==========
rows : number of rows in the dataframe
names: list of names to represent assets
Example
=======
>>> returns(rows = 2, names = ['A', 'B'])
A B
2017-01-01 0.0027 0.0075
2017-01-02 -0.0050 -0.0024
'''
listVars= names
rng = pd.date_range('1/1/2017', periods=rows, freq='D')
df_temp = pd.DataFrame(np.random.randint(-100,100,size=(rows, len(listVars))), columns=listVars)
df_temp = df_temp.set_index(rng)
df_temp = df_temp / 10000
return df_temp
df = df_sample(15,list('AB'))
df['A'][1:6] = np.nan
df['B'][3:8] = np.nan
dfi = df
# convert to boolean values
df = dfi
df = df.isnull()
# specify pattern
pattern = [True,True, True, True, True]
# prepare for a for loop
idx = []
# loop through all columns and identify sequence of missing values
for col in df:
df_temp = df[col].to_frame()
matched = df_temp.rolling(len(pattern)).apply(lambda x: all(np.equal(x, pattern)))
matched = matched.sum(axis = 1).astype(bool)
idx_matched = np.where(matched)[0]
subset = [range(match-len(pattern)+1, match+1) for match in idx_matched]
result = pd.concat([df.iloc[subs,:] for subs in subset], axis = 0).index
idx.append(result)
print(idx)
Вывод (индексы для последовательностей nan, столбец за столбцом):
[DatetimeIndex(['2017-01-02', '2017-01-03', '2017-01-04', '2017-01-05','2017-01-06'],
dtype='datetime64[ns]', freq=None),
DatetimeIndex(['2017-01-04', '2017-01-05', '2017-01-06', '2017-01-07', '2017-01-08'],
dtype='datetime64[ns]', freq=None)]