Как я могу эффективно объединить эти два набора данных?

#python #arrays #merge

#python #массивы #слияние

Вопрос:

Итак, у меня есть два списка данных, которые выглядят так (сокращенно):

 [[1.0, 1403603100],
 [0.0, 1403603400],
 [2.0, 1403603700],
 [0.0, 1403604000],
 [None, 1403604300]]

[1.0, 1403603100],
[0.0, 1403603400],
[1.0, 1403603700],
[None, 1403604000],
[5.0, 1403604300]]
 

То, что я хочу сделать, это объединить их, суммируя первые элементы каждого набора данных или делая его 0.0, если значение любого счетчика равно None. Итак, приведенный выше пример станет следующим:

 [[2.0, 1403603100],
[0.0, 1403603400],
[3.0, 1403603700],
[0.0, 1403604000],
[0.0, 1403604300]]
 

Это то, что я придумал до сих пор, извиняюсь, если это немного неуклюже.

 def emit_datum(datapoints):
    for datum in datapoints:
        yield datum

def merge_data(data_set1, data_set2):

    assert len(data_set1) == len(data_set2)
    data_length = len(data_set1)

    data_gen1 = emit_datum(data_set1)
    data_gen2 = emit_datum(data_set2)

    merged_data = []

    for _ in range(data_length):

        datum1 = data_gen1.next()
        datum2 = data_gen2.next()

        if datum1[0] is None or datum2[0] is None:
            merged_data.append([0.0, datum1[1]])
            continue

        count = datum1[0]   datum2[0]
        merged_data.append([count, datum1[1]])

    return merged_data
 

Я могу только надеяться / предполагать, что есть что-то хитрое, что я могу сделать с помощью itertools или коллекций?

Комментарии:

1. можете ли вы опубликовать два набора данных отдельно, чтобы просто скопировать и вставить их

2. Используйте iter вместо emit_datum

3. упрощено копирование / вставка данных

4. В вашем примере правый столбец всегда находится в одном и том же порядке, это то, что мы можем считать истинным?

5. Да, на самом деле это временные метки.

Ответ №1:

Если вы делаете оба значения равными 0.0, если ни одно из них не равно None, вам просто нужен простой цикл.

  l1 = [1.0, 1403603100],
 [0.0, 1403603400],
 [2.0, 1403603700],
 [0.0, 1403604000],
 [None, 1403604300]]

l2 = [[1.0, 1403603100],
[0.0, 1403603400],
[1.0, 1403603700],
[None, 1403604000],
[5.0, 1403604300]]

final = []
assert len(l1)== len(l2)
for x, y in zip(l1, l2):
    if x[0] is  None or y[0] is  None:
        y[0] = 0.0
        final.append(y)
    else:
        final.append([x[0]   y[0], x[-1]])
print final

[[2.0, 1403603100], [0.0, 1403603400], [3.0, 1403603700], [0.0, 1403604000], [0.0, 1403604300]]


In [51]: %timeit merge_data(l1,l2)
100000 loops, best of 3: 5.76 µs per loop


 In [52]: %%timeit                 
   ....: final = []
   ....: assert len(l1)==len(l2)
   ....: for x, y in zip(l1, l2):
   ....:     if x[0] is  None or y[0] is None:
   ....:         y[0] = 0.0
   ....:         final.append(y)
   ....:     else:
   ....:         final.append([x[0]   y[0], x[-1]])
   ....: 
100000 loops, best of 3: 2.64 µs per loop
 

Комментарии:

1. Решение numpy, вероятно, быстрее, но это пока самое быстрое и простое. Приветствия!

2. Не беспокойтесь, я внес правку, я изменил на if x[0] is None or y[0] is None

Ответ №2:

Как насчет «объединения» данных на основе идентификатора, то есть сбора всех значений, соответствующих одному идентификатору (например, 1403603400), а затем суммирования его. Словарь отлично подходит для сбора всех значений, соответствующих идентификатору (ключу), а defaultdict списка типов делает это особенно простым:

 >>> data = [[1.0, 1403603100],  [1.0, 1403603100],
...  [0.0, 1403603400],  [0.0, 1403603400],
...  [2.0, 1403603700],  [1.0, 1403603700],
...  [0.0, 1403604000],  [None, 1403604000],
...  [None, 1403604300],  [5.0, 1403604300]]

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for value, identifier in data:
...     d[identifier].append(value)
... 
 

Теперь мы отсортировали данные и можем их условно суммировать:

 >>> for identifier, valuelist in d.iteritems():
...     if not None in valuelist:
...         print identifier, sum(valuelist)
...     else:
...         print identifier, 0.0
... 
1403603400 0.0
1403603700 3.0
1403603100 2.0
1403604300 0.0
1403604000 0.0
 

Короче говоря, последняя часть, чтобы получить список, который вы хотели:

 >>> [[i, sum(v)] if None not in v else [i, .0] for i, v in d.iteritems()]
[[1403603400, 0.0], [1403603700, 3.0], [1403603100, 2.0], [1403604300, 0.0], [1403604000, 0.0]]
 

Этот подход требует, чтобы наборы данных были смешаны в первую очередь, как это было в первой версии вашего примера ввода.

Комментарии:

1. У каждого из двух наборов данных есть свой собственный список, не уверен, как бы я изменил ваше решение, чтобы учесть это.

2. .extend() один список с другим, чтобы сделать их единым целым. (в вашей первой версии все было по-другому, я просто скопировал ваш код, теперь он изменился).

3. Кроме того, вам нужно лучше указать, как должно происходить слияние. В моем решении не имеет значения, есть ли соответствие между одним и другим набором данных. Вы хотите объединить только совпадения и опустить те точки данных, которые содержатся только в одном из списков? Если да, то мое решение в том виде, в каком оно есть сейчас, не работает. Если нет, то разделение на несколько наборов данных не требуется, у вас есть только одна большая коллекция точек данных.

Ответ №3:

используйте массив numpy, и вам не нужно выполнять какие-либо циклы.это ускорит ваш код, если вы имеете дело с большими наборами данных.

 import numpy as np

In [68]: a = np.asarray(a)


In [69]: b = np.asarray(b)

In [71]: a_none_idx = np.equal(a,None)

In [72]: b_none_idx = np.equal(b,None)

In [73]: a[a_none_idx]=0

In [74]: b[b_none_idx]=0

In [76]: c = np.zeros(a.shape)

In [77]: c[:,0]= a[:,0]   b[:,0]

In [78]: c
Out[78]: 
array([[ 2.,  0.],
       [ 0.,  0.],
       [ 3.,  0.],
       [ 0.,  0.],
       [ 5.,  0.]])

In [79]: c[a_none_idx]=0

In [80]: c[b_none_idx]=0

In [81]: c[:,1] = a[:,1]

In [82]: c
Out[82]: 
array([[  2.00000000e 00,   1.40360310e 09],
       [  0.00000000e 00,   1.40360340e 09],
       [  3.00000000e 00,   1.40360370e 09],
       [  0.00000000e 00,   1.40360400e 09],
       [  0.00000000e 00,   1.40360430e 09]]
 

Комментарии:

1. Вы должны упомянуть, что np это numpy. Многие знают, но не все знают 🙂

Ответ №4:

Вы можете использовать zip , например, так:

 def merge(list1, list2):
    returnlist = []
    for x, y in zip(list1, list2):
        if x[0] is None or y[0] is None:
            returnlist.append([0.0, x[1]])
        else:
            returnlist.append([x[0]   y[0], x[1]])

    return returnlist
 

zip возвращает итератор по кортежам, содержащим элементы из каждого входного списка с одинаковым индексом ( (list1[0], list2[0]) (list1[1], list2[1]) т. Е. , и т.д.)