【问题标题】:How to reduce the memory usage and speed up the code如何减少内存使用并加快代码速度
【发布时间】:2019-10-08 08:02:06
【问题描述】:

我正在使用包含 5 列和超过 9000 万行的庞大数据集。该代码适用于部分数据,但是当涉及到整个数据时,我得到了内存错误。我阅读了有关生成器的信息,但对我来说似乎非常复杂。我可以根据此代码得到解释吗?

df = pd.read_csv('D:.../test.csv', names=["id_easy","ordinal", "timestamp", "latitude", "longitude"])

df = df[:-1]
df.loc[:,'timestamp'] = pd.to_datetime(df.loc[:,'timestamp'])
pd.set_option('float_format', '{:f}'.format)
df['epoch'] = df.loc[:, 'timestamp'].astype('int64')//1e9
df['day_of_week'] = pd.to_datetime(df['epoch'], unit="s").dt.weekday_name
del df['timestamp']

for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']:
    day_df = df.loc[df['day_of_week'] == day]
    day_df.to_csv(f'{day}.csv', index=False,)

最后一次for loop操作出现错误

样本数据:

d4ace40905729245a5a0bc3fb748d2b3    1   2016-06-01T08:18:46.000Z    22.9484 56.7728
d4ace40905729245a5a0bc3fb748d2b3    2   2016-06-01T08:28:05.000Z    22.9503 56.7748

更新

我这样做了:

chunk_list = []  

for chunk in df_chunk:  
    chunk_list.append(chunk)
df_concat = pd.concat(chunk_list)

我现在不知道如何进行?如何应用其余代码?

【问题讨论】:

  • 分块读取文件
  • 你能分享你的数据框的可测试片段吗?
  • @RomanPerekhrest 示例数据已添加
  • 为什么不分别解析每一行(并将其写入相应的日期)?
  • @Mamed,阻碍的是跳过最后一行df[:-1]。您是否能够找出文件中的实际行数?你用的是什么操作系统?

标签: python pandas out-of-memory


【解决方案1】:

我的建议是切换到DaskSpark

如果您想继续使用 pandas,请尝试以下提示以读取 CSV 文件,并使用pandas.read_csv

  1. chunksize 参数:允许您一次读取一个文件。为了 例如,在您的情况下,您可以使用等于一百万的块大小,您 将获得 90 个块,您可以对每个块进行操作 个人。
  2. dtype 参数:使用此参数,您可以简单地通过传递这样的字典来指定每列的数据类型:{‘a’: np.float32, ‘b’: np.int32, ‘c’: ‘Int32’}
    Pandas 可以使用 64 位数据类型,而 32 位可能就足够了。使用这个技巧,您可以节省 50% 的空间。

你的案例研究

试试这个代码:

df_chunks = pd.read_csv('test.csv', chunksize=1000000, iterator=True, 
                         parse_dates=['timestamp'], error_bad_lines=False,
                         dtype={"ordinal":'int32', "latitude": 'float32', "longitude":'float32'})
for chunk in df_chunks:
    # chunk = chunk.apply(...) # process the single chunk 
    for day in ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']:
        day_df = chunk.loc[chunk['day_of_week'] == day]
        day_df.to_csv(f'{day}.csv', mode='a', index=0, header=False)

这样,您一次处理一大块数据,而永远不会同时处理所有数据。 mode='a' 告诉 pandas 追加。

注意1: 您在这里不需要pandas.concatiteratorchunksize=1000000 所做的唯一事情就是为您提供一个 iterates 1000000 行 DataFrames 的读取器对象,而不是读取整个内容。使用 concat 您将失去使用 iterators 的所有优势,并且将整个文件加载到内存中,就像使用 csv 规则而不指定块大小一样。

注意2:如果'MemoryError'错误仍然存​​在,请尝试更小的块大小。

【讨论】:

  • 你能再检查一下我的问题吗?我已经申请了chunksize并得到了结果,但现在不知道该怎么办
【解决方案2】:

复杂的改进:

  • 懒惰地遍历(可能非常大的)文件,而不是将整个文件读入内存 - 指定 chunksizeread_csv 调用(指定在一次迭代中读取的行数)
  • 语句df = df[:-1] 不适用于迭代器 方法,并假设最后一行格式错误99695386 [space] NaN NaN NaN NaN - 我们可以通过指定选项error_bad_lines=False 处理它并跳过
  • 语句df.loc[:,'timestamp'] = pd.to_datetime(df.loc[:,'timestamp']) 也可以通过使用parse_dates=['timestamp'] 作为pd.read_csv 调用的选项来消除
  • 我们将应用mode='a'附加到现有的目标csv文件(附加到文件)

在实践中:

n_rows = 10 * 6  # adjust empirically
reader = pd.read_csv('test.csv', names=["id_easy","ordinal", "timestamp", "latitude", "longitude"], 
                     parse_dates=['timestamp'], chunksize=n_rows, error_bad_lines=False)                               
day_names = ('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')

for df in reader: 
    if not df.empty: 
        df['epoch'] = df.loc[:, 'timestamp'].astype('int64') // 1e9 
        df['day_of_week'] = pd.to_datetime(df['epoch'], unit="s").dt.weekday_name 
        del df['timestamp']
        for day in day_names: 
            day_df = df.loc[df['day_of_week'] == day] 
            if not day_df.empty:
                day_df.to_csv(f'{day}.csv', index=False, header=False, mode='a') 

https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#io-chunking

【讨论】:

  • 疯了,太棒了!
  • 1 个问题。由于我最后一个 raw 有 NaN,在使用块之前我添加了 [:-1] (删除最后一行)。但现在我得到了错误。有什么想法吗?
  • @Mamed,实际上你不能用[:-1] 分割reader 对象——阅读器是一个迭代器。正如我在第二篇论文中所解释的那样,您的最后一行应使用error_bad_lines=False 处理
  • 好的。然后这是我的错误:OverflowError: int too big to convertOutOfBoundsDatetime: cannot convert input -9223372037.0 with the unit 's'
  • 对于OutOfBoundsDatetime - df['day_of_week'] = pd.to_datetime(df['epoch'], unit="s").dt.weekday_name 对于OverflowError - pandas/_libs/tslib.pyx in pandas._libs.tslib.array_with_unit_to_datetime() + pandas/_libs/tslibs/timedeltas.pyx in pandas._libs.tslibs.timedeltas.cast_from_unit()
【解决方案3】:

您可以使用 pypy 之类的东西(这是 Python 的替代实现,与许多包不兼容,但速度更快,内存管理更好)。它不用于支持 pandas(因此您需要遍历每一行,但 pypy 执行此操作非常快),但我相信如果您 use a version since this release 现在它可以与 pandas 一起使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-27
    • 2012-02-06
    • 2018-03-30
    • 2014-10-07
    • 1970-01-01
    • 2021-07-30
    相关资源
    最近更新 更多