2009-06-21 3 views
5

дана таблица, в которой первый столбец секунд мимо определенного опорной точки, а второй представляет собой произвольное измерение:сглаживающие нерегулярно дискретизированные данные времени

6 0.738158581 
21 0.801697222 
39 1.797224596 
49 2.77920469 
54 2.839757536 
79 3.832232283 
91 4.676794376 
97 5.18244704 
100 5.521878863 
118 6.316630137 
131 6.778507504 
147 7.020395216 
157 7.331607129 
176 7.637492223 
202 7.848079136 
223 7.989456499 
251 8.76853608 
278 9.092367123 
    ... 

Как видно, измерения пробы в нерегулярных временных точках , Мне нужно сгладить данные путем усреднения показаний до 100 секунд перед каждым измерением (в Python). Поскольку таблица данных огромна, метод на основе итератора действительно является предпочтительным. К сожалению, после двух часов кодирования я не могу найти эффективное и элегантное решение.

Может ли кто-нибудь мне помочь?

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

  1. Хочу один сглаженный чтение для каждого сырого чтения и сглаженного чтение должно быть среднее арифметическое сырого чтения и любые другие в предыдущих 100 (дельта) секунд , (Джон, ты прав)

  2. Огромные ~ 1E6 - 10e6 линии + должны работать с жесткой RAM

  3. Данные приблизительно блуждание

  4. Данные сортируются

РЕШЕНИЕ

Я тестировал решения, предложенные J Machin и yairchu. Тем не менее оба они дали те же результаты, что и в моем наборе данных, версия Дж. Мачина выполняется экспоненциально, а у yairchu - линейная. Ниже приведены времена выполнения как измерено % timeit IPython в (в микросекундах):

data size J Machin yairchu 
10  90.2  55.6 
50   930   258 
100   3080  514 
500   64700  2660 
1000  253000  5390 
2000  952000  11500 

Спасибо всем за помощь.

+1

Он слишком велик, чтобы обрабатываться в массивах numpy? Сколько у вас предметов? –

+0

Эта линейная интерполяция позволяет найти точки, кратные 100? –

+0

Если у вас есть требования к сглаживанию, пожалуйста, уточните немного больше. Я пробовал пару раз, но я не могу разобрать это описание: «Мне нужно сгладить данные путем усреднения показаний до 100 секунд перед каждым измерением». – rix0rrr

ответ

2

Я использую результат суммы, к которому я добавляю новых членов и вычитаю старые. Тем не менее, таким образом, вы можете столкнуться с накоплением неточностей с плавающей запятой.

Поэтому я реализую «Deque» со списком. И всякий раз, когда мой Deque перераспределяется на меньший размер. Я пересчитываю сумму по той же причине.

Я также вычисляю среднее значение до точки x, включая точку x, поэтому есть как минимум одна точка выборки в среднем.

def getAvgValues(data, avgSampleTime): 
    lastTime = 0 
    prevValsBuf = [] 
    prevValsStart = 0 
    tot = 0 
    for t, v in data: 
    avgStart = t - avgSampleTime 
    # remove too old values 
    while prevValsStart < len(prevValsBuf): 
     pt, pv = prevValsBuf[prevValsStart] 
     if pt > avgStart: 
     break 
     tot -= pv 
     prevValsStart += 1 
    # add new item 
    tot += v 
    prevValsBuf.append((t, v)) 
    # yield result 
    numItems = len(prevValsBuf) - prevValsStart 
    yield (t, tot/numItems) 
    # clean prevVals if it's time 
    if prevValsStart * 2 > len(prevValsBuf): 
     prevValsBuf = prevValsBuf[prevValsStart:] 
     prevValsStart = 0 
     # recalculate tot for not accumulating float precision error 
     tot = sum(v for (t, v) in prevValsBuf) 
+0

(1) Это очень интересная реализация deque. (2) Я сомневаюсь, что ОП очень обеспокоен накоплением ошибок округления с плавающей запятой; они наверняка будут очень небольшими сокращениями по сравнению с серьезными изменениями сглаживания ... но если он есть, использование сумматора Кахана для поддержания текущей суммы можно было бы рассмотреть. –

+0

Обратите внимание, что это очень вычислительно интенсивно по сравнению с экспоненциальной скользящей средней (http://stackoverflow.com/questions/1023860/exponential-moving-average-sampled-at-varying-times/1024008). Если вам особенно не нужно, убедитесь, что все образцы в пределах временного диапазона вносят одинаковый вклад, а более старые не вносят никакого вклада, я бы пошел с гораздо более эффективной EMA. –

+0

@ Курт Сэмпсон: ОП особо спросил об этом – yairchu

-1

Что-то вроде этого, сохраняйте значения до разницы во времени с прошлым временем> 100, среднее значение и давайте такие значения , например.

def getAvgValues(data): 
    lastTime = 0 
    prevValues = [] 
    avgSampleTime=100 

    for t, v in data: 
     if t - lastTime < avgSampleTime: 
      prevValues.append(v) 
     else: 
      avgV = sum(prevValues)/len(prevValues) 
      lastTime = t 
      prevValues = [v] 
      yield (t,avgV) 

for v in getAvgValues(data): 
    print v 
+0

он запросил средние значения за предыдущие 100 секунд за все время первоначальных измерений. вы даете только 2 результата для своего примера – yairchu

+0

хм я его неправильно понял, любой способ выглядит так, как будто вы его модифицировали для правильного решения –

+0

Я не изменял его на самом деле. Я просто использовал ваши имена переменных. – yairchu

2

Вы не указали точно, когда хотите выход. Я предполагаю, что вам нужно одно сглаженное чтение для каждого сырого чтения, а сглаженное чтение должно быть средним арифметическим для сырого чтения и любых других в предыдущие 100 (дельта) секунд.

Короткий ответ: используйте коллекцию.deque ... он никогда не будет содержать больше «дельта» секунд показаний. Как я его настраивал, вы можете относиться к deque точно так же, как к списку, и легко вычислить среднюю или некоторую причудливую гизмоиду, которая придает больший вес недавним показаниям.

Длинный ответ:

>>> the_data = [tuple(map(float, x.split())) for x in """\ 
... 6  0.738158581 
... 21  0.801697222 
[snip] 
... 251  8.76853608 
... 278  9.092367123""".splitlines()] 
>>> import collections 
>>> delta = 100.0 
>>> q = collections.deque() 
>>> for t, v in the_data: 
...  while q and q[0][0] <= t - delta: 
...   # jettison outdated readings 
...   _unused = q.popleft() 
...  q.append((t, v)) 
...  count = len(q) 
...  print t, sum(item[1] for item in q)/count, count 
... 
... 
6.0 0.738158581 1 
21.0 0.7699279015 2 
39.0 1.112360133 3 
49.0 1.52907127225 4 
54.0 1.791208525 5 
79.0 2.13137915133 6 
91.0 2.49500989771 7 
97.0 2.8309395405 8 
100.0 3.12993279856 9 
118.0 3.74976297144 9 
131.0 4.41385300278 9 
147.0 4.99420529389 9 
157.0 5.8325615685 8 
176.0 6.033109419 9 
202.0 7.15545189083 6 
223.0 7.4342562845 6 
251.0 7.9150342134 5 
278.0 8.4246097095 4 
>>> 

Редактировать

Интернет-магазин: получите ваши фантазии gizmoid здесь.Вот код:

numerator = sum(item[1] * upsilon ** (t - item[0]) for item in q) 
denominator = sum(upsilon ** (t - item[0]) for item in q) 
gizmoid = numerator/denominator 

где ипсилон должна быть немного меньше, чем 1,0 (< = ноль незаконна, чуть выше нулевой делает немного сглаживания, один получает вас среднее арифметическое плюс потраченное впустую время центрального процессора, и больше, чем один дает обратная ваша цель).

+0

Мне кажется, что обычный список будет работать здесь, используя .pop (0) вместо .popleft(). В чем преимущество collection.deque? – Paul

+2

Показ левой части списка Python равен O (N); Поворот слева от дека - O (1) –

0

Ваших данные, как представляется, примерно линейно:

Plot of your data http://rix0r.nl/~rix0r/share/shot-20090621.144851.gif

Какого сглаживания вы ищете? Меньше квадратов подходят линии к этому набору данных? Какой-то фильтр нижних частот? Или что-то другое?

Пожалуйста, сообщите нам заявку, чтобы мы могли посоветовать вам немного лучше.

EDIT: Например, в зависимости от приложения интерполирование линии между первой и последней точкой может быть достаточно хорошим для ваших целей.

+0

На этот раз он может быть линейным. – Nosredna

0

Это один делает линейный:

def process_data(datafile): 
    previous_n = 0 
    previous_t = 0 
    for line in datafile: 
     t, number = line.strip().split() 
     t = int(t) 
     number = float(number) 
     delta_n = number - previous_n 
     delta_t = t - previous_t 
     n_per_t = delta_n/delta_t 
     for t0 in xrange(delta_t): 
      yield previous_t + t0, previous_n + (n_per_t * t0) 
     previous_n = n 
     previous_t = t 

f = open('datafile.dat') 

for sample in process_data(f): 
    print sample 
+0

(1). .strip() является избыточным. (2) Кажется, вы забыли обновить предыдущий_ * каждый раз вокруг (3). Даже тогда не очевидно, что это должно делать ... казалось бы, он выполняет линейную интерполяцию между предыдущим чтением и текущим чтением, с интервалом в одну секунду - интересная интерпретация требований ОП. (3) Я думаю, что вы имели в виду 'для t0 в xrange (1, delta_t + 1)' –

-2

Похоже, вам нужна простая формула округления. Чтобы закруглить любое число в произвольном интервале:

раунд (число/интервал) * Интервал

Вы можете заменить круглые с пола или потолка для «ведущих к» или «так» влияет. Он может работать на любом языке, включая SQL.

0

Память O (1) в случае, если вы можете повторять ввод более одного раза - вы можете использовать один итератор для «левого» и один для «правого».

def getAvgValues(makeIter, avgSampleTime): 
    leftIter = makeIter() 
    leftT, leftV = leftIter.next() 
    tot = 0 
    count = 0 
    for rightT, rightV in makeIter(): 
    tot += rightV 
    count += 1 
    while leftT <= rightT - avgSampleTime: 
     tot -= leftV 
     count -= 1 
     leftT, leftV = leftIter.next() 
    yield rightT, tot/count 
+1

Я предполагаю, что OP хочет отобразить сглаженное значение в реальном времени ... подумайте, что монитор сердца в отделении интенсивной терапии. –

0

В то время как он дает экспоненциально затухающий в среднем, а не общее среднее, я думаю, что вы можете, что я называется exponential moving average with varying alpha, который действительно однополюсный фильтр нижних частот. Теперь есть решение этого вопроса, и оно работает во времени линейно по количеству точек данных. Посмотрите, работает ли это для вас.

 Смежные вопросы

  • Нет связанных вопросов^_^