Как улучшить производительность в заполнении пробелов во временных рядах и списках данных с помощью Python - PullRequest
1 голос
/ 30 сентября 2011

У меня есть наборы данных временного ряда, содержащие данные 10 Гц за несколько лет.За один год мои данные содержат около 3,1 * 10 ^ 8 строк данных (каждая строка имеет метку времени и 8 значений с плавающей запятой).В моих данных есть пробелы, которые мне нужно идентифицировать и заполнить «NaN».Мой код на Python ниже способен на это, но производительность для моей проблемы слишком плохая.Я не могу получить хотя бы мои данные, даже близко к разумному времени.

Ниже приведен минимальный рабочий пример.У меня есть, например, ряды (time-seris-data) и данные в виде строк одинаковой длины:

series      = [1.1, 2.1, 3.1, 7.1, 8.1, 9.1, 10.1, 14.1, 15.1, 16.1, 20.1]
data_a      = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
data_b      = [1.2, 1.2, 1.2, 2.2, 2.2, 2.2, 2.2, 3.2, 3.2, 3.2, 4.2]

Я бы хотел, чтобы серии проходили с интервалами 1, поэтому промежутки между рядами составляют 4.1, 5.1,6.1, 11.1, 12.1, 13.1, 17.1, 18.1, 19.1.Списки data_a и data_b должны быть заполнены значениями типа float (nan).поэтому data_a, например, должно стать:

[1,2, 1,2, 1,2, нан, нан, нан, 2,2, 2,2, 2,2, 2,2, нан, нан, нан, 3,2, 3,2, 3,2, нан, нан,nan, 4.2]

Я архивировал это, используя:

d_max = 1.0    # Normal increment in series where no gaps shall be filled
shift = 0

for i in range(len(series)-1):
    diff = series[i+1] - series[i]
    if diff > d_max:
        num_fills = round(diff/d_max)-1    # Number of fills within one gap
        for it in range(num_fills):
            data_a.insert(i+1+it+shift, float(nan))
            data_b.insert(i+1+it+shift, float(nan))
        shift = int(shift + num_fills)     # Shift the index by the number of inserts from the previous gap filling

Я искал другие решения этой проблемы, но наткнулся только на использование функции find (), получающей индексы пробелов,Функция find () быстрее моего решения?Но тогда как мне вставить NaN в data_a и data_b более эффективным способом?

Ответы [ 2 ]

4 голосов
/ 30 сентября 2011

Во-первых, поймите, что ваш внутренний цикл не нужен:

for it in range(num_fills):
    data_a.insert(i+1+it+shift, float(nan))

- это то же самое, что и

data_a[i+1+shift:i+1+shift] = [float(nan)] * int(num_fills)

. Это может сделать его немного быстрее, потому что меньше места и меньше движущихся предметов.продолжается.

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

import numpy as np

series = np.array([1.1, 2.1, 3.1, 7.1, 8.1, 9.1, 10.1, 14.1, 15.1, 16.1, 20.1])
data_a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
data_b = [1.2, 1.2, 1.2, 2.2, 2.2, 2.2, 2.2, 3.2, 3.2, 3.2, 4.2]

d_max = 1.0    # Normal increment in series where no gaps shall be filled
shift = 0

# the following two statements use NumPy's broadcasting
# to implicit run some loop at the C level
diff = series[1:] - series[:-1]
num_fills = np.round(diff / d_max) - 1
for i in np.where(diff > d_max)[0]:
    nf = num_fills[i]
    nans = [np.nan] * nf
    data_a[i+1+shift:i+1+shift] = nans
    data_b[i+1+shift:i+1+shift] = nans
    shift = int(shift + nf)
1 голос
/ 30 сентября 2011

IIRC, вставки в списки Python стоят дорого, с размером списка.

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

from itertools import izip

series      = [1.1, 2.1, 3.1, 7.1, 8.1, 9.1, 10.1, 14.1, 15.1, 16.1, 20.1]
data_a      = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
data_b      = [1.2, 1.2, 1.2, 2.2, 2.2, 2.2, 2.2, 3.2, 3.2, 3.2, 4.2]

def fillGaps(series,data_a,data_b,d_max=1.0):
  prev = None
  for s, a, b in izip(series,data_a,data_b):
    if prev is not None:
      diff = s - prev
      if s - prev > d_max:
        for x in xrange(int(round(diff/d_max))-1):
          yield (float('nan'),float('nan'))
    prev = s
    yield (a,b)

newA = []
newB = []
for a,b in fillGaps(series,data_a,data_b):
  newA.append(a)
  newB.append(b)

например. прочитайте данные в izip и запишите их вместо добавления списка.

...