Dividir diccionario / lista dentro de una columna de Pandas en columnas separadas

Tengo datos guardados en una base de datos postgreSQL. Estoy consultando estos datos utilizando Python2.7 y convirtiéndolos en un DataFrame de Pandas. Sin embargo, la última columna de este dataframe tiene un diccionario (o lista?) De valores dentro de él. El DataFrame se ve así:

[1] df Station ID Pollutants 8809 {"a": "46", "b": "3", "c": "12"} 8810 {"a": "36", "b": "5", "c": "8"} 8811 {"b": "2", "c": "7"} 8812 {"c": "11"} 8813 {"a": "82", "c": "15"} 

Necesito dividir esta columna en columnas separadas para que el DataFrame se vea así:

 [2] df2 Station ID abc 8809 46 3 12 8810 36 5 8 8811 NaN 2 7 8812 NaN NaN 11 8813 82 NaN 15 

El principal problema que tengo es que las listas no tienen la misma longitud. Pero todas las listas solo contienen hasta los mismos 3 valores: a, b y c. Y siempre aparecen en el mismo orden (a primera, b segunda, c tercera).

El siguiente código UTILIZADO para trabajar y devolver exactamente lo que quería (df2).

 [3] df [4] objs = [df, pandas.DataFrame(df['Pollutant Levels'].tolist()).iloc[:, :3]] [5] df2 = pandas.concat(objs, axis=1).drop('Pollutant Levels', axis=1) [6] print(df2) 

Estaba ejecutando este código la semana pasada y estaba funcionando bien. Pero ahora mi código está roto y recibo este error de la línea [4]:

 IndexError: out-of-bounds on slice (end) 

No hice cambios al código, pero ahora estoy recibiendo el error. Siento que esto se debe a que mi método no es robusto o apropiado.

¡Cualquier sugerencia u orientación sobre cómo dividir esta columna de listas en columnas separadas sería muy apreciada!

EDITAR: Creo que los métodos .tolist () y .apply no están funcionando en mi código porque es una cadena Unicode, es decir:

 #My data format u{'a': '1', 'b': '2', 'c': '3'} #and not {u'a': '1', u'b': '2', u'c': '3'} 

Los datos se importan de la base de datos postgreSQL en este formato. ¿Alguna ayuda o ideas con este tema? ¿Hay una manera de convertir el Unicode?

Related of "Dividir diccionario / lista dentro de una columna de Pandas en columnas separadas"

Para convertir la cadena en un dict real, puede hacer df['Pollutant Levels'].map(eval) . Después, la solución a continuación se puede utilizar para convertir el dict a diferentes columnas.


Usando un pequeño ejemplo, puedes usar .apply(pd.Series) :

 In [2]: df = pd.DataFrame({'a':[1,2,3], 'b':[{'c':1}, {'d':3}, {'c':5, 'd':6}]}) In [3]: df Out[3]: ab 0 1 {u'c': 1} 1 2 {u'd': 3} 2 3 {u'c': 5, u'd': 6} In [4]: df['b'].apply(pd.Series) Out[4]: cd 0 1.0 NaN 1 NaN 3.0 2 5.0 6.0 

Para combinarlo con el rest del dataframe, puede concat las otras columnas con el resultado anterior:

 In [7]: pd.concat([df.drop(['b'], axis=1), df['b'].apply(pd.Series)], axis=1) Out[7]: acd 0 1 1.0 NaN 1 2 NaN 3.0 2 3 5.0 6.0 

Usando su código, esto también funciona si dejo fuera la parte iloc :

 In [15]: pd.concat([df.drop('b', axis=1), pd.DataFrame(df['b'].tolist())], axis=1) Out[15]: acd 0 1 1.0 NaN 1 2 NaN 3.0 2 3 5.0 6.0 

Intente esto: los datos devueltos desde SQL se deben convertir en un Dict. o podría ser "Pollutant Levels" ahora es Pollutants'

  StationID Pollutants 0 8809 {"a":"46","b":"3","c":"12"} 1 8810 {"a":"36","b":"5","c":"8"} 2 8811 {"b":"2","c":"7"} 3 8812 {"c":"11"} 4 8813 {"a":"82","c":"15"} df2["Pollutants"] = df2["Pollutants"].apply(lambda x : dict(eval(x)) ) df3 = df2["Pollutants"].apply(pd.Series ) abc 0 46 3 12 1 36 5 8 2 NaN 2 7 3 NaN NaN 11 4 82 NaN 15 result = pd.concat([df, df3], axis=1).drop('Pollutants', axis=1) result StationID abc 0 8809 46 3 12 1 8810 36 5 8 2 8811 NaN 2 7 3 8812 NaN NaN 11 4 8813 82 NaN 15 

La respuesta de Merlín es mejor y muy fácil, pero no necesitamos una función lambda. La evaluación del diccionario se puede ignorar de forma segura mediante una de las siguientes dos formas, como se ilustra a continuación:

Camino 1: dos pasos

 # step 1: convert the `Pollutants` column to Pandas dataframe series df_pol_ps = data_df['Pollutants'].apply(pd.Series) df_pol_ps: abc 0 46 3 12 1 36 5 8 2 NaN 2 7 3 NaN NaN 11 4 82 NaN 15 # step 2: concat columns `a, b, c` and drop/remove the `Pollutants` df_final = pd.concat([df, df_pol_ps], axis = 1).drop('Pollutants', axis = 1) df_final: StationID abc 0 8809 46 3 12 1 8810 36 5 8 2 8811 NaN 2 7 3 8812 NaN NaN 11 4 8813 82 NaN 15 

Forma 2: los dos pasos anteriores se pueden combinar de una sola vez:

 df_final = pd.concat([df, df['Pollutants'].apply(pd.Series)], axis = 1).drop('Pollutants', axis = 1) df_final: StationID abc 0 8809 46 3 12 1 8810 36 5 8 2 8811 NaN 2 7 3 8812 NaN NaN 11 4 8813 82 NaN 15 

Puedes usar join con pop + tolist . El rendimiento es comparable a concat con drop + tolist , pero algunos pueden encontrar esta syntax más limpia:

 res = df.join(pd.DataFrame(df.pop('b').tolist())) 

Benchmarking con otros métodos:

 df = pd.DataFrame({'a':[1,2,3], 'b':[{'c':1}, {'d':3}, {'c':5, 'd':6}]}) def joris1(df): return pd.concat([df.drop('b', axis=1), df['b'].apply(pd.Series)], axis=1) def joris2(df): return pd.concat([df.drop('b', axis=1), pd.DataFrame(df['b'].tolist())], axis=1) def jpp(df): return df.join(pd.DataFrame(df.pop('b').tolist())) df = pd.concat([df]*1000, ignore_index=True) %timeit joris1(df.copy()) # 1.33 s per loop %timeit joris2(df.copy()) # 7.42 ms per loop %timeit jpp(df.copy()) # 7.68 ms per loop 

Sé que la pregunta es bastante antigua, pero llegué buscando respuestas. En realidad, ahora hay una forma mejor (y más rápida) de hacerlo utilizando json_normalize :

 import pandas as pd from pandas.io.json import json_normalize df2 = json_normalize(df['Pollutant Levels']) 

Esto evita costosas funciones de aplicación …

en una línea:

 df = pd.concat([df['a'], df.b.apply(pd.Series)], axis=1)`