pandas:计算时内存不足怎么办(eval和query)
前言
前面几篇文章笔者介绍了如何使pandas计算得到大幅提升,其中包括pandas快速处理字符串方法和使用map、apply和applymap函数批量处理数据,并且比普通循环操作处理数据快500多倍,本文将谈及为pandas计算节省内存和加速,希望能帮助大家。
提示:为方便快捷地解决问题,本文仅介绍函数的主要用法,并非全面介绍
一、eval()和query()的由来
numpy与pandas都是Python建立强大生态的基础库,他们的厉害之处是通过内置的方法将基本操作转换成C语言,numpy里面是向量化和广播运算,pandas则是分组型运算。便捷快速的同时带来的问题是常需要建立一个临时中间对象,这样做会占用大量的计算内存和时间,pandas的eval()和query()就是为解决此问题而生的,其中eval()函数还分pandas.eval()和pandas.DataFrame.eval():pandas.eval()可用于多个DataFrame之间的计算,pandas.DataFrame.eval()仅用于DataFrame中列之间的计算。
二、eval()的介绍
1.pandas.DataFrame.eval
DataFrame.eval(expr, inplace=False, **kwargs)
参数:
expr:str;要计算的表达式字符串。
inplace:bool, 默认 False;设置是覆盖原DataFrame还是输出新的DataFrame。
**kwargs:可输入pandas.eval()的变量,后面详细说明。
返回ndarray, scalar或者pandas数据类型
下面是例子:
先创建一个DataFrame
import pandas as pd
import numpy as np
import time
data = pd.DataFrame(np.random.rand(10000,2),columns=['A','B'])
data.head()
A B
0 0.161016 0.166286
1 0.859612 0.221388
2 0.814103 0.984277
3 0.776477 0.302750
4 0.996164 0.760127
使用普通方式进行计算A、B列之和并计算耗时
start = time.time()
result = data['A'] + data['B']
end = time.time()
print(str(end-start))
0.9478774070739746
使用DataFrame.eval()方法计算并计算耗时
start = time.time()
result = data.eval('A+B')
end = time.time()
print(str(end-start))
0.1408524513244629
可以看出比直接列相加快了6.7倍。
提示:计算机环境不同,其计算速度的差距也不同,笔者的实验仅作为参考。
DataFrame.eval()还可以将计算结果放到新增到一列中:
data.eval('C = A+B',inplace=True)
data.head()
A B C
0 0.161016 0.166286 0.327302
1 0.859612 0.221388 1.081000
2 0.814103 0.984277 1.798380
3 0.776477 0.302750 1.079227
4 0.996164 0.760127 1.756291
我们可以使用@方法将局部变量使用到计算之中
test_num = 100
result = data.eval('A + B + @test_num')
result.head()
0 100.327302
1 101.081000
2 101.798380
3 101.079227
4 101.756291
dtype: float64
2.pandas.eval
pandas.eval可以让多个DataFrame或Series进行计算。
pandas.eval(expr, parser=‘pandas’, engine=None, truediv=, local_dict=None, global_dict=None, resolvers=(), level=0, target=None, inplace=False)
主要参数:
提示:参数过多,仅介绍我认为主要且常用的几个参数,如需全面了解请参考官方介绍
expr:str;要计算的表达式,该字符串只能包含Python表达式。
parser:{‘pandas’, ‘python’}, 默认‘pandas’;选择用pandas语法还是Python语法解析表达式,这里让它默认就行了。
target:object, optional, 默认None;选择要处理的目标。
inplace:bool, 默认 False;选择是否替换target所指定的对象。
下面是例子:
对于一个DataFrame
import pandas as pd
import numpy as np
import time
data = pd.DataFrame(np.random.rand(10000,2),columns=['A','B'])
result = pd.eval('data.A+data.B')
result.head()
0 1.265516
1 1.191837
2 1.443026
3 0.595921
4 1.442945
dtype: float64
对于不同的DataFrame(参考官方案例)
nrows, ncols = 20000, 100
df1, df2, df3, df4 = [pd.DataFrame(np.random.randn(nrows, ncols)) for _ in range(4)]
add_pd = pd.eval('df1 + df2 + df3 + df4')
add_pd.head()
0 1 2 ... 97 98 99
0 -0.484481 -1.593759 -0.540393 ... -0.688837 -1.212668 2.375383
1 2.846088 -0.053718 2.861517 ... -0.514399 -0.520711 1.378200
2 1.034824 -1.387115 -1.056278 ... 0.017538 -0.339709 1.773171
3 -4.820074 3.141094 -0.829376 ... -0.494888 -1.280396 -0.803026
4 0.950161 0.253045 -0.977115 ... 3.878627 3.171613 -0.349569
[5 rows x 100 columns]
三、query()的介绍
pandas.DataFrame.query()的作用是让DataFrame的查询节省内存,实际使用来看速度并没有使用普通方法快。
DataFrame.query(expr, inplace=False, **kwargs)
参数:
expr:str;要计算的表达式。
inplace:bool;选择是修改数据还是返回修改后的副本数据。
下面是例子和对比情况
import pandas as pd
import numpy as np
import time
data = pd.DataFrame(np.random.rand(10000,2),columns=['A','B'])
data.head()
A B
0 0.941237 0.568980
1 0.191276 0.083936
2 0.612065 0.662790
3 0.985512 0.446884
4 0.033947 0.610931
筛选出A列和B列都小于0.5的数据
result = data.query('A < 0.5 and B < 0.5')
result.head()
A B
1 0.191276 0.083936
9 0.395155 0.495593
19 0.284068 0.399290
24 0.334158 0.301924
25 0.297931 0.326648
同样也可以用@方法调用局部变量
num = 0.5
result = data.query('A < @num and B < @num')
result.head()
A B
1 0.191276 0.083936
9 0.395155 0.495593
19 0.284068 0.399290
24 0.334158 0.301924
25 0.297931 0.326648
总结
当我们使用pandas对大量数据进行计算时难免会遇到内存不够和时间消耗太久的问题,使用eval和query方法可以很方便的解决此类问题,但是当我们的数据量不大时使用此方法并没有任何效果。后续笔者还会继续分享一些关于数据科学的方法和技巧,请大家多多关注∩__∩。