使用 pypyodbc 和 pandas 加载 1GB .accdb 时出现内存错误

Posted

技术标签:

【中文标题】使用 pypyodbc 和 pandas 加载 1GB .accdb 时出现内存错误【英文标题】:Memory error loading a 1GB .accdb using pypyodbc and pandas 【发布时间】:2016-07-04 21:52:16 【问题描述】:

我正在尝试做一些可能不可能或应该以不同方式完成的事情......

我必须读取一个 1 GB 的 Access 文件并在 pandas 中对其进行操作;由于cursor.fetchall() 直接与Memory Error 失败,我尝试了以下函数以查看内存错误何时发生:它在获取400.000 行后出现(总数为1.12 Mrows)。

这很奇怪,因为我的机器中有 8 GB 内存,而且它似乎是 50% 的免费内存。我还将我的虚拟内存设置为 16 GB,但结果没有改变。

我不需要微积分速度,所以欢迎任何肮脏的解决方案 :) 包括使用硬盘作为内存(我有一个 ssd)。

也许有办法让所有内存都可用于 python?

已经失败的方式:

单行获取:cursor.fetchone() 多行获取:cursor.fetchmany() 所有行获取:cursor.fetchall() pandas read_sql 传递 chunksize: pandas.read_sql(query, conn, chunksize=chunksize)(感谢 MaxU 用户)

功能:

def msaccess_to_df (abs_path, query):
    conn = pypyodbc.connect(
        r"Driver=Microsoft Access Driver (*.mdb, *.accdb);"
        r"Dbq=" + abs_path + ";" )

    cur = conn.cursor()
    cur.execute( query )

    fields = zip(*cur.description)[0]
    df = pandas.DataFrame(columns=fields)

    fetch_lines_per_block = 5000
    i = 0
    while True:
        rows = cur.fetchmany(fetch_lines_per_block) # <-----
        if len(rows) == 0: break
        else:
            rd = [dict(zip(fields, r)) for r in rows]
            df = df.append(rd, ignore_index=True)
            del rows
            del rd
        i+=1
        print 'fetched', i*fetch_lines_per_block, 'lines'

    cur.close()
    conn.close()

    return df

错误:

df = df.append(rd, ignore_index=True)
  File "C:\Python27\lib\site-packages\pandas\core\frame.py", line 4338, in append
    verify_integrity=verify_integrity)
  File "C:\Python27\lib\site-packages\pandas\tools\merge.py", line 845, in concat
    copy=copy)
  File "C:\Python27\lib\site-packages\pandas\tools\merge.py", line 904, in __init__
    obj.consolidate(inplace=True)
  File "C:\Python27\lib\site-packages\pandas\core\generic.py", line 2747, in consolidate
    self._consolidate_inplace()
  File "C:\Python27\lib\site-packages\pandas\core\generic.py", line 2729, in _consolidate_inplace
    self._protect_consolidate(f)
  File "C:\Python27\lib\site-packages\pandas\core\generic.py", line 2718, in _protect_consolidate
    result = f()
  File "C:\Python27\lib\site-packages\pandas\core\generic.py", line 2727, in f
    self._data = self._data.consolidate()
  File "C:\Python27\lib\site-packages\pandas\core\internals.py", line 3273, in consolidate
    bm._consolidate_inplace()
  File "C:\Python27\lib\site-packages\pandas\core\internals.py", line 3278, in _consolidate_inplace
    self.blocks = tuple(_consolidate(self.blocks))
  File "C:\Python27\lib\site-packages\pandas\core\internals.py", line 4269, in _consolidate
    _can_consolidate=_can_consolidate)
  File "C:\Python27\lib\site-packages\pandas\core\internals.py", line 4292, in _merge_blocks
    new_values = new_values[argsort]
MemoryError

####################编辑 - 已解决####################

我终于解决了

将机器虚拟内存设置为 16GB 安装 Python 64 位 将 Access 驱动程序替换为 64 位驱动程序 (https://www.microsoft.com/en-us/download/confirmation.aspx?id=13255)

有了这个任何方法都可以。

【问题讨论】:

啊!如果您的机器允许,我将推荐 Python 64 位。请将您的编辑发布为未来读者的答案。 【参考方案1】:

我会使用原生 pandas 方法 - read_sql() 而不是在循环中手动获取行:

def msaccess_to_df (abs_path, query):
    conn = pypyodbc.connect(
        r"Driver=Microsoft Access Driver (*.mdb, *.accdb);"
        r"Dbq=" + abs_path + ";" )

    df = pd.read_sql(query, conn)
    conn.close()
    return df

如果您仍然收到MemoryError 异常,请尝试分块读取数据:

def msaccess_to_df (abs_path, query, chunksize=10**5):
    conn = pypyodbc.connect(
        r"Driver=Microsoft Access Driver (*.mdb, *.accdb);"
        r"Dbq=" + abs_path + ";" )

    df = pd.concat([x for x in pd.read_sql(query, conn, chunksize=chunksize)],
                   ignore_index=True)
    conn.close()
    return df

PS 这应该给你一个想法,但请注意我没有测试这段代码,所以它可能需要一些调试......

【讨论】:

谢谢,但也有chunksize,即使我有 3 GB 的可用内存(在 Windows 资源监视器中查看),我仍然会在一段时间后遇到内存错误。

以上是关于使用 pypyodbc 和 pandas 加载 1GB .accdb 时出现内存错误的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 pypyodbc 在 python 中使用 where 子句运行 mssql 选择查询

Pypyodbc查询返回不正确的数据

pypyodbc 从 python 在 netezza 数据库中创建存储过程的错误

错误 - 多级文件夹内的 pypyodbc.win_create_mdb

如何使 pandas.read_sql() 不将所有标题转换为小写

带参数的 PypyODBC:[ODBC Microsoft Access 驱动程序] 参数太少。预期 3(不是日期问题)