Объединение NumPy массивов по столбцам с вложенной структурой - PullRequest
3 голосов
/ 22 января 2020

У меня есть следующие 3 NumPy массивы:

arr1 = np.array(['a', 'b', 'c', 'd', 'e', 'f']).reshape(2, 3)
arr2 = np.array(['g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p']).reshape(2, 5)
arr3 = np.array(['r', 's', 't', 'u']).reshape(2, 2)

Я хотел бы присоединиться к ним по столбцам, но пусть они поддерживают разделение между элементами, поступающими из каждого массива, например:

Output:
array([[['a', 'b', 'c'], ['g', 'h', 'i', 'j', 'k'], ['r', 's']],
       [['d', 'e', 'f'], ['l', 'm', 'n', 'o', 'p'], ['t', 'u']]], dtype='<U1')

Однако я не могу найти функцию NumPy, которая бы достигла этого для меня. Самым близким, что я получил, был простой np.concatenate (), но вывод не сохраняет разделение, которое я хочу:

Input: np.concatenate([arr1, arr2, arr3], axis = 1)
Output:
array([['a', 'b', 'c', 'g', 'h', 'i', 'j', 'k', 'r', 's'],
       ['d', 'e', 'f', 'l', 'm', 'n', 'o', 'p', 't', 'u']], dtype='<U1')

Любые предложения о том, как мне достичь желаемого эффекта?

ОБНОВЛЕНИЕ: Спасибо за отличные ответы. В качестве дополнительного уровня сложности я также хотел бы, чтобы решение учитывало возможное переменное число входных массивов, которые по-прежнему имели бы такое же количество строк. Следовательно, иногда их будет 3, а иногда, например, 6 et c.

Ответы [ 4 ]

2 голосов
/ 22 января 2020

Вы можете попробовать:

print(np.array([[x, y, z] for x, y, z in zip(arr1.tolist(), arr2.tolist(), arr3.tolist())]))

Или, если вы хотите, чтобы внутренние строки также были массивами:

print(np.array([np.array([x, y, z]) for x, y, z in zip(arr1.tolist(), arr2.tolist(), arr3.tolist())]))

Вывод:

[[['a', 'b', 'c'] ['g', 'h', 'i', 'j', 'k'] ['r', 's']]
 [['d', 'e', 'f'] ['l', 'm', 'n', 'o', 'p'] ['t', 'u']]]

И форма (2, 3), как и ожидалось.

Редактировать:

Как вы упомянули в комментарии, попробуйте:

l = [arr1, arr2, arr3] # list of the arrays:
print(np.array([np.array([x, y, z]) for x, y, z in zip(*[i.tolist() for i in l])]))
1 голос
/ 22 января 2020

Я думаю, что это должно дать вам желаемый результат. Это модификация ответа, данного @ U10-Forward-ReinstateMonica, где внутренними элементами были python списки

print(np.array([[np.array(x), np.array(y), np.array(z)] for x, y, z in zip(arr1.tolist(), arr2.tolist(), arr3.tolist())]))
1 голос
/ 22 января 2020

Это может быть долгий путь, но это работает:

arr_all = []
for i in range(arr1.shape[0]):
    row = []
    row.append([arr[i,:] for arr in [arr1, arr2, arr3]])
    arr_all.append(row)
arr_all = np.array(arr_all).reshape(2,3)
0 голосов
/ 22 января 2020
In [13]: arr1 = np.array(['a', 'b', 'c', 'd', 'e', 'f']).reshape(2, 3) 
    ...: arr2 = np.array(['g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p']).reshape(2, 5) 
    ...: arr3 = np.array(['r', 's', 't', 'u']).reshape(2, 2)                                     

Если я пытаюсь создать массив dtype объекта из этих массивов, я получаю ошибку:

In [22]: np.array([arr1, arr2, arr3])                                                            
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-22-155b98609c5b> in <module>
----> 1 np.array([arr1, arr2, arr3])

ValueError: could not broadcast input array from shape (2,3) into shape (2)

Если они отличаются по количеству строк, это будет работать, но с общей строкой Номер результата - ошибка. В таком случае я обычно рекомендую определить массив объектов правильного размера и заполнить его следующим образом:

In [14]: arr = np.empty((2,3), object)                                                           
In [15]: arr                                                                                     
Out[15]: 
array([[None, None, None],
       [None, None, None]], dtype=object)

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

In [17]: arr[:,0] = arr1                                                                         
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-17-9894797aa09e> in <module>
----> 1 arr[:,0] = arr1

ValueError: could not broadcast input array from shape (2,3) into shape (2)

Вместо этого я могу назначить строку за строкой:

In [18]: arr[0,0] = arr1[0]                                                                      
In [19]: arr[1,0] = arr1[1]                                                                      
In [20]: arr[0,1] = arr2[0] 
...                                                                     
In [21]: arr                                                                                     
Out[21]: 
array([[array(['a', 'b', 'c'], dtype='<U1'),
        array(['g', 'h', 'i', 'j', 'k'], dtype='<U1'), None],
       [array(['d', 'e', 'f'], dtype='<U1'), None, None]], dtype=object)

В качестве альтернативы, мы можем назначить вложенные списки столбцам без ошибки трансляции. Это фактически то, что делает принятый ответ:

In [23]: arr[:,0] = arr1.tolist()                                                                
In [24]: arr[:,1] = arr2.tolist()                                                                
In [25]: arr[:,2] = arr3.tolist()                                                                
In [26]: arr                                                                                     
Out[26]: 
array([[list(['a', 'b', 'c']), list(['g', 'h', 'i', 'j', 'k']),
        list(['r', 's'])],
       [list(['d', 'e', 'f']), list(['l', 'm', 'n', 'o', 'p']),
        list(['t', 'u'])]], dtype=object)

Эти трудности в создании желаемого массива являются хорошим индикатором того, что это НЕ, НЕ, хорошая структура массива numpy. Если это трудно сделать, то, вероятно, также будет трудно использовать или, по крайней мере, медленно. Итерация в массиве dtype объекта выполняется медленнее, чем итерация в списке. Его единственное преимущество по сравнению со списком состоит в том, что его легко изменить.

====

np.array действительно работает, если входными данными являются списки, а не массив:

In [33]: np.array([arr1.tolist(), arr2.tolist(), arr3.tolist()])                                 
Out[33]: 
array([[list(['a', 'b', 'c']), list(['d', 'e', 'f'])],
       [list(['g', 'h', 'i', 'j', 'k']), list(['l', 'm', 'n', 'o', 'p'])],
       [list(['r', 's']), list(['t', 'u'])]], dtype=object)

или преобразовать в список, чтобы получить более «чистое» отображение:

In [34]: _.tolist()                                                                              
Out[34]: 
[[['a', 'b', 'c'], ['d', 'e', 'f']],
 [['g', 'h', 'i', 'j', 'k'], ['l', 'm', 'n', 'o', 'p']],
 [['r', 's'], ['t', 'u']]]

, а транспонирование этого массива дает желаемый (3,2) массив:

In [35]: _33.T.tolist()                                                                          
Out[35]: 
[[['a', 'b', 'c'], ['g', 'h', 'i', 'j', 'k'], ['r', 's']],
 [['d', 'e', 'f'], ['l', 'm', 'n', 'o', 'p'], ['t', 'u']]]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...