打开一个20GB的文件用于熊猫分析


33

我当前正在尝试使用pandas和python打开文件以进行机器学习,这对我来说将它们全部放在DataFrame中是理想的选择。现在,该文件的大小为18GB,RAM为32GB,但是我一直遇到内存错误。

根据您的经验,可能吗?如果不是,您是否知道解决此问题的更好方法?(蜂巢表?将我的RAM的大小增加到64?创建数据库并从python访问它)


我遇到了同样的问题,建议您增加交换空间,页面调度和硬盘驱动器的大小。
媒体

加载数据时的经验法则pandas是,您需要多5到10倍的RAM。我建议进行inplace操作,显式调用garbage.collector以取消分配对象。
Kiritee Gak,

4
说明您的最终目标,使这个问题更好。您是在进行探索性数据分析,数据清理,训练模型还是什么?什么样的数据?
皮特

1
您是否考虑过使用dask
rpanai

Answers:


32

如果这是一个csv文件,并且在训练算法时不需要一次访问所有数据,则可以分块读取。该pandas.read_csv方法使您可以像这样大块地读取文件:

import pandas as pd
for chunk in pd.read_csv(<filepath>, chunksize=<your_chunksize_here>)
    do_processing()
    train_algorithm()

这是方法的文档


这也适用于zip文件吗?
詹姆士·维尔茨巴

如果压缩文件也是csv文件,它应该也可以工作,您需要将压缩类型作为方法的参数传递
Olel Daniel

22

有两种可能性:要么需要将所有数据存储在内存中进行处理(例如,机器学习算法希望一次使用所有数据),要么可以不使用它(例如,算法只需要行样本或列)。

在第一种情况下,您需要解决内存问题。增加内存大小,租用高内存云机,使用就地操作,提供有关您正在读取的数据类型的信息,确保删除所有未使用的变量并收集垃圾等。

32GB的RAM很可能不足以让Pandas处理您的数据。请注意,整数“ 1”在存储为文本时只是一个字节,而在以表示时int64则为8个字节(这是熊猫从文本中读取它时的默认值)。您可以使用浮点数“ 1.0”创建相同的示例,该浮点数float64默认从3字节字符串扩展为8字节。通过让Pandas准确知道每列要使用的类型并强制使用最小的表示形式,您可能会赢得一些空间,但是我们甚至没有在这里开始谈论Python的数据结构开销,这可能会在此处或那里轻松添加一个或两个额外的指针。和指针在64位计算机上每个为8个字节。

总结:不,32GB RAM可能不足以使Pandas处理20GB文件。

在第二种情况下(这更现实,可能适用于您),您需要解决数据管理问题。确实,当您只需要部分数据进行处理时就必须加载所有数据,这可能表示数据管理不善。这里有多个选项:

  1. 使用SQL数据库。如果可以的话,它几乎始终是首选,也是一个不错的解决方案。20GB的声音听起来像大多数SQL数据库都能很好地处理,甚至不需要在(高端)笔记本电脑上分发。您将能够为列建立索引,通过SQL进行基本聚合,并使用简单的即可将所需的子样本放入Pandas中,以进行更复杂的处理pd.read_sql。将数据移至数据库还将使您有机会考虑列的实际数据类型和大小。

  2. 如果您的数据主要是数字数据(即数组或张量),则可以考虑将其保存为HDF5格式(请参阅PyTables),这使您可以方便地从磁盘上仅读取必要的大型数组切片。基本的numpy.save和numpy.load也通过内存映射磁盘上的阵列来达到相同的效果。对于GIS和相关的栅格数据,有专用的 数据库,这些数据库可能不像SQL那样直接连接到大熊猫,但还应该使您能够方便地进行切片和查询。

  3. 据我所知,Pandas不支持HDF5或numpy数组的这种“部分”内存映射。如果您仍然需要一种“纯熊猫”解决方案,则可以尝试通过“分片”解决:将大型表的分别存储(例如,存储在单个HDF5的单独文件中或单独的“表”中)文件),并且仅按需加载必要的文件,或分别存储大块的行。但是,您随后需要实现用于加载必要块的逻辑,从而重新发明大多数SQL数据库中已经植入的自行车,因此,这里的选项1可能仍会更容易。但是,如果数据以CSV格式输入,则可以通过将chunksize参数指定为来分块处理pd.read_csv


5
在“第一种情况”中应该提到的一点是,如果OP中有很多条目具有相同的值(例如零),则数据被认为是稀疏的,可以使用稀疏的稀疏矩阵而不是a pandas数据框-稀疏数据需要更少的内存。
里卡多·克鲁兹

9

我几天前刚遇到这个问题!由于您没有提供太多详细信息,因此不确定这是否对您的特定情况有用,但是我的情况是要在“大型”数据集上脱机工作。数据是从电表以20GB压缩CSV文件格式获取的,时间序列数据间隔为几秒钟。

文件IO:

data_root = r"/media/usr/USB STICK"
fname = r"meters001-050-timestamps.csv.gz"
this_file = os.path.join(data_root,fname)
assert os.path.exists(this_file), this_file
this_file

直接在gzip文件上创建一个块迭代器(请勿解压缩!)

cols_to_keep = [0,1,2,3,7]
column_names = ['METERID','TSTAMP','ENERGY','POWER_ALL','ENERGY_OUT',]
parse_dates = ['TSTAMP']
dtype={'METERID': np.int32, 
       'ENERGY': np.int32,
       'POWER_ALL': np.int32,
       'ENERGY_OUT': np.int32,
      }
df_iterator = pd.read_csv(this_file, 
                        skiprows=0, 
                        compression='gzip',
                        chunksize=1000000, 
                        usecols=cols_to_keep,
                        delimiter=";",
                        header=None,
                        names = column_names,
                      dtype=dtype,
                     parse_dates=parse_dates,
                     index_col=1,
                     )

遍历大块

new_df = pd.DataFrame()
count = 0
for df in df_iterator:
    chunk_df_15min = df.resample('15T').first()
    #chunk_df_30min = df.resample('30T').first()
    #chunk_df_hourly = df.resample('H').first()
    this_df = chunk_df_15min
    this_df = this_df.pipe(lambda x: x[x.METERID == 1])
    #print("chunk",i)
    new_df = pd.concat([new_df,chunk_df_15min])
    print("chunk",count, len(chunk_df_15min), 'rows added')
    #print("chunk",i, len(temp_df),'rows added')
    #break
    count += 1

在块循环中,我正在按时进行一些过滤和重新采样。为此,我将大小从20GB减小到数百MB HDF5,以进行进一步的脱机数据探索。


5

以我的经验,在读取大文件时,read_csv()使用参数初始化low_memory=False往往会有所帮助。我认为您没有提到要读取的文件类型,因此我不确定这对您的情况是否适用。


1

如果您的文件是CSV,则只需在Chunk by Chunk中进行即可。您可以简单地做:

import pandas as pd
for chunk in pd.read_csv(FileName, chunksize=ChunkSizeHere)
(Do your processing and training here)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.