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()
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]]'