web-dev-qa-db-fra.com

Insérer un cadre de données de pandas dans mongodb en utilisant PyMongo

Quel est le moyen le plus rapide d’insérer un DataFrame pandas dans mongodb en utilisant PyMongo?

Tentatives

db.myCollection.insert(df.to_dict())

a donné une erreur 

InvalidDocument: documents must have only string keys, the key was Timestamp('2013-11-23 13:31:00', tz=None)

db.myCollection.insert(df.to_json())

a donné une erreur 

TypeError: 'str' object does not support item assignment

db.myCollection.insert({id: df.to_json()})

a donné une erreur 

InvalidDocument: documents must have only string a keys, key was <built-in function id>

df

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 150 entries, 2013-11-23 13:31:26 to 2013-11-23 13:24:07
Data columns (total 3 columns):
amount    150  non-null values
price     150  non-null values
tid       150  non-null values
dtypes: float64(2), int64(1)
27
Nyxynyx

Je doute qu'il existe une méthode à la fois quickest et simple. Si vous ne vous souciez pas de la conversion des données, vous pouvez le faire.

>>> import json
>>> df = pd.DataFrame.from_dict({'A': {1: datetime.datetime.now()}})
>>> df
                           A
1 2013-11-23 21:14:34.118531

>>> records = json.loads(df.T.to_json()).values()
>>> db.myCollection.insert(records)

Mais si vous essayez de charger les données , vous obtiendrez:

>>> df = read_mongo(db, 'myCollection')
>>> df
                     A
0  1385241274118531000
>>> df.dtypes
A    int64
dtype: object

vous devrez donc reconvertir la colonne 'A' en datetimes, ainsi que tous les champs non int, float ou str de votre DataFrame. Pour cet exemple:

>>> df['A'] = pd.to_datetime(df['A'])
>>> df
                           A
0 2013-11-23 21:14:34.118531
24
alko

Ici vous avez le moyen le plus rapide. Utilisation de la méthode insert_many de pymongo 3 et du paramètre 'records' de to_dict method .

db.insert_many(df.to_dict('records'))
21
dieguico

odo peut le faire en utilisant

odo(df, db.myCollection)
9
Femto Trader

Si votre cadre de données a des données manquantes (c'est-à-dire Aucune, nan) et que vous ne souhaitez pas que les valeurs de clé null soient dans vos documents:

db.insert_many(df.to_dict("records")) insérera des clés avec des valeurs NULL. Si vous ne voulez pas que les valeurs de clé vides dans vos documents, vous pouvez utiliser une version modifiée du code pandas .to_dict("records") ci-dessous:

from pandas.core.common import _maybe_box_datetimelike
my_list = [dict((k, _maybe_box_datetimelike(v)) for k, v in Zip(df.columns, row) if v != None and v == v) for row in df.values]
db.insert_many(my_list)

où le if v != None and v == v j'ai ajouté vérifie que la valeur n'est pas None ou nan avant de la placer dans le dictionnaire de la ligne. Désormais, votre .insert_many n'inclura que les clés avec des valeurs dans les documents (et aucun type de données null).

3
Radical Edward

Je pense qu'il y a des idées cool dans cette question. Dans mon cas, je passe plus de temps à m'occuper du déplacement de grandes trames de données. Dans ces cas, les pandas ont tendance à vous permettre de choisir chunksize (par exemple, dans le pandas.DataFrame.to_sql ). Donc, je pense que je peux contribuer ici en ajoutant la fonction que j'utilise dans cette direction.

def write_df_to_mongoDB(  my_df,\
                          database_name = 'mydatabasename' ,\
                          collection_name = 'mycollectionname',
                          server = 'localhost',\
                          mongodb_port = 27017,\
                          chunk_size = 100):
    #"""
    #This function take a list and create a collection in MongoDB (you should
    #provide the database name, collection, port to connect to the remoete database,
    #server of the remote database, local port to tunnel to the other machine)
    #
    #---------------------------------------------------------------------------
    #Parameters / Input
    #    my_list: the list to send to MongoDB
    #    database_name:  database name
    #
    #    collection_name: collection name (to create)
    #    server: the server of where the MongoDB database is hosted
    #        Example: server = '132.434.63.86'
    #    this_machine_port: local machine port.
    #        For example: this_machine_port = '27017'
    #    remote_port: the port where the database is operating
    #        For example: remote_port = '27017'
    #    chunk_size: The number of items of the list that will be send at the
    #        some time to the database. Default is 100.
    #
    #Output
    #    When finished will print "Done"
    #----------------------------------------------------------------------------
    #FUTURE modifications.
    #1. Write to SQL
    #2. Write to csv
    #----------------------------------------------------------------------------
    #30/11/2017: Rafael Valero-Fernandez. Documentation
    #"""



    #To connect
    # import os
    # import pandas as pd
    # import pymongo
    # from pymongo import MongoClient

    client = MongoClient('localhost',int(mongodb_port))
    db = client[database_name]
    collection = db[collection_name]
    # To write
    collection.delete_many({})  # Destroy the collection
    #aux_df=aux_df.drop_duplicates(subset=None, keep='last') # To avoid repetitions
    my_list = my_df.to_dict('records')
    l =  len(my_list)
    ran = range(l)
    steps=ran[chunk_size::chunk_size]
    steps.extend([l])

    # Inser chunks of the dataframe
    i = 0
    for j in steps:
        print j
        collection.insert_many(my_list[i:j]) # fill de collection
        i = j

    print('Done')
    return
2
Rafael Valero

que dis-tu de ça:

db.myCollection.insert({id: df.to_json()})

id sera une chaîne unique pour ce df

1
PasteBT

Il suffit de faire des clés de chaîne!

import json
dfData = json.dumps(df.to_dict('records'))
savaData = {'_id': 'a8e42ed79f9dae1cefe8781760231ec0', 'df': dfData}
res = client.insert_one(savaData)

##### load dfData
data = client.find_one({'_id': 'a8e42ed79f9dae1cefe8781760231ec0'}).get('df')
dfData = json.loads(data)
df = pd.DataFrame.from_dict(dfData)
0
Jon Pan