从ElasticSearch结果创建DataFrame

时间:2014-08-07 15:16:04

标签: python pandas elasticsearch

我正在尝试使用ElasticSearch的一个非常基本的查询结果在pandas中构建一个DataFrame。我得到了我需要的数据,但它的结果是以一种方式构建正确的数据框。我真的只关心每个结果的时间戳和路径。我尝试了一些不同的es.search模式。

代码:

from datetime import datetime
from elasticsearch import Elasticsearch
from pandas import DataFrame, Series
import pandas as pd
import matplotlib.pyplot as plt
es = Elasticsearch(host="192.168.121.252")
res = es.search(index="_all", doc_type='logs', body={"query": {"match_all": {}}}, size=2, fields=('path','@timestamp'))

这提供了4个数据块。 [u' hits',u' _shards',u' take',u' timed_out']。我的结果是在命中。

res['hits']['hits']
Out[47]: 
[{u'_id': u'a1XHMhdHQB2uV7oq6dUldg',
  u'_index': u'logstash-2014.08.07',
  u'_score': 1.0,
  u'_type': u'logs',
  u'fields': {u'@timestamp': u'2014-08-07T12:36:00.086Z',
   u'path': u'app2.log'}},
 {u'_id': u'TcBvro_1QMqF4ORC-XlAPQ',
  u'_index': u'logstash-2014.08.07',
  u'_score': 1.0,
  u'_type': u'logs',
  u'fields': {u'@timestamp': u'2014-08-07T12:36:00.200Z',
   u'path': u'app1.log'}}]

我唯一关心的是获取时间戳和每次点击的路径。

res['hits']['hits'][0]['fields']
Out[48]: 
{u'@timestamp': u'2014-08-07T12:36:00.086Z',
 u'path': u'app1.log'}

我不能为我的生活找出将这个结果导入熊猫数据框的人。因此,对于我返回的2个结果,我希望有一个像。的数据框。

   timestamp                   path
0  2014-08-07T12:36:00.086Z    app1.log
1  2014-08-07T12:36:00.200Z    app2.log

8 个答案:

答案 0 :(得分:21)

或者您可以使用pandas的json_normalize函数:

transaction {
     val conn = TransactionManager.current().connection
     val statement = conn.createStatement()
     val query = "REFRESH MATERIALIZED VIEW someview"
     statement.execute(query)
}

然后按列名称过滤结果数据框

答案 1 :(得分:10)

有一个名为pd.DataFrame.from_dict的好玩具可以在这样的情况下使用:

In [34]:

Data = [{u'_id': u'a1XHMhdHQB2uV7oq6dUldg',
      u'_index': u'logstash-2014.08.07',
      u'_score': 1.0,
      u'_type': u'logs',
      u'fields': {u'@timestamp': u'2014-08-07T12:36:00.086Z',
       u'path': u'app2.log'}},
     {u'_id': u'TcBvro_1QMqF4ORC-XlAPQ',
      u'_index': u'logstash-2014.08.07',
      u'_score': 1.0,
      u'_type': u'logs',
      u'fields': {u'@timestamp': u'2014-08-07T12:36:00.200Z',
       u'path': u'app1.log'}}]
In [35]:

df = pd.concat(map(pd.DataFrame.from_dict, Data), axis=1)['fields'].T
In [36]:

print df.reset_index(drop=True)
                 @timestamp      path
0  2014-08-07T12:36:00.086Z  app2.log
1  2014-08-07T12:36:00.200Z  app1.log

分四步显示:

1,将列表中的每个项目(dictionary)读入DataFrame

2,我们可以将列表中的所有项目排成DataFrame concat行,因为我们将为每个项目执行步骤#1,我们可以使用{{1这样做。

3,然后我们访问标有map

的列

4,如果我们希望索引成为默认的'fields'序列,我们可能希望将DataFrame 90度(转置)和reset_index旋转。

enter image description here

答案 2 :(得分:10)

更好的是,您可以使用精彩的pandasticsearch库:

from elasticsearch import Elasticsearch
es = Elasticsearch('http://localhost:9200')
result_dict = es.search(index="recruit", body={"query": {"match_all": {}}})

from pandasticsearch import Select
pandas_df = Select.from_dict(result_dict).to_pandas()

答案 3 :(得分:5)

我测试了所有性能答案,然后发现pandasticsearch方法是最快的方法:

测试:

test1(使用from_dict)

%timeit -r 2 -n 5 teste1(resp)

每个循环10.5 s±247毫秒(平均±标准偏差,运行2次,每个循环5个循环)

test2(使用列表)

%timeit -r 2 -n 5 teste2(resp)

每个循环2.05 s±8.17 ms(平均±标准偏差,运行2次,每个循环5个循环)

test3(使用导入的pandasticsearch作为pdes)

%timeit -r 2 -n 5 teste3(resp)

每个循环39.2 ms±5.89 ms(平均±标准偏差,两次运行,每个循环5次)

test4(从pandas.io.json导入json_normalize使用)

%timeit -r 2 -n 5 teste4(resp)

387 ms±19 ms每个循环(平均±标准偏差,2次运行,每个5循环)

我希望它对任何人都有用

代码:

index = 'teste_85'
    size = 10000
    fields = True
    sort = ['col1','desc']
    query = 'teste'
    range_gte = '2016-01-01'
    range_lte = 'now'
    resp = esc.search(index = index,
                        size = size,
                        scroll = '2m',
                        _source = fields,
                        doc_type = '_doc',
                        body = {
                            "sort" : { "{0}".format(sort[0]) : {"order" : "{0}".format(sort[1])}},
                            "query": {
                                    "bool": {
                                    "must": [
                                        { "query_string": { "query": "{0}".format(query) } },
                                        { "range": { "anomes": { "gte": "{0}".format(range_gte), "lte": "{0}".format(range_lte) } } },
                                    ]
                                    }
                                }
                                })

    def teste1(resp):
        df = pd.DataFrame(columns=list(resp['hits']['hits'][0]['_source'].keys()))
        for hit in resp['hits']['hits']:
            df = df.append(df.from_dict(hit['_source'], orient='index').T)
        return df

    def teste2(resp):
        col=list(resp['hits']['hits'][0]['_source'].keys())
        for hit in resp['hits']['hits']:
            df = pd.DataFrame(list(hit['_source'].values()), col).T
        return df

    def teste3(resp):
        df = pdes.Select.from_dict(resp).to_pandas()
        return df

    def teste4(resp):
        df = json_normalize(resp['hits']['hits'])
        return df

答案 4 :(得分:1)

对于同样遇到此问题的任何人.. @CT Zhu给出了一个很好的答案,,但我认为它有些过时。,但是当您使用elasticsearch_dsl软件包时。结果有些不同。在这种情况下,请尝试以下操作:

# Obtain the results..
res = es_dsl.Search(using=con, index='_all')
res_content = res[0:100].execute()
# convert it to a list of dicts, by using the .to_dict() function
res_filtered = [x['_source'].to_dict() for x in res_content['hits']['hits']]

# Pass this on to the 'from_dict' function
A = pd.DataFrame.from_dict(res_filtered)

答案 5 :(得分:0)

如果您的请求可能会从Elasticsearch返回10,000多个文档,则需要使用Elasticsearch的滚动功能。很难找到此功能的文档和示例,因此,我将为您提供完整的示例:

import pandas as pd
from elasticsearch import Elasticsearch
import elasticsearch.helpers


es = Elasticsearch('http://localhost:9200')

body={"query": {"match_all": {}}}
results = elasticsearch.helpers.scan(es, query=body, index="my_index")
df = pd.DataFrame.from_dict([document['_source'] for document in results])

只需修改以“ my_”开头的字段即可对应您自己的值

答案 6 :(得分:0)

使用elasticsearch_dsl,您可以搜索文档,通过ID获取文档等。

例如

from elasticsearch_dsl import Document

# retrieve document whose _id is in the list of ids
s = Document.mget(ids,using=es_connection,index=myindex)

from elasticsearch_dsl import Search

# get (up to) 100 documents from a given index
s = Search(using=es_connection,index=myindex).extra(size=100)

然后,如果要创建DataFrame并在数据帧索引中使用elasticsearch id,可以执行以下操作:

df = pd.DataFrame([{'id':r.meta.id, **r.to_dict()} 
                            for r 
                            in s.execute()]).set_index('id',drop=True)

答案 7 :(得分:0)

redata = map(lambda x:x['_source'], res['hits']['hits'])
pd.DataFrame(redata)

如果我仅使用pandas模块,它将是最佳解决方案。 就我而言,这些代码花费20.5ms

如果使用pandas.io.json.json_normalize(res['hits']['hits']),则将花费291ms,结果会有所不同。