从数组中提取值的最快方法?

时间:2019-12-30 17:35:20

标签: python elasticsearch parquet

我在Elasticsearch中有一组11mm的文档,每个文档都有一个标识符数组。每个标识符都是一个包含类型,值和日期的字典。这是一个示例记录:

{
  "name": "Bob",
  "identifiers": [
    {
      "date": "2019-01-01",
      "type": "a",
      "value": "abcd"
    },
    {
      "date": "2019-01-01",
      "type": "b",
      "value": "efgh"
    }
  ]
}

我需要每晚将这些记录传输到一个镶木地板数据存储区,在该存储区中仅将标识符的值保存在数组中。喜欢:

{
  "name": "Bob",
  "identifiers": ["abcd", "efgh"]
}

我通过遍历所有记录并展平标识符来做到这一点。这是我的扁平变压器:

    def _transform_identifier_values(self, identifiers: List[dict]):
        ret = [
            identifier['value']
            for identifier in identifiers
        ]
        return ret

这有效,但是很慢。有更快的方法吗?可能是我可以利用的本机实现?

编辑:

尝试了Sunny的建议。我惊讶地发现原件实际上表现最好。我的假设是itemgetter的表现会更好。

这是我测试的方式:

import time
from functools import partial
from operator import itemgetter


def main():

    docs = []
    for i in range(10_000_000):
        docs.append({
            'name': 'Bob',
            'identifiers': [
                {
                    'date': '2019-01-01',
                    'type': 'a',
                    'value': 'abcd'
                },
                {
                    'date': '2019-01-01',
                    'type': 'b',
                    'value': 'efgh'
                }
            ]
        })

    start = time.time()
    for doc in docs:
        _transform_identifier_values_original(doc['identifiers'])
    end = time.time()

    print(f'Original took {end-start} seconds')

    start = time.time()
    for doc in docs:
        _transform_identifier_values_getter(doc['identifiers'])
    end = time.time()

    print(f'Item getter took {end-start} seconds')

    start = time.time()
    for doc in docs:
        _transform_identifier_values_partial_lambda(doc['identifiers'])
    end = time.time()

    print(f'Lambda partial took {end-start} seconds')

    start = time.time()
    for doc in docs:
        _transform_identifier_values_partial(doc['identifiers'])
    end = time.time()

    print(f'Partial took {end-start} seconds')


def _transform_identifier_values_original(identifiers):
    ret = [
        identifier['value']
        for identifier in identifiers
    ]
    return ret


def _transform_identifier_values_getter(identifiers):
    return list(map(itemgetter('value'), identifiers))


def _transform_identifier_values_partial_lambda(identifiers):
    flatten_ids = partial(lambda o: list(map(itemgetter('value'), o)))
    return flatten_ids(identifiers)


def _transform_identifier_values_partial(identifiers):
    flatten = partial(map, itemgetter('value'))
    return list(flatten(identifiers))

if __name__ == '__main__':
    main()

结果:

  

原始耗时4.6204328536987305秒

     

吸气剂花费了7.186180114746094秒

     

Lambda部分花费了10.534514904022217秒

     

部分花费了9.07079291343689秒

2 个答案:

答案 0 :(得分:0)

这是我想出的解决方案:

def changeJSON(dictionary):
    new_dict = {'name': dictionary['name'], 'identifiers': []}
    for i in dictionary['identifiers']:
        new_dict['identifiers'].append(i['value'])
    return new_dict

此函数将使用一个字典,并以您所需的新格式返回字典。然后,您可以从内置的json库中json.dumps()函数。它接收字典列表并将其转储到json文件中。

答案 1 :(得分:0)

您可以尝试使用operator.itemgetter

from operator import itemgetter
def _transform_identifier_values(self, identifiers: List[dict]):
    return list(map(itemgetter('value'), identifiers))

甚至可以用它来制作partial function

from operator import itemgetter
from functools import partial
flatten_ids = partial(lambda o: list(map(itemgetter('value'), o['identifiers'])))
print(flatten_ids(obj))

如果您想避免使用lambda,可以尝试:

flatten = partial(map, itemgetter('value'))
print(list(flatten(obj['identifiers'])))

我很好奇看到这些结果。