как создать маску из временных точек для массива numpy?

данные представляют собой матрицу, содержащую 2500 временных рядов измерения. Мне нужно усреднять каждый временной ряд с течением времени, отбрасывая точки данных, которые были записаны вокруг всплеска (в интервале tspike-dt*10... tspike+10*dt). Количество спайков является переменным для каждого нейрона и хранится в словаре с 2500 записями. Мой текущий код выполняет итерацию по нейронам и временам спайков и устанавливает замаскированные значения в NaN. Затем вызывается Bottleneck.nanmean(). Однако этот код работает медленно в текущей версии, и мне интересно, есть ли более быстрое решение. Спасибо!

import bottleneck
import numpy as np
from numpy.random import rand, randint

t = 1
dt = 1e-4
N = 2500
dtbin = 10*dt

data = np.float32(ones((N, t/dt)))
times = np.arange(0,t,dt)
spiketimes = dict.fromkeys(np.arange(N))
for key in spiketimes:
  spiketimes[key] = rand(randint(100))

means = np.empty(N)

for i in range(N):        
  spike_times = spiketimes[i]
  datarow = data[i]
  if len(spike_times) > 0:
    for spike_time in spike_times:                        
      start=max(spike_time-dtbin,0)
      end=min(spike_time+dtbin,t)
      idx = np.all([times>=start,times<=end],0)
      datarow[idx] = np.NaN
  means[i] = bottleneck.nanmean(datarow)

person maryam roayaee    schedule 03.08.2012    source источник


Ответы (2)


Подавляющее большинство времени обработки в вашем коде приходится на эту строку:

idx = np.all([times>=start,times<=end],0)

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

# This replaces the last loop in your example:
for i in range(N):        
    spike_times = spiketimes[i]
    datarow = data[i]
    if len(spike_times) > 0:
        for spike_time in spike_times:
            start=max(spike_time-dtbin,0)
            end=min(spike_time+dtbin,t)
            #idx = np.all([times>=start,times<=end],0)
            #datarow[idx] = np.NaN
            datarow[int(start/dt):int(end/dt)] = np.NaN
    ## replaced this with equivalent for testing
    means[i] = datarow[~np.isnan(datarow)].mean()  

Это сокращает время выполнения для меня с ~ 100 с до ~ 1,5 с. Вы также можете сэкономить немного больше времени, векторизовав цикл по Spike_Times. Эффект от этого будет зависеть от характеристик ваших данных (должен быть наиболее эффективным при высокой частоте всплесков):

kernel = np.ones(20, dtype=bool)
for i in range(N):        
    spike_times = spiketimes[i]
    datarow = data[i]
    mask = np.zeros(len(datarow), dtype=bool)
    indexes = (spike_times / dt).astype(int)
    mask[indexes] = True  
    mask = np.convolve(mask, kernel)[10:-9]

    means[i] = datarow[~mask].mean()
person Luke    schedule 04.08.2012
comment
Векторизация внутреннего цикла была именно тем, что я искал. также спасибо за подсказку использовать convolve для создания интервалов для маски. У меня ускорение от нескольких минут до секунды - person maryam roayaee; 10.08.2012

Вместо использования nanmean вы можете просто индексировать нужные вам значения и использовать mean.

means[i] = data[ (times<start) | (times>end) ].mean()

Если я неправильно понял, и вам нужна ваша индексация, вы можете попробовать

means[i] = data[numpy.logical_not( np.all([times>=start,times<=end],0) )].mean()

Кроме того, в коде вы, вероятно, захотите не использовать if len(spike_times) > 0 (я предполагаю, что вы удаляете пиковое время на каждой итерации, иначе это утверждение всегда будет истинным, и у вас будет бесконечный цикл), используйте только for spike_time in spike_times.

person jmetz    schedule 03.08.2012
comment
прием средств уже должен быть оптимизирован. в соответствии с stackoverflow.com/questions/5480694/ метод Bottleneck.mean() быстрее всего справляется со средними значениями по маскированным массивам. Я надеялся, что создание маски из словаря пиковых значений без итерации может улучшить производительность. - person maryam roayaee; 04.08.2012
comment
@maryamroayaee: я не думаю, что вам нужно иметь NaN или вообще использовать маску - вы можете просто проиндексировать нужные значения и взять mean - это должно быть быстрее, чем установка элементов в NaN. - person jmetz; 04.08.2012
comment
@maryamroayaee: Я также думаю, что в вашем коде есть ошибка: потому что, когда вы устанавливаете элементы в NaN на каждой итерации, элементы не восстанавливаются до своих значений до NaN для следующей итерации! - person jmetz; 04.08.2012