Python 将不规则时间序列转换为相对于最近邻居的Z核
我有一个不规则间隔索引的时间序列。我想通过减去平均值,然后除以每个点的标准偏差来转换数据。但是,我只想使用预定义的时间距离的数据值来计算平均值和标准偏差。在我下面的例子中,我使用了规则间隔的距离,但我希望它也能适应不规则的距离 例如:Python 将不规则时间序列转换为相对于最近邻居的Z核,python,numpy,pandas,Python,Numpy,Pandas,我有一个不规则间隔索引的时间序列。我想通过减去平均值,然后除以每个点的标准偏差来转换数据。但是,我只想使用预定义的时间距离的数据值来计算平均值和标准偏差。在我下面的例子中,我使用了规则间隔的距离,但我希望它也能适应不规则的距离 例如: n = 20 ts = pd.Series(np.random.rand(n), pd.date_range('2014-05-01', periods=n, freq='T', name='Time')) 假设我希望每个点的zsc
n = 20
ts = pd.Series(np.random.rand(n),
pd.date_range('2014-05-01', periods=n, freq='T', name='Time'))
假设我希望每个点的zscore相对于该点一分钟内的所有点
最终结果应类似于以下系列
Time
2014-05-01 00:00:00 0.707107
2014-05-01 00:01:00 -0.752435
2014-05-01 00:02:00 0.866662
2014-05-01 00:03:00 -0.576136
2014-05-01 00:04:00 -0.580471
2014-05-01 00:05:00 -0.253403
2014-05-01 00:06:00 -0.076657
2014-05-01 00:07:00 1.054413
2014-05-01 00:08:00 0.095783
2014-05-01 00:09:00 -1.030982
2014-05-01 00:10:00 1.041127
2014-05-01 00:11:00 -1.028084
2014-05-01 00:12:00 0.198363
2014-05-01 00:13:00 0.851951
2014-05-01 00:14:00 -1.152701
2014-05-01 00:15:00 1.070238
2014-05-01 00:16:00 -0.395849
2014-05-01 00:17:00 -0.968585
2014-05-01 00:18:00 0.077004
2014-05-01 00:19:00 0.707107
Freq: T, dtype: float64
这是我一直在做的事情。请记住,这与pandas
rolling
功能相关,但不同于(我想您知道,否则您可能不会问这个问题)。对于你给出的规则间隔的数据,它会很好地结合起来,我们可以用它来比较
我要做的是使用np.subtract.outer
计算序列中所有项目的距离
假设我们有您的时间序列ts
import pandas as pd
import numpy as np
n = 20
np.random.seed([3,1415])
data = np.random.rand(n)
tidx = pd.date_range('2014-05-01', periods=n, freq='T', name='Time')
# ^
# |
# Minute Frequency
ts = pd.Series(data, tidx, name='Bliggles')
pd.Series(ts.ix[lt_delta.index.to_series().str.get(1)].values, lt_delta.index)
现在我可以使用时间索引来计算距离,就像这样
distances = pd.DataFrame(np.subtract.outer(tidx, tidx), tidx, tidx).abs()
从这里开始,我测试小于所需距离的距离。假设该距离称为delta
lt_delta = (distances <= delta).stack()
lt_delta = lt_delta[lt_delta]
我返回一个groupby
对象,这样它看起来和感觉上都像是在调用rolling
。当我把它包装成一个函数时,它看起来
超功能
让我们测试一下。我将使用一个delta=pd.Timedelta(1,'m')
(这是一分钟)。对于我创建的时间序列,对于每个日期时间索引,我应该看到该索引、前一分钟和后一分钟。这应等同于ts.rolling(3,center=True)
,边缘除外。我将两者都做并比较
gbdelta = groupbydelta(ts, pd.Timedelta(1, 'm')).mean()
rolling = ts.rolling(3, center=True).mean()
pd.concat([gbdelta, rolling], axis=1, keys=['Delta', 'Rolling']).head()
看起来很棒!两者的区别在于rolling
在边缘有NaN
,而gbdelta
不需要特定数量的元素,但这是设计的
不规则指数呢
np.random.seed([3,1415])
n = 7200
data = np.random.rand(n)
tidx = (pd.to_datetime(['2013-02-06']) + np.random.rand(n) * pd.Timedelta(1, 'd'))
irregular_series = pd.Series(data, tidx, name='Sketch').sort_index()
并绘制不规则_序列
和一些基于最近邻的过滤版本
但你要的是zscores:
zd = (irregular_series - gbirr.mean()) / gbirr.std()
这个z得分有点棘手。我必须找到分组平均值和标准偏差,然后将它们用于原始序列。我还在想一个窒息的方法。但这已经足够顺利了
它看起来像什么
fig, axes = plt.subplots(1, 2, sharey=True, figsize=[10, 5])
irregular_series.plot(style='.', ax=axes[0], title='Original')
zd.plot(style='.', ax=axes[1], title='Z-Scored')
答复 最后,您询问了数据示例的z分数。为了确保我得到了正确的答案
gbd = groupbydelta(ts, pd.Timedelta(1, 'm'))
ts.sub(gbd.mean()).div(gbd.std())
Time
2014-05-01 00:00:00 0.707107
2014-05-01 00:01:00 -0.752435
2014-05-01 00:02:00 0.866662
2014-05-01 00:03:00 -0.576136
2014-05-01 00:04:00 -0.580471
2014-05-01 00:05:00 -0.253403
2014-05-01 00:06:00 -0.076657
2014-05-01 00:07:00 1.054413
2014-05-01 00:08:00 0.095783
2014-05-01 00:09:00 -1.030982
2014-05-01 00:10:00 1.041127
2014-05-01 00:11:00 -1.028084
2014-05-01 00:12:00 0.198363
2014-05-01 00:13:00 0.851951
2014-05-01 00:14:00 -1.152701
2014-05-01 00:15:00 1.070238
2014-05-01 00:16:00 -0.395849
2014-05-01 00:17:00 -0.968585
2014-05-01 00:18:00 0.077004
2014-05-01 00:19:00 0.707107
Freq: T, dtype: float64
时机 受root答案的启发,我重新编写了基于区间的函数。对于一定长度的时间序列,它比寻找外部差异更有效,这是有道理的 代码
这不是一个
pandas
/numpy
解决方案,但应该提供良好的性能。本质上,要找到最近的点,可以使用PyPI上的包构建一个
intervaltree
包使用非常简单,在语法上非常类似于字典。这个包需要记住的一点是,区间中不包含上界,因此在构建树时需要填充上界。请注意,在我下面的代码中,我向上限添加了额外的纳秒
import intervaltree
def get_ts_zscore(ts, delta):
# Get the upper and lower bounds, padding the upper bound.
lower = ts.index - delta
upper = ts.index + delta + pd.Timedelta(1, 'ns')
# Build the interval tree.
t = intervaltree.IntervalTree().from_tuples(zip(lower, upper, ts))
# Extract the overlaping data points for each index value.
ts_grps = [[iv.data for iv in t[idx]]for idx in ts.index]
# Compute the z-scores.
ts_data = [(x - np.mean(grp))/np.std(grp, ddof=1) for x, grp in zip(ts, ts_grps)]
return pd.Series(ts_data, ts.index)
我无法复制您确切的预期输出,可能是因为我随机生成数据的方式?我的输出与运行@piRSquared的代码完全匹配,所以我很确定它是正确的
计时
样本数据的计时(n=20
):
较大数据上的计时(n=10**4
):
哇<代码>一个表示努力。我需要一段时间来处理这个。太棒了!我会调查的+1谢谢你。我需要一个pandas/numpy解决方案,但我相信这将帮助其他人。我重写了我的函数并运行了一些额外的时间。我想你可能会发现它很有趣。基于时间窗口的滚动将在0.19.0中,请参阅;最近合并
gbd = groupbydelta(ts, pd.Timedelta(1, 'm'))
ts.sub(gbd.mean()).div(gbd.std())
Time
2014-05-01 00:00:00 0.707107
2014-05-01 00:01:00 -0.752435
2014-05-01 00:02:00 0.866662
2014-05-01 00:03:00 -0.576136
2014-05-01 00:04:00 -0.580471
2014-05-01 00:05:00 -0.253403
2014-05-01 00:06:00 -0.076657
2014-05-01 00:07:00 1.054413
2014-05-01 00:08:00 0.095783
2014-05-01 00:09:00 -1.030982
2014-05-01 00:10:00 1.041127
2014-05-01 00:11:00 -1.028084
2014-05-01 00:12:00 0.198363
2014-05-01 00:13:00 0.851951
2014-05-01 00:14:00 -1.152701
2014-05-01 00:15:00 1.070238
2014-05-01 00:16:00 -0.395849
2014-05-01 00:17:00 -0.968585
2014-05-01 00:18:00 0.077004
2014-05-01 00:19:00 0.707107
Freq: T, dtype: float64
def pirsquared(ts, delta):
gbd = groupbydelta(ts, delta)
return ts.sub(gbd.mean()).div(gbd.std())
cols = ['pirsquared', 'root']
ts_len = [500, 1000, 2000, 3000, 4000]
dt_len = [1, 5, 10, 20]
summary = pd.DataFrame([], pd.MultiIndex.from_product([ts_len, dt_len], names=['Points', 'Delta']), cols)
for n in ts_len:
for d in dt_len:
np.random.seed([3,1415])
data = np.random.rand(n)
tidx = (pd.to_datetime(['2013-02-06']) + np.random.rand(n) * pd.Timedelta(1, 'd'))
ts = pd.Series(data, tidx, name='Sketch').sort_index()
delta = pd.Timedelta(d, 'm')
pt = timeit(lambda: pirsquared(ts, delta), number=2) / 2
rt = timeit(lambda: root(ts, delta), number=2) / 2
summary.loc[(n, d), cols] = pt, rt
summary.unstack().swaplevel(0, 1, 1).sort_index(1)
import intervaltree
def get_ts_zscore(ts, delta):
# Get the upper and lower bounds, padding the upper bound.
lower = ts.index - delta
upper = ts.index + delta + pd.Timedelta(1, 'ns')
# Build the interval tree.
t = intervaltree.IntervalTree().from_tuples(zip(lower, upper, ts))
# Extract the overlaping data points for each index value.
ts_grps = [[iv.data for iv in t[idx]]for idx in ts.index]
# Compute the z-scores.
ts_data = [(x - np.mean(grp))/np.std(grp, ddof=1) for x, grp in zip(ts, ts_grps)]
return pd.Series(ts_data, ts.index)
%timeit get_ts_zscore(ts, pd.Timedelta(1, 'm'))
100 loops, best of 3: 2.89 ms per loop
%%timeit
gbd = groupbydelta(ts, pd.Timedelta(1, 'm'))
ts.sub(gbd.mean()).div(gbd.std())
100 loops, best of 3: 7.13 ms per loop
%timeit get_ts_zscore(ts, pd.Timedelta(1, 'm'))
1 loops, best of 3: 1.44 s per loop
%%timeit
gbd = groupbydelta(ts, pd.Timedelta(1, 'm'))
ts.sub(gbd.mean()).div(gbd.std())
1 loops, best of 3: 5.92 s per loop