Python Pandas合并导致内存溢出

16

我刚开始使用Pandas,尝试合并几个数据子集。这里提供一个具体案例,但问题是普遍的:为什么会发生这种情况,如何解决?

我加载的数据大约有85兆字节左右,但我经常看到我的Python会话占用接近10吉字节的内存,然后出现内存错误。

我不知道为什么会这样,但这让我感到很痛苦,因为我甚至无法按照想要的方式查看数据。

下面是我所做的:

导入主要数据

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笔记本上完成了所有这些操作,但并不认为这会改变任何事情。


在合并之前和之后,检查每个数据框的形状。 - Alexander
3
当“on”关键列中存在重复项时,我遇到了这种行为。当我删除了重复项后,一切都正常工作了。您可以查看DataFrame的形状以检查是否得到了任何意外的结果。 - hilberts_drinking_problem
有一件事可能会有所帮助,那就是将DataFrames的创建放在函数中,这样字符串和zip文件可以被关闭和垃圾回收(而不是停留在内存中)。一个问题是你期望最终合并的大小是多少?如果你有一个n行与m行合并,它可能是n*m行,这可能非常大。 - Andy Hayden
数据框中有两亿行数据!!!我的电脑已经提示我读取这些数据时已经占用了大约2.5GB的内存。 - Andy Hayden
@ Alexander:使用 .size() 吗?@ yakym:你能举个例子吗?@ Andy:2亿行很大,但这不是pandas的优点吗?更好地处理数据和“大数据”。我喜欢在函数内合并的想法。必须收集更多像这样的内存技巧。 - pefmath
1个回答

8

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

在我的情况下,两个数据框都需要有重复键,并且我需要一种能够预测计算合并时是否适合内存的方法,如果不适合,则更改计算方法。

我想出的方法如下:

计算合并大小:

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)只能是一个标签(label),而不能是一个列表(list)。如果对group_by使用列表(list),则会返回每个列表中标签(label)的合并大小总和。这将导致合并大小远大于实际合并大小。

如果您正在使用标签(label)列表进行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

在这个问题中,pandasmerge_size方法被提出作为扩展。 https://github.com/pandas-dev/pandas/issues/15068


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接