J'ai des données sauvegardées dans une base de données postgreSQL. J'interroge ces données à l'aide de Python2.7 et les transforme en Pandas DataFrame. Cependant, la dernière colonne de cette image de données contient un dictionnaire (ou une liste?) De valeurs. 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 diviser cette colonne en colonnes séparées pour 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
Mon problème majeur est que les listes ne sont pas de la même longueur. Mais toutes les listes ne contiennent que les 3 mêmes valeurs: a, b et c. Et ils apparaissent toujours dans le même ordre (une première, une seconde, une troisième).
Le code suivant utilisé pour travailler et retourner 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'exécutais ce code la semaine dernière et tout fonctionnait bien. Mais maintenant mon code est cassé et j'obtiens cette erreur de la ligne [4]:
IndexError: out-of-bounds on slice (end)
Je n’ai apporté aucune modification au code, mais je reçois maintenant l’erreur. Je pense que cela est dû à ma méthode qui n'est ni robuste ni appropriée.
Toutes suggestions ou conseils sur la manière de diviser cette colonne de listes en colonnes séparées seraient super appréciés!
EDIT: Je pense que les méthodes .tolist () et .apply ne fonctionnent pas sur mon code car il s'agit d'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 importent de la base de données postgreSQL dans ce format. Toute aide ou idées avec ce problème? existe-t-il un moyen de convertir l'unicode?
Pour convertir la chaîne en dictée réelle, vous pouvez utiliser df['Pollutant Levels'].map(eval)
. Ensuite, la solution ci-dessous peut être utilisée pour convertir le dict en différentes colonnes.
En utilisant 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
Pour le combiner avec le reste du cadre de données, 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
En utilisant votre code, cela fonctionne aussi si je laisse de côté la partie iloc
:
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
Essayez ceci: Les données renvoyées par SQL doivent être converties en un 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
Je sais que la question est assez ancienne, mais je suis arrivé ici pour chercher des réponses. Il existe actuellement un moyen plus efficace (et plus rapide) d'utiliser json_normalize
:
import pandas as pd
from pandas.io.json import json_normalize
df2 = json_normalize(df['Pollutant Levels'])
Cela évite des fonctions d'application coûteuses ...
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é de l'une des deux manières suivantes, comme illustré ci-dessous:
Voie 1: Deux étapes
# step 1: convert the `Pollutants` column to Pandas dataframe series
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
# 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 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
Voie 2: 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
Vous pouvez utiliser join
avec pop
+ tolist
. Les performances sont comparables à concat
avec drop
+ tolist
, mais certains trouveront 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
Je recommande fortement la méthode extraire la colonne 'Pollutants':
df_pollutants = pd.DataFrame(df['Pollutants'].values.tolist(), index=df.index)
c'est beaucoup plus rapide que
df_pollutants = df['Pollutants'].apply(pd.Series)
quand la taille de df est géante.