2014-04-29 4 views
9

J'essaie de lire quelques centaines de tables à partir d'ascii, puis de les écrire sur mySQL. Cela semble facile à faire avec les Pandas mais je rencontre une erreur qui n'a pas de sens pour moi:Les Pandas Python écrivent en sql avec des valeurs NaN

J'ai une trame de données de 8 colonnes. Voici la liste des colonnes/index:

metricDF.columns 

Index([u'FID', u'TYPE', u'CO', u'CITY', u'LINENO', u'SUBLINE', u'VALUE_010', u'VALUE2_015'], dtype=object) 

J'utilise ensuite to_sql pour ajouter les données jusqu'à mySQL

metricDF.to_sql(con=con, name=seqFile, if_exists='append', flavor='mysql') 

je reçois une étrange erreur sur une colonne étant "nan":

OperationalError: (1054, "Unknown column 'nan' in 'field list'") 

Comme vous pouvez le voir, toutes mes colonnes ont des noms. Je me rends compte que le support de mysql/sql pour l'écriture apparaît dans le développement alors peut-être que c'est la raison? Si oui, y a-t-il un travail? Toutes les suggestions seraient grandement appréciées.

Répondre

18

Mise à jour: à partir de 0,15 pandas géants, to_sql supporte l'écriture NaN valeurs (ils seront écrits comme NULL dans la base de données), de sorte que la solution de contournement décrite ci-dessous ne doit pas être plus nécessaire (voir https://github.com/pydata/pandas/pull/8208).
Pandas 0.15 sera publié en octobre prochain, et la fonctionnalité est fusionnée dans la version de développement.


Ceci est probablement dû à NaN valeurs dans votre table, ce qui est une lacune connue au moment où les fonctions SQL pandas géants ne gèrent pas bien NaN (https://github.com/pydata/pandas/issues/2754, https://github.com/pydata/pandas/issues/4199)

Pour contourner ce problème à ce moment (pour les versions pandas 0.14.1 et inférieure), vous pouvez convertir manuellement les valeurs nan Aucun avec:

df2 = df.astype(object).where(pd.notnull(df), None) 

puis écrire le dataframe sql. Cela convertit cependant toutes les colonnes en objet dtype. Pour cette raison, vous devez créer la table de base de données en fonction de la base de données d'origine. Par exemple, si votre première ligne ne contient pas NaN s:

df[:1].to_sql('table_name', con) 
df2[1:].to_sql('table_name', con, if_exists='append') 
+0

Génial! Totalement travaillé. Tu dois aimer une solution simple comme celle-ci. Merci. – user3221876

+0

Notez que cette solution de contournement ne supprime pas les valeurs NaT des colonnes datetime64 (du moins pas lorsque j'ai essayé) – aensm

+0

@aensm Merci de noter que ce bogue sera également résolu en 0.15. – joris

2

en utilisant la solution précédente changera DTYPE colonne de float64 à object_.

J'ai trouvé une meilleure solution, il suffit d'ajouter la fonction _write_mysql suivante:

from pandas.io import sql 

def _write_mysql(frame, table, names, cur): 
    bracketed_names = ['`' + column + '`' for column in names] 
    col_names = ','.join(bracketed_names) 
    wildcards = ','.join([r'%s'] * len(names)) 
    insert_query = "INSERT INTO %s (%s) VALUES (%s)" % (
     table, col_names, wildcards) 

    data = [[None if type(y) == float and np.isnan(y) else y for y in x] for x in frame.values] 

    cur.executemany(insert_query, data) 

Et puis outrepasser sa mise en œuvre en pandas géants comme ci-dessous:

sql._write_mysql = _write_mysql 

Avec ce code, les valeurs nan seront enregistré correctement dans la base de données sans modifier le type de colonne.

+1

Notez que cela ne fonctionnera pas avec les pandas 0.14 et plus (il y avait un refactor dans les pandas 0.14) – joris

+0

Je viens de découvrir cela. J'essaie de trouver une solution similaire pour les pandas 0.14 –

+1

Vous pouvez par exemple faire la vérification dans 'maybe_asscalar' (https://github.com/pydata/pandas/blob/master/pandas/io/sql.py#L580) – joris

-1

NaT à MySQL non encore traité dans les pandas 15.2

+0

Pouvez-vous ouvrir un problème sur https://github.com/pydata/pandas/issues si vous rencontrez des problèmes spécifiques? Comme cela est supposé fonctionner, nous apprécierions un rapport de bug. – joris

Questions connexes