Скользящее окно по списку списков в Python
Я пытаюсь использовать numpy/pandas для построения компаратора стиля скользящего окна. У меня есть список списков, каждый из которых имеет разную длину. Я хочу сравнить каждый список с другим списком, как показано ниже:
lists = [[10,15,5],[5,10],[5]]
window_diff(l[1],l[0]) = 25
Окно diff для списков [0] и списков[1] дал бы 25, используя следующую технику скольжения окна, показанную на рисунке ниже. Потому что списки[1] это более короткий путь, мы сдвигаем его один раз вправо, в результате чего получается 2 окна сравнения. Если вы суммируете в последней строке на рисунке ниже мы получаем общую разницу между двумя списками, используя два окна сравнения; в этом случае всего 25. Отметим, что мы берем абсолютную разницу.
Функция должна агрегировать общий window_diff между каждым списком и другими списками, поэтому в этом случае
tot = total_diffs(lists)
tot>>[40, 30, 20]
# where tot[0] represents the sum of lists[0] window_diff with all other lists.
Я хотел бы знать, если есть быстрый путь, чтобы делать это в панды или NumPy и обратно. В настоящее время я использую очень долгий процесс зацикливания через каждый из списков, а затем сравнение побитового путем сдвига более короткого списка в соответствии с более длинным списком.
Мой подход прекрасно работает для коротких списков, но мой набор данных составляет 10 000 списков в длину, и некоторые из этих списков содержат 60 или около того точек данных, поэтому скорость здесь является критерием. Мне было интересно, есть ли у numpy, pandas какие-нибудь советы по этому поводу? Спасибо
Пример проблемных данных
from random import randint
lists = [[random.randint(0,1000) for r in range(random.randint(0,60))] for x in range(100000)]
2 ответов:
Шаги:
Среди каждой пары списков из входного списка списков создайте скользящие окна для большего массива, а затем получите абсолютную разницу против меньшего в этой паре. Мы можем использовать
NumPy stridesЧтобы получить эти раздвижные окна.Получите общую сумму и сохраните это суммирование как парное дифференцирование.
Наконец, суммируйте вдоль каждой строки и col на массиве
2Dиз предыдущего шага, и их суммирование является окончательным выход.Таким образом, реализация будет выглядеть примерно так -
import itertools def strided_app(a, L, S=1 ): # Window len = L, Stride len/stepsize = S a = np.asarray(a) nrows = ((a.size-L)//S)+1 n = a.strides[0] return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n)) N = len(lists) pair_diff_sums = np.zeros((N,N),dtype=type(lists[0][0])) for i, j in itertools.combinations(range(N), 2): A, B = lists[i], lists[j] if len(A)>len(B): pair_diff_sums[i,j] = np.abs(strided_app(A,L=len(B)) - B).sum() else: pair_diff_sums[i,j] = np.abs(strided_app(B,L=len(A)) - A).sum() out = pair_diff_sums.sum(1) + pair_diff_sums.sum(0)Для действительно тяжелых наборов данных, вот один метод, использующий еще один уровень циклирования -
N = len(lists) out = np.zeros((N),dtype=type(lists[0][0])) for k,i in enumerate(lists): for j in lists: if len(i)>len(j): out[k] += np.abs(strided_app(i,L=len(j)) - j).sum() else: out[k] += np.abs(strided_app(j,L=len(i)) - i).sum()
strided_appвдохновляется отhere.Вход образца, выход -
In [77]: lists Out[77]: [[10, 15, 5], [5, 10], [5]] In [78]: pair_diff_sums Out[78]: array([[ 0, 25, 15], [25, 0, 5], [15, 5, 0]]) In [79]: out Out[79]: array([40, 30, 20])
Просто для полноты великого ответа @ Divakar и для его применения к очень большим наборам данных:
import itertools N = len(lists) out = np.zeros(N, dtype=type(lists[0][0])) for i, j in itertools.combinations(range(N), 2): A, B = lists[i], lists[j] if len(A)>len(B): diff = np.abs(strided_app(A,L=len(B)) - B).sum() else: diff = np.abs(strided_app(B,L=len(A)) - A).sum() out[i] += diff out[j] += diffОн не создает ненужных больших наборов данных и обновляет один вектор, повторяя только верхний треугольный массив.
Вычисление все равно займет некоторое время, поскольку существует компромисс между вычислительной сложностью и наборами данных размером больше ОЗУ. Решения для больших, чем ОЗУ наборов данных часто полагаются на итерации, и python не очень хорош в этом. Пробегом в python над большим набором данных работает медленно, очень медленно.
Перевод вышеприведенного кода на cython может немного ускорить процесс.
Comments