数据类型#
另请参阅
数据类型控制物理数据的解释方式。它们规范允许不同 Arrow 实现之间进行二进制互操作,包括来自不同编程语言和运行时(例如,可以使用 pyarrow.jvm
桥接模块从 Python 和 Java 访问相同数据,而无需复制)。
C++ 中的数据类型信息可以用三种方式表示:
使用
arrow::DataType
实例(例如,作为函数参数)使用
arrow::DataType
的具体子类(例如,作为模板参数)使用
arrow::Type::type
枚举值(例如,作为 switch 语句的条件)
第一种形式(使用 arrow::DataType
实例)是最惯用的和灵活的。运行时参数化类型只能用 DataType 实例完整地表示。例如,arrow::TimestampType
需要在运行时使用 arrow::TimeUnit::type
参数构造;arrow::Decimal128Type
使用scale和precision参数;arrow::ListType
使用完整子类型(本身是 arrow::DataType
实例)。
其他两种形式可以在性能至关重要的地方使用,以避免为动态类型和多态性付出代价。但是,对于参数化类型,可能仍然需要进行一定程度的运行时切换。不可能在编译时具体化所有可能的类型,因为 Arrow 数据类型允许任意嵌套。
创建数据类型#
为了实例化数据类型,建议调用提供的 工厂函数
std::shared_ptr<arrow::DataType> type;
// A 16-bit integer type
type = arrow::int16();
// A 64-bit timestamp type (with microsecond granularity)
type = arrow::timestamp(arrow::TimeUnit::MICRO);
// A list type of single-precision floating-point values
type = arrow::list(arrow::float32());
类型特质#
如果沒有类型特质,编写能够处理具体 arrow::DataType
子类的代码将非常冗长。Arrow 的类型特质将 Arrow 数据类型映射到专门的数组、标量、构建器和其他关联类型。例如,布尔类型具有以下特质:
template <>
struct TypeTraits<BooleanType> {
using ArrayType = BooleanArray;
using BuilderType = BooleanBuilder;
using ScalarType = BooleanScalar;
using CType = bool;
static constexpr int64_t bytes_required(int64_t elements) {
return bit_util::BytesForBits(elements);
}
constexpr static bool is_parameter_free = true;
static inline std::shared_ptr<DataType> type_singleton() { return boolean(); }
};
请参阅类型特质,了解这些字段的解释。
使用类型特质,可以编写可以处理各种 Arrow 类型的模板函数。例如,要编写一个为任何 Arrow 数值类型创建斐波那契值数组的函数:
template <typename DataType,
typename BuilderType = typename arrow::TypeTraits<DataType>::BuilderType,
typename ArrayType = typename arrow::TypeTraits<DataType>::ArrayType,
typename CType = typename arrow::TypeTraits<DataType>::CType>
arrow::Result<std::shared_ptr<ArrayType>> MakeFibonacci(int32_t n) {
BuilderType builder;
CType val = 0;
CType next_val = 1;
for (int32_t i = 0; i < n; ++i) {
builder.Append(val);
CType temp = val + next_val;
val = next_val;
next_val = temp;
}
std::shared_ptr<ArrayType> out;
ARROW_RETURN_NOT_OK(builder.Finish(&out));
return out;
}
对于一些常见情况,类本身存在类型关联。使用:
Scalar::TypeClass
获取标量的数据类型类Array::TypeClass
获取数组的数据类型类DataType::c_type
获取与 Arrow 数据类型关联的 C 类型
类似于std::type_traits中提供的类型谓词,Arrow 提供了诸如is_number_type
之类的类型谓词,以及相应的包装std::enable_if_t
的模板,例如enable_if_number
。这些可以限制模板函数仅为相关类型编译,这在需要实现其他重载时很有用。例如,要为任何数值(整数或浮点数)数组编写一个求和函数:
template <typename ArrayType, typename DataType = typename ArrayType::TypeClass,
typename CType = typename DataType::c_type>
arrow::enable_if_number<DataType, CType> SumArray(const ArrayType& array) {
CType sum = 0;
for (std::optional<CType> value : array) {
if (value.has_value()) {
sum += value.value();
}
}
return sum;
}
请参阅类型谓词,了解这些谓词的列表。
访问者模式#
为了处理 arrow::DataType
、arrow::Scalar
或 arrow::Array
,您可能需要编写根据特定 Arrow 类型进行专门化的逻辑。在这些情况下,请使用访问者模式。Arrow 提供了以下模板函数:
要使用这些函数,请为每个专门类型实现 Status Visit()
方法,然后将类实例传递给内联访问函数。为了避免重复代码,请使用上一节中记录的类型特质。以下是一个简短的示例,说明如何对任意数值类型的列进行求和:
class TableSummation {
double partial = 0.0;
public:
arrow::Result<double> Compute(std::shared_ptr<arrow::RecordBatch> batch) {
for (std::shared_ptr<arrow::Array> array : batch->columns()) {
ARROW_RETURN_NOT_OK(arrow::VisitArrayInline(*array, this));
}
return partial;
}
// Default implementation
arrow::Status Visit(const arrow::Array& array) {
return arrow::Status::NotImplemented("Cannot compute sum for array of type ",
array.type()->ToString());
}
template <typename ArrayType, typename T = typename ArrayType::TypeClass>
arrow::enable_if_number<T, arrow::Status> Visit(const ArrayType& array) {
for (std::optional<typename T::c_type> value : array) {
if (value.has_value()) {
partial += static_cast<double>(value.value());
}
}
return arrow::Status::OK();
}
};
Arrow 还提供抽象访问者类(arrow::TypeVisitor
、arrow::ScalarVisitor
、arrow::ArrayVisitor
)以及每个相应基类型上的 Accept()
方法(例如,arrow::Array::Accept()
)。但是,这些无法使用模板函数实现,因此您通常会更喜欢使用内联类型访问者。