28个重大版本更新

52.2.0 2024年7月28日
52.0.0 2024年6月6日
51.0.0 2024年3月18日
50.0.0 2024年1月12日
24.0.0 2022年10月3日

#126Rust模式

Download history 161127/week @ 2024-05-01 189425/week @ 2024-05-08 184147/week @ 2024-05-15 176017/week @ 2024-05-22 221363/week @ 2024-05-29 290019/week @ 2024-06-05 277621/week @ 2024-06-12 268487/week @ 2024-06-19 325206/week @ 2024-06-26 262604/week @ 2024-07-03 286324/week @ 2024-07-10 283717/week @ 2024-07-17 301083/week @ 2024-07-24 309590/week @ 2024-07-31 299541/week @ 2024-08-07 253144/week @ 2024-08-14

1,221,878 每月下载量
363 个crate中使用了(直接使用82个)

Apache-2.0

2MB
36K SLoC

Apache Arrow的核心类型是数组,它是一个已知长度的值序列,所有值都具有相同的类型。此crate提供了每种类型的具体实现,以及一个可以用于类型擦除的Array trait。

构建数组

大多数Array实现可以直接从迭代器或[Vec]

#
Int32Array::from(vec![1, 2]);
Int32Array::from(vec![Some(1), None]);
Int32Array::from_iter([1, 2, 3, 4]);
Int32Array::from_iter([Some(1), Some(2), None, Some(4)]);

StringArray::from(vec!["foo", "bar"]);
StringArray::from(vec![Some("foo"), None]);
StringArray::from_iter([Some("foo"), None]);
StringArray::from_iter_values(["foo", "bar"]);

ListArray::from_iter_primitive::<Int32Type, _, _>([
    Some(vec![Some(1), None, Some(3)]),
    None,
    Some(vec![])
]);

此外,可以使用ArrayBuilder实现来使用基于推的接口构建数组

#
// Create a new builder with a capacity of 100
let mut builder = Int16Array::builder(100);

// Append a single primitive value
builder.append_value(1);
// Append a null value
builder.append_null();
// Append a slice of primitive values
builder.append_slice(&[2, 3, 4]);

// Build the array
let array = builder.finish();

assert_eq!(5, array.len());
assert_eq!(2, array.value(2));
assert_eq!(&array.values()[3..5], &[3, 4])

底层API

内部,数组由一个或多个由Buffer支持的共享内存区域组成,其数量和含义取决于数组的类型,如Arrow规范中所述。

例如,类型 Int16Array 表示16位整数数组,由以下组成

类似地,类型 StringArray 表示UTF-8字符串数组,由以下组成

  • 一个可选的 NullBuffer 识别任何null值
  • 用于在值缓冲区中标识有效UTF-8序列的OffsetBuffer<i32>
  • UTF-8编码的字符串数据的Buffer值缓冲区

PrimitiveArray::try_new这样的数组构造函数能够以低廉的代价从这些部分构建数组,而PrimitiveArray::into_parts则提供了相反的操作。

#
// Create a Int32Array from Vec without copying
let array = Int32Array::new(vec![1, 2, 3].into(), None);
assert_eq!(array.values(), &[1, 2, 3]);
assert_eq!(array.null_count(), 0);

// Create a StringArray from parts
let offsets = OffsetBuffer::new(vec![0, 5, 10].into());
let array = StringArray::new(offsets, b"helloworld".into(), None);
let values: Vec<_> = array.iter().map(|x| x.unwrap()).collect();
assert_eq!(values, &["hello", "world"]);

由于[Vec]可以无拷贝地创建Buffer及其派生类,这提供了一个高效的方式,不仅可以与其他Rust代码交互,还可以实现针对箭头数据布局优化的内核 - 例如通过处理缓冲区而不是值。

零拷贝切片

给定任意长度的Array,可以创建此数据的所有者切片。内部这仅仅增加了一些引用计数,因此非常便宜。

let array = Int32Array::from_iter([1, 2, 3]);

// Slice with offset 1 and length 2
let sliced = array.slice(1, 2);
assert_eq!(sliced.values(), &[2, 3]);

向下转换数组

数组通常以动态类型&dyn ArrayArrayRef的形式传递。例如,RecordBatch将列存储为ArrayRef

虽然这些数组可以直接传递给computecsvjson等API,但通常您希望直接与具体数组交互。

这需要将数组向下转换为具体类型


// Safely downcast an `Array` to an `Int32Array` and compute the sum
// using native i32 values
fn sum_int32(array: &dyn Array) -> i32 {
    let integers: &Int32Array = array.as_any().downcast_ref().unwrap();
    integers.iter().map(|val| val.unwrap_or_default()).sum()
}

// Safely downcasts the array to a `Float32Array` and returns a &[f32] view of the data
// Note: the values for positions corresponding to nulls will be arbitrary (but still valid f32)
fn as_f32_slice(array: &dyn Array) -> &[f32] {
    array.as_any().downcast_ref::<Float32Array>().unwrap().values()
}

扩展特质cast::AsArray可以使此操作更加方便


fn as_f32_slice(array: &dyn Array) -> &[f32] {
    array.as_primitive::<Float32Type>().values()
}

依赖项

~2.8–9MB
~71K SLoC