Mongodb'dan pandalara veri nasıl aktarılır?


100

Mongodb'deki bir koleksiyonda analiz etmem gereken büyük miktarda veriye sahibim. Bu verileri pandalara nasıl aktarırım?

Pandalar ve uyuşuklukta yeniyim.

DÜZENLEME: Mongodb koleksiyonu, tarih ve saat ile etiketlenmiş sensör değerlerini içerir. Sensör değerleri şamandıra veri türündedir.

Örnek veri:

{
"_cls" : "SensorReport",
"_id" : ObjectId("515a963b78f6a035d9fa531b"),
"_types" : [
    "SensorReport"
],
"Readings" : [
    {
        "a" : 0.958069536790466,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:26:35.297Z"),
        "b" : 6.296118156595,
        "_cls" : "Reading"
    },
    {
        "a" : 0.95574014778624,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:27:09.963Z"),
        "b" : 6.29651468650064,
        "_cls" : "Reading"
    },
    {
        "a" : 0.953648289182713,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:27:37.545Z"),
        "b" : 7.29679823731148,
        "_cls" : "Reading"
    },
    {
        "a" : 0.955931884300997,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:28:21.369Z"),
        "b" : 6.29642922525632,
        "_cls" : "Reading"
    },
    {
        "a" : 0.95821381,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:41:20.801Z"),
        "b" : 7.28956613,
        "_cls" : "Reading"
    },
    {
        "a" : 4.95821335,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:41:36.931Z"),
        "b" : 6.28956574,
        "_cls" : "Reading"
    },
    {
        "a" : 9.95821341,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:42:09.971Z"),
        "b" : 0.28956488,
        "_cls" : "Reading"
    },
    {
        "a" : 1.95667927,
        "_types" : [
            "Reading"
        ],
        "ReadingUpdatedDate" : ISODate("2013-04-02T08:43:55.463Z"),
        "b" : 0.29115237,
        "_cls" : "Reading"
    }
],
"latestReportTime" : ISODate("2013-04-02T08:43:55.463Z"),
"sensorName" : "56847890-0",
"reportCount" : 8
}

MongoEngine ile özel bir alan türü kullanmak , Pandas DataFrame'lerin depolanmasını ve geri alınmasını olabildiğince basit hale getirebilirmongo_doc.data_frame = my_pandas_df
Jthorpe

Yanıtlar:


135

pymongo size yardımcı olabilir, kullandığım bazı kodlar şunlardır:

import pandas as pd
from pymongo import MongoClient


def _connect_mongo(host, port, username, password, db):
    """ A util for making a connection to mongo """

    if username and password:
        mongo_uri = 'mongodb://%s:%s@%s:%s/%s' % (username, password, host, port, db)
        conn = MongoClient(mongo_uri)
    else:
        conn = MongoClient(host, port)


    return conn[db]


def read_mongo(db, collection, query={}, host='localhost', port=27017, username=None, password=None, no_id=True):
    """ Read from Mongo and Store into DataFrame """

    # Connect to MongoDB
    db = _connect_mongo(host=host, port=port, username=username, password=password, db=db)

    # Make a query to the specific DB and Collection
    cursor = db[collection].find(query)

    # Expand the cursor and construct the DataFrame
    df =  pd.DataFrame(list(cursor))

    # Delete the _id
    if no_id:
        del df['_id']

    return df

Teşekkürler, bu benim kullandığım yöntem. Ayrıca her satırda bir dizi gömülü belge vardı. Bu yüzden bunu her satırda da yinelemek zorunda kaldım. Bunu yapmanın daha iyi bir yolu var mı?
Nithin

Moğolunuzun yapısının bazı örneklerini vermek mümkün mü?
Waitkuo

3
list()İç df = pd.DataFrame(list(cursor))kısmın CPU'yu soğuk tutmak için bir liste veya oluşturucu olarak değerlendirildiğini unutmayın . Eğer zilyon bir veri öğeniz varsa ve sonraki birkaç satır makul bir şekilde bölümlere ayrılmış, ayrıntılı düzeyde ve kırpılmış olsaydı, tüm shmegegge yine de güvenlidir. Güzel.
Phlip

2
@ Çok yavaş df = pd.DataFrame(list(cursor)). Saf db sorgulama çok daha hızlıdır. listOyuncu seçimi başka bir şeye değiştirebilir miyiz ?
Peter.k

1
@Peter o çizgi de gözüme takıldı. Yinelenebilir şekilde tasarlanmış ve potansiyel olarak büyük miktarda veriyi bellek içi bir listeye saran bir veritabanı imlecini kullanmak bana akıllıca görünmüyor.
Rafa

42

Bu kodu kullanarak mongodb verilerinizi pandalar DataFrame'e yükleyebilirsiniz. Benim için çalışıyor. Umarım senin için de.

import pymongo
import pandas as pd
from pymongo import MongoClient
client = MongoClient()
db = client.database_name
collection = db.collection_name
data = pd.DataFrame(list(collection.find()))

24

Monarytam olarak bunu yapıyor ve süper hızlı . ( başka bir bağlantı )

Bkz bu serin yazı hızlı bir eğitim ve bazı zamanlamaları içerir.


Monary, dize veri türünü destekliyor mu?
Snehal Parmar

Monary'yi denedim ama çok zaman alıyor. Bazı optimizasyonları mı kaçırıyorum? Tried client = Monary(host, 27017, database="db_tmp") columns = ["col1", "col2"] data_type = ["int64", "int64"] arrays = client.query("db_tmp", "coll", {}, columns, data_type)For 50000kayıtları sürüyor 200s.
nishant

Kulağa son derece yavaş geliyor ... Açıkçası, bu projenin durumunun ne olduğunu bilmiyorum, şimdi, 4 yıl sonra ...
shx2

17

PEP'e göre basit, karmaşık olmaktan daha iyidir:

import pandas as pd
df = pd.DataFrame.from_records(db.<database_name>.<collection_name>.find())

Normal mongoDB veritabanıyla çalışırken yaptığınız gibi koşulları dahil edebilir veya hatta veritabanından yalnızca bir öğe almak için find_one () kullanabilirsiniz.

ve voila!


pd.DataFrame.from_records, DataFrame (list ()) kadar yavaş görünüyor, ancak sonuçlar çok tutarsız. %% zaman 800 ms'den 1,9 saniyeye kadar herhangi bir şey gösterdi
AFD

1
Bu, büyük kayıtlar için iyi değildir, çünkü bu bellek hatasını göstermez, instread sistemi çok büyük veriler için askıya alır. pd.DataFrame (liste (imleç)) bellek hatasını gösterirken.
Amulya Acharya

13
import pandas as pd
from odo import odo

data = odo('mongodb://localhost/db::collection', pd.DataFrame)

9

Çekirdek dışı (RAM'e uymayan) verilerle verimli bir şekilde (yani paralel yürütmeyle) başa çıkmak için Python Blaze ekosistemini deneyebilirsiniz : Blaze / Dask / Odo.

Blaze (ve Odo ), MongoDB ile başa çıkmak için kullanıma hazır işlevlere sahiptir.

Başlamak için birkaç yararlı makale:

Ve Blaze yığınıyla ne gibi harika şeylerin mümkün olduğunu gösteren bir makale: Blaze ve Impala ile 1.7 Milyar Reddit Yorumunu Analiz Etmek (esasen 975 Gb of Reddit yorumunu saniyeler içinde sorgulamak).

Not: Bu teknolojilerden hiçbirine bağlı değilim.


1
Ayrıca bir yazdım yayını Dask hatta tek bir makinede birden göbekler kullanılarak elde belleğe oturan bir veri hızlanma çalıştırılmasına yardımcı olur nasıl bir örnekle Jupyter Notebook kullanılmıştır.
Dennis Golomazov

9

Çok yararlı bulduğum bir başka seçenek ise:

from pandas.io.json import json_normalize

cursor = my_collection.find()
df = json_normalize(cursor)

Bu şekilde iç içe geçmiş mongodb belgelerinin açılımını ücretsiz olarak elde edersiniz.


2
Bu yöntemle ilgili bir hata aldımTypeError: data argument can't be an iterator
Gabriel Fair

2
Garip, bu benim python'umda 3.6.7panda kullanarak çalışıyor 0.24.2. Belki df = json_normalize(list(cursor))onun yerine deneyebilirsin ?
Ikar Pohorský

+1 için. docs, max_level argümanı maksimum dikte derinliği seviyesini tanımlar. Az önce bir test yaptım ve bu doğru değil, bu nedenle bazı sütunların .str accesrors ile bölünmesi gerekecek. Yine de, mongodb ile çalışmak için çok güzel bir özellik.
Mauricio Maroto

5

Kullanma

pandas.DataFrame(list(...))

yineleyici / oluşturucu sonucu büyükse çok fazla bellek tüketir

sonunda küçük parçalar ve concat oluşturmak daha iyidir

def iterator2dataframes(iterator, chunk_size: int):
  """Turn an iterator into multiple small pandas.DataFrame

  This is a balance between memory and efficiency
  """
  records = []
  frames = []
  for i, record in enumerate(iterator):
    records.append(record)
    if i % chunk_size == chunk_size - 1:
      frames.append(pd.DataFrame(records))
      records = []
  if records:
    frames.append(pd.DataFrame(records))
  return pd.concat(frames)


1

Waitkuo ile verilen bu harika cevabın ardından , bunu .read_sql () ve .read_csv () doğrultusunda chunksize kullanarak yapma olasılığını eklemek istiyorum . Deu Leung'dan gelen yanıtı , "yineleyici" / "imleç" in her bir "kaydını" tek tek gitmekten kaçınarak genişletiyorum . Önceki read_mongo işlevini ödünç alacağım .

def read_mongo(db, 
           collection, query={}, 
           host='localhost', port=27017, 
           username=None, password=None,
           chunksize = 100, no_id=True):
""" Read from Mongo and Store into DataFrame """


# Connect to MongoDB
#db = _connect_mongo(host=host, port=port, username=username, password=password, db=db)
client = MongoClient(host=host, port=port)
# Make a query to the specific DB and Collection
db_aux = client[db]


# Some variables to create the chunks
skips_variable = range(0, db_aux[collection].find(query).count(), int(chunksize))
if len(skips_variable)<=1:
    skips_variable = [0,len(skips_variable)]

# Iteration to create the dataframe in chunks.
for i in range(1,len(skips_variable)):

    # Expand the cursor and construct the DataFrame
    #df_aux =pd.DataFrame(list(cursor_aux[skips_variable[i-1]:skips_variable[i]]))
    df_aux =pd.DataFrame(list(db_aux[collection].find(query)[skips_variable[i-1]:skips_variable[i]]))

    if no_id:
        del df_aux['_id']

    # Concatenate the chunks into a unique df
    if 'df' not in locals():
        df =  df_aux
    else:
        df = pd.concat([df, df_aux], ignore_index=True)

return df

1

Kullanarak Rafael Valero, waitingkuo ve Deu Leung gibi benzer bir yaklaşım pagination :

def read_mongo(
       # db, 
       collection, query=None, 
       # host='localhost', port=27017, username=None, password=None,
       chunksize = 100, page_num=1, no_id=True):

    # Connect to MongoDB
    db = _connect_mongo(host=host, port=port, username=username, password=password, db=db)

    # Calculate number of documents to skip
    skips = chunksize * (page_num - 1)

    # Sorry, this is in spanish
    # https://www.toptal.com/python/c%C3%B3digo-buggy-python-los-10-errores-m%C3%A1s-comunes-que-cometen-los-desarrolladores-python/es
    if not query:
        query = {}

    # Make a query to the specific DB and Collection
    cursor = db[collection].find(query).skip(skips).limit(chunksize)

    # Expand the cursor and construct the DataFrame
    df =  pd.DataFrame(list(cursor))

    # Delete the _id
    if no_id:
        del df['_id']

    return df

0

Pdmongo ile üç satırda istediğinizi elde edebilirsiniz :

import pdmongo as pdm
import pandas as pd
df = pdm.read_mongo("MyCollection", [], "mongodb://localhost:27017/mydb")

Verileriniz çok büyükse, önce istemediğiniz verileri filtreleyerek toplu bir sorgu yapabilir, ardından bunları istediğiniz sütunlarla eşleştirebilirsiniz.

Readings.aSütuna eşleme ve sütuna göre afiltreleme için bir örnek reportCount:

import pdmongo as pdm
import pandas as pd
df = pdm.read_mongo("MyCollection", [{'$match': {'reportCount': {'$gt': 6}}}, {'$unwind': '$Readings'}, {'$project': {'a': '$Readings.a'}}], "mongodb://localhost:27017/mydb")

read_mongopymongo aggregate ile aynı argümanları kabul eder

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.