arrow系列15---数据类型(Data Types)
作者:yunjinqi   类别:    日期:2023-10-15 20:41:48    阅读:133 次   消耗积分:0 分    

数据类型决定了如何解释物理数据。它们的规范允许不同的Arrow实现之间进行二进制互操作,包括不同的编程语言和运行时环境(例如,可以使用pyarrow.jvm桥接模块从Python和Java中访问相同的数据,而无需复制)。

在C++中,有三种方式可以表示有关数据类型的信息:

  1. 使用arrow::DataType实例(例如,作为函数参数)。

  2. 使用arrow::DataType具体子类(例如,作为模板参数)。

  3. 使用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;

// 16位整数类型
type = arrow::int16();
// 64位时间戳类型(微秒粒度)
type = arrow::timestamp(arrow::TimeUnit::MICRO);
// 单精度浮点值的列表类型
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提供了以下模板函数:

  • arrow::VisitTypeInline()

  • arrow::VisitScalarInline()

  • arrow::VisitArrayInline()

要使用这些函数,为每个专用类型实现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;
  }

  // 默认实现
  arrow::Status Visit(const arrow::Array& array) {
    return arrow::Status::NotImplemented("Can not 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())。但是,这些不能使用模板函数实现,因此通常更喜欢使用内联类型访问者。


版权所有,转载本站文章请注明出处:云子量化, http://www.woniunote.com/article/348
上一篇:arrow系列14---数组(Arrays)
下一篇:arrow系列16---表格数据(Tabular Data)