Almacenamiento de matrices multidimensionales en columnas de DataFrame pandas

Espero utilizar pandas como el objeto principal de Rastreo (serie de puntos en el espacio de parámetros de MCMC).

Tengo una lista de dictados de string-> array que me gustaría almacenar en pandas. Las claves en los dictados son siempre las mismas, y para cada clave la forma de la matriz numpy es siempre la misma, pero la forma puede ser diferente para diferentes claves y podría tener un número diferente de dimensiones.

Había estado usando self.append(dict_list, ignore_index = True) que parece funcionar bien para los valores 1d, pero para nd> 1 valores pandas almacena los valores como objetos que no permiten una buena gráfica y otras cosas agradables. ¿Alguna sugerencia sobre cómo obtener un mejor comportamiento?

Data de muestra

 point = {'x': array(-0.47652306228698005), 'y': array([[-0.41809043], [ 0.48407823]])} points = 10 * [ point] 

Me gustaría poder hacer algo como

 df = DataFrame(points) 

o

 df = DataFrame() df.append(points, ignore_index=True) 

y tiene

 >> df['x'][1].shape () >> df['y'][1].shape (2,1) 

La relativamente nueva biblioteca xray [1] tiene estructuras Dataset y DataArray que hacen exactamente lo que pides.

Aquí está mi opinión sobre su problema, escrita como una sesión de IPython :

 >>> import numpy as np >>> import xray >>> ## Prepare data: >>> # >>> point = {'x': np.array(-0.47652306228698005), ... 'y': np.array([[-0.41809043], ... [ 0.48407823]])} >>> points = 10 * [point] >>> ## Convert to Xray DataArrays: >>> # >>> list_x = [p['x'] for p in points] >>> list_y = [p['y'] for p in points] >>> da_x = xray.DataArray(list_x, [('x', range(len(list_x)))]) >>> da_y = xray.DataArray(list_y, [ ... ('x', range(len(list_y))), ... ('y0', range(2)), ... ('y1', [0]), ... ]) 

Estas son las dos instancias de DataArray hemos construido hasta ahora:

 >>> print(da_x)  array([-0.47652306, -0.47652306, -0.47652306, -0.47652306, -0.47652306, -0.47652306, -0.47652306, -0.47652306, -0.47652306, -0.47652306]) Coordinates: * x (x) int32 0 1 2 3 4 5 6 7 8 9 >>> print(da_y.T) ## Transposed, to save lines.  array([[[-0.41809043, -0.41809043, -0.41809043, -0.41809043, -0.41809043, -0.41809043, -0.41809043, -0.41809043, -0.41809043, -0.41809043], [ 0.48407823, 0.48407823, 0.48407823, 0.48407823, 0.48407823, 0.48407823, 0.48407823, 0.48407823, 0.48407823, 0.48407823]]]) Coordinates: * x (x) int32 0 1 2 3 4 5 6 7 8 9 * y0 (y0) int32 0 1 * y1 (y1) int32 0 

Ahora podemos combinar estos dos DataArray en su dimensión x común en un DataSet :

 >>> ds = xray.Dataset({'X':da_x, 'Y':da_y}) >>> print(ds)  Dimensions: (x: 10, y0: 2, y1: 1) Coordinates: * x (x) int32 0 1 2 3 4 5 6 7 8 9 * y0 (y0) int32 0 1 * y1 (y1) int32 0 Data variables: X (x) float64 -0.4765 -0.4765 -0.4765 -0.4765 -0.4765 -0.4765 -0.4765 ... Y (x, y0, y1) float64 -0.4181 0.4841 -0.4181 0.4841 -0.4181 0.4841 -0.4181 ... 

Y, finalmente, podemos acceder y agregar datos de la forma que desee:

 >>> ds['X'].sum()  array(-4.765230622869801) >>> ds['Y'].sum()  array(0.659878) >>> ds['Y'].sum(axis=1)  array([[ 0.0659878], [ 0.0659878], [ 0.0659878], [ 0.0659878], [ 0.0659878], [ 0.0659878], [ 0.0659878], [ 0.0659878], [ 0.0659878], [ 0.0659878]]) Coordinates: * x (x) int32 0 1 2 3 4 5 6 7 8 9 * y1 (y1) int32 0 >>> np.all(ds['Y'].sum(axis=1) == ds['Y'].sum(dim='y0')) True >>>> ds['X'].sum(dim='y0') Traceback (most recent call last): ValueError: 'y0' not found in array dimensions ('x',) 

[1] Una biblioteca para manejar datos en N-dimensional con tags, como hacen los pandas para 2D: http://xray.readthedocs.org/en/stable/data-structures.html#dataset

Va un poco en contra de la filosofía de Pandas, que parece ver a Series como una estructura de datos unidimensional. Por lo tanto, tiene que crear la Series a mano, dígales que tienen el tipo de datos "object" . Esto significa que no se aplican conversiones de datos automáticas.

Puedes hacerlo así (reordenado la sesión de Ipython):

 In [9]: import pandas as pd In [1]: point = {'x': array(-0.47652306228698005), ...: 'y': array([[-0.41809043], ...: [ 0.48407823]])} In [2]: points = 10 * [ point] In [5]: lx = [p["x"] for p in points] In [7]: ly = [p["y"] for p in points] In [40]: sx = pd.Series(lx, dtype=numpy.dtype("object")) In [38]: sy = pd.Series(ly, dtype=numpy.dtype("object")) In [43]: df = pd.DataFrame({"x":sx, "y":sy}) In [45]: df['x'][1].shape Out[45]: () In [46]: df['y'][1].shape Out[46]: (2, 1) 

Combinar la respuesta de @ Eike y el comentario de @ JohnSalvatier parece bastante Pandasonic :

 >>> import pandas as pd >>> np = pandas.np >>> point = {'x': np.array(-0.47652306228698005), ... 'y': np.array([[-0.41809043], ... [ 0.48407823]])} >>> points = 10 * [point] # this creates a list of 10 point dicts >>> df = pd.DataFrame().append(points) >>> df.x # 0 -0.476523062287 # ... # 9 -0.476523062287 # Name: x, dtype: object >>> df.y # 0 [[-0.41809043], [0.48407823]] # ... # 9 [[-0.41809043], [0.48407823]] # Name: y, dtype: object >>> df.y[0] # array([[-0.41809043], # [ 0.48407823]]) >>> df.y[0].shape # (2, 1) 

Para trazar (y hacer todas las otras cosas geniales de Pandas en 2-D) todavía tienes que convertir manualmente la columna de matrices de nuevo a un DataFrame:

 >>> dfy = pd.DataFrame([row.T[0] for row in df2.y]) >>> dfy += np.matrix([[0] * 10, range(10)]).T >>> dfy *= np.matrix([range(10), range(10)]).T >>> dfy.plot() 

ejemplo de gráfico 2-D

Para almacenar esto en el disco, use to_pickle :

 >>> df.to_pickle('/tmp/sotest.pickle') >>> df2 = pd.read_pickle('/tmp/sotest.pickle') >>> df.y[0].shape # (2, 1) 

Si usas to_csv tus np.array s se convierten en cadenas:

 >>> df.to_csv('/tmp/sotest.csv') >>> df2 = pd.DataFrame.from_csv('/tmp/sotest.csv') >>> df2.y[0] # '[[-0.41809043]\n [ 0.48407823]]'