通过 pandas 的曲线识别 "similar" 个数据集
identifying "similar" datasets by their curves with pandas
考虑以下
import pandas as pd
import numpy as np
df = pd.DataFrame({'y':np.sin(np.linspace(1,100,10000)),'x':pandas.date_range('2019-01-01','2020-01-01',periods=10000})
df.index = df['x']
现在聚合形式 1
#this cut might not be exactly right ... but i dont have the actual code in front of me at the minute... the idea should be clear however
df['y'].groupby(pd.cut(df.index,1400)).agg('mean') # cut into 1400 equalish bins
在版本 2 中
df['y'].resample("3H").agg('mean') # cut into 3 hour bins
这两个实际上是相同的数据集,曲线应该是相同的......但是什么是验证曲线相同(ish)而不绘制它们并目视检查它们的好方法(当前方法)
你可以在下图中直观地识别相同性(蓝线是一个聚合,黄色是另一个聚合)
这里的动机是开发人员可能错误地使用了错误的聚合(求和而不是均值,或者可能是错误的精度舍入)
并且能够以编程方式验证输出大致相同将是一个巨大的福音
(另见 https://repl.it/@JoranBeasley/EvilAwfulSimulation 可运行示例)
我的一个想法是尝试将图表分割成 N 个部分并在 x 轴的每个子集上使用 np.trapz 并比较值
另一个想法可能是检查 x 轴每个分段的正态分布...到目前为止,我用这两种方法都没有取得多大成功
简介
好问题!这是信号处理中的一个常见问题。让我们加深对要考虑的内容的理解。一些假设是:
没有实际的等价性,所以我们必须说明有多少差异是允许的。通常,这看起来像是差异性度量的阈值;如果测量值小于某个值,则信号足够相似,可以认为是等效的。
时间跨度有歧义。如果信号不共享时间跨度,则可能会出现多种解释。我们可以丢弃不连续时间跨度上的信号信息,只关注重叠时间。此外,我们可以 关联 信号以找到信号之间的最佳重叠。或者,当信号在感兴趣区域之外处于活动状态时,我们可以进行惩罚。在这种情况下,我们将假设信号在时间跨度上足够接近,不需要对其进行预处理。
等价应该对时间长度不敏感。也就是说,相异性度量应该归一化为时间长度,以便在信号延长时不会导致不同的结果。对于统一时间采样,取一些误差量的平均值可以实现这一点,因为样本计数与时间跨度成正比。
实施
让我们考虑一个天真的方法。让我们对两个信号的差异进行衡量,以获得差异的概念。一旦我们减去两个信号,就可以通过取值的平方和来计算信号功率。如前所述,这个值应该通过除以涉及的点数来归一化。总结:
- 使用信号理论确定的足够采样率对两个信号重新采样
- 减去两个信号,因为两个信号在同一时间步都有值
- 通过累加差值信号的平方来计算功率
- 标准化这个值,通常使用 MSE
- 如果两个信号小于预定义值,则这两个信号是等价的,否则不等价
在代码中,
def naive_equivalence(y0, y1, thresh=1e-3):
""" for signals with identical timespans, use MSE of signal difference to determine equivalence """
from scipy.signal import resample
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
diff_pow = np.sum(np.abs(x0r - x1r)**2) / len(x0r)
return diff_pow < thresh
这种方法有问题。即,它对
敏感
- time skew -- 如果两个信号相同但时间偏移很小,我们可能希望将它们视为等价的。在这种情况下,如果发生这种情况,差异信号仍然可能很大,这意味着不会报告等效性。
- time slew -- 两个信号可能具有相似的特征,但发生在不同的时间跨度和变化率上。在特征方面,信号可能被认为是相同的,如果要对此进行测试,那么天真的方法是不够的。
这可以使用 动态时间扭曲 来解决,可以是 read about here。一个示例方法是,
def dtw_equivalence(y0, y1, thresh=1e-3):
""" use notion of distance from dtw to show equivalence
"""
from tslearn.metric import dtw
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
meas = dtw(x0r, x1r)**2 / len(x0r)
return meas < thresh
代码
import pandas as pd
import numpy as np
def get_data():
df = pd.DataFrame({'y': np.sin(np.linspace(1,100,10000)),'x': pd.date_range('2019-01-01','2020-01-01', periods=10000)})
df.index = df['x']
y0 = df['y'].groupby(pd.cut(df.index,1400)).agg('mean') # cut into 1400 equalish bins
y1 = df['y'].resample("3H").agg('mean') # cut into 3 hour bins
return y0, y1
def decompose_timeseries(y):
return np.array([(t.right.value if type(t) == pd.Interval else t.value)/10**9 for t in y.index]), np.array(y.values)
def naive_equivalence(y0, y1, thresh=1e-3):
""" for signals with identical timespans, use MSE of signal difference to determine equivalence
"""
from scipy.signal import resample
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
diff_pow = np.sum(np.abs(x0r - x1r)**2) / len(x0r)
return diff_pow < thresh
def dtw_equivalence(y0, y1, thresh=1e-3):
""" use notion of distance from dtw to show equivalence
"""
from tslearn.metric import dtw
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
meas = dtw(x0r, x1r)**2 / len(x0r)
return meas < thresh
if __name__ == "__main__":
import matplotlib.pyplot as plt
y0, y1 = get_data()
print("Is Naive Equal: ", naive_equivalence(y0, y1))
print("Is DTW Equal: ", dtw_equivalence(y0, y1))
plt.plot(*decompose_timeseries(y0))
plt.plot(*decompose_timeseries(y1))
plt.show()
考虑以下
import pandas as pd
import numpy as np
df = pd.DataFrame({'y':np.sin(np.linspace(1,100,10000)),'x':pandas.date_range('2019-01-01','2020-01-01',periods=10000})
df.index = df['x']
现在聚合形式 1
#this cut might not be exactly right ... but i dont have the actual code in front of me at the minute... the idea should be clear however
df['y'].groupby(pd.cut(df.index,1400)).agg('mean') # cut into 1400 equalish bins
在版本 2 中
df['y'].resample("3H").agg('mean') # cut into 3 hour bins
这两个实际上是相同的数据集,曲线应该是相同的......但是什么是验证曲线相同(ish)而不绘制它们并目视检查它们的好方法(当前方法)
你可以在下图中直观地识别相同性(蓝线是一个聚合,黄色是另一个聚合)
这里的动机是开发人员可能错误地使用了错误的聚合(求和而不是均值,或者可能是错误的精度舍入)
并且能够以编程方式验证输出大致相同将是一个巨大的福音
(另见 https://repl.it/@JoranBeasley/EvilAwfulSimulation 可运行示例)
我的一个想法是尝试将图表分割成 N 个部分并在 x 轴的每个子集上使用 np.trapz 并比较值
另一个想法可能是检查 x 轴每个分段的正态分布...到目前为止,我用这两种方法都没有取得多大成功
简介
好问题!这是信号处理中的一个常见问题。让我们加深对要考虑的内容的理解。一些假设是:
没有实际的等价性,所以我们必须说明有多少差异是允许的。通常,这看起来像是差异性度量的阈值;如果测量值小于某个值,则信号足够相似,可以认为是等效的。
时间跨度有歧义。如果信号不共享时间跨度,则可能会出现多种解释。我们可以丢弃不连续时间跨度上的信号信息,只关注重叠时间。此外,我们可以 关联 信号以找到信号之间的最佳重叠。或者,当信号在感兴趣区域之外处于活动状态时,我们可以进行惩罚。在这种情况下,我们将假设信号在时间跨度上足够接近,不需要对其进行预处理。
等价应该对时间长度不敏感。也就是说,相异性度量应该归一化为时间长度,以便在信号延长时不会导致不同的结果。对于统一时间采样,取一些误差量的平均值可以实现这一点,因为样本计数与时间跨度成正比。
实施
让我们考虑一个天真的方法。让我们对两个信号的差异进行衡量,以获得差异的概念。一旦我们减去两个信号,就可以通过取值的平方和来计算信号功率。如前所述,这个值应该通过除以涉及的点数来归一化。总结:
- 使用信号理论确定的足够采样率对两个信号重新采样
- 减去两个信号,因为两个信号在同一时间步都有值
- 通过累加差值信号的平方来计算功率
- 标准化这个值,通常使用 MSE
- 如果两个信号小于预定义值,则这两个信号是等价的,否则不等价
在代码中,
def naive_equivalence(y0, y1, thresh=1e-3):
""" for signals with identical timespans, use MSE of signal difference to determine equivalence """
from scipy.signal import resample
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
diff_pow = np.sum(np.abs(x0r - x1r)**2) / len(x0r)
return diff_pow < thresh
这种方法有问题。即,它对
敏感- time skew -- 如果两个信号相同但时间偏移很小,我们可能希望将它们视为等价的。在这种情况下,如果发生这种情况,差异信号仍然可能很大,这意味着不会报告等效性。
- time slew -- 两个信号可能具有相似的特征,但发生在不同的时间跨度和变化率上。在特征方面,信号可能被认为是相同的,如果要对此进行测试,那么天真的方法是不够的。
这可以使用 动态时间扭曲 来解决,可以是 read about here。一个示例方法是,
def dtw_equivalence(y0, y1, thresh=1e-3):
""" use notion of distance from dtw to show equivalence
"""
from tslearn.metric import dtw
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
meas = dtw(x0r, x1r)**2 / len(x0r)
return meas < thresh
代码
import pandas as pd
import numpy as np
def get_data():
df = pd.DataFrame({'y': np.sin(np.linspace(1,100,10000)),'x': pd.date_range('2019-01-01','2020-01-01', periods=10000)})
df.index = df['x']
y0 = df['y'].groupby(pd.cut(df.index,1400)).agg('mean') # cut into 1400 equalish bins
y1 = df['y'].resample("3H").agg('mean') # cut into 3 hour bins
return y0, y1
def decompose_timeseries(y):
return np.array([(t.right.value if type(t) == pd.Interval else t.value)/10**9 for t in y.index]), np.array(y.values)
def naive_equivalence(y0, y1, thresh=1e-3):
""" for signals with identical timespans, use MSE of signal difference to determine equivalence
"""
from scipy.signal import resample
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
diff_pow = np.sum(np.abs(x0r - x1r)**2) / len(x0r)
return diff_pow < thresh
def dtw_equivalence(y0, y1, thresh=1e-3):
""" use notion of distance from dtw to show equivalence
"""
from tslearn.metric import dtw
num = max(len(y0), len(y1))
t0, x0 = decompose_timeseries(y0)
t1, x1 = decompose_timeseries(y1)
x0r, t0r = resample(x0, num, t=t0)
x1r, t1r = resample(x1, num, t=t1)
meas = dtw(x0r, x1r)**2 / len(x0r)
return meas < thresh
if __name__ == "__main__":
import matplotlib.pyplot as plt
y0, y1 = get_data()
print("Is Naive Equal: ", naive_equivalence(y0, y1))
print("Is DTW Equal: ", dtw_equivalence(y0, y1))
plt.plot(*decompose_timeseries(y0))
plt.plot(*decompose_timeseries(y1))
plt.show()