mongodb에서 pandas로 데이터를 가져 오는 방법은 무엇입니까?


97

mongodb의 컬렉션에 분석해야 할 많은 양의 데이터가 있습니다. 해당 데이터를 Pandas로 어떻게 가져 옵니까?

나는 pandas와 numpy를 처음 사용합니다.

편집 : mongodb 컬렉션에는 날짜 및 시간 태그가 지정된 센서 값이 포함되어 있습니다. 센서 값은 float 데이터 유형입니다.

샘플 데이터 :

{
"_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로하는 것은 저장과 같이 간단하게 판다 DataFrames를 검색 할 수 있습니다mongo_doc.data_frame = my_pandas_df
Jthorpe

답변:


131

pymongo 다음은 내가 사용하는 코드입니다.

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

고마워, 이것이 내가 사용한 방법입니다. 또한 각 행에 포함 된 문서 배열이 있습니다. 그래서 각 행 내에서도 반복해야했습니다. 이 작업을 수행하는 더 좋은 방법이 있습니까 ??
니씬

mongodb 구조의 일부 샘플을 제공 할 수 있습니까?
waitingkuo 2013

3
메모 list()내부 df = pd.DataFrame(list(cursor))는 CPU 냉각을 유지하기 위해, 목록 또는 발전기로 평가합니다. 데이터 항목이 엄청나게 많고 다음 몇 줄이 합리적으로 분할되고, 세부 수준이 높고, 잘린 경우 전체 shmegegge는 여전히 안전합니다. 좋습니다.
Phlip

2
@ 매우 느립니다 df = pd.DataFrame(list(cursor)). 순수한 db quering이 훨씬 빠릅니다. list캐스팅을 다른 것으로 바꿀 수 있습니까?
Peter.k

1
@Peter 그 라인도 내 눈을 사로 잡았습니다. 반복 가능하고 잠재적으로 많은 양의 데이터를 감쌀 수 있도록 설계된 데이터베이스 커서를 메모리 내 목록으로 캐스팅하는 것은 나에게 영리 해 보이지 않습니다.
Rafa

39

이 코드를 사용하여 mongodb 데이터를 pandas DataFrame에로드 할 수 있습니다. 그것은 나를 위해 작동합니다. 당신도 희망합니다.

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

Monary정확하게 수행하고 매우 빠릅니다 . ( 다른 링크 )

빠른 자습서와 몇 가지 타이밍이 포함 된 이 멋진 게시물 을 참조하십시오 .


Monary는 문자열 데이터 유형을 지원합니까?
Snehal Parmar

Monary를 시도했지만 시간이 많이 걸립니다. 최적화를 놓치고 있습니까? 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 50000레코드는 200s.
nishant 2011

매우 느리게 들립니다 ... 솔직히,이 프로젝트의 상태가 4 년 후
인지 모르겠습니다

16

PEP에 따르면 단순한 것이 복잡한 것보다 낫습니다.

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

일반 mongoDB 데이터베이스로 작업하는 것처럼 조건을 포함하거나 find_one ()을 사용하여 데이터베이스에서 하나의 요소 만 가져 오는 등의 작업을 수행 할 수 있습니다.

그리고 짜잔!


pd.DataFrame.from_records는 DataFrame (list ())만큼 느리지 만 결과는 매우 일관성이 없습니다. %% time은 800ms에서 1.9 초까지 모든 것을 보여주었습니다
AFD

1
이것은 메모리 오류를 표시하지 않고 너무 큰 데이터 때문에 시스템을 중단하므로 대용량 레코드에는 좋지 않습니다. pd.DataFrame (list (cursor))은 메모리 오류를 표시합니다.
Amulya Acharya 19

13
import pandas as pd
from odo import odo

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

9

Out-of-core (RAM에 맞지 않음) 데이터를 효율적으로 처리하기 위해 (즉, 병렬 실행으로) Python Blaze 생태계 ( Blaze / Dask / Odo)를 사용해 볼 수 있습니다 .

Blaze (및 Odo )에는 MongoDB를 처리하는 기본 기능이 있습니다.

시작하기위한 몇 가지 유용한 기사 :

Blaze 스택으로 가능한 놀라운 일을 보여주는 기사 : Blaze 및 Impala를 사용하여 17 억 개의 Reddit 댓글 분석 (본질적으로 975Gb의 Reddit 댓글을 몇 초 만에 쿼리).

PS 저는 이러한 기술과 관련이 없습니다.


1
또한 Dask가 단일 컴퓨터에서 여러 코어를 사용하여 메모리에 맞는 데이터에서도 실행 속도를 높이는 데 도움이되는 예제와 함께 Jupyter Notebook을 사용하여 게시물 을 작성했습니다 .
Dennis Golomazov

8

매우 유용한 또 다른 옵션은 다음과 같습니다.

from pandas.io.json import json_normalize

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

이렇게하면 중첩 된 mongodb 문서를 무료로 펼칠 수 있습니다.


2
이 방법에 오류가 있습니다TypeError: data argument can't be an iterator
Gabriel Fair

2
이상하게도 이것은 3.6.7pandas를 사용 하는 내 파이썬에서 작동합니다 0.24.2. df = json_normalize(list(cursor))대신 시도해 볼 수 있습니까?
Ikar Pohorský

+1을 위해. docs, max_level 인수는 dict 깊이의 최대 수준을 정의합니다. 방금 테스트를했는데 사실이 아니므로 일부 열은 .str accesrors로 분할해야합니다. 그래도 mongodb 작업에 매우 좋은 기능입니다.
Mauricio Maroto

5

사용

pandas.DataFrame(list(...))

반복기 / 생성기 결과가 큰 경우 많은 메모리를 소비합니다.

작은 청크를 생성하고 끝에 연결하는 것이 좋습니다.

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 의이 훌륭한 답변에 따라 .read_sql ().read_csv () 와 함께 chunksize를 사용하여 수행 할 가능성을 추가하고 싶습니다 . '반복자'/ '커서'의 '레코드'를 하나씩 이동하는 것을 피함으로써 Deu Leung 의 답변을 확대합니다 . 이전 read_mongo 함수 를 빌릴 것 입니다.

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

페이지 매김을 사용하는 Rafael Valero, waitingkuo 및 Deu Leung과 같은 유사한 접근 방식 :

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 로 원하는 것을 세 줄로 얻을 수 있습니다 .

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

데이터가 매우 큰 경우 먼저 원하지 않는 데이터를 필터링하여 집계 쿼리를 수행 한 다음 원하는 열에 매핑 할 수 있습니다.

다음은 Readings.a열에 매핑 하고 열로 a필터링 하는 예입니다 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 집계 와 동일한 인수를 허용합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.