Python 基于列值从数据帧插值
假设我有以下问题:Python 基于列值从数据帧插值,python,pandas,numpy,eval,interpolation,Python,Pandas,Numpy,Eval,Interpolation,假设我有以下问题: import pandas as pd import numpy as np xp = [0.0, 0.5, 1.0] np.random.seed(100) df = pd.DataFrame(np.random.rand(10, 4), columns=['x0', 'y1', 'y2', 'y3']) df x0 y1 y2 y3 0 0.5434 0.2784 0.4245 0.8448 1 0.0047 0.1216 0.6
import pandas as pd
import numpy as np
xp = [0.0, 0.5, 1.0]
np.random.seed(100)
df = pd.DataFrame(np.random.rand(10, 4), columns=['x0', 'y1', 'y2', 'y3'])
df
x0 y1 y2 y3
0 0.5434 0.2784 0.4245 0.8448
1 0.0047 0.1216 0.6707 0.8259
2 0.1367 0.5751 0.8913 0.2092
3 0.1853 0.1084 0.2197 0.9786
4 0.8117 0.1719 0.8162 0.2741
5 0.4317 0.9400 0.8176 0.3361
6 0.1754 0.3728 0.0057 0.2524
7 0.7957 0.0153 0.5988 0.6038
8 0.1051 0.3819 0.0365 0.8904
9 0.9809 0.0599 0.8905 0.5769
我想插入一个名为interp
的列。要插值的x坐标值包含在列x0
中,数据点的x坐标将是xp
,数据点的y坐标将包含在y1
、y2
和y3
中
到目前为止,我得出了以下结论:
df['interp'] = df.apply(lambda x: np.interp(x.x0, xp, [x.y1, x.y2, x.y3]), axis=1)
df
x0 y1 y2 y3 interp
0 0.5434 0.2784 0.4245 0.8448 0.4610
1 0.0047 0.1216 0.6707 0.8259 0.1268
2 0.1367 0.5751 0.8913 0.2092 0.6616
3 0.1853 0.1084 0.2197 0.9786 0.1496
4 0.8117 0.1719 0.8162 0.2741 0.4783
5 0.4317 0.9400 0.8176 0.3361 0.8344
6 0.1754 0.3728 0.0057 0.2524 0.2440
7 0.7957 0.0153 0.5988 0.6038 0.6018
8 0.1051 0.3819 0.0365 0.8904 0.3093
9 0.9809 0.0599 0.8905 0.5769 0.5889
但是,将在其上执行此计算的数据帧包含超过一百万行,因此我想推荐一种比apply
更快的方法。有什么想法吗
np.interp
似乎只使用一维数组,这就是我选择apply
的原因,一个让它更快的好方法是:
TL;DR
Seconds per number of rows
Rows: 100 1000 10000 1E5 1E6 1E7
apply: 0.076 0.734 7.812
eval: 0.056 0.053 0.058 0.087 0.338 2.887
从这些计时可以看出,eval()
有很多设置开销,最多10000行基本上需要相同的时间。但是,它比apply快两个数量级,因此它肯定值得为大型数据集增加开销
这是什么?
从()
使用各种后端将Python表达式作为字符串进行求值
支持以下算术运算:+、-、*、/、**、%、/(仅限python引擎)以及以下布尔运算:|(或)、&(和)和~(不)。此外,“pandas”解析器允许使用and、or和not,其语义与相应的位运算符不同。Series和DataFrame对象受支持,其行为与纯Python计算相同
针对此问题执行的技巧:
import pandas as pd
import numpy as np
xp = [0.0, 0.5, 1.0]
np.random.seed(100)
def method1():
df['interp'] = df.apply(
lambda x: np.interp(x.x0, xp, [x.y1, x.y2, x.y3]), axis=1)
def method2():
exp = '((y%d-y%d) / %s * (x0-%s) + y%d) * (x0 %s 0.5)'
exp_1 = exp % (2, 1, xp[1] - xp[0], xp[0], 1, '<')
exp_2 = exp % (3, 2, xp[2] - xp[1], xp[1], 2, '>=')
df['interp2'] = df.eval(exp_1 + '+' + exp_2)
from timeit import timeit
def runit(stmt):
print("%s: %.3f" % (
stmt, timeit(stmt + '()', number=10,
setup='from __main__ import ' + stmt)))
def runit_size(size):
global df
df = pd.DataFrame(
np.random.rand(size, 4), columns=['x0', 'y1', 'y2', 'y3'])
print('Rows: %d' % size)
if size <= 10000:
runit('method1')
runit('method2')
for i in (100, 1000, 10000, 100000, 1000000, 10000000):
runit_size(i)
print(df.head())
x0 y1 y2 y3 interp interp2
0 0.060670 0.949837 0.608659 0.672003 0.908439 0.908439
1 0.462774 0.704273 0.181067 0.647582 0.220021 0.220021
2 0.568109 0.954138 0.796690 0.585310 0.767897 0.767897
3 0.455355 0.738452 0.812236 0.927291 0.805648 0.805648
4 0.826376 0.029957 0.772803 0.521777 0.608946 0.608946
下面的代码利用了这样一个事实,即插值始终仅在两个段中。它实际计算两个段的插值,然后通过乘以布尔测试(即0,1)丢弃未使用的段
传递给eval的实际表达式是:
((y2-y1) / 0.5 * (x0-0.0) + y1) * (x0 < 0.5)+((y3-y2) / 0.5 * (x0-0.5) + y2) * (x0 >= 0.5)
@StevenG,
eval()
非常快,如果你能把你的计算压缩到它的模型中的话。:-)谢谢Stephen,我会尽力使我的问题适应你的答案。问题是我试图提供一个简化的问题,而我的插值在最后需要更多的列。我将试着看看没有熊猫我能做些什么(仅限numpy)。不过,谢谢你的回答,这可能是使用熊猫的最佳洞察!因为这比apply快100倍,所以您可以很容易地将此解决方案扩展到5或10个段,而且速度仍然会快很多。此外,如果您可以先验地将一组特定的行限制在一个可能的范围内,那么这仍然会快很多。祝你好运。我刚刚在一个有更多专栏的dafaframe上测试了你的答案,效果非常好!
x0 y1 y2 y3 interp interp2
0 0.060670 0.949837 0.608659 0.672003 0.908439 0.908439
1 0.462774 0.704273 0.181067 0.647582 0.220021 0.220021
2 0.568109 0.954138 0.796690 0.585310 0.767897 0.767897
3 0.455355 0.738452 0.812236 0.927291 0.805648 0.805648
4 0.826376 0.029957 0.772803 0.521777 0.608946 0.608946