之前看到过一篇文章,讲的就是如何在使用pandas的时候降低内存的开销。笔者亲自尝试了一下,发现确实不错,但是也会有很多问题,譬如,一些第三方包(例如statsmodels、alphalens等)的运算要求数据就是float64类型的,这使得我们很尴尬呀。
不管怎么样,如果我们自己处理数据的时候,或者第三方包支持的时候,这一系列方法还是很有用的。
1.查看dataframe占用空间
例如,我们读取之前的所有行情和因子数据:
data = pd.read_csv('total_data.csv', index_col=0) data.info(memory_usage='deep')首先,我们读取total_data.csv这个数据,并制定第一列是index,然后,我们获取一下这个dataframe这个对象在内存中的情况。
<class 'pandas.core.frame.DataFrame'>
Int64Index: 91427 entries, 6095 to 91426
Data columns (total 13 columns):
date 91427 non-null object
open 91427 non-null float64
close 91427 non-null float64
high 91427 non-null float64
low 91427 non-null float64
volume 91427 non-null float64
openinterest 91427 non-null int64
raw_factor 91427 non-null float64
sec_id 91427 non-null object
win_secore 91427 non-null float64
z-score 91427 non-null float64
group 91427 non-null int64
neuted_score 78408 non-null float64
dtypes: float64(9), int64(2), object(2)
memory usage: 14.4 MB
我们可以看到,这个dataframe每一列的数据类型,以及,一共占用的内存空间:14.4M。看起来不大的样子。别忘了,我们这里是令标的池为sz50,同时只有一个因子。某种意义上,完全没有意义,笔者只是为了展示多因子模型的整个流程和框架罢了,对于50个股票的标的池,做多因子策略,几乎是没有任何意义的。我们想一想,如果范围是全市场,3000个股票,那么大概就是864M,而且这仅仅是一个因子。如果我们需要把100个因子的内容load到内存中,虽然有时候并不需要这样,那么就是8G,好吧,内存就不够了。
2.时间的处理
大家把数据本地化为csv,然后读取的时候,尽可能养成一个习惯,就是把时间那一列变成timestamp格式。这样有两个好处,一个是存储空间会减小,同时检索的速度会快,而且会减少很多不必要的错误。
csv读取进来的时候,默认时间是str格式,这一格式在pandas中被存储为object格式,还是很占内存的。
data['date'] = pd.to_datetime(data['date'])然后我们在info一下,就是下面这样了:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 91427 entries, 6095 to 91426
Data columns (total 13 columns):
date 91427 non-null datetime64[ns]
open 91427 non-null float64
close 91427 non-null float64
high 91427 non-null float64
low 91427 non-null float64
volume 91427 non-null float64
openinterest 91427 non-null int64
raw_factor 91427 non-null float64
sec_id 91427 non-null object
win_secore 91427 non-null float64
z-score 91427 non-null float64
group 91427 non-null int64
neuted_score 78408 non-null float64
dtypes: datetime64[ns](1), float64(9), int64(2), object(1)
memory usage: 12.0 MB
我们看到,内存占用少了2M+,这是个不错的现象。
3.修改数字
其实,pandas在读取csv的时候,可以定义读取每一列的类型的,我们看到上面默认是float64,对于整数,默认是int64,知道一点计算机知识的都明白,很多时候我们是不需要这么float64这么大的存储范围的。
假设,我们一开始就定义好浮点数列的数据类型为float16
data = pd.read_csv('total_data.csv', index_col=0, dtype={'open': 'float16', 'close': 'float16', 'high': 'float16', 'low': 'float16','volume': 'float32', 'openinterest': 'int','raw_factor': 'float16','win_secore': 'float16', 'neuted_score': 'float16','z-score': 'float16' })然后info一下,
<class 'pandas.core.frame.DataFrame'>
Int64Index: 91427 entries, 6095 to 91426
Data columns (total 13 columns):
date 91427 non-null datetime64[ns]
open 91427 non-null float16
close 91427 non-null float16
high 91427 non-null float16
low 91427 non-null float16
volume 91427 non-null float32
openinterest 91427 non-null int32
raw_factor 91427 non-null float16
sec_id 91427 non-null object
win_secore 91427 non-null float16
z-score 91427 non-null float16
group 91427 non-null int64
neuted_score 78408 non-null float16
dtypes: datetime64[ns](1), float16(8), float32(1), int32(1), int64(1), object(1)
memory usage: 7.1 MB
现在,内存占用只有7.1M了,连原来的一半都不到。
4.catrgory类
然后是最后一个大杀器,就是当某一列中,有很多重复元素的时候,其实必然是存在冗余的,比如,我们的dataframe中股票代码,sec_id和行业类别,group这两列,肯定有很多重复的,那么,我们就可以把这两列设置为category类,这一类本质上就是一个字典的映射。
data['group'] = data['group'].astype('category') data['sec_id'] = data['sec_id'].astype('category')然后,我们看一下效果:
<class'pandas.core.frame.DataFrame'>
Int64Index: 91427entries, 6095 to 91426
Data columns(total 13 columns):
date 91427 non-null datetime64[ns]
open 91427 non-null float16
close 91427 non-null float16
high 91427 non-null float16
low 91427 non-null float16
volume 91427 non-null float32
openinterest 91427 non-null int32
raw_factor 91427 non-null float16
sec_id 91427 non-null category
win_secore 91427 non-null float16
z-score 91427 non-null float16
group 91427 non-null category
neuted_score 78408 non-null float16
dtypes:category(2), datetime64[ns](1), float16(8), float32(1), int32(1)
memory usage: 3.7MB
居然只占用了3.7M!连原来的个位数都不到。