为什么 arrow::ListArray::offsets() 产生的偏移量散布着零?

vin*_*bie 3 c++ parquet apache-arrow python-polars

我正在尝试使用 C++ 读取包含浮点数列表的镶木地板文件中的数据。

\n

我使用以下 python 代码生成了一个简单的镶木地板文件:

\n
import polars as pl\nimport struct\nimport random\nimport pyarrow.parquet as pq\n\nfloatlist = []\nfor _ in range(10):\n  lstlen = random.choice([3, 4, 5])\n  floatlist.append([random.random() for _ in range(lstlen)])\n\ndf = pl.DataFrame({"float_list": floatlist})\n\nfile_out_path = \'test.parquet\'\ndf.write_parquet(file_out_path)\nprint(pl.read_parquet(file_out_path))\n
Run Code Online (Sandbox Code Playgroud)\n

结果看起来非常合理:

\n
shape: (10, 1)\n\xe2\x94\x8c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x90\n\xe2\x94\x82 float_list                       \xe2\x94\x82\n\xe2\x94\x82 ---                              \xe2\x94\x82\n\xe2\x94\x82 list[f64]                        \xe2\x94\x82\n\xe2\x95\x9e\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\xa1\n\xe2\x94\x82 [0.863913, 0.831073, 0.264516]   \xe2\x94\x82\n\xe2\x94\x82 [0.51377, 0.434267, \xe2\x80\xa6 0.131684]  \xe2\x94\x82\n\xe2\x94\x82 [0.978071, 0.251396, \xe2\x80\xa6 0.142218] \xe2\x94\x82\n\xe2\x94\x82 [0.495616, 0.628793, 0.434872]   \xe2\x94\x82\n\xe2\x94\x82 \xe2\x80\xa6                                \xe2\x94\x82\n\xe2\x94\x82 [0.19035, 0.68318, \xe2\x80\xa6 0.778707]   \xe2\x94\x82\n\xe2\x94\x82 [0.103636, 0.08755, \xe2\x80\xa6 0.526014]  \xe2\x94\x82\n\xe2\x94\x82 [0.803863, 0.382698, \xe2\x80\xa6 0.728598] \xe2\x94\x82\n\xe2\x94\x82 [0.323969, 0.412751, \xe2\x80\xa6 0.12993]  \xe2\x94\x82\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98\n\n
Run Code Online (Sandbox Code Playgroud)\n

现在我尝试使用以下代码在 C++ 中读取相同的 test.parquet 文件:

\n
void read_parquet(std::string file_name) {\n\n    arrow::Status st;\n    std::shared_ptr<arrow::io::ReadableFile> infile;\n\n    PARQUET_ASSIGN_OR_THROW(infile, arrow::io::ReadableFile::Open(file_name));\n\n    std::unique_ptr<parquet::arrow::FileReader> reader;\n    PARQUET_THROW_NOT_OK(parquet::arrow::OpenFile(infile, arrow::default_memory_pool(), &reader));\n\n    std::shared_ptr<arrow::RecordBatchReader> record_batch_reader;\n    st = reader->GetRecordBatchReader(&record_batch_reader);\n    std::shared_ptr<arrow::RecordBatch> record_batch;\n\n    while (true) {\n      arrow::Status status = record_batch_reader->ReadNext(&record_batch);\n\n      if (!status.ok() || record_batch == nullptr) {\n          break;  // Exit the loop if there are no more batches or if an error occurs\n      }\n\n      std::cout << "Num records in batch: " << record_batch->num_rows() << std::endl;\n      auto floatlist = std::static_pointer_cast<arrow::ListArray>(record_batch->column(0));\n      std::cout << "Length of floatlist: " << floatlist->length() << std::endl;\n      std::cout << "Offsets: " << floatlist->offsets()->ToString() << std::endl;\n    \n      auto floatlist_vals = std::static_pointer_cast<arrow::DoubleArray>(floatlist->values());\n      const double* floatlist_ptr = floatlist_vals->raw_values();\n\n      for (int64_t i = 0; i < record_batch->num_rows(); i++) {\n\n          std::cout << "\\n\\n";\n\n          if ( floatlist->IsValid(i) ) {\n            std::cout << "Row " << i << " IsValid" << std::endl;\n          }\n          if ( floatlist->IsNull(i) ) {\n            std::cout << "Row " <<  i << " IsNull" << std::endl;\n          }\n                  \n          const double* first = floatlist_ptr + floatlist->value_offset(2*i);\n          const double* last = floatlist_ptr + floatlist->value_offset(2*(i + 1));\n          if (last > first) {\n            std::vector<double> flst(first, last);\n            std::cout << "Length of flst is: " << flst.size() << std::endl;\n            for (size_t i = 0; i < flst.size(); i++) {\n              std::cout << flst[i] << ", ";\n            }\n            std::cout << std::endl;\n          }\n      }\n    }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

我发现的是:

\n
std::cout << "Offsets: " << floatlist->offsets()->ToString() << std::endl;\n
Run Code Online (Sandbox Code Playgroud)\n

生成以下输出:

\n
Offsets: [\n  0,\n  0,\n  3,\n  0,\n  7,\n  0,\n  12,\n  0,\n  15,\n  0,\n  19\n]\n\n
Run Code Online (Sandbox Code Playgroud)\n

一些示例表明我可以使用以下方法恢复数据:

\n
const double* first = floatlist_ptr + floatlist->value_offset(i);\nconst double* last = floatlist_ptr + floatlist->value_offset(i + 1);\nstd::vector<double> flst(first, last);\n
Run Code Online (Sandbox Code Playgroud)\n

但由于 value_offsets 列表中的零,这显然会崩溃。\n我可以使用以下命令使其工作:

\n
const double* first = floatlist_ptr + floatlist->value_offset(2*i);\nconst double* last = floatlist_ptr + floatlist->value_offset(2*(i + 1));\nif (last > first) {\n    std::vector<double> flst(first, last);\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

我的问题:

\n
    \n
  1. 为什么偏移量列表中散布着零?

    \n
  2. \n
  3. 为什么我们会认为偏移量是num_rows + 1,但实际上

    \n
  4. \n
\n
arrow::BaseListArray<arrow::ListType>::value_offset(int64_t i)\n
Run Code Online (Sandbox Code Playgroud)\n

接受远高于 num_rows + 1 的索引值。事实上,您至少需要达到 2*num_rows 才能获取所有数据?

\n
    \n
  1. 这就是箭头和镶木地板总是如何工作的吗?如果我的镶木地板文件包含约 100000 行的列,此模式是否成立?
  2. \n
\n

phi*_*lix 5

Pola.rs 默认以Type::LARGE_LIST物理方式表示逻辑列表类型,因此当您从 Pola.rs 写入的文件中获取数据时,您应该将其转换LargeListArrayListArray.

您看到的零是 64 位偏移量的额外 32 位一半,可以适合 32 位偏移量。

要对强制转换进行调试构建检查,您可以在强制转换为 之前使用arrow::internal::checked_cast<T>或。assert(array.type_id() == arrow::Type::LIST_TYPE)ListArray