对一系列数字进行重采样(上采样,内插)

时间:2018-06-19 23:16:25

标签: python pandas numpy interpolation resampling

我有一个用逗号分隔的整数值系列,我想对其重新采样,以便拥有两倍的整数值,其中在每个现有值之间添加一个新值。例如,如果这是我的来源:

1,5,11,9,13,21

结果将是:

1,3,5,8,11,10,9,11,13,17,21

如果不清楚,我尝试在源系列的每个值之间添加一个数字,如下所示:

1   5   11    9    13    21
1 3 5 8 11 10 9 11 13 17 21

我已经搜索了很多,似乎scipy.signal.resample或panda应该可以工作,但是我是一个全新的人,我一直无法使它工作。例如,这是我尝试scipy的尝试之一:

import numpy as np
from scipy import signal
InputFileName = "sample.raw"
DATA250  = np.loadtxt(InputFileName, delimiter=',', dtype=int);
print(DATA250)
DATA500 = signal.resample(DATA250, 11)
print(DATA500)

哪个输出:

[ 1  5 11  9 13 21]
[ 1.         -0.28829461  6.12324489 10.43251996 10.9108191   9.84503237
  8.40293529 10.7641676  18.44182898 21.68506897 12.68267746]

很明显,我使用了signal.resample错误。有没有办法我可以使用signal.resample或panda做到这一点?我应该使用其他方法吗?

此外,在我的示例中,所有源编号之间都存在一个整数。在我的实际数据中,情况并非如此。因此,如果数字中的两个为10,15,则新数字将为12.5。但是,我希望所有结果数字均为整数。因此,要插入的新数字必须为12或13(对我来说这无关紧要)。

请注意,一旦我开始工作,源文件实际上将是一个由2,000个数字组成的逗号分隔列表,并且输出应为4,000个数字(或从技术上讲为3,999个数字,因为末尾将不会再添加一个)。同样,这将用于处理类似于ECG记录的内容-当前,ECG在250 Hz下采样8秒钟,然​​后传递到单独的过程中以分析记录。但是,该单独的过程需要以500 Hz采样采样。因此,工作流程是,我将每8秒记录250 Hz,并将其上采样到500 Hz,然后将输出结果传递给分析过程。

感谢您提供的任何指导。

3 个答案:

答案 0 :(得分:2)

由于插值很简单,因此您可以手工完成:

import numpy as np
a = np.array([1,5,11,9,13,21])
b = np.zeros(2*len(a)-1, dtype=np.uint32)
b[0::2] = a
b[1::2] = (a[:-1] + a[1:]) // 2

您还可以通过以下方式使用scipy.signal.resample

import numpy as np
from scipy import signal
a = np.array([1,5,11,9,13,21])
b = signal.resample(a, len(a) * 2)
b_int = b.astype(int)

诀窍是使元素的数量恰好是两倍,以使奇数点与您的初始点匹配。另外,我认为scipy.signal.resample完成的傅里叶插值比您要求的线性插值更适合您的ECG信号。

答案 1 :(得分:0)

由于您建议使用熊猫解决方案,因此有一种可能性:

import pandas as pd
import numpy as np

l = [1,4,11,9,14,21]
n = len(l)

df = pd.DataFrame(l, columns = ["l"]).reindex(np.linspace(0, n-1, 2*n-1)).interpolate().astype(int)

print(df)

但是,这感觉不必要的复杂。我标记了熊猫,所以更熟悉熊猫功能的人们会看到它。

答案 2 :(得分:0)

尽管我可能只在这里使用NumPy,与J. Martinot-Lagarde's answer非常相似,但实际上并不需要。


首先,您可以仅使用csv模块来读取一行用逗号分隔的数字:

with open(path) as f:
    numbers = map(int, next(csv.reader(f))

…或只是字符串操作:

with open(path) as f:
    numbers = map(int, next(f).split(','))

然后您可以轻松地对其进行插值:

def interpolate(numbers):
    last = None
    for number in numbers:
        if last is not None:
            yield (last+number)//2
        yield number
        last=number

如果您希望它具有完全的通用性和可重用性,只需接受一个function参数和yield function(last, number),然后将None替换为sentinel = object()


现在,您所需要做的就是join个结果并write个结果:

with open(outpath, 'w') as f:
    f.write(','.join(map(str, interpolate(numbers))))

此解决方案是否有任何优势?好吧,除了读/拆分和联接/写之外,它纯粹是惰性的。而且,我们可以很轻松地(或手动完成)编写延迟拆分和联接函数。因此,如果您不得不处理十亿个逗号分隔的数字而不是一千个,那就是所有您需要更改的地方。

这是一个懒惰的split

def isplit(s, sep):
    start = 0
    while True:
        nextpos = s.find(sep, start)
        if nextpos == -1:
            yield s[start:]
            return
        yield s[start:nextpos]
        start=nextpos+1

您可以使用mmap作为延迟读取的字符串(bytes,但是我们的数据是纯ASCII的,所以没关系):

with open(path, 'rb') as f:
    with mmap.mmap(inf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
        numbers = map(int, isplit(mm, b','))

让我们使用另一种解决方案进行懒惰写作,只是为了多样化:

def icsvwrite(f, seq, sep=','):
    first = next(seq, None)
    if not first: return
    f.write(first)
    for value in seq:
        f.write(sep)
        f.write(value)

因此,将它们放在一起:

with open(inpath, 'rb') as inf, open(outpath, 'w') as outf:
    with mmap.mmap(inf.fileno(), 0, access=mmap.ACCESS_READ) as mm:
        numbers = map(int, isplit(mm, b','))
        icsvwrite(outf, map(str, interpolate(numbers)))

但是,即使我能够很快将其拍打在一起,并且所有片段都可以很好地重用,但我还是会仍然使用NumPy解决您的特定问题。您不会读到十亿个数字的行。您已经在唯一要运行此脚本的计算机上安装了NumPy。每8秒导入一次的成本(您可以通过让脚本在两次运行之间处于睡眠状态来解决)。因此,很难击败一个优雅的三线解决方案。