Làm cách nào để nhập dữ liệu từ mongodb sang gấu trúc?


97

Tôi có một lượng lớn dữ liệu trong bộ sưu tập mongodb mà tôi cần phân tích. Làm cách nào để nhập dữ liệu đó vào gấu trúc?

Tôi mới làm quen với gấu trúc và numpy.

CHỈNH SỬA: Bộ sưu tập mongodb chứa các giá trị cảm biến được gắn thẻ ngày và giờ. Các giá trị cảm biến thuộc loại dữ liệu float.

Dữ liệu mẫu:

{
"_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
}

Sử dụng một loại lĩnh vực tùy chỉnh với MongoEngine có thể làm cho việc lưu trữ và lấy Pandas DataFrames đơn giản nhưmongo_doc.data_frame = my_pandas_df
Jthorpe

Câu trả lời:


131

pymongo có thể giúp bạn một tay, sau đây là một số mã tôi đang sử dụng:

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

Cảm ơn, đây là phương pháp tôi đã sử dụng. Tôi cũng có một loạt các tài liệu nhúng trong mỗi hàng. Vì vậy, tôi đã phải lặp lại điều đó trong mỗi hàng. Có cách nào tốt hơn để làm điều này??
Nithin

Có thể cung cấp một số mẫu cấu trúc mongodb của bạn không?
waitkuo,

3
Lưu ý rằng list()bên trong df = pd.DataFrame(list(cursor))đánh giá là một danh sách hoặc bộ tạo, để giữ cho CPU luôn mát mẻ. Nếu bạn có một mục dữ liệu zillionty-one, và một vài dòng tiếp theo sẽ được phân chia hợp lý, mức độ chi tiết và cắt chúng, toàn bộ shmegegge vẫn an toàn.
Phlip

2
Nó rất chậm @ df = pd.DataFrame(list(cursor)). Định dạng db thuần túy nhanh hơn nhiều. Chúng ta có thể thay đổi listquá trình truyền sang một thứ khác không?
Peter.k

1
@Peter dòng đó cũng đập vào mắt tôi. Truyền một con trỏ cơ sở dữ liệu, được thiết kế để có thể lặp lại và có khả năng bao bọc một lượng lớn dữ liệu, vào danh sách trong bộ nhớ có vẻ không thông minh đối với tôi.
Rafa

39

Bạn có thể tải dữ liệu mongodb của mình vào DataFrame của gấu trúc bằng mã này. Nó làm việc cho tôi. Hy vọng cho bạn cũng vậy.

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

Monarythực hiện chính xác điều đó và nó siêu nhanh . ( liên kết khác )

Xem bài đăng thú vị này bao gồm hướng dẫn nhanh và một số thời gian.


Monary có hỗ trợ kiểu dữ liệu chuỗi không?
Snehal Parmar

Tôi đã thử Monary, nhưng nó mất rất nhiều thời gian. Tôi có thiếu một số tối ưu hóa không? Đã thử client = Monary(host, 27017, database="db_tmp") columns = ["col1", "col2"] data_type = ["int64", "int64"] arrays = client.query("db_tmp", "coll", {}, columns, data_type)cho 50000các bản ghi mất xung quanh 200s.
nishant

Đó là âm thanh cực kỳ chậm ... Thành thật mà nói, tôi không biết những gì tình trạng của dự án này là, bây giờ, 4 năm sau ...
shx2

16

Theo PEP, đơn giản tốt hơn phức tạp:

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

Bạn có thể bao gồm các điều kiện như khi làm việc với cơ sở dữ liệu mongoDB thông thường hoặc thậm chí sử dụng find_one () để chỉ lấy một phần tử từ cơ sở dữ liệu, v.v.

và Voila!


pd.DataFrame.from_records có vẻ chậm như DataFrame (list ()), nhưng kết quả rất không nhất quán. %% thời gian hiển thị bất cứ điều gì từ 800 mili giây đến 1,9 giây
AFD

1
Điều này không tốt cho các bản ghi lớn vì điều này không hiển thị lỗi bộ nhớ, máy chủ treo hệ thống vì dữ liệu quá lớn. trong khi pd.DataFrame (danh sách (con trỏ)) hiển thị lỗi bộ nhớ.
Amulya Acharya

13
import pandas as pd
from odo import odo

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

9

Để xử lý dữ liệu ngoài lõi (không vừa với RAM) một cách hiệu quả (tức là thực thi song song), bạn có thể thử dùng hệ sinh thái Python Blaze : Blaze / Dask / Odo.

Blaze (và Odo ) có các chức năng vượt trội để đối phó với MongoDB.

Một vài bài viết hữu ích để bắt đầu:

Và một bài báo cho thấy những điều tuyệt vời có thể xảy ra với Blaze stack: Phân tích 1,7 tỷ bình luận Reddit với Blaze và Impala (về cơ bản, truy vấn 975 Gb bình luận trên Reddit trong vài giây).

Tái bút Tôi không liên kết với bất kỳ công nghệ nào trong số này.


1
Tôi cũng đã viết một bài bằng cách sử dụng Jupyter Notebook với ví dụ về cách Dask giúp tăng tốc độ thực thi ngay cả trên dữ liệu vừa với bộ nhớ bằng cách sử dụng nhiều lõi trên một máy.
Dennis Golomazov

8

Một tùy chọn khác mà tôi thấy rất hữu ích là:

from pandas.io.json import json_normalize

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

bằng cách này, bạn nhận được miễn phí việc mở ra các tài liệu mongodb lồng nhau.


2
Tôi gặp lỗi với phương pháp nàyTypeError: data argument can't be an iterator
Gabriel Fair

2
Thật kỳ lạ, điều này hoạt động trên con trăn của tôi 3.6.7bằng cách sử dụng gấu trúc 0.24.2. Có lẽ bạn có thể thử df = json_normalize(list(cursor))thay thế?
Ikar Pohorský

Đối với +1. docs, đối số max_level xác định mức tối đa của độ sâu dict. Tôi vừa thực hiện một thử nghiệm và nó không đúng sự thật, vì vậy một số cột sẽ cần được tách bằng lỗi .str. Tuy nhiên, một tính năng rất hay để làm việc với mongodb.
Mauricio Maroto

5

Sử dụng

pandas.DataFrame(list(...))

sẽ tiêu tốn rất nhiều bộ nhớ nếu kết quả của trình lặp / trình tạo lớn

tốt hơn để tạo các phần nhỏ và nối ở cuối

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

Sau câu trả lời tuyệt vời này của waitkuo, tôi muốn thêm khả năng làm điều đó bằng cách sử dụng chunksize theo dòng .read_sql ().read_csv () . Tôi phóng to câu trả lời từ Deu Leung bằng cách tránh đi từng 'bản ghi' của 'vòng lặp' / 'con trỏ'. Tôi sẽ mượn hàm read_mongo trước đó .

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

Một cách tiếp cận tương tự như Rafael Valero, waitkuo và Deu Leung sử dụng phân trang :

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

Bạn có thể đạt được những gì bạn muốn với pdmongo trong ba dòng:

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

Nếu dữ liệu của bạn rất lớn, trước tiên bạn có thể thực hiện một truy vấn tổng hợp bằng cách lọc dữ liệu bạn không muốn, sau đó ánh xạ chúng đến các cột mong muốn của bạn.

Đây là một ví dụ về ánh xạ Readings.atheo cột avà lọc theo reportCountcột:

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_mongochấp nhận các đối số giống như pymongo tổng hợp

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.