Я полагаю, что большинство (но не все) ингредиентов этого ответа фактически присутствуют в других ответах, но на всех ответах до сих пор я не видел сравнения между яблоками, в том смысле, что некоторые подходыне возвращали список np.ndarray
объектов, а скорее (удобно, на мой взгляд) сингл np.ndarray()
.
Не ясно, приемлемо ли это для вас, поэтому я добавляю соответствующий код для этого,Кроме того, производительность может отличаться, потому что в некоторых случаях вы добавляете дополнительный шаг, в то время как для некоторых других вам может не понадобиться создавать большие объекты (которые могут находиться на разных страницах памяти).
В конце концов,для небольших входных данных (3 x 10) список np.ndarray()
s - это просто дополнительная нагрузка, которая значительно увеличивает время.Для больших входных данных (3 x 1000) и выше дополнительное вычисление больше не имеет значения, но подход, включающий в себя понимание и избегание создания большого массива numpy
, может стать таким же быстрым, как (или даже быстрее чем) самые быстрые методы для меньших затрат.
Кроме того, весь код, который я представляю, работает для произвольных размеров кортежей / списка (если, конечно, все внутренние кортежи имеют одинаковый размер).
(РЕДАКТИРОВАТЬ: добавлен комментарий к окончательным результатам)
Проверены следующие методы:
import numpy as np
def to_arrays_zip(items):
return np.array(list(zip(*items)))
def to_arrays_transpose(items):
return np.array(items).transpose()
def to_arrays_zip_split(items):
return [arr for arr in np.array(list(zip(*items)))]
def to_arrays_transpose_split(items):
return [arr for arr in np.array(items).transpose()]
def to_arrays_comprehension(items):
return [np.array([items[i][j] for i in range(len(items))]) for j in range(len(items[0]))]
def to_arrays_comprehension2(items):
return [np.array([item[j] for item in items]) for j in range(len(items[0]))]
(Это удобная функция для проверки совпадения результатов.)
def test_equal(items1, items2):
return all(np.all(x == y) for x, y in zip(items1, items2))
Для небольших входов:
N = 3
M = 10
ll = [tuple(range(N)) for _ in range(M)]
print(to_arrays_comprehension2(ll))
print('Returning `np.ndarray()`')
%timeit to_arrays_zip(ll)
# 2.82 µs ± 28 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit to_arrays_transpose(ll)
# 3.18 µs ± 30 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
print('Returning a list')
%timeit to_arrays_zip_split(ll)
# 3.71 µs ± 47 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit to_arrays_transpose_split(ll)
# 3.97 µs ± 42.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit to_arrays_comprehension(ll)
# 5.91 µs ± 96.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit to_arrays_comprehension2(ll)
# 5.14 µs ± 109 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Где подиум:
to_arrays_zip_split()
(не _split
если вы в порядке с одним массивом) to_arrays_zip_transpose_split()
(не-_split
, если вы в порядке с одним массивом) to_arrays_comprehension2()
Для более крупных входов:
N = 3
M = 1000
ll = [tuple(range(N)) for _ in range(M)]
print('Returning `np.ndarray()`')
%timeit to_arrays_zip(ll)
# 146 µs ± 2.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit to_arrays_transpose(ll)
# 222 µs ± 2.01 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
print('Returning a list')
%timeit to_arrays_zip_split(ll)
# 147 µs ± 1.68 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit to_arrays_transpose_split(ll)
# 221 µs ± 2.58 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit to_arrays_comprehension(ll)
# 261 µs ± 2.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit to_arrays_comprehension2(ll)
# 212 µs ± 1.68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Подиум становится:
to_arrays_zip_split()
(используете ли вы варианты _split
или не _split
, не имеет большого значения) to_arrays_comprehension2()
to_arrays_zip_transpose_split()
(используете ли вы варианты _split
или не _split
, не имеет большого значения)
Для еще больших входов:
N = 3
M = 1000000
ll = [tuple(range(N)) for _ in range(M)]
print('Returning `np.ndarray()`')
%timeit to_arrays_zip(ll)
# 215 ms ± 4.27 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit to_arrays_transpose(ll)
# 220 ms ± 4.62 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
print('Returning a list')
%timeit to_arrays_zip_split(ll)
# 218 ms ± 6.21 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit to_arrays_transpose_split(ll)
# 222 ms ± 3.48 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit to_arrays_comprehension(ll)
# 248 ms ± 3.55 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit to_arrays_comprehension2(ll)
# 186 ms ± 481 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
подиум становится:
to_arrays_comprehension2()
to_arrays_zip_split()
(используете ли вы варианты _split
или не _split
, не имеет большого значения) to_arrays_zip_transpose_split()
(используете ли вы варианты _split
или не _split
, не имеет большого значения)
, а варианты _zip
и _transpose
довольно близки
(я также пытался ускорить процесс с Numba, который не прошел хорошо)