214 votes

Fractionner/liste des dictionnaires à l’intérieur d’une colonne de Pandas en colonnes séparées

J'ai des données enregistrées dans une base de données postgreSQL. Je suis d'interrogation de ces données à l'aide de Python2.7 et de la transformer en une Pandas DataFrame. Cependant, la dernière colonne de ce dataframe a un dictionnaire (ou une liste?) des valeurs au sein de. Le DataFrame ressemble à ceci:

[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"}

J'ai besoin de partager cette colonne dans des colonnes distinctes, de sorte que le DataFrame ressemble à ceci:

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

Le problème majeur que je rencontre est que les listes ne sont pas les mêmes longueurs. Mais toutes les listes ne contiennent que les mêmes 3 valeurs: a, b, et c. Et ils apparaissent toujours dans le même ordre (d'abord, b, deuxième, c troisième).

Le code suivant l'habitude de travailler et de revenir exactement ce que je voulais (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)

J'ai été l'exécution de ce code, la semaine dernière, et ça marchait très bien. Mais maintenant, mon code est cassé et je reçois ce message d'erreur de la ligne [4]:

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

Je n'ai pas fait de modifier le code, mais je suis maintenant l'obtention de l'erreur. J'ai l'impression que c'est à cause de ma méthode n'est pas robuste ou la bonne.

Des suggestions ou des conseils sur la façon de diviser cette colonne de listes dans des colonnes distinctes serait super apprécié!

EDIT: je pense que l' .tolist() et .appliquer les méthodes ne fonctionnent pas sur mon code, car c'est une chaîne unicode, c'est à dire:

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

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

Les données de l'importation de la base de données postgreSQL dans ce format. Toute aide ou d'idées avec ce problème? est-il un moyen de convertir le format unicode?

234voto

joris Points 10700

Pour convertir la chaîne de caractères à une dict, vous pouvez le faire df['Pollutant Levels'].map(eval). Par la suite, la solution ci-dessous peut être utilisé pour convertir le dict des différentes colonnes.


À l'aide d'un petit exemple, vous pouvez utiliser .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]:
   a                   b
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]:
     c    d
0  1.0  NaN
1  NaN  3.0
2  5.0  6.0

À combiner avec le reste de la dataframe, vous pouvez concat les autres colonnes avec le résultat ci-dessus:

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

À l'aide de votre code, cela fonctionne aussi si je quitte l' iloc partie:

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

23voto

Merlin Points 8274

Essayez ceci: Les données renvoyées par SQL doivent être converties en Dict. ou pourrait-il s'agir de "Pollutant Levels" est maintenant 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 )

    a    b   c
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    a    b   c
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
 

13voto

Hafizur Rahman Points 379

La réponse de Merlin est meilleure et très facile, mais nous n’avons pas besoin d’une fonction lambda. L'évaluation du dictionnaire peut être ignorée en toute sécurité, comme illustré ci-dessous:

 df_pol_ps = data_df['Pollutants'].apply(pd.Series)

df_pol_ps:
    a   b   c
0   46  3   12
1   36  5   8
2   NaN 2   7
3   NaN NaN 11
4   82  NaN 15

df_final = pd.concat([df, df_pol_ps], axis = 1).drop('Pollutants', axis = 1)

df_final:
    StationID   a   b   c
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
 

Les deux étapes ci-dessus peuvent être combinées en une fois:

 df_final = pd.concat([df, df['Pollutants'].apply(pd.Series)], axis = 1).drop('Pollutants', axis = 1)

df_final:
    StationID   a   b   c
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
 

10voto

jpp Points 83462

Vous pouvez utiliser join avec pop + tolist . Les performances sont comparables à concat avec drop + tolist , mais certains peuvent trouver ce nettoyeur de syntaxe:

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

Analyse comparative avec d'autres méthodes:

 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
 

1voto

Siraj S. Points 1067

en une ligne:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X