You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升Python代码内存效率?处理400万行数据时遭遇内存错误

嘿,针对你400万行数据集的内存占用问题,我来分享几个实打实的优化思路,不管是数据加载还是日期拆分环节都能帮你省不少内存~

一、核心日期拆分逻辑的优化:绕开字符串转换的冗余开销

你现在用strftime转成字符串再转类型的操作,其实是绕了大弯——pandas的dt属性本身就提供了直接提取日期组件的方法,不仅更快,还能大幅降低内存占用:

优化后的代码:

# 先定义星期映射(对应你的分类顺序)
weekday_map = {0: 'Mon', 1: 'Tue', 2: 'Wed', 3: 'Thu', 4: 'Fri', 5: 'Sat', 6: 'Sun'}

# 处理start_time相关列
trip_df['start_date'] = trip_df['start_time'].dt.floor('D')  # 直接截断到日期,比strftime再转datetime高效N倍
trip_df['start_year'] = trip_df['start_time'].dt.year.astype('int16')  # 年份范围小,用int16足够
trip_df['start_month'] = trip_df['start_time'].dt.month.astype('uint8')  # 月份1-12,uint8完全覆盖
trip_df['start_hour'] = trip_df['start_time'].dt.hour.astype('uint8')  # 小时0-23,同样用uint8
trip_df['start_weekday'] = trip_df['start_time'].dt.weekday.map(weekday_map).astype(shrtwkday)

# 处理end_time相关列,逻辑完全一致
trip_df['end_date'] = trip_df['end_time'].dt.floor('D')
trip_df['end_year'] = trip_df['end_time'].dt.year.astype('int16')
trip_df['end_month'] = trip_df['end_time'].dt.month.astype('uint8')
trip_df['end_hour'] = trip_df['end_time'].dt.hour.astype('uint8')
trip_df['end_weekday'] = trip_df['end_time'].dt.weekday.map(weekday_map).astype(shrtwkday)

为什么这样优化?

  1. 避免字符串转换:strftime会把日期转成字符串,再转int的过程不仅慢,还会产生临时字符串对象占用内存,直接用dt.year/dt.month提取数值是原生操作,效率高很多。
  2. 压缩数值类型:默认的int是int64(8字节),而年份用int16(2字节)、月份/小时用uint8(1字节)就完全足够,400万行的话,单列就能省6-7倍的内存。
  3. 直接生成日期列:dt.floor('D')直接把datetime截断到日期,不用先转字符串再转datetime,一步到位。

二、数据加载阶段的优化:从源头减少内存占用

你的加载代码已经做了usecols和dtypes的设置,这很好,但还有几个可以再抠细节的地方:

优化后的加载代码:

import os
import pandas as pd
from pandas.api.types import CategoricalDtype

shrtwkday = CategoricalDtype(categories=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'], ordered=True)
use_cols = ['duration_sec','start_time','end_time','start_station_id','start_station_name','start_station_latitude', 'start_station_longitude','end_station_id','end_station_name','end_station_latitude', 'end_station_longitude','bike_id','user_type']

# 进一步压缩dtypes,用最小够用的类型
dtypes = {
    'duration_sec': 'int32',  # 骑行时长一般不会超过2^31-1,用int32比int64省一半内存
    'start_station_id': pd.Int64Dtype(),
    'start_station_name': 'str',
    'start_station_latitude': 'float32',  # 经纬度用float32精度完全足够(误差在厘米级)
    'start_station_longitude': 'float32',
    'end_station_id': pd.Int64Dtype(),
    'end_station_name': 'str',
    'end_station_latitude': 'float32',
    'end_station_longitude': 'float32',
    'bike_id': 'int32',
    'user_type': CategoricalDtype()  # 把user_type直接设为类别型,比字符串省大量内存
}

parse_dates = ['start_time','end_time']

# 用生成器表达式代替列表append,减少中间列表的内存占用
trip_df = pd.concat(
    (pd.read_csv(
        os.path.join("./source_files/", file),
        compression='zip',
        delimiter=';',
        usecols=use_cols,
        dtype=dtypes,
        header=0,
        parse_dates=parse_dates,
        na_values=['', ' '],
        infer_datetime_format=True  # 自动推断日期格式,加速datetime解析
    ) for file in os.listdir("./source_files/")),
    axis=0,
    ignore_index=True
)

关键优化点:

  1. 压缩浮点类型:经纬度用float32代替float64,内存直接减半,而且完全不影响实际使用。
  2. 类别型列提前设置:user_type这类只有少数类别的列,直接设为CategoricalDtype,比字符串类型省70%以上的内存。
  3. 生成器代替列表:用(pd.read_csv(...) for file in files)生成器代替li.append(df),不需要把所有子DataFrame都存在列表里再concat,减少中间内存占用。
  4. 加速日期解析:加infer_datetime_format=True让pandas自动推断日期格式,比默认解析快很多。

三、额外的内存小技巧

你提到的用10%样本测试、复用原DataFrame的思路非常棒,再补充两个小技巧:

  • 定期清理内存:在处理完大步骤后,可以用del temp_obj然后调用gc.collect()手动释放内存(尤其是Jupyter环境,内存回收有时候不及时)。
  • 分块处理(可选):如果全量加载还是压力大,可以用pd.read_csv的chunksize参数分块加载,每处理一块就合并到结果中,避免一次性加载全量数据。

内容的提问来源于stack exchange,提问作者J. Brown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 11:42:35