2016-11-03 3 views
6

Есть ли способ для векторизации операции, которая принимает несколько массивов numpy и помещает их в список словарей?Список словарей из массива numpy без цикла

Вот упрощенный пример. Реальный сценарий может включать больше массивов и больше словарных клавиш.

import numpy as np 
x = np.arange(10) 
y = np.arange(10, 20) 
z = np.arange(100, 110) 

print [dict(x=x[ii], y=y[ii], z=z[ii]) for ii in xrange(10)] 

я мог бы иметь тысячи или сотни тысяч итераций в xrange вызова. Все манипуляции для создания x, y и z векторизованы (мой пример не так прост, как указано выше). Таким образом, есть только 1 для цикла, чтобы избавиться, что, я ожидаю, приведет к огромным ускорениям.

Я попытался использовать map с функцией создания дикта и всех видов других работ вокруг. Кажется, что цикл Python for является медленной частью (как обычно). Я как бы застрял в использовании словарей из-за ранее существовавших требований API. Тем не менее, было бы интересно увидеть решения без dicts и массивов записей или что-то еще, но в конечном итоге я не думаю, что это будет работать с существующим API.

+0

'г = г [II]', хороший улов! –

+0

'[dict (x = x_, y = y_, z = z_) для x_, y_, z_ в zip (x, y, z)]. Это векторизован до чистого Python. –

+0

Вы пробовали со списком и пониманием? это слишком медленно? – MMF

ответ

2

Вот один (Num) Pythonic путь:

In [18]: names = np.array(['x', 'y', 'z']) 
In [38]: map(dict, np.dstack((np.repeat(names[None, :], 10, axis=0), np.column_stack((x, y, z))))) 
Out[38]: 
[{'x': '0', 'y': '10', 'z': '100'}, 
{'x': '1', 'y': '11', 'z': '101'}, 
{'x': '2', 'y': '12', 'z': '102'}, 
{'x': '3', 'y': '13', 'z': '103'}, 
{'x': '4', 'y': '14', 'z': '104'}, 
{'x': '5', 'y': '15', 'z': '105'}, 
{'x': '6', 'y': '16', 'z': '106'}, 
{'x': '7', 'y': '17', 'z': '107'}, 
{'x': '8', 'y': '18', 'z': '108'}, 
{'x': '9', 'y': '19', 'z': '109'}] 

Кроме того? , обратите внимание, что если вам не нужны все словари сразу, вы можете просто создать генератора и доступа к каждому элементу по требованию.

(dict(x=x[ii], y=y[ii], z=z[ii]) for ii in xrange(10)) 

Если вы хотите вложенный словарь, я предлагаю список понимание:

In [88]: inner = np.dstack((np.repeat(names[None, :], 10, axis=0), np.column_stack((x, y)))) 

In [89]: [{'connection': d} for d in map(dict, inner)] 
Out[89]: 
[{'connection': {'x': '0', 'y': '10'}}, 
{'connection': {'x': '1', 'y': '11'}}, 
{'connection': {'x': '2', 'y': '12'}}, 
{'connection': {'x': '3', 'y': '13'}}, 
{'connection': {'x': '4', 'y': '14'}}, 
{'connection': {'x': '5', 'y': '15'}}, 
{'connection': {'x': '6', 'y': '16'}}, 
{'connection': {'x': '7', 'y': '17'}}, 
{'connection': {'x': '8', 'y': '18'}}, 
{'connection': {'x': '9', 'y': '19'}}] 
+0

Ницца! А если мне нужно создать вложенные словари. Например, '{'connection': {'x': x [ii], 'y': y [ii]}}'. Я понял, что для своего сценария я слишком упростил свой вопрос. –

+0

@ durden2.0 Вы имеете в виду, вы хотите вложенный словарь для всех предметов? и без 'z'? или это необязательно? – Kasramvd

+0

Да, вложенный словарь для всех элементов, 'z' требуется в моем решении, но на самом деле это не имеет особого значения для самого решения, потому что ключ является вложенным словарем без выполнения цикла python for. –

1

Вот подход, использующий сочетание NumPy и Pandas -

# Stack into columns & create a pandas dataframe with appropriate col names 
a = np.column_stack((x.ravel(),y.ravel(),z.ravel())) 
df = pd.DataFrame(a,columns=[['x','y','z']]) 

# Convert to list of dicts 
out = df.T.to_dict().values() 

Пример запуска -

In [52]: x 
Out[52]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

In [53]: y 
Out[53]: array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) 

In [54]: z 
Out[54]: array([100, 101, 102, 103, 104, 105, 106, 107, 108, 109]) 

In [55]: out 
Out[55]: 
[{'x': 0, 'y': 10, 'z': 100}, 
{'x': 1, 'y': 11, 'z': 101}, 
{'x': 2, 'y': 12, 'z': 102}, 
{'x': 3, 'y': 13, 'z': 103}, 
{'x': 4, 'y': 14, 'z': 104}, 
{'x': 5, 'y': 15, 'z': 105}, 
{'x': 6, 'y': 16, 'z': 106}, 
{'x': 7, 'y': 17, 'z': 107}, 
{'x': 8, 'y': 18, 'z': 108}, 
{'x': 9, 'y': 19, 'z': 109}] 
+0

Умное решение! Я упростил настройку, но на самом деле я создаю вложенный словарь, поэтому я попытаюсь немного подкорректировать его. Например, список dicts, который я возвращаю, на самом деле похож на '[{{'connection': {'xy': x [ii], 'yy': y [ii]}}]'. –

+0

@ durden2.0 Не могли бы вы отредактировать код понимания цикла в вопросе для этого требования? – Divakar

3

С вашим небольшим примером, у меня возникают проблемы с получением ничего быстрее, чем комбинация списка и словарем постижений

In [105]: timeit [{'x':i, 'y':j, 'z':k} for i,j,k in zip(x,y,z)] 
100000 loops, best of 3: 15.5 µs per loop 
In [106]: timeit [{'key':{'x':i, 'y':j, 'z':k}} for i,j,k in zip(x,y,z)] 
10000 loops, best of 3: 37.3 µs per loop 

Альтернативы, которые используют конкатенацию массива для объединения массивов перед секционированием, являются более медленными.

In [108]: timeit [{'x':x_, 'y':y_, 'z':z_} for x_, y_, z_ in np.column_stack((x,y,z))] 
.... 
10000 loops, best of 3: 58.2 µs per loop 

=======================

структурированный массив проще с recfunctions:

In [109]: from numpy.lib import recfunctions 
In [112]: M=recfunctions.merge_arrays((x,y,z)) 
In [113]: M.dtype.names=['x','y','z'] 
In [114]: M 
Out[114]: 
array([(0, 10, 100), (1, 11, 101), (2, 12, 102), (3, 13, 103), 
     (4, 14, 104), (5, 15, 105), (6, 16, 106), (7, 17, 107), 
     (8, 18, 108), (9, 19, 109)], 
     dtype=[('x', '<i4'), ('y', '<i4'), ('z', '<i4')]) 
In [115]: M['x'] 
Out[115]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) 

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

np.rec.fromarrays((x,y,z),names=['x','y','z']) 

производит повтор с указанием названий. О той же скорости.

Я мог бы также построить пустой массив правильного типа и формы и скопировать в него массивы.Это, вероятно, так же быстро, как и этот merge, но сложнее описать.

Я бы предложил оптимизировать структуру данных для использования/доступа, а не скорости строительства. Как правило, вы строите его один раз и используете его много раз.

============

In [125]: dt=np.dtype([('x',x.dtype),('y',y.dtype),('z',z.dtype)]) 
In [126]: xyz=np.zeros(x.shape,dtype=dt) 
In [127]: xyz['x']=x; xyz['y']=y; xyz['z']=z 
# or for n,d in zip(xyz.dtype.names, (x,y,z)): xyz[n] = d 
In [128]: xyz 
Out[128]: 
array([(0, 10, 100), (1, 11, 101), (2, 12, 102), (3, 13, 103), 
     (4, 14, 104), (5, 15, 105), (6, 16, 106), (7, 17, 107), 
     (8, 18, 108), (9, 19, 109)], 
     dtype=[('x', '<i4'), ('y', '<i4'), ('z', '<i4')]) 
+0

Использование массива структуры является более правильным способом, но нет необходимости использовать 'recfunctions'. – Kasramvd

+0

Я добавил версию без повторной настройки. – hpaulj

Смежные вопросы