Получить строки, временные метки которых находятся в пределах определенного интервала времени скользящего окна pandas (временной ряд) - PullRequest
4 голосов
/ 17 октября 2019

У меня есть такой фрейм данных:

i = pd.to_datetime(np.random.randint(time.time(), time.time()+5000, 10), unit='ms').sort_values()
df = pd.DataFrame({'A':range(10),'B':range(10,30,2),'C':range(10,40,3)},index = i)

df
                         A   B   C
1970-01-19 04:28:30.030  0  10  10
1970-01-19 04:28:30.374  1  12  13
1970-01-19 04:28:31.055  2  14  16
1970-01-19 04:28:32.026  3  16  19
1970-01-19 04:28:32.234  4  18  22
1970-01-19 04:28:32.569  5  20  25
1970-01-19 04:28:32.595  6  22  28
1970-01-19 04:28:33.520  7  24  31
1970-01-19 04:28:33.882  8  26  34
1970-01-19 04:28:34.019  9  28  37

Для каждого индекса я хочу получить последнюю строку, которая находится в интервале «1 с» от этого индекса:

df2
                                    ix            A   B   C
1970-01-19 04:28:30.030  1970-01-19 04:28:30.374  1  12  13
1970-01-19 04:28:30.374  1970-01-19 04:28:31.055  2  14  16
1970-01-19 04:28:31.055  1970-01-19 04:28:32.026  3  16  19
1970-01-19 04:28:32.026  1970-01-19 04:28:32.595  6  22  28
1970-01-19 04:28:32.234  1970-01-19 04:28:32.595  6  22  28
1970-01-19 04:28:32.569  1970-01-19 04:28:33.520  7  24  31
1970-01-19 04:28:32.595  1970-01-19 04:28:33.520  7  24  31
1970-01-19 04:28:33.520  1970-01-19 04:28:34.019  9  28  37
1970-01-19 04:28:33.882  1970-01-19 04:28:34.019  9  28  37
1970-01-19 04:28:34.019             nan          nan nan nan

В настоящее время я делаю это с петлями. В каждом индексе я использую df.between_time, чтобы получить все строки во временном интервале, а затем выбираю последнюю строку. Но это действительно медленно, как и ожидалось. Мне нужно что-то вроде df.shift для времени, я проверил tshift и shift(periods = 1, freq = 'S'), но они не работают как сдвиг, скорее, они добавляют указанное время к каждому индексу. Может ли кто-нибудь помочь мне в достижении этого? Спасибо.

Примечание: столбцы ix в желаемом выводе являются необязательными.

PS: Если возможен параметр min_periods (например, pd.df.rolling), это было бы здорово!


РЕДАКТИРОВАТЬ:

Для начального DF:

                         A   B   C
1970-01-19 04:28:34.883  0  10  10
1970-01-19 04:28:34.900  1  12  13
1970-01-19 04:28:35.531  2  14  16
1970-01-19 04:28:36.845  3  16  19
1970-01-19 04:28:37.664  4  18  22
1970-01-19 04:28:38.332  5  20  25
1970-01-19 04:28:38.444  6  22  28
1970-01-19 04:28:38.724  7  24  31
1970-01-19 04:28:38.787  8  26  34
1970-01-19 04:28:38.951  9  28  37

df['time'] = df.index
def last_time(time):
    time = str(time)
    start_time = datetime.datetime.strptime(time[11:],'%H:%M:%S.%f')
    end_time = start_time + datetime.timedelta(0,1)
    return df.between_time(start_time = str(start_time)[11:-7],end_time= 
                                        str(end_time)[11:-7]).iloc[-1]
df.apply(lambda x:last_time(x['time']),axis = 1)

# Output:
                         A   B   C                    time
1970-01-19 04:28:34.883  1  12  13 1970-01-19 04:28:34.900
1970-01-19 04:28:34.900  1  12  13 1970-01-19 04:28:34.900
1970-01-19 04:28:35.531  2  14  16 1970-01-19 04:28:35.531
1970-01-19 04:28:36.845  3  16  19 1970-01-19 04:28:36.845
1970-01-19 04:28:37.664  4  18  22 1970-01-19 04:28:37.664
1970-01-19 04:28:38.332  9  28  37 1970-01-19 04:28:38.951
1970-01-19 04:28:38.444  9  28  37 1970-01-19 04:28:38.951
1970-01-19 04:28:38.724  9  28  37 1970-01-19 04:28:38.951

Но, как вы можете видеть, я могу получить только second точность уровня, то естьрассматривает между 34 to 35, следовательно, отсутствует 35.531, который находится в интервале от 34.883 и 34.900.

Ответы [ 2 ]

1 голос
/ 17 октября 2019

при условии, что ваше время отсортировано, тогда соответствующая строка для строки 2 будет строго больше, чем строка для строки 1. Например: если строка 6 является строкой для строки 1, тогда строке 2 потребуется только поиск строки, которая> = 6

Имея это в виду, нам просто нужно один раз пройти по индексу (сложность линейная: O (n)):

import pandas as pd
from datetime import datetime

def time_compare(t1,t2):
     return datetime.strptime(t1,'%Y-%m-%d %H:%M:%S.%f').timestamp() - datetime.strptime(t2,'%Y-%m-%d %H:%M:%S.%f').timestamp() < 1

index_j = []
cursor = 0
tmp = list(df.index)
for i in tmp:
    if cursor < len(tmp):
        pass
    else:
        index_j.append(cursor-1)
        continue
    while time_compare(tmp[cursor],i):
        cursor += 1
        if cursor < len(tmp):
            pass
        else:
            break
    index_j.append(cursor-1)

Используя эту df:

>>> df
                         A   B   C
1970-01-19 04:28:34.883  0  10  10
1970-01-19 04:28:34.900  1  12  13
1970-01-19 04:28:35.531  2  14  16
1970-01-19 04:28:36.845  3  16  19
1970-01-19 04:28:37.664  4  18  22
1970-01-19 04:28:38.332  5  20  25
1970-01-19 04:28:38.444  6  22  28
1970-01-19 04:28:38.724  7  24  31
1970-01-19 04:28:38.787  8  26  34
1970-01-19 04:28:38.951  9  28  37



>>> index_j
[2, 2, 2, 4, 6, 9, 9, 9, 9, 9]

Используя индекс:

>>> [tmp[i] for i in index_j]
['1970-01-19 04:28:35.531', '1970-01-19 04:28:35.531', '1970-01-19 04:28:35.531', '1970-01-19 04:28:37.664', '1970-01-19 04:28:38.444', '1970-01-19 04:28:38.951', '1970-01-19 04:28:38.951', '1970-01-19 04:28:38.951', '1970-01-19 04:28:38.951', '1970-01-19 04:28:38.951']
0 голосов
/ 17 октября 2019

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

i = pd.to_datetime(np.random.randint(time.time(), time.time()+5000, 10), unit='ms').sort_values()
df = pd.DataFrame({'A':range(10),'B':range(10,30,2),'C':range(10,40,3)},index = i)
df
df
                         A   B   C
1970-01-19 04:28:30.030  0  10  10
1970-01-19 04:28:30.374  1  12  13
1970-01-19 04:28:31.055  2  14  16
1970-01-19 04:28:32.026  3  16  19
1970-01-19 04:28:32.234  4  18  22
1970-01-19 04:28:32.569  5  20  25
1970-01-19 04:28:32.595  6  22  28
1970-01-19 04:28:33.520  7  24  31
1970-01-19 04:28:33.882  8  26  34
1970-01-19 04:28:34.019  9  28  37

df['time'] = df.index
def last_time(time):
    time = str(time)
    start_time = datetime.datetime.strptime(time[11:],'%H:%M:%S.%f')
    end_time = start_time + datetime.timedelta(0,1)
    tempdf = df.between_time(*pd.to_datetime([str(start_time),str(end_time)]).time).iloc[-1]
    if str(tempdf['time']) == str(time):
        tempdf.iloc[:] = np.nan
        return tempdf
    else:
        return tempdf
df.apply(lambda x:last_time(x['time']),axis = 1)

                           A     B     C                        time
1970-01-19 04:28:34.883  2.0  14.0  16.0  1970-01-19 04:28:35.531000
1970-01-19 04:28:34.900  2.0  14.0  16.0  1970-01-19 04:28:35.531000
1970-01-19 04:28:35.531  NaN   NaN   NaN                         NaN
1970-01-19 04:28:36.845  4.0  18.0  22.0  1970-01-19 04:28:37.664000
1970-01-19 04:28:37.664  6.0  22.0  28.0  1970-01-19 04:28:38.444000
1970-01-19 04:28:38.332  9.0  28.0  37.0  1970-01-19 04:28:38.951000
1970-01-19 04:28:38.444  9.0  28.0  37.0  1970-01-19 04:28:38.951000
1970-01-19 04:28:38.724  9.0  28.0  37.0  1970-01-19 04:28:38.951000
1970-01-19 04:28:38.787  9.0  28.0  37.0  1970-01-19 04:28:38.951000
1970-01-19 04:28:38.951  NaN   NaN   NaN                         NaN
...