Как выровнять два массива numeries с неравными размерами?
У меня есть два массива numpy, содержащих временные ряды (временные метки unix).
Я хочу найти пары временных меток (1 из каждого массива), чья разница находится в пределах .
Для этого мне нужно выровнять два из данных временных рядов на два массива, так что каждый индекс имеет свою ближайшую пару. (В случае двух временных меток в массивах, одинаково близких к другой метке времени в другом массиве, я не против выбирать один из них, поскольку количество пар более важно, чем фактические значения.)
Таким образом, выровненный набор данных будет содержать два массива одного и того же размера, а также меньший массив, заполненный пустыми данными.
Я думал об использовании пакета timeseries
и функции align
.
Но я не уверен, что использовать выровненные для моих данных, которые являются таймсерами.
Пример рассмотрим два массива timeseries:
ts1=np.array([ 1311242821.0, 1311242882.0, 1311244025.0, 1311244145.0, 1311251330.0,
1311282555.0, 1311282614.0])
ts2=np.array([ 1311226761.0, 1311227001.0, 1311257033.0, 1311257094.0, 1311281265.0])
Выходной образец:
Теперь для ts2[2] (1311257033.0)
его ближайшая пара должна быть ts1[4] (1311251330.0)
, потому что разница составляет 5703.0
, которая находится внутри threshold
, и она наименьшая. Теперь, когда ts2[2]
и ts1[4]
уже спарены, они должны быть исключены из других вычислений.
Такие пары должны быть найдены, поэтому массив Output может быть длиннее фактических массивов
abs (ts1 [0] -ts2 [0]) = 16060
abs (ts1 [0] -ts2 [1]) = 15820//пара
abs (ts1 [0] -ts2 [2]) = 14212
abs (ts1 [0] -ts2 [3]) = 14273
abs (ts1 [0] -ts2 [4]) = 38444
abs (ts1 [1] -ts2 [0]) = 16121
abs (ts1 [1] -ts2 [1]) = 15881
abs (ts1 [1] -ts2 [2]) = 14151
abs (ts1 [1] -ts2 [3]) = 14212
abs (ts1 [1] -ts2 [4]) = 38383
abs (ts1 [2] -ts2 [0]) = 17264
abs (ts1 [2] -ts2 [1]) = 17024
abs (ts1 [2] -ts2 [2]) = 13008
abs (ts1 [2] -ts2 [3]) = 13069
abs (ts1 [2] -ts2 [4]) = 37240
abs (ts1 [3] -ts2 [0]) = 17384
abs (ts1 [3] -ts2 [1]) = 17144
abs (ts1 [3] -ts2 [2]) = 12888
abs (ts1 [3] -ts2 [3]) = 17144
abs (ts1 [3] -ts2 [4]) = 37120
abs (ts1 [4] -ts2 [0]) = 24569
abs (ts1 [4] -ts2 [1]) = 24329
abs (ts1 [4] -ts2 [2]) = 5703//пара
abs (ts1 [4] -ts2 [3]) = 5764
abs (ts1 [4] -ts2 [4]) = 29935
abs (ts1 [5] -ts2 [0]) = 55794
abs (ts1 [5] -ts2 [1]) = 55554
abs (ts1 [5] -ts2 [2]) = 25522
abs (ts1 [5] -ts2 [3]) = 25461
abs (ts1 [5] -ts2 [4]) = 1290//пара
abs (ts1 [6] -ts2 [0]) = 55853
abs (ts1 [6] -ts2 [1]) = 55613
abs (ts1 [6] -ts2 [2]) = 25581
abs (ts1 [6] -ts2 [3]) = 25520
abs (ts1 [6] -ts2 [4]) = 1349
Итак, пары: (ts1[0],ts2[1]), (ts1[4],ts2[2]), (ts1[5],ts2[4]
)
Остальные элементы должны иметь null
как их пару
Последние два массива будут иметь размер 9.
Пожалуйста, дайте мне знать, ясно ли этот вопрос.
Ответы
Ответ 1
Я не знаю, что вы имеете в виду с выравниванием временных меток. Но вы можете использовать временной модуль для представления временных меток как плавающих или целых чисел. На первом этапе вы можете преобразовать любой пользовательский формат в массив, определенный time.struct_time
. На втором этапе вы можете преобразовать это в начало формы эпохи в секундах. Тогда у вас есть integervalues для выполнения вычислений с отметками времени.
Как преобразовать формат пользователя с помощью time.strptime()
хорошо объясняется в docs:
>>> import time
>>> t = time.strptime("30 Nov 00", "%d %b %y")
>>> t
time.struct_time(tm_year=2000, tm_mon=11, tm_mday=30, tm_hour=0, tm_min=0,
tm_sec=0, tm_wday=3, tm_yday=335, tm_isdst=-1)
>>> time.mktime(t)
975538800.0
Ответ 2
Помимо небольших ошибок в вопросе, я мог догадаться, в чем проблема, на самом деле проблема.
То, что вы смотрите, является классическим примером Задача назначения. Scipy предоставляет вам реализацию венгерский алгоритм, проверьте документ здесь. Это не должно быть timestamps, может быть любое число (целое или плавающее).
Ниже фрагмент будет работать с 2 массивами numpy разных размеров вместе с порогом, чтобы дать вам либо массив затрат (отфильтрованный по пороговому значению), либо пары индексов, соответствующие массивам с двумя numpy (опять же, пары, стоимость которых фильтруется порог).
Комментарии пройдут вас, используя пример массивов временных меток в качестве примера.
import numpy as np
from scipy.optimize import linear_sum_assignment
def closest_pairs(inp1, inp2, threshold=np.inf):
cost = np.zeros((inp1.shape[0], inp2.shape[0]), dtype=np.int64)
for x in range(ts1.shape[0]):
for y in range(ts2.shape[0]):
cost[x][y] = abs(ts1[x] - ts2[y])
print(cost)
# cost for the above example:
# [[16060 15820 14212 14273 38444]
# [16121 15881 14151 14212 38383]
# [17264 17024 13008 13069 37240]
# [17384 17144 12888 12949 37120]
# [24569 24329 5703 5764 29935]
# [55794 55554 25522 25461 1290]
# [55853 55613 25581 25520 1349]]
# hungarian algorithm implementation provided by scipy
row_ind, col_ind = linear_sum_assignment(cost)
# row_ind = [0 1 3 4 5], col_ind = [1 0 3 2 4]
# where (ts1[5] - ts2[4]) = 1290
# if you want the distances only
out = [item
for item in cost[row_ind, col_ind]
if item < threshold]
# if you want the pair of indices filtered by the threshold
pairs = [(row, col)
for row, col in zip(row_ind, col_ind)
if cost[row, col] < threshold]
return out, pairs
if __name__ == '__main__':
out, pairs = closest_pairs(ts1, ts2, 6000)
print(out, pairs)
# out = [5703, 1290]
# pairs = [(4, 2), (5, 4)]
out, pairs = closest_pairs(ts1, ts2)
print(out, pairs)
# out = [15820, 16121, 12949, 5703, 1290]
# pairs = [(0, 1), (1, 0), (3, 3), (4, 2), (5, 4)]
Ответ 3
Решение с использованием numpy Mask arrays
выводит выровненные Timeseries (_ts1
, _ts2
).
Результат - 3 пары и только Пары с расстоянием 1 могут использоваться для выравнивания Timeseries the Thfore Threshold = 1.
def compute_diffs(threshold):
dtype = [('diff', int), ('ts1', int), ('ts2', int), ('threshold', int)]
diffs = np.empty((ts1.shape[0], ts2.shape[0]), dtype=dtype)
pairs = np.ma.make_mask_none(diffs.shape)
for i1, t1 in enumerate(ts1):
for i2, t2 in enumerate(ts2):
diffs[i1, i2] = (abs(t1 - t2), i1, i2, abs(i1-i2))
d1 = diffs[i1][diffs[i1]['threshold'] == threshold]
if d1.size == 1:
(diff, y, x, t) = d1[0]
pairs[y, x] = True
return diffs, pairs
def align_timeseries(diffs):
def _sync(ts, ts1, ts2, i1, i2, ii):
while i1 < i2:
ts1[ii] = ts[i1]; i1 +=1
ts2[ii] = DTNULL
ii += 1
return ii, i1
_ts1 = np.array([DTNULL]*9)
_ts2 = np.copy(_ts1)
ii = _i1 = _i2 = 0
for n, (diff, i1, i2, t) in enumerate(np.sort(diffs, order='ts1')):
ii, _i1 = _sync(ts1, _ts1, _ts2, _i1, i1, ii)
ii, _i2 = _sync(ts2, _ts2, _ts1, _i2, i2, ii)
if _i1 == i1:
_ts1[ii] = ts1[i1]; _i1 += 1
_ts2[ii] = ts2[i2]; _i2 += 1
ii += 1
ii, _i1 = _sync(ts1, _ts1, _ts2, _i1, ts1.size, ii)
return _ts1, _ts2
главная:
diffs, pairs = compute_diffs(threshold=1)
print('diffs[pairs]:{}'.format(diffs[pairs]))
_ts1, _ts2 = align_timeseries(diffs[pairs])
pprint(ts1, ts2, _ts1, _ts2)
Выход
diffs[pairs]:[(15820, 0, 1) ( 5703, 4, 2) ( 1290, 5, 4)]
ts1 ts2 _ts1 diff _ts2
0: 2011-07-21 12:07:01 2011-07-21 07:39:21 ---- -- -- -- -- -- ---- 2011-07-21 07:39:21
1: 2011-07-21 12:08:02 2011-07-21 07:43:21 2011-07-21 12:07:01 15820 2011-07-21 07:43:21
2: 2011-07-21 12:27:05 2011-07-21 16:03:53 2011-07-21 12:08:02 ---- ---- -- -- -- -- --
3: 2011-07-21 12:29:05 2011-07-21 16:04:54 2011-07-21 12:27:05 ---- ---- -- -- -- -- --
4: 2011-07-21 14:28:50 2011-07-21 22:47:45 2011-07-21 12:29:05 ---- ---- -- -- -- -- --
5: 2011-07-21 23:09:15 ---- -- -- -- -- -- 2011-07-21 14:28:50 5703 2011-07-21 16:03:53
6: 2011-07-21 23:10:14 ---- -- -- -- -- -- ---- -- -- -- -- -- ---- 2011-07-21 16:04:54
7: ---- -- -- -- -- -- ---- -- -- -- -- -- 2011-07-21 23:09:15 1290 2011-07-21 22:47:45
8: ---- -- -- -- -- -- ---- -- -- -- -- -- 2011-07-21 23:10:14 ---- ---- -- -- -- -- --
Протестировано с помощью Python: 3.4.2
Ответ 4
Чтобы иметь пары временных рядов, я советую вам сначала вычислить ваши пары индексов (get_pairs
). А затем вычислить пару временных рядов (get_tspairs
).
В get_pairs
я сначала вычисляю матрицу m
, которая воспроизводит разницу между каждой точкой между двумя временными рядами. Таким образом, матрица формы (len(ts1), len(ts2))
. Затем я выбираю наименьшее расстояние среди всех. Чтобы не выбирать несколько раз один и тот же индекс, я устанавливал на np.inf
расстояние для выбранных индексов. Я продолжаю этот процесс, пока мы не сможем выбрать больше кортежей индексов. Если минимальное расстояние выше порогового значения, процесс прерывается.
Как только я получил свои пары индексов, я вызываю get_tspairs
, чтобы генерировать пары временных рядов. Первым шагом здесь является объединение временных рядов с выбранным набором индексов, затем добавление индексов, которые не были выбраны, и связать их с None
(эквивалент NULL в Python).
Что дает:
import numpy as np
import operator
ts1=np.array([ 1311242821.0, 1311242882.0, 1311244025.0, 1311244145.0, 1311251330.0,
1311282555.0, 1311282614.0])
ts2=np.array([ 1311226761.0, 1311227001.0, 1311257033.0, 1311257094.0, 1311281265.0])
def get_pairs(ts1, ts2, threshold=np.inf):
m = np.abs(np.subtract.outer(ts1, ts2))
indices = []
while np.ma.masked_invalid(m).sum() != 'masked':
ind = np.unravel_index(np.argmin(m), m.shape)
if m[ind] < threshold:
indices.append(ind)
m[:,ind[1]] = np.inf
m[ind[0],:] = np.inf
else:
m= np.inf
return indices
def get_tspairs(pairs, ts1, ts2):
ts_pairs = [(ts1[p[0]], ts2[p[1]]) for p in pairs]
# We separate the selected indices from ts1 and ts2, then sort them
ind_ts1 = sorted(map(operator.itemgetter(0), pairs))
ind_ts2 = sorted(map(operator.itemgetter(1), pairs))
# We only keep the non-selected indices
l1 = np.delete(np.arange(len(ts1), dtype=np.int64), ind_ts1)
l2 = np.delete(np.arange(len(ts2), dtype=np.int64), ind_ts1)
ts_pairs.extend([(ts1[i], None) for i in l1])
ts_pairs.extend([(ts2[i], None) for i in l2])
return ts_pairs
if __name__ == '__main__':
pairs = get_pairs(ts1, ts2)
print(pairs)
# [(5, 4), (4, 2), (3, 3), (0, 1), (1, 0)]
ts_pairs = get_tspairs(pairs, ts1, ts2)
print(ts_pairs)
# [(1311282555.0, 1311281265.0), (1311251330.0, 1311257033.0), (1311244145.0, 1311257094.0), (1311242821.0, 1311227001.0), (1311242882.0, 1311226761.0), (1311244025.0, None), (1311282614.0, None), (1311257033.0, None)]
Ответ 5
У вас есть два отсортированных списка временных меток, и вам нужно объединить их в один, сохраняя элементы каждого списка отдельно друг от друга, вычисляя разницу, когда есть переключатель или изменение списка.
Мое первое решение без использования numpy и состоит из 1) добавления к каждому элементу идентификатора списка, к которому он принадлежит, 2) сортировки по метке времени, 3) группы по идентификатору списка, 4 ) построить новый список, разделяющий каждый элемент и вычисляющий разницу при необходимости:
import numpy as np
from itertools import groupby
from operator import itemgetter
ts1 = np.array([1311242821.0, 1311242882.0, 1311244025.0, 1311244145.0, 1311251330.0, 1311282555.0, 1311282614.0])
ts2 = np.array([1311226761.0, 1311227001.0, 1311257033.0, 1311257094.0, 1311281265.0])
def without_numpy():
# 1) Add the list id to each element
all_ts = [(_, 0) for _ in ts1] + [(_, 1) for _ in ts2]
merged_ts = [[], [], []]
# 2) Sort by timestamp and 3) Group by list id
groups = groupby(sorted(all_ts), key=itemgetter(1))
# 4) Construct the new list
diff = False
for key, g in groups:
group = list(g)
### See Note
for ts, k in group:
if diff:
merged_ts[key] = merged_ts[key][:-1]
merged_ts[2][-1] = abs(end - ts)
diff = False
else:
merged_ts[not key].append(None)
merged_ts[2].append(None)
merged_ts[key].append(ts)
end = ts
diff = True
return merged_ts
Используя numpy, процедура немного отличается и состоит в 1) добавлении к каждому элементу идентификатора списка, к которому он принадлежит, и некоторых вспомогательных индексов, 2) сортировки по метке времени, 3) пометить каждый коммутатор или изменить список, 4) выполнить сканирование суммы предыдущих флагов, 5) рассчитать собственный индекс каждого элемента в объединенном списке:
import numpy as np
ts1 = np.array([1311242821.0, 1311242882.0, 1311244025.0, 1311244145.0, 1311251330.0, 1311282555.0, 1311282614.0])
ts2 = np.array([1311226761.0, 1311227001.0, 1311257033.0, 1311257094.0, 1311281265.0])
def with_numpy():
dt = np.dtype([('ts', np.float), ('key', np.int), ('idx', np.int)])
all_ts = np.sort(
np.array(
[(_, 0, 1, 0) for _ in ts1] + [(_, 1, 1, 0) for _ in ts2],
dtype=np.dtype([('ts', np.float),
('key', np.int), # list id
('index', np.int), # index in result list
('single', np.int), # flag groups with only one element
])
),
order='ts'
)
#### See NOTE
sh_dn = np.roll(all_ts, 1)
all_ts['index'] = np.add.accumulate(all_ts['index']) - np.cumsum(
np.not_equal(all_ts['key'], sh_dn['key']))
merged_ts = np.full(shape=(3, all_ts['index'][-1]+1), fill_value=np.nan)
merged_ts[all_ts['key'], all_ts['index']] = all_ts['ts']
merged_ts[2] = np.abs(merged_ts[0] - merged_ts[1])
merged_ts = np.delete(merged_ts, -1, axis=1)
merged_ts = np.transpose(merged_ts)
return merged_ts
Обе функции, с или без numpy, дают одинаковый результат. Печать и форматирование могут быть выполнены по мере необходимости. Какая функция лучше зависит от данных, которые у вас есть.
ПРИМЕЧАНИЕ.. В случае, если есть переключатель в другой список, и после того, как только одно значение вернется к предыдущему списку, функции, как они выше, будут содержать только последние разница, возможно, потеряет меньшую разницу. В этом случае вы можете вставить следующие разделы в том месте, где "#### См. Примечание":
Для функции without_numpy
вставьте:
if len(group) == 1:
group.append(group[0])
Для функции with_numpy
вставьте:
sh_dn = np.roll(all_ts, 1)
sh_up = np.roll(all_ts, -1)
all_ts['single'] = np.logical_and( np.not_equal(all_ts['key'], sh_dn['key']),
np.equal(sh_dn['key'], sh_up['key']))
singles = np.where(all_ts['single']==1)[0]
all_ts = np.insert(all_ts, singles, all_ts[singles])
Ответ 6
Я не уверен, правильно ли получил ваш вопрос. Если это так и предполагается, что ваши данные уже отсортированы, вы можете сделать это за один проход при использовании итераторов. просто адаптируйте пример к вашим потребностям.
left = iter(range(15, 60, 3))
right = iter(range(0, 50, 5))
try:
i = next(left)
j = next(right)
while True:
if abs(i-j) < 1:
print("pair", i, j)
i = next(left)
j = next(right)
elif i <= j:
print("left", i, None)
i = next(left)
else:
print("right", None, j)
j = next(right)
except StopIteration:
pass
# one of the iterators may have leftover elements
for i in left:
print("left", i, None)
for j in right:
print("right", None, j)
печатает
('right', None, 0)
('right', None, 5)
('right', None, 10)
('pair', 15, 15)
('left', 18, None)
('right', None, 20)
('left', 21, None)
('left', 24, None)
('right', None, 25)
('left', 27, None)
('pair', 30, 30)
('left', 33, None)
('right', None, 35)
('left', 36, None)
('left', 39, None)
('right', None, 40)
('left', 42, None)
('pair', 45, 45)
('left', 51, None)
('left', 54, None)
('left', 57, None)