Как бы вы объединили N-массивы с разными размерами? - PullRequest
1 голос
/ 07 марта 2020

:

 np.union1d(a, b)

может объединять два массива разных размеров.

 np.vstack((a, b, c)).T.ravel()

может объединять N массивов одинакового размера.

Как бы объединить N-массивы с разными размерами? И, конечно, это должно быть быстро;)!


кстати, объединение - это не просто конкатенация ... еще тестирование, но будет ли это сделано:

 np.unique(np.concatenate((a,b,c)))

Ответы [ 2 ]

1 голос
/ 07 марта 2020

Вот один с array-assignment + masking для положительных чисел -

def unionize_ndarrays(L, maxnum=None):
    if maxnum is None:
        maxnum = max([np.max(i) for i in L])+1
        # for lists : max([max(i) for i in L])+1
    id_ar = np.zeros(maxnum, dtype=bool)
    for i in L:
        id_ar[i] = True
    return np.flatnonzero(id_ar)

Вычисление максимального числа maxnum имеет заметное время выполнения и может стать узким местом даже для большого количества маленьких массивов. Так что, если это известно, кормление в этом случае очень поможет в этих сценариях ios.

Пробный прогон -

In [43]: a = np.array([0, 1, 3, 4, 3])
    ...: b = np.array([0, 10, 3, 1, 2, 1])
    ...: c = np.array([6, 3, 4, 2])

In [44]: np.unique(np.concatenate((a,b,c)))
Out[44]: array([ 0,  1,  2,  3,  4,  6, 10])

In [45]: unionize_ndarrays((a,b,c))
Out[45]: array([ 0,  1,  2,  3,  4,  6, 10])

Сравнительный анализ

1) Малогабаритные массивы -

In [106]: L = [np.random.randint(0,10,n) for n in np.random.randint(4,10,10000)]

In [107]: %timeit unionize_ndarrays(L, maxnum=10)
2.74 ms ± 207 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [108]: %timeit np.unique(np.concatenate((L)))
3.06 ms ± 24.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# Without maxnum fed
In [109]: %timeit unionize_ndarrays(L)
40.4 ms ± 542 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Если порядок не важен, мы также можем рассмотреть pandas.factorize, если мы имеем дело с малогабаритными массивами -

In [76]: a = np.array([0, 1, 3, 4, 3])
    ...: b = np.array([0, 10, 3, 1, 2, 1])
    ...: c = np.array([6, 3, 4, 2])

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

In [80]: import pandas as pd

In [81]: pd.factorize(np.concatenate(L))[1]
Out[81]: array([ 0,  1,  3,  4, 10,  2,  6])

Связанные тайминги -

In [82]: L = [np.random.randint(0,10,n) for n in np.random.randint(4,10,10000)]

In [84]: %timeit pd.factorize(np.concatenate(L))[1]
2.1 ms ± 13.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

2) Массивы больших размеров (большие различия в размерах) -

Тайминги -

In [2]: L = [np.random.randint(0,1000,n) for n in np.random.randint(10,1000,10000)]

In [3]: %timeit unionize_ndarrays(L, maxnum=1000)
   ...: %timeit unionize_ndarrays(L)
   ...: %timeit np.unique(np.concatenate((L)))
14 ms ± 925 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
56.6 ms ± 641 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
242 ms ± 773 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

Таким образом, выбор одного из них будет зависеть от есть ли у нас априорная информация о максимальном числе и изменении размера.

0 голосов
/ 07 марта 2020

С NumPy инструкция :

Чтобы найти объединение более двух массивов, используйте functools.reduce:

>>> from functools import reduce
>>> reduce(np.union1d, ([1, 3, 4, 3], [3, 1, 2, 1], [6, 3, 4, 2]))
array([1, 2, 3, 4, 6])

Это также работает с массивами разных размеров:

>>> reduce(np.union1d, ([0, 1, 3, 4, 3], [0, 10, 3, 1, 2, 1], [6, 3, 4, 2]))
array([ 0,  1,  2,  3,  4,  6, 10])
...