【问题标题】:Building dataFrame from a list of list of objects takes too long从对象列表构建 dataFrame 花费的时间太长
【发布时间】:2020-01-17 17:35:02
【问题描述】:

我正在提取大量数据。它以对象列表的形式出现。

示例:[[objectA,objectB],[objectC],[],[ObjectD]...]

每个对象都有很多属性,但是对于我的数据框,我只需要名称、值、时间戳、描述。 我尝试了两件事:

for events in events_list:
    if len(events) > 0:
       for event in events:
           df = DataFrame([])
           df['timestamp'] = event.timestamp
           df['value'] = event.value
           df['name'] = event.name
           df['desc'] = event.desc
           final_df = final_df.append(df)

完成此过程大约需要 15 分钟

我将代码更改为使用 python 列表:

df_list = list()
for events in events_list:
    if len(events) > 0:
       for event in events:
           df_list.append([event.timestamp, event.value, event.name, event.desc])
final_df = pd.DataFrame(df_list, columns=['timestamp', 'value', 'name', 'desc'])

通过此更改,我设法将时间减少到大约 ~10-11 分钟

我仍在研究是否有更快的方法。在我使用 python list 进行更改之前,我尝试了字典,但它比我预期的要慢。目前我正在阅读看起来非常快的 Panads 矢量化,但是我不确定是否可以将它用于我的目的。 我知道 Python 循环有点慢,我对它们无能为力,所以我也在想办法在数据帧中执行这些循环。

我的问题是,你们之前有没有解决过这个问题,有没有更好的方法来解决这个问题?

编辑: 有关于数据的问题。它通过一个 API 来构造,因为每组对象都按名称分组。例如:

[[objectA, objectB (都同名)],[objectC],[EMPTY - 没有 此名称的数据],[ObjectD]...]

因为我无法改变获取数据的方式,所以我必须使用这种数据结构。

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

初始方法中计算量大的操作是追加 - 每次使用 final_df.append(df) 时,您都在创建一个全新的(并且每次迭代都更大!)数据帧。相反,将所有数据框聚合到一个列表中并使用pd.concat(df_list)

为了比这更快,您可能需要考虑在某种程度上使用多处理,无论是通过标准 python 多处理库还是通过框架 - 我推荐 Dask。

编辑:附言 如果您的数据最初是 csv/excel/parquet 或 pandas 支持的其他格式,您可以使用 pandas 以非常有效的方式一次加载所有数据。即使您的事件包含不必要的列,加载整个数据集然后过滤掉冗余列也会快得多。

【讨论】:

  • 我试过 pd.concat 它并没有加快速度,唯一的好处是它降低了内存使用量。但是,在这种情况下,内存不是问题。到目前为止,构建列表并将其提供给 pd.DataFrame 是最快的方法。我正在考虑使用多处理来创建列表,但我仍然希望有更好的方法来使用例如 pandas 或 numpy。
  • 如何查询数据库?你在使用 ORM 吗? Pandas 确实有一个 pd.read_sql 方法。
  • 是的,我们使用 ORM,但是我猜出于安全原因,我无法直接访问数据库。我们使用 api 根据查询返回此对象列表。
【解决方案2】:

这样的事情怎么样?

import datetime
import itertools as itt
import operator
import random
from dataclasses import dataclass

import pandas as pd


# DUMMY DATA SETUP

@dataclass
class Obj:
    name: str
    timestamp: datetime.datetime
    value: int
    desc: str


group_lens = [random.randint(0, 1000) for _ in range(200000)]
event_count = 1

events = []
for curr_group_len in group_lens:
    curr_group = []
    for _ in range(curr_group_len):
        curr_group.append(
            Obj(f"event_{event_count}", datetime.datetime.now(), random.randint(-100, 100), f"event_{event_count} desc"))
        event_count += 1
    events.append(curr_group)

# DATAFRAME CREATION

cust_getter = operator.attrgetter('name', 'timestamp', 'value', 'desc')

df = pd.DataFrame(data=(cust_getter(elem) for elem in itt.chain.from_iterable(events)))

我在 10,006,766 个元素的二维列表上对其进行了测试,只用了 9 秒。

【讨论】:

  • 605.20 秒循环;创建 df 需要 6.15 秒; 12,412,942 个事件计数。我猜那是它的对象。我将不得不尝试提取数据。
  • @Noonewins 605.20 秒循环; 哪个循环?我的意思是使用我分享的 DataFrame 创建方法对您的数据进行基准测试。
  • .\test.py > 10,007,089 个元素 166.06090259552002 秒 - 这是我使用我正在使用的对象运行脚本时的结果。
  • 是的,结果很有希望 :) 我现在正尝试在我的代码中实现它以查看最终结果。谢谢。
  • @Noonewins 你能分享完成后的“最终”/完整基准测试结果吗?顺便说一下,可能值得再次对其进行分析。
【解决方案3】:

我使用生成器找到了我的问题的答案: 这是一个指向另一个线程的链接,该线程专门用于确定如何从 python 生成器列表中创建数据框。在那里,我们找到了一种从这个线程解决问题的方法: Create Pandas Dataframe from List of Generators

总结一下,我替换了这个:

for events in events_list:
    if len(events) > 0:
       for event in events:
           for record in event:
               df_list.append([record.timestamp, record.value, record.name, record.desc])
final_df = pd.DataFrame(df_list, columns=['timestamp', 'value', 'name', 'desc'])

有了这个:

data= (record.Timestamp, record.Value, record.Name, record.desc) 
                for events in events_list for event in events for record in event)

dataframe = pd.DataFrame(data, columns=["timestamp", "value", "name", "desc])

使用列表理解,我通过将列表构建为一个整体而不是连续追加来节省大量时间。

测试 1500 万条记录(包括创建 DF):

使用 for 循环添加列表 = 16 分钟

list_comprehension = 3 分钟

我将在接下来的几天内继续使用不同数量的数据对此进行测试。

【讨论】:

    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    相关资源
    最近更新 更多