arrow系列17---计算函数(Compute Functions)
作者:yunjinqi   类别:    日期:2023-10-16 09:28:31    阅读:134 次   消耗积分:0 分    

计算函数


通用计算 API


API 函数和函数注册表 


函数表示对可能具有不同类型的输入进行计算操作。在内部,一个函数由一个或多个“核”实现,具体取决于具体的输入类型(例如,一个将两个输入的值相加的函数,根据输入是否是整数或浮点数,可以有不同的核)。

函数存储在全局的 FunctionRegistry 中,可以通过名称查找。


输入形状 


计算输入表示为通用的 Datum 类,它是几种数据形状(如 Scalar、Array 和 ChunkedArray)的标记联合体。许多计算函数支持数组(分块或非分块)和标量输入,但有些会要求特定的输入类型。例如,array_sort_indices 需要其第一个且唯一的输入是数组,而通用的 sort_indices 函数则接受数组、分块数组、记录批处理或表格作为输入。


调用函数 

可以通过名称调用计算函数,使用 arrow::compute::CallFunction():

std::shared_ptr<arrow::Array> numbers_array = ...;
std::shared_ptr<arrow::Scalar> increment = ...;
arrow::Datum incremented_datum;

ARROW_ASSIGN_OR_RAISE(incremented_datum,
                      arrow::compute::CallFunction("add", {numbers_array, increment}));
std::shared_ptr<Array> incremented_array = std::move(incremented_datum).make_array();

(请注意,此示例使用从 std::shared_ptr<Array> 到 Datum 的隐式转换)


许多计算函数也可以直接使用具体的 API,例如 arrow::compute::Add():

std::shared_ptr<arrow::Array> numbers_array = ...;
std::shared_ptr<arrow::Scalar> increment = ...;
arrow::Datum incremented_datum;

ARROW_ASSIGN_OR_RAISE(incremented_datum,
                      arrow::compute::Add(numbers_array, increment));
std::shared_ptr<Array> incremented_array = std::move(incremented_datum).make_array();


一些函数接受或要求确定函数的精确语义的选项结构:

ScalarAggregateOptions scalar_aggregate_options;
scalar_aggregate_options.skip_nulls = false;

std::shared_ptr<arrow::Array> array = ...;
arrow::Datum min_max;

ARROW_ASSIGN_OR_RAISE(min_max,
                      arrow::compute::CallFunction("min_max", {array},
                                                   &scalar_aggregate_options));

// 解包结构标量结果(一个两字段的 {"min", "max"} 结构标量)
std::shared_ptr<arrow::Scalar> min_value, max_value;
min_value = min_max.scalar_as<arrow::StructScalar>().value[0];
max_value = min_max.scalar_as<arrow::StructScalar>().value[1];

然而,分组聚合不能通过 CallFunction 调用。另请参阅 计算 API 参考


隐式转换 

如果核不精确匹配参数类型,函数可能需要在执行之前将其参数转换。例如,核不直接支持字典编码数组的比较,但可以进行隐式转换,允许与解码数组进行比较。

每个函数可以根据需要定义隐式转换行为。例如,比较和算术核要求参数类型完全相同,并通过将其参数提升为可以容纳来自任一输入的任何值的数字类型来支持不同的数值类型。


常见的数字类型 

一组输入数字类型的常见数字类型是可以容纳任何输入的最小数字类型。如果输入中包含浮点类型,那么常见数字类型将是输入中的最宽浮点类型。否则,常见数字类型是整数,如果输入中有任何带符号的输入,则为带符号整数。例如:

截图 2023-10-16 08-58-21.png

特别需要注意,如果将 uint64 列与 int16 列进行比较,如果其中一个 uint64 值不能表示为公共类型 int64(例如,2 ** 63),则可能会引发错误。


可用函数 

类型类别 

为了避免详细列出支持的类型,下面的表格使用了一些通用的类型类别:

  • “数值”:整数类型(Int8、等)和浮点类型(Float32、Float64、有时也是 Float16)。一些函数还接受 Decimal128 和 Decimal256 输入。

  • “时间”:日期类型(Date32、Date64)、时间类型(Time32、Time64)、时间戳(Timestamp)、持续时间(Duration)、时间区间(Interval)。

  • “二进制类似”:Binary、LargeBinary,有时也是 FixedSizeBinary。

  • “字符串类似”:String、LargeString。

  • “列表类似”:List、LargeList,有时也是 FixedSizeList。

  • “嵌套”:列表类似(包括 FixedSizeList)、Struct、Union,以及 Map 等相关类型。

如果不确定函数是否支持具体的输入类型,建议尝试。不支持的输入类型会返回 TypeError 状态。


聚合 

标量聚合操作对(分块的)数组或标量值进行操作,并将输入减少到单个输出值。

截图 2023-10-16 09-00-41.png

截图 2023-10-16 09-01-12.png

  • (1) 如果考虑了空值,通过将 ScalarAggregateOptions 参数 skip_nulls = false 设置为 false,那么将应用 Kleene 逻辑逻辑。不考虑 min_count 选项。

  • (2) CountMode 控制计算时是否仅计算非空值(默认值),仅计算空值,还是计算所有值。

  • (3) 如果未找到值,则返回 -1。无论输入中是否包含空值,空值的索引始终为 -1。

  • (4) 对于十进制输入,生成的十进制值将具有相同的精度和标度。结果向零四舍五入。

  • (5) 输出是一个 {"min": 输入类型, "max": 输入类型} 结构标量。

  • (6)输出是一个数组,包含输入中按降序排列的 N 个最常见元素,其中 N 在 ModeOptions::n 中给定。如果两个值具有相同的计数,较小的值将首先出现。注意,如果输入具有少于 N 个不同的值,输出可能少于 N 个。

  • (7)输出是 Int64、UInt64、Float64 或 Decimal128/256,具体取决于输入类型。

  • (8)输出是 Float64 或输入类型,具体取决于 QuantileOptions。

  • (9) 十进制参数首先转换为 Float64。

  • (10) TDigest/t-digest 计算近似分位数,因此只需要固定数量的内存。有关详细信息,请参阅参考实现。

  • (11) 结果基于输入数据的排序。


分组聚合(“group by”) 

分组聚合不能直接调用,但作为 SQL 风格的“group by”操作的一部分使用。与标量聚合一样,分组聚合将输入值基于某些“键”列的一组分组,然后分别聚合每个组,并发出每个输入组的一个输出值。

例如,对于以下表:

截图 2023-10-16 09-03-35.png

我们可以计算列 x 的总和,根据列键分组。这给了我们三个组,以下是结果。请注意,null 被视为一个不同的键值。

截图 2023-10-16 09-04-40.png

支持的聚合函数如下。所有函数名称都以 hash_ 为前缀,以区分它们与上面的标量等效函数,反映了它们在内部是如何实现的。

截图 2023-10-16 09-05-45.png

截图 2023-10-16 09-06-19.png

  • (1) 如果考虑了空值,通过将 ScalarAggregateOptions 参数 skip_nulls = false 设置为 false,那么将应用 Kleene 逻辑逻辑。不考虑 min_count 选项。

  • (2) CountMode 控制计算时是否仅计算非空值(默认值),仅计算空值,还是计算所有值。对于 hash_distinct,它更改的是 null 值是否被生成。这不会影响分组键,只会影响分组值(即可能会获得具有键为空的分组)。

  • (3) hash_distinct 和 hash_list 将分组的值收集到一个列表数组中。

  • (4) 对于十进制输入,生成的十进制值将具有相同的精度和标度。结果向零四舍五入。

  • (5) 输出是一个 {"min": 输入类型, "max": 输入类型} 结构标量数组。

  • (6) hash_one 返回每个组的一个任意值。该函数偏向于非空值:如果某个组至少有一个非空值,则返回该值,只有当该组的所有值都为空时,该函数才会返回空值。

  • (7) 输出是 Int64、UInt64、Float64 或 Decimal128/256,具体取决于输入类型。

  • (8) Decimal 参数首先转换为 Float64。

  • (9) T-digest 计算近似分位数,因此只需要固定数量的内存。有关详细信息,请参阅参考实现。

  • (10) 结果基于输入数据的排序。

元素级(“标量”)函数 

所有元素级函数都接受数组和标量作为输入。一元函数的语义如下:

  • 标量输入产生标量输出

  • 数组输入产生数组输出

二进制函数具有以下语义(在其他系统(如 NumPy)中有时称为“广播”):

  • (标量,标量)输入产生标量输出

  • (数组,数组)输入产生数组输出(并且两个输入必须具有相同的长度)

  • (标量,数组)和(数组,标量)产生数组输出。将标量输入处理为与另一个输入具有相同长度 N 的数组,其中相同的值重复 N 次。

算术函数 

这些函数期望数值类型的输入,并将给定的算术操作应用于从输入(s)中获取的每个元素。如果输入元素中有任何一个为空,相应的输出元素也为空。对于二进制函数,在应用操作之前,输入将被转换为公共数值类型(如果适用,还会解码字典)。

这些函数的默认变体不会检测溢出(结果通常会环绕)。大多数函数也有一个检查溢出的变体,后缀为 _checked,当检测到溢出时返回无效状态。

对于支持十进制输入的函数(目前为 add、subtract、multiply 和 divide 以及它们的检查变体),将适当提升不同精度/标度的十进制。混合十进制和浮点参数将将所有参数都转换为浮点数,而混合十进制和整数参数将将所有参数都转换为十进制。混合时间分辨率时间输入将转换为最细的输入分辨率。

截图 2023-10-16 09-10-32.png

(1) 计算 DECIMAL 结果的精度和标度

截图 2023-10-16 09-15-33.png

与 Redshift 的十进制升级规则兼容。在加法、减法和乘法操作中,保留所有十进制数字。除法的结果精度至少等于两个操作数的精度之和,同时保留足够的标度。如果结果精度超出十进制值范围,则会返回错误。

(2) 非零输入的输出为 -1 或 1,零输入的输出为 0。NaN 值返回 NaN。整数和十进制值返回 Int8 的带符号性,浮点值返回与输入相同类型的带符号性。

位操作函数:

截图 2023-10-16 09-18-23.png

(1) 如果位移量(即第二个输入)超出了数据类型的范围,将会引发错误。但是,当对第一个输入进行位移时发生溢出时,不会引发错误,多余的位将被静默丢弃。

舍入函数 

舍入函数将数值输入转换为基于舍入准则的近似值,采用更简单的表示形式。

截图 2023-10-16 09-21-46.png

  • (1) 对于整数输入,输出值为64位浮点数,对于浮点数和小数输入,保留相同的类型。默认情况下,舍入函数将值舍入到最接近的整数,使用HALF_TO_EVEN解决平局情况。可以使用选项来控制舍入准则。round和round_to_multiple都具有round_mode选项,用于设置舍入模式。

  • (2) 舍入到指定的数字位数,其中RoundOptions的ndigits选项指定以数字位数为单位的舍入精度。负值对应于非小数部分的位数。例如,-2表示四舍五入到最接近的100的倍数(将十位和个位数设置为零)。ndigits的默认值为0,它将四舍五入到最接近的整数。

  • (3) 舍入到倍数,其中RoundToMultipleOptions的multiple选项指定舍入的比例。舍入倍数必须是正值。例如,100对应于舍入到最接近的100的倍数(将十位和个位数设置为零)。multiple的默认值为1,它将四舍五入到最接近的整数。

截图 2023-10-16 09-22-29.png

对于round和round_to_multiple,提供以下舍入模式。带HALF前缀的模式用于解决平局情况,将非平局情况四舍五入到最接近的整数。以下是默认的ndigits和multiple值的示例。

下表给出了ndigits(用于round函数)和multiple(用于round_to_multiple函数)分别如何影响操作的示例。

截图 2023-10-16 09-23-00.png

对数函数 

对数函数也得到支持,并提供了_checked变体,以检查是否需要域错误。

十进制值是被接受的,但首先被转换为Float64。

截图 2023-10-16 09-24-14.png

三角函数 

三角函数也得到支持,还提供了需要时检查域错误的_checked变体。

十进制值会被接受,但首先被转换为Float64。

截图 2023-10-16 09-25-27.png

比较 

这些函数期望两个数值类型的输入(在这种情况下,它们将在比较之前被强制转换为通用的数值类型),或者两个二进制或类字符串的输入,或两个时间类型的输入。如果任何输入被字典编码,它将被扩展以进行比较。如果一对输入元素中有任何一个是null,相应的输出元素也将是null。十进制参数将按照加法和减法的方式进行提升。

截图 2023-10-16 09-27-12.png

这些函数接受任意数量的数值类型的输入(在这种情况下,它们将在比较之前被强制转换为通用的数值类型)或时间类型的输入。如果任何输入被字典编码,它将被扩展以进行比较。

截图 2023-10-16 09-27-34.png

(1) 默认情况下,null 值会被跳过(但可以配置内核来传播 null 值)。对于浮点数值,NaN 会覆盖 null 值,但不会覆盖其他任何值。对于二进制和类字符串的值,仅支持相同类型参数的比较。


逻辑函数


这些函数的正常行为是,如果其中任何输入为null(类似于浮点计算中NaN的语义),则发出null。

其中一些也以Kleene逻辑变体(后缀为_kleene)可用,其中将null视为“未定义”。这是SQL系统以及R和Julia等系统中使用的null解释方式,例如。

因此,对于Kleene逻辑变体:

“true AND null”,“null AND true”会生成“null”(结果未定义)

“true OR null”,“null OR true”会生成“true”

“false AND null”,“null AND false”会生成“false”

“false OR null”,“null OR false”会生成“null”(结果未定义)

截图 2023-10-16 19-10-32.png

字符串断定

这些函数根据其字符内容对输入字符串元素进行分类。空字符串元素在输出中生成false。对于这些函数的ASCII变体(前缀为ascii_),如果字符串元素包含非ASCII字符,则在输出中生成false。

第一组函数是基于字符的,如果输入仅包含给定类别的字符,则在输出中生成true:

截图 2023-10-16 19-12-11.png

  • (1)还匹配所有数字ASCII字符和所有ASCII数字。

  • (2)非大小写字符(如标点符号)不匹配。

  • (3)目前与utf8_is_decimal相同。

  • (4)与utf8_is_decimal不同,非十进制数字字符也匹配。

第二组函数还考虑字符串元素中的字符顺序:

截图 2023-10-16 19-13-59.png

(1)只有输入字符串元素是标题大小写时,输出为true,即任何单词以大写字符开头,然后是小写字符。单词边界由非大小写字符定义。

第三组函数在字节级别上检查字符串元素:

image.png

(1)仅当输入字符串元素仅包含ASCII字符时,输出为true,即只包含[0, 127]中的字节。


字符串转换

截图 2023-10-16 19-17-11.png

截图 2023-10-16 19-17-40.png

  • (1)将输入中的每个ASCII字符转换为小写或大写。非ASCII字符保持不变。

  • (2)将ASCII输入反转为输出。如果存在非ASCII字符,则将返回无效状态。

  • (3)输出是每个输入元素的字节物理长度。对于二进制/字符串,输出类型为Int32,对于LargeBinary/LargeString,输出类型为Int64。

  • (4)重复输入二进制字符串一定次数。

  • (5)使用ReplaceSliceOptions::start(包含)到ReplaceSliceOptions::stop(不包含)之间的子字符串切片ReplaceSliceOptions::replacement。二进制内核按字节测量切片,而UTF8内核按代码单元测量切片。

  • (6)执行字节级反转。

  • (7)替换匹配ReplaceSubstringOptions::pattern的非重叠子字符串ReplaceSubstringOptions::replacement。如果ReplaceSubstringOptions::max_replacements!= -1,则它决定从左边开始的替换次数的最大值。

  • (8)使用Google RE2库,将匹配到的正则表达式ReplaceSubstringOptions::pattern替换为ReplaceSubstringOptions::replacement的非重叠子字符串。如果ReplaceSubstringOptions::max_replacements!= -1,则它决定从左边开始的最大替换数。请注意,如果模式包含组,可以使用反向引用。

  • (9)将输入中的每个UTF8编码字符转换为小写或大写。

  • (10)输出是每个输入元素的字符数(而不是字节)。对于String,输出类型为Int32,对于LargeString,输出类型为Int64。

  • (11)将每个UTF8编码的代码单元按相反的顺序写入输出。如果输入不是有效的UTF8,则输出是未定义的(但将保留输出缓冲区的大小)。



字符串填充

这些函数附加/前置给定的填充字节(ASCII)或代码点(UTF8),以便将字符串居中(center),右对齐(lpad)或左对齐(rpad)。

截图 2023-10-16 19-19-27.png

字符串修剪

这些函数从两侧(trim),左侧(ltrim)或右侧(rtrim)修剪字符。

image.png

  • (1)仅将TrimOptions::characters中指定的字符删除。输入字符串和字符参数都解释为ASCII字符。

  • (2)仅删除ASCII空白字符('\t'、'\n'、'\v'、'\f'、'\r'和' ')。

  • (3)仅将TrimOptions::characters中指定的字符删除。

  • (4)仅删除Unicode空白字符。


字符串分割

这些函数将字符串拆分为字符串列表。所有内核都可以配置max_splits和reverse参数,其中max_splits == -1表示无限制(默认)。当reverse为true时,从字符串末尾开始拆分,这仅在给定正max_splits时相关。

截图 2023-10-16 19-22-53.png

  • (1)ASCII定义的ASCII空白字节序列('\t'、'\n'、'\v'、'\f'、'\r'和' ')被视为分隔符。

  • (2)当找到精确的模式时拆分字符串(模式本身不包括在输出中)。

  • (3)当找到正则表达式匹配时拆分字符串(匹配的子字符串本身不包括在输出中)。

  • (4)一定长度的Unicode定义的空白代码点序列被视为分隔符。


字符串组件提取

image.png

(1)使用Google RE2库定义的正则表达式提取子字符串。输出结构字段名称是指命名捕获组,例如正则表达式(?P[ab])(?P\d)中的'letter'和'digit'。


字符串连接

这些函数执行与字符串拆分相反的操作。

image.png

(1)第一个输入必须是数组,而第二个可以是标量或数组。将第一个输入中的值列表使用第二个输入作为分隔符连接。如果任何输入列表为null或包含null,则相应的输出将为null。

(2)所有参数都是逐元素连接的,最后一个参数被视为分隔符(标量在任何情况下都会被循环使用)。空分隔符发出null。如果任何其他参数为null,默认情况下相应的输出将为null,但也可以跳过或替换为给定的字符串。


字符串切片

这个函数根据开始和结束索引以及非零步骤(默认为1)将输入数组的每个序列转换为子序列。切片语义遵循Python切片语义:开始索引包括,结束索引不包括;如果步骤为负,将反向跟随序列。

image.png

(1)使用SliceOptions中的(start、stop、step)定义的子字符串切片字符串,其中开始和停止以字节为单位测量。null输入会发出null。

(2)使用SliceOptions中的(start、stop、step)定义的子字符串切片字符串,其中开始和停止以代码单元为单位测量。null输入会发出null。


包含测试

image.png

(1)输出是MatchSubstringOptions::pattern在相应输入字符串中的出现次数。Binary/String的输出类型为Int32,LargeBinary/LargeString的输出类型为Int64。

(2)输出为true,当MatchSubstringOptions::pattern是相应输入的后缀/前缀时。

(3)输出是MatchSubstringOptions::pattern在相应输入字符串中的第一次出现的索引,否则为-1。Binary/String的输出类型为Int32,LargeBinary/LargeString的输出类型为Int64。

(4)如果在SetLookupOptions::value_set中找到相应的输入元素,输出是相应的输入元素的索引。否则,输出为null。

(5)如果相应的输入元素等于SetLookupOptions::value_set中的一个元素,则输出为true。

(6)输出为true,如果SQL样式LIKE模式MatchSubstringOptions::pattern完全匹配相应的输入元素。换句话说,%将匹配任意数量的字符,_将仅匹配一个字符,其他任何字符将匹配自身。要匹配文字百分比符号或下划线,前面加上反斜杠。

(7)输出为true,如果MatchSubstringOptions::pattern是相应输入元素的子字符串。

(8)输出为true,如果MatchSubstringOptions::pattern在相应输入元素的任何位置匹配。


分类函数

image.png

(1)仅当相应的输入元素是有限值(既不是无穷大、-无穷大,也不是NaN)时输出为true。因此,对于Decimal和整数输入,这始终返回true。

(2)仅当相应的输入元素是无穷大/-无穷大时输出为true。因此,对于Decimal和整数输入,这始终返回false。

(3)仅当相应的输入元素为NaN时输出为true。因此,对于Decimal和整数输入,这始终返回false。

(4)仅当相应的输入元素为null时输出为true。如果将NullOptions::nan_is_null设置为false,NaN值也可以被视为null。

(5)仅当相应的输入元素为非null时输出为true,否则输出为false。

(6)仅当相应的输入元素为非null时输出为true,否则输出为true。





版权所有,转载本站文章请注明出处:云子量化, http://www.woniunote.com/article/350
上一篇:arrow系列16---表格数据(Tabular Data)
下一篇:arrow系列18---计算函数2(Computer Functions)