Все.

У меня есть отсортированный временной ряд my_ts, и мне нужно найти парные различия (ниже некоторого порога, называемого horizon) между всеми элементами серии (и не только между последовательными элементами).

Для этого я написал следующий код, но, как вы можете видеть, он применяет itertools , который кажется ненужным в среде pandas.

from itertools import combinations
my_ts = pd.Series(pd.date_range('1/1/2018', periods=6, freq='d'))

def count_gaps(ts, horizon):
    # returns counts of all gaps shorter than horizon
    diffs = ((t2-t1) for (t1, t2) in combinations(ts, 2) if t2-t1<=horizon)
    return pd.Series(diffs).value_counts()

count_gaps(my_ts, horizon=pd.to_timedelta(3, unit='d'))

Есть предложения для более пандаистического (и, надеюсь, более быстрого) решения?

1
user3121900 14 Мар 2018 в 17:52

2 ответа

Лучший ответ

Я думаю, что вы можете

s=pd.DataFrame(columns=my_ts,index=my_ts).apply(lambda x : x.name-x.index)

s.mask((s<pd.Timedelta('1 days'))|(s>pd.Timedelta('3 days'))).stack().value_counts()
Out[528]: 
1 days    5
2 days    4
3 days    3
dtype: int64
1
YOBEN_S 14 Мар 2018 в 15:19

Вот значительно более быстрое (около x100 в быстром тесте ниже) решение NumPy:

import numpy as np
import pandas as pd

def count_gaps1(ts, horizon):
    diffs = ts.values[:, np.newaxis] - ts.values[np.newaxis, :]
    idx, c = np.unique(diffs[(diffs > np.timedelta64(0)) & (diffs <= horizon.to_timedelta64())], return_counts=True)
    return pd.Series(c, idx)

my_ts = pd.Series(pd.date_range('1/1/2018', periods=6, freq='d'))
print(count_gaps(my_ts, horizon=pd.to_timedelta(3, unit='d')))

Выход:

1 days    5
2 days    4
3 days    3
dtype: int64

Тест IPython %timeit:

import itertools
import numpy as np
import pandas as pd

def count_gaps_original(ts, horizon):
    diffs = ((t2-t1) for (t1, t2) in itertools.combinations(ts, 2) if t2-t1<=horizon)
    return pd.Series(diffs).value_counts()

def count_gaps_Wen(ts, horizon):
    s = pd.DataFrame(columns=my_ts,index=my_ts).apply(lambda x : x.name-x.index)
    return s.mask((s < pd.Timedelta('1 days')) | (s > pd.Timedelta('3 days'))).stack().value_counts()

def count_gaps_jdehesa(ts, horizon):
    diffs = ts.values[:, np.newaxis] - ts.values[np.newaxis, :]
    idx, c = np.unique(diffs[(diffs > np.timedelta64(0)) & (diffs <= horizon.to_timedelta64())], return_counts=True)
    return pd.Series(c, idx)

my_ts = pd.Series(pd.date_range('1/1/2018', periods=100, freq='d'))
%timeit count_gaps_original(my_ts, horizon=pd.to_timedelta(3, unit='d'))
>>> 145 ms ± 1.87 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit count_gaps_Wen(my_ts, horizon=pd.to_timedelta(3, unit='d'))
>>> 44.1 ms ± 942 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit count_gaps_jdehesa(my_ts, horizon=pd.to_timedelta(3, unit='d'))
>>> 409 µs ± 9.19 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
0
jdehesa 14 Мар 2018 в 15:56