ate*_*evm 5 sql-server python external-scripts
Python 整数类型具有无限精度,因此它能够容纳 MS SQL(64 位)的 bigint 值。当传递给外部脚本时,它仍然隐式映射到 float64 python 类型。
这可能会导致大整数的严重计算错误。
那么为什么它映射到 float64 呢?
我的猜测是:
R 是通过可扩展性架构在 Python 之前添加的,它具有固定精度的整数(32 位)。所以它不能容纳 bigint。所以也许这是一个兼容性问题。
确保精确计算的最佳做法是什么?
简单但可行的想法:将 bigint 作为字符串传递,然后将它们解析为 int。
我知道它在实践中引起问题的可能性很小,但还是很高兴知道。
我写了一个简单的例子来演示它怎么会是一个问题:
CREATE TABLE #test (
big_integer BIGINT
);
INSERT INTO #test
(big_integer)
VALUES
(36028797018963968),
(36028797018963968 + 1);
EXECUTE sp_execute_external_script
@language = N'Python',
@input_data_1 = N'SELECT big_integer FROM #test',
@script = N'
print(InputDataSet.dtypes)
OutputDataSet = InputDataSet
'
Run Code Online (Sandbox Code Playgroud)
在 SQL Server 2019 上执行此代码将为您提供以下结果:
| | (No column name) |
|---------------------|
|1| 36028797018963970 |
|2| 36028797018963970 |
Run Code Online (Sandbox Code Playgroud)
由于该print(InputDataSet.dtypes)
声明,我们可以看到以下消息:
...
STDOUT message(s) from external script:
big_integer float64
dtype: object
...
Run Code Online (Sandbox Code Playgroud)
所以我们得到了一个浮点舍入错误。对于足够大的整数,此错误的值大于 1,这就是此问题的根源。
教授浮点算术超出了这个问题的范围,但如果你不明白发生了什么,我会链接一些很好的材料:
如果您想对此进行试验,我还会分享一个小的 ipython 示例(这不能替代学习其背后的理论):
In [16]: import numpy as np
In [17]: a = 2**55
In [18]: a
Out[18]: 36028797018963968
In [19]: float(a) == float(a + 1)
Out[19]: True
In [20]: float(a)
Out[20]: 3.602879701896397e+16
In [21]: float(a + 1)
Out[21]: 3.602879701896397e+16
In [22]: np.nextafter(float(a), np.inf)
Out[22]: 3.6028797018963976e+16
Run Code Online (Sandbox Code Playgroud)
要运行我的示例 T-SQL,必须满足一些条件:
我写下我发现的最佳解决方案:
CREATE TABLE #test (
big_integer BIGINT
);
INSERT INTO #test
(big_integer)
VALUES
(36028797018963968),
(36028797018963968 + 1);
CREATE TABLE #out (
big_integer BIGINT
);
INSERT INTO #out
EXECUTE sp_execute_external_script
@language = N'Python',
@input_data_1 = N'SELECT CAST(big_integer AS VARCHAR(20)) AS big_integer FROM #test',
@script = N'
import numpy as np
print(InputDataSet)
InputDataSet["big_integer"] = InputDataSet["big_integer"].astype(np.int64)
InputDataSet["big_integer"] = InputDataSet["big_integer"] + 1
InputDataSet["big_integer"] = InputDataSet["big_integer"].astype(str)
OutputDataSet = InputDataSet
';
SELECT big_integer FROM #out;
Run Code Online (Sandbox Code Playgroud)
我做了我在问题中的假设:
big_integer
列转换为VARCHAR(20)
64 位有符号整数的字符串表示形式的最大长度:In [34]: len(str(-2**63))
Out[34]: 20
In [35]: len(str(2**63-1))
Out[35]: 19
Run Code Online (Sandbox Code Playgroud)
在外部脚本中将其转换回numpy.int64 类型。
做了一个简单的计算:增加列中的所有值
将其转换回仍在 python 中的字符串。这一步也是必要的,因为隐式类型转换是双向的。
将值插入表big_integer
的列中#out
。它也有一个BIGINT
类型,并且返回的字符串被隐式转换为BIGINT
您需要处理此问题的情况很少见。整数值必须大于 2^52,因此两个 float64 之间的距离将大于 1。
In [50]: def float_distance(x):
...: x_float = float(x)
...: x_next_float = np.nextafter(x_float, np.inf)
...: x_float_diff = x_next_float - x_float
...: return(x, x_float, x_next_float, x_float_diff)
In [51]: float_distance(2**52)
Out[51]: (4503599627370496, 4503599627370496.0, 4503599627370497.0, 1.0)
In [52]: float_distance(2**53)
Out[52]: (9007199254740992, 9007199254740992.0, 9007199254740994.0, 2.0)
Run Code Online (Sandbox Code Playgroud)
我想如果您存储物理学或生物信息学中高通量科学研究的结果,可能会发生这种情况。
归档时间: |
|
查看次数: |
163 次 |
最近记录: |