Forzar el numpy para crear una matriz de objetos

Tengo una matriz:

x = np.array([[1, 2, 3], [4, 5, 6]]) 

y quiero crear otra matriz de shape=(1, 1) y dtype=np.object cuyo único elemento es x.

He intentado este código:

 a = np.array([[x]], dtype=np.object) 

pero produce una serie de formas (1, 1, 2, 3) .

Por supuesto que puedo hacer:

 a = np.zeros(shape=(1, 1), dtype=np.object) a[0, 0] = x 

pero quiero que la solución sea fácilmente escalable a formas más grandes, como:

 [[x, x], [x, x]] 

Sin tener que correr for bucles sobre todos los índices.

¿Alguna sugerencia de cómo se podría lograr esto?


UPD1

Las matrices pueden ser diferentes, como en:

 x = np.array([[1, 2, 3], [4, 5, 6]]) y = np.array([[7, 8, 9], [0, 1, 2]]) u = np.array([[3, 4, 5], [6, 7, 8]]) v = np.array([[9, 0, 1], [2, 3, 4]]) [[x, y], [u, v]] 

También pueden ser de diferentes formas, pero para ese caso un simple np.array([[x, y], [u, v]]) funciona bien


UPD2

Realmente quiero una solución que funcione con formas x, y, u, v arbitrarias, no necesariamente todas iguales.

Este es un método bastante general: funciona con listas anidadas, listas de listas de matrices, independientemente de si las formas de estas matrices son diferentes o iguales. También funciona cuando los datos se agrupan en una sola matriz, lo que de hecho es el caso más complicado. (Otros métodos publicados hasta ahora no funcionarán en este caso).

Comencemos con el caso difícil, una gran variedad:

 # create example # pick outer shape and inner shape >>> osh, ish = (2, 3), (2, 5) # total shape >>> tsh = (*osh, *ish) # make data >>> data = np.arange(np.prod(tsh)).reshape(tsh) >>> # recalculate inner shape to cater for different inner shapes # this will return the consensus bit of all inner shapes >>> ish = np.shape(data)[len(osh):] >>> # block them >>> data_blocked = np.frompyfunc(np.reshape(data, (-1, *ish)).__getitem__, 1, 1)(range(np.prod(osh))).reshape(osh) >>> # admire >>> data_blocked array([[array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]), array([[10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]), array([[20, 21, 22, 23, 24], [25, 26, 27, 28, 29]])], [array([[30, 31, 32, 33, 34], [35, 36, 37, 38, 39]]), array([[40, 41, 42, 43, 44], [45, 46, 47, 48, 49]]), array([[50, 51, 52, 53, 54], [55, 56, 57, 58, 59]])]], dtype=object) 

Usando el ejemplo de OP que es una lista de listas de arreglos:

 >>> x = np.array([[1, 2, 3], [4, 5, 6]]) >>> y = np.array([[7, 8, 9], [0, 1, 2]]) >>> u = np.array([[3, 4, 5], [6, 7, 8]]) >>> v = np.array([[9, 0, 1], [2, 3, 4]]) >>> data = [[x, y], [u, v]] >>> >>> osh = (2,2) >>> ish = np.shape(data)[len(osh):] >>> >>> data_blocked = np.frompyfunc(np.reshape(data, (-1, *ish)).__getitem__, 1, 1)(range(np.prod(osh))).reshape(osh) >>> data_blocked array([[array([[1, 2, 3], [4, 5, 6]]), array([[7, 8, 9], [0, 1, 2]])], [array([[3, 4, 5], [6, 7, 8]]), array([[9, 0, 1], [2, 3, 4]])]], dtype=object) 

Y un ejemplo con vT diferentes formas (note la vT ):

 >>> data = [[x, y], [u, vT]] >>> >>> osh = (2,2) >>> ish = np.shape(data)[len(osh):] >>> data_blocked = np.frompyfunc(np.reshape(data, (-1, *ish)).__getitem__, 1, 1)(range(np.prod(osh))).reshape(osh)>>> data_blocked array([[array([[1, 2, 3], [4, 5, 6]]), array([[7, 8, 9], [0, 1, 2]])], [array([[3, 4, 5], [6, 7, 8]]), array([[9, 2], [0, 3], [1, 4]])]], dtype=object) 
 a = np.empty(shape=(2, 2), dtype=np.object) a.fill(x) 

Encontré una solución yo mismo:

 a=np.zeros(shape=(2, 2), dtype=np.object) a[:] = [[x, x], [x, x]] 

El uso de @ PaulPanzer de np.frompyfunc es inteligente, pero la reshaping y el uso de __getitem__ hacen que sea difícil de entender:

Separar la creación de funciones de la aplicación podría ayudar:

 func = np.frompyfunc(np.reshape(data, (-1, *ish)).__getitem__, 1, 1) newarr = func(range(np.prod(osh))).reshape(osh) 

Esto resalta la separación entre las dimensiones ish y las osh .

También sospecho que una función lambda podría sustituir al __getitem__ .

Esto funciona porque frompyfunc devuelve una matriz de tipo de objeto. np.vectorize también usa frompyfunc pero nos permite especificar un frompyfunc diferente. Pero ambos pasan un escalar a la función, por lo que el enfoque de Paul utiliza un range y un elemento aplanados. np.vectorize con una signature nos permite pasar una matriz a la función, pero utiliza una iteración ndindex lugar de frompyfunc .

Inspirado por eso, aquí hay un método de relleno np.empty plus, pero con ndindex como iterador:

 In [385]: >>> osh, ish = (2, 3), (2, 5) ...: >>> tsh = (*osh, *ish) ...: >>> data = np.arange(np.prod(tsh)).reshape(tsh) ...: >>> ish = np.shape(data)[len(osh):] ...: In [386]: tsh Out[386]: (2, 3, 2, 5) In [387]: ish Out[387]: (2, 5) In [388]: osh Out[388]: (2, 3) In [389]: res = np.empty(osh, object) In [390]: for idx in np.ndindex(osh): ...: res[idx] = data[idx] ...: In [391]: res Out[391]: array([[array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]), .... [55, 56, 57, 58, 59]])]], dtype=object) 

Para el segundo ejemplo:

 In [399]: arr = np.array(data) In [400]: arr.shape Out[400]: (2, 2, 2, 3) In [401]: res = np.empty(osh, object) In [402]: for idx in np.ndindex(osh): ...: res[idx] = arr[idx] 

En el tercer caso, np.array(data) ya crea la matriz de tipo de objeto deseada (2,2). Este res crear y rellenar todavía funciona, aunque produce la misma cosa.

La velocidad no es muy diferente (aunque este ejemplo es pequeño)

 In [415]: timeit data_blocked = np.frompyfunc(np.reshape(data, (-1, *ish)).__get ...: item__, 1, 1)(range(np.prod(osh))).reshape(osh) 49.8 µs ± 172 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) In [416]: %%timeit ...: arr = np.array(data) ...: res = np.empty(osh, object) ...: for idx in np.ndindex(osh): res[idx] = arr[idx] ...: 54.7 µs ± 68.7 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) 

Tenga en cuenta que cuando los data son una lista (anidada), np.reshape(data, (-1, *ish) es, efectivamente, np.array(data).reshape(-1 *ish) . Esa lista se debe cambiar primero en una matriz.

Además de la velocidad, sería interesante ver si un enfoque es más general que el otro. ¿Hay casos que uno maneja, pero el otro no?