numpy读取csv_pandas读取大文件特别慢?用一下这招吧

当用pandas处理GB级以上的大文件时,会出现两个问题,一个是读取数据很慢,动辄要好几分钟,另外就是出现内存不足导致程序运行失败。笔者这段时间查了一些解决此问题的方法,在这里分享给大家。

首先我们要知道的是,处理数据的效率跟机器的内存大小有直接关系,所以面对比较大的数据时,先考量下单机内存吃不吃得消,如果可以,再去考虑性能优化的问题,否则我建议还是别用pandas处理了,改用云服务器或者spark等适合处理大数据的工具吧。

这里我用一份数据来做讲解和测试,这份数据有1.65个G,3700万行/5列,算的上大文件了。

numpy读取csv_pandas读取大文件特别慢?用一下这招吧_第1张图片

先用pandas的read_csv直接读取该文件:

start = time.time()df = pd.read_csv(file_path+'jdata_action.csv')end = time.time()print('pandas read_csv cost:{}'.format(end-start))

67c32055aaad54b8bef2513aceecda28.png

可以看到直接读取需要将近一分钟,现在我们设置Dataframe.info()里的memory_usage参数来准确获得内存使用量。

df.info(memory_usage='deep')

numpy读取csv_pandas读取大文件特别慢?用一下这招吧_第2张图片

虽然文件是1.6个G,但实际使用内存却达到了3.7G,这是由于pandas的内存占用机制造成的,所以用pandas处理数据真的比较吃内存。

下面我从“提升读取效率”和“节省内存”两个方面讲一下解决办法。

1.节省内存

在底层,pandas会按照数据类型将列分组形成数据块(blocks),使用ObjectBlock类来表示包含字符串列的数据块,使用FloatBlock类来表示包含浮点型的数据块,用IntBlock类来表示包含整数型的数据块。对于数值型数据(整数型和浮点型)pandas会合并存储为一个numpy数组,由于numpy是基于C实现的,所以对其访问还是比较快的。

但是pandas将数据存储为dataframe时,整数型都会默认存成int64,浮点型会默认存成float64,这样会造成内存上的浪费。这里先普及一下数据类型字节的东西,数据类型具有多个子类型,它们可以使用较少的字节去表示不同数据,比如,float型就有float16、float32和float64这些子类型。这些类型名称的数字部分表明了这种类型使用了多少比特来表示数据,数字越小,占用的内存也越小,所以我们可以通过优化数据类型来节省内存。

另外对于字符型的数据,可以用类别型(category)来优化默认的object型,category类型在底层使用整型数值来表示该列的值,而不是用原值。pandas用一个字典来构建这些整型数据到原数据的映射关系。当一列只包含有限种值时,这种设计是很不错的。当我们把一列转换成category类型时,pandas会用一种最省空间的int子类型去表示这一列中所有的唯一值。

2.提升读取效率

对于加快读取数据的速度,pandas本身有内置的解决方案,例如HDF5和feather format,HDF5是一种存储表格数据的高性能存储方式,pandas的HDFStore函数允许将dataframe存储在HDF5文件中,以便进行读取访问。feather format是一种内置的压缩格式,再读取数据时获得更快的加速。

下面通过代码介绍下两种文件格式的使用方法:

1)HDF5:

# 创建 HDF5文件并保存,可以设置成本地路径data_store = pd.HDFStore('processed_data.h5')# 将dataframe存储在HDF5文件中,并对dataframe命名data_store.put('preprocessed_df', df, format='table')# 关闭HDF5文件,完成存储,以便后续导入data_store.close()# 读取 HDF5文件中的dataframeprocess_df = pd.read_hdf('processed_data.h5','preprocessed_df')

2) feather format

# 直接用to_feather将dataframe转为feather文件并保存df.to_feather('processed_data.feather')# pandas直接读取feather文件process_df = pd.read_feather('processed_data.feather')

完整的解决方案:

代码逻辑:用pandas读取本地文件,先优化数据类型,再选择存储为HDF5或者feather格式

def reduce_data(file_path,use_HDF5=False,use_feather=False):    # 直接用pandas读取文件,读取前衡量下内存够不够用    df = pd.read_csv(file_path)    # 计算源文件占用内存大小    start_mem = df.memory_usage().sum() / 1024 ** 2    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))        for col in df.columns:        col_type = df[col].dtype        # 处理数值型特征,用子类型优化数值型列        if col_type != object:            c_min = df[col].min()            c_max = df[col].max()            # 对整数型进行处理            if str(col_type)[:3] == 'int':            # 用numpy.iinfo类来确认每一个整型子类型的最小和最大值,更改子类型                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)            else:            # 对浮点型进行处理,用numpy.finfo类来确认每一个浮点子类型的最小和最大值                if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:                    df[col] = df[col].astype(np.float16)                elif 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)        else:            # 将object类型改为category类型            df[col] = df[col].astype('category')    # 计算优化子类型后的内存占用    end_mem = df.memory_usage().sum() / 1024 ** 2    print('Memory usage after optimization is: {:.2f} MB'.format(end_mem))    print('Decreased by {:.1f}%'.format(100 * (start_mem - end_mem) / start_mem))        if use_HDF5 == True and use_feather == False:        start = time.time()        # 新建 HDF5文件,将df存储至文件中,计算转换时间        data_store = pd.HDFStore('processed_data.h5')        data_store.put('preprocessed_df', df, format='table')        data_store.close()        print('hd5 trans cost :{}'.format(time.time()-start))        start = time.time()        # 读取 HDF5文件中的df,计算读取时间        process_df = pd.read_hdf('processed_data.h5','preprocessed_df')        print('hd5 read cost:{}'.format(time.time()-start))    elif use_HDF5 == False and use_feather == True:        start = time.time()        # 转换df为feather格式,计算转换时间        df.to_feather('processed_data.feather')        print('feather trans cost :{}'.format(time.time()-start))        # 读取feather文件,计算读取时间        process_df = pd.read_feather('processed_data.feather')        print('feather read cost:{}'.format(time.time()-start))    else:        print('Please choose the only way to compress:True or False')        return process_df

效果展示:

1) 转换为HDF5格式

numpy读取csv_pandas读取大文件特别慢?用一下这招吧_第3张图片

与直接用pandas读取相比,内存占用从3.7个G减少到1.7个G,速度从58s 提升至47s 。

2) 转换为feather格式

numpy读取csv_pandas读取大文件特别慢?用一下这招吧_第4张图片

转换成feather格式的效果更好,内存占用从3.7个G减少到1.4个G,速度从58s 提升至29s 。

这次分享就到这了,大家如果有更好的方法可以私信我的公众号,一起交流下。

numpy读取csv_pandas读取大文件特别慢?用一下这招吧_第5张图片

你可能感兴趣的:(numpy读取csv)