如何减少内存使用并加快代码速度

How to reduce the memory usage and speed up the code

我正在使用包含 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)

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

您可以使用类似 pypy (which is an alternative implementation of Python that is not compatible with many packages but faster and has better memory management). It did not use to support pandas (so you would need to iterate through each row, but pypy is very fast at doing this), but I believe if you use a version since this release 的东西,现在它可以与 pandas 一起使用。

我的建议是切换到 Dask or Spark

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

  1. chunksize参数:一次读取一个文件。为了 例如,在您的情况下,您可以使用等于一百万的块大小,您 会得到 90 个块,你可以对每个块进行操作 个别地。
  2. dtype 参数:使用此参数,您只需传递一个字典即可指定每一列的数据类型:{‘a’: np.float32, ‘b’: np.int32, ‘c’: ‘Int32’}
    Pandas could use 64位数据类型,而 32 位可能就足够了。使用此技巧,您可以节省 50% 的 space.

您的案例研究

试试这个代码:

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 唯一做的就是给你一个 reader 对象 iterates 1000000-行 DataFrames 而不是读取整个内容。使用 concat 你失去了使用 iterators 和将整个文件加载到内存中的所有优势,就像使用 csv 法则而不指定块大小一样。

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

复杂的改进:

  • 懒惰地遍历一个(可能非常大的)文件而不是将整个文件读入内存 - 指定一个 chunksizeread_csv 调用(指定一次迭代读取的行数)
  • 语句 df = df[:-1] 不适用于 iterator 方法并假设最后一行格式错误 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