Python Pandas 合并导致内存溢出

Posted

技术标签:

【中文标题】Python Pandas 合并导致内存溢出【英文标题】:Python Pandas Merge Causing Memory Overflow 【发布时间】:2015-12-21 10:39:15 【问题描述】:

我是 Pandas 的新手,正在尝试合并一些数据子集。我给出了一个发生这种情况的具体案例,但问题是一般性的:它是如何/为什么会发生的,我该如何解决?

我加载的数据大约是 85 Megs 左右,但我经常看到我的 python 会话运行接近 10 gigs 的内存使用然后出现内存错误。

我不知道为什么会发生这种情况,但这让我很生气,因为我什至无法开始以我想要的方式查看数据。

这是我所做的:

导入主要数据

import requests, zipfile, StringIO
import numpy as np
import pandas as pd 


STAR2013url="http://www3.cde.ca.gov/starresearchfiles/2013/p3/ca2013_all_csv_v3.zip"
STAR2013fileName = 'ca2013_all_csv_v3.txt'

r = requests.get(STAR2013url)
z = zipfile.ZipFile(StringIO.StringIO(r.content))

STAR2013=pd.read_csv(z.open(STAR2013fileName))

导入一些交叉引用表

STARentityList2013url = "http://www3.cde.ca.gov/starresearchfiles/2013/p3/ca2013entities_csv.zip"
STARentityList2013fileName = "ca2013entities_csv.txt"
r = requests.get(STARentityList2013url)
z = zipfile.ZipFile(StringIO.StringIO(r.content))
STARentityList2013=pd.read_csv(z.open(STARentityList2013fileName))

STARlookUpTestID2013url = "http://www3.cde.ca.gov/starresearchfiles/2013/p3/tests.zip"
STARlookUpTestID2013fileName = "Tests.txt"
r = requests.get(STARlookUpTestID2013url)
z = zipfile.ZipFile(StringIO.StringIO(r.content))
STARlookUpTestID2013=pd.read_csv(z.open(STARlookUpTestID2013fileName))

STARlookUpSubgroupID2013url = "http://www3.cde.ca.gov/starresearchfiles/2013/p3/subgroups.zip"
STARlookUpSubgroupID2013fileName = "Subgroups.txt"
r = requests.get(STARlookUpSubgroupID2013url)
z = zipfile.ZipFile(StringIO.StringIO(r.content))
STARlookUpSubgroupID2013=pd.read_csv(z.open(STARlookUpSubgroupID2013fileName))

重命名列 ID 以允许合并

STARlookUpSubgroupID2013 = STARlookUpSubgroupID2013.rename(columns='001':'Subgroup ID')
STARlookUpSubgroupID2013

成功合并

merged = pd.merge(STAR2013,STARlookUpSubgroupID2013, on='Subgroup ID')

尝试第二次合并。这就是发生内存溢出的地方

merged=pd.merge(merged, STARentityList2013, on='School Code')

我在 ipython notebook 中完成了所有这些操作,但我认为这不会改变任何事情。

【问题讨论】:

检查合并前后每个数据框的形状 on 键列中存在重复项时,我遇到了这种行为。当我删除重复项时,一切正常。您可以查看 DataFrame 形状以检查是否得到任何意外结果。 可能有帮助的一件事是将 DataFrames 的创建放在函数中,这样可以关闭字符串和 zip 文件并进行垃圾收集(而不是坐在内存中)。一个问题是您预计最终合并的规模有多大?如果你有一个 n 行与一个 m 行合并,它可以是 n*m 行,这里可能非常大。 DataFrame也是2亿行!!!我的机器表明我已经在 2.5Gb 左右阅读。 @Alexander:使用 .size() ? @yakym:你能举个例子吗? @Andy:2亿行很大,但这不是panda的重点吗?更好地处理数据和“大数据”。我喜欢在功能理念中进行合并。必须收集更多这样的记忆技巧。 【参考方案1】:

虽然这是一个老问题,但我最近遇到了同样的问题。

在我的例子中,两个数据帧中都需要重复的键,我需要一种方法来判断合并是否适合在计算之前放入内存,如果不适合,则更改计算方法。

我想出的方法如下:

计算合并大小:

def merge_size(left_frame, right_frame, group_by, how='inner'):
    left_groups = left_frame.groupby(group_by).size()
    right_groups = right_frame.groupby(group_by).size()
    left_keys = set(left_groups.index)
    right_keys = set(right_groups.index)
    intersection = right_keys & left_keys
    left_diff = left_keys - intersection
    right_diff = right_keys - intersection

    left_nan = len(left_frame[left_frame[group_by] != left_frame[group_by]])
    right_nan = len(right_frame[right_frame[group_by] != right_frame[group_by]])
    left_nan = 1 if left_nan == 0 and right_nan != 0 else left_nan
    right_nan = 1 if right_nan == 0 and left_nan != 0 else right_nan

    sizes = [(left_groups[group_name] * right_groups[group_name]) for group_name in intersection]
    sizes += [left_nan * right_nan]

    left_size = [left_groups[group_name] for group_name in left_diff]
    right_size = [right_groups[group_name] for group_name in right_diff]
    if how == 'inner':
        return sum(sizes)
    elif how == 'left':
        return sum(sizes + left_size)
    elif how == 'right':
        return sum(sizes + right_size)
    return sum(sizes + left_size + right_size)

注意:

目前用这种方法,key只能是标签,不能是列表。使用group_by 的列表当前会返回列表中每个标签的合并大小总和。这将导致合并大小远大于实际合并大小。

如果您对 group_by 使用标签列表,则最终行大小为:

min([merge_size(df1, df2, label, how) for label in group_by])

检查这是否适合内存

此处定义的merge_size 函数返回将通过将两个数据帧合并在一起而创建的行数。

通过将此与两个数据帧的列数相乘,然后乘以 np.float[32/64] 的大小,您可以大致了解生成的数据帧在内存中的大小。然后可以将其与 psutil.virtual_memory().available 进行比较,以查看您的系统是否可以计算完全合并。

def mem_fit(df1, df2, key, how='inner'):
    rows = merge_size(df1, df2, key, how)
    cols = len(df1.columns) + (len(df2.columns) - 1)
    required_memory = (rows * cols) * np.dtype(np.float64).itemsize

    return required_memory <= psutil.virtual_memory().available

merge_size 方法已在本期中作为pandas 的扩展提出。 https://github.com/pandas-dev/pandas/issues/15068.

【讨论】:

以上是关于Python Pandas 合并导致内存溢出的主要内容,如果未能解决你的问题,请参考以下文章

JMeter内存溢出:java.lang.OutOfMemoryError: Java heap space解决方法(实测有效)

python 中的out of memory是怎么回事,内存不够吗?

在java中上传文件出现内存溢出怎么解决标签:java,内存,溢出

C# 使用大数组内存溢出的解决办法

解决Java POI导出海量Excel数据内存溢出

Java 循环读取文件导致内存溢出!