【问题标题】:How to create a large pandas dataframe from an sql query without running out of memory?如何在不耗尽内存的情况下从 sql 查询创建大熊猫数据框?
【发布时间】:2013-08-09 02:22:48
【问题描述】:

我无法从 MS SQL Server 数据库中查询超过 500 万条记录的表。我想选择所有记录,但是在选择到内存时,我的代码似乎失败。

这行得通:

import pandas.io.sql as psql
sql = "SELECT TOP 1000000 * FROM MyTable" 
data = psql.read_frame(sql, cnxn)

...但这不起作用:

sql = "SELECT TOP 2000000 * FROM MyTable" 
data = psql.read_frame(sql, cnxn)

它返回此错误:

File "inference.pyx", line 931, in pandas.lib.to_object_array_tuples
(pandas\lib.c:42733) Memory Error

我读过here,在从 csv 文件创建 dataframe 时存在类似问题,解决方法是像这样使用 'iterator' 和 'chunksize' 参数:

read_csv('exp4326.csv', iterator=True, chunksize=1000)

是否有类似的从 SQL 数据库查询的解决方案?如果没有,首选的解决方法是什么?我应该使用其他一些方法来分块读取记录吗?我阅读了一些关于在 pandas 中处理大型数据集的讨论 here,但执行 SELECT * 查询似乎需要做很多工作。当然有更简单的方法。

【问题讨论】:

  • 你有多少内存?
  • @PhillipCloud 我的机器有 4GB 内存。
  • 取决于您的列的dtype 以及您可以轻松达到 4GB 的列数。例如,
  • 从 pandas 0.15 开始,您在 read_sql 中有一个 chunksize 选项来逐块读取和处理查询块:pandas.pydata.org/pandas-docs/version/0.15.0/io.html#querying

标签: python sql pandas bigdata


【解决方案1】:

正如评论中提到的,从 pandas 0.15 开始,您在 read_sql 中有一个 chunksize 选项来逐块读取和处理查询:

sql = "SELECT * FROM My_Table"
for chunk in pd.read_sql_query(sql , engine, chunksize=5):
    print(chunk)

参考:http://pandas.pydata.org/pandas-docs/version/0.15.2/io.html#querying

【讨论】:

  • 这是处理 RAM 大小 问题的方法
  • 任何依赖使用 chunksize 选项的人都应该首先阅读github.com/pandas-dev/pandas/issues/12265。对于许多数据库,在返回迭代器之前,仍将整个数据集读入内存。对于某些数据库,适当地设置连接选项可以解决这个问题——例如使用 Postgres,在创建引擎时设置 execution_options={'stream_results': True}...
  • 这不会节省内存——它会拉下整个表,然后将其分块。
  • 查看 Janak Mayer 链接的最后一条评论
  • @JanakMayer 设置stream_results时还需要指定chunksize吗?
【解决方案2】:

更新:请务必查看下面的答案,因为 Pandas 现在内置了对分块加载的支持。

您可以简单地尝试逐块读取输入表,然后从各个部分组装完整的数据框,如下所示:

import pandas as pd
import pandas.io.sql as psql
chunk_size = 10000
offset = 0
dfs = []
while True:
  sql = "SELECT * FROM MyTable limit %d offset %d order by ID" % (chunk_size,offset) 
  dfs.append(psql.read_frame(sql, cnxn))
  offset += chunk_size
  if len(dfs[-1]) < chunk_size:
    break
full_df = pd.concat(dfs)

也可能是整个数据框太大而无法放入内存,在这种情况下,除了限制您选择的行数或列数之外,您别无选择。

【讨论】:

  • -谢谢,我会试试这个,虽然我担心内存空间可能确实是我的问题。此外,由于我使用的是 MS SQL-Server2008,因此我无法使用 LIMIT 和 OFFSET SQL 选项。其他人应该知道参考here 以获取特定于其设置的解决方案
  • 您还可以将这些 df 写入 HDF5 文件(您引用的问题使用该文件,还可以阅读文档,附加表格:pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables。然后回读(部分,或根据需要迭代); HDF5 比 SQL 更紧凑
  • 对于 postgres order by 在 limit 之前:SELECT * FROM my_table order by id limit %d offset %d ;新的 pandas 使用 read_sql 而不是 read_frame。
【解决方案3】:

代码解决方案和备注。

# Create empty list
dfl = []  

# Create empty dataframe
dfs = pd.DataFrame()  

# Start Chunking
for chunk in pd.read_sql(query, con=conct, ,chunksize=10000000):

    # Start Appending Data Chunks from SQL Result set into List
    dfl.append(chunk)

# Start appending data from list to dataframe
dfs = pd.concat(dfl, ignore_index=True)

但是,我的内存分析告诉我,即使在提取每个块后释放内存,列表也会越来越大并占用该内存,从而导致可用 RAM 的净净收益。

很想听听作者/其他人怎么说。

【讨论】:

  • 将块保存到磁盘,不保存数据集,通过“del”删除块应该没问题。您还可以将块保存到您将其 dtype 更改为更少内存消耗的磁盘。您可以将 df 保存为 parquets 格式,然后只读取需要的列。
【解决方案4】:

我发现处理此问题的最佳方法是利用 SQLAlchemy steam_results 连接选项

conn = engine.connect().execution_options(stream_results=True)

并将 conn 对象传递给 pandas in

pd.read_sql("SELECT *...", conn, chunksize=10000)

这将确保光标在服务器端而不是客户端处理

【讨论】:

    【解决方案5】:

    如果要限制输出的行数,只需使用:

    data = psql.read_frame(sql, cnxn,chunksize=1000000).__next__()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-28
      • 1970-01-01
      • 2011-06-13
      • 2023-03-24
      • 2019-09-25
      • 1970-01-01
      相关资源
      最近更新 更多