• 【Pandas】优化读取文件内存占用过大的问题


    编写于2022.11.6

    1、内存占用计算

    做了个小实验,发现pandas读取文件时,内存占用是真的高:

    import sys
    
    # 源文件大小:12957KB,12.6MB
    file = r"G:\test.csv"
    
    df = pd.read_csv(file, encoding='utf-8')
    print(str(sys.getsizeof(df)/1024/1024)+' MB')
    # 180.20847511291504 MB
    
    # 作为对比
    with open(file, "r", encoding='utf-8') as f:
        data = f.readlines()
    # 注意list的实际内存占用计算方式
    print(str((sys.getsizeof(data[0])*len(data) + sys.getsizeof(data))/1024/1024)+' MB')
    # 41.51472091674805 MB
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其它查看DataFrame内存占用的方式:

    方式一:df.info(memory_usage=‘deep’),查看完整信息

    方式二:df.memory_usage(deep=True).sum(),只查看内存占用(推荐)

    方式三:sys.getsizeof(df)

    可以很清楚的看到这两种读取方式在内存占用上的巨大差距,同时我还观察到,将文件更改为xls格式时(38.1M),用pandas来读取占用的内存也几乎一样大:180.2985897064209 MB。

    原因应该是保存的内容相同,所以占用空间相同。

    同时,我还测试了同样的数据,用python的数据类型和pandas的DataFrame来表示时的内存占用大小:

    data = {'name': "张三", "age": 18, "sex": "男"}
    print(str(sys.getsizeof(data))+' Bytes')
    # 240 Bytes
    
    df = pd.DataFrame(data, index=[0])
    print(str(sys.getsizeof(df))+' Bytes')
    # 210 Bytes
    
    data = [1,2,3,4,5]
    print(str(sys.getsizeof(data))+' Bytes')
    # 104 Bytes
    
    df = pd.DataFrame(data)
    print(str(sys.getsizeof(df))+' Bytes')
    # 192 Bytes
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    观察发现:
    (1)在字典上,python占用内存更高(python字典占用内存是有名的高啊);
    (2)在列表上,pandas占用内存更高。

    言归正传,回到文件读取上,我有这么几个疑问:
    1.为什么Pandas 读取文件这么占内存?
    2.如何优化pandas的内存占用过大的问题?

    2、为什么Pandas 读取文件这么占内存?

    原因:
    pandas的读取数据时的处理机制默认会按照最大的规格去设置数据类型,如int64

    pandas数据格式总的说有三类:数字,类别,时间。
    数字类型数据的具体格式有:int,float64
    类别类型数据的格式是object,也就是字符串,不可相加减。
    时间类型数据的格式主要是datetime[ns]。

    img

    解决办法:
    人工替换每列的数据类型,查看数据类型转换

    3、如何优化pandas的内存占用过大的问题?

    (1)已读取的数据进行类型转换

    pandas读取时,遇到无法自动甄别的数据类型,会读取为****object类型,通过观察和手动更改object类型为最优的数据类型,将会大大减少内存占用。

    参考:https://blog.csdn.net/m0_47384542/article/details/109742814

    几种常见的转换:

    • pd.to_numeric():提取数字型
    • pd.to_datetime():提取日期格式数据
    • df.astype(‘category’):字符转换为维度数据
    • df.astype(‘bool’):转换为布尔数据
    # 替换某列
    # 方式一
    df['A'] = df['A'].astype("int", errors="raise")
    # 方式二
    df['A'] = pd.to_numeric(df['A'], errors="coerce")
    
    # 全部替换
    df = df.apply(pd.to_numeric, errors='coerce') # 把能转换成数字的都转换成数字,不
    # 21.022361755371094 MB, 新的内存占用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注意:astype()方法存在着一些局限性,只要待转换的数据中存在非数字以外的字符,在使用astype()方法进行类型转换时就会出现错误(**errors=‘raise’**会抛出错误,**errors=‘ignore’**当出现错误时会忽略该列的转换),而to_numeric()函数的可以解决了这个问题(**errors=‘coerce’**当出现错误时会替换为NAN,此时某列中有无法甄别的字段,该列都会变成float64)。

    更近一步的,可以将pandas的数据类型继续细化为numpy的数据类型

    dtypes范围下限(含)范围上限(含)
    unit80255
    unit16065535
    unit16065535
    int8-128127
    int16-3276832767
    int32-21474836482147483647
    int64–9,223,372,036,854,775,8089,223,372,036,854,775,807

    子类型转换函数:

    def reduce_mem_usage(df, verbose=True):
        numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
        # 计算当前占用的内存 
        start_mem = df.memory_usage(deep=True).sum() / 1024**2
        # 循环每一列
        for col in df.columns:
            # 获取每一列的数据类型
            col_type = df[col].dtypes
            # 如果数据类型属于上面定义的类型之
            if col_type in numerics:
                # 计算该列数据的最小值和最大值 用于我们指定相应的数据类型 
                c_min = df[col].min()
                c_max = df[col].max()
    
                # 如果 该列的数据类型属于 int 类型,然后进行判断
                if str(col_type)[:3] == 'int':
                    # 如果 该列最小的值 大于int8类型的最小值,并且最大值小于int8类型的最大值,则采用int8类型 
                    if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                        df[col] = df[col].astype(np.int8)
    
                    # 同上
                    elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                        df[col] = df[col].astype(np.int16)
    
                    # 同上
                    elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                        df[col] = df[col].astype(np.int32)
    
                    # 同上
                    elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                        df[col] = df[col].astype(np.int64)
    
                # 否则 则采用 float 的处理方法       
                else:
    
                    if c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                        df[col] = df[col].astype(np.float32)
    
                    else:
                        df[col] = df[col].astype(np.float64)
    
        end_mem = df.memory_usage(deep=True).sum() / 1024**2
    
        if verbose: print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))
        return df
    
    df = reduce_mem_usage(df, verbose=True)
    print(str(sys.getsizeof(df)/1024/1024)+' MB')
    # 18.629207611083984 MB
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    经过上述操作,pandas读取文件后占用的的内存从180M减少到18M,内存占用减少了90%!有效!

    参考:https://blog.csdn.net/weixin_44510615/article/details/115801785

    (2)在读取时指定数据类型(推荐)

    参考:https://blog.csdn.net/cainiao_python/article/details/119950400

    首先,我们可将每一列的最佳类型存储在一个词典中,其中键值表示列名称,首先移除日期列,因为日期列需要不同的处理方式。

    column_types = {'A''category','B''bool','C''object',.....}
    
    df = pd.read_csv('tests.csv',
                     dtype=column_types,
                     parse_dates=['date'],
                     infer_datetime_format=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    此时,一步到位了!然后可进一步的进行上述的子类型转换,进一步减少内存占用。

    4、总结

    pandas读取文件占用内存多主要是没有准确识别每一列的数据类型,采用了object进行存储,所有的优化办法都是围绕数据类型转换进行的:一是在读取时指定最佳的数据类型,二是在读取后进行数据转换;更进一步的的优化操作有:(1)将数值向下转换为更高效的类型;(2)将字符串列转换为categorical类型。

  • 相关阅读:
    IDEA工具之debug第三方jar包源码顺序错乱
    【C++笔试强训】第三十天
    星戈瑞Annexin V-FITC细胞凋亡检测试剂盒
    RK3588平台开发系列讲解(DisplayPort篇)DP相关模式说明
    React:实现一个定时器计数器,每秒自动+1
    FastestDet为arm而生
    VWware-安装AD域服务
    VScode开发ARM环境搭建
    iOS系统下轻松构建自动化数据收集流程
    华为云物联网平台对接之MQTT设备接入
  • 原文地址:https://blog.csdn.net/xiangxiang613/article/details/127714968