Skip to main content

rs_arrow_array2freq2rbat/
lib.rs

1use std::collections::BTreeMap;
2use std::io;
3
4use arrow::array::PrimitiveArray;
5use arrow::record_batch::RecordBatch;
6
7use arrow::datatypes::ArrowPrimitiveType;
8
9use arrow::datatypes::SchemaRef;
10
11/// Computes the frequencies of each value in a [`PrimitiveArray`].
12///
13/// # Arguments
14///
15/// * `arr`: A [`PrimitiveArray`] of values.
16///
17/// # Returns
18///
19/// A [`Result`] containing a [`BTreeMap`] with the frequencies of each value,
20/// or an [`io::Error`] if an error occurs.
21///
22/// # Example
23///
24/// ```
25/// use arrow::array::Int32Array;
26/// use rs_arrow_array2freq2rbat::frequencies;
27///
28/// let arr = Int32Array::from(vec![Some(2), Some(3), Some(3), Some(5), Some(5), Some(5)]);
29/// let freqs = frequencies(arr).unwrap();
30///
31/// assert_eq!(freqs.get(&2), Some(&1));
32/// assert_eq!(freqs.get(&3), Some(&2));
33/// assert_eq!(freqs.get(&5), Some(&3));
34/// ```
35pub fn frequencies<T>(arr: PrimitiveArray<T>) -> Result<BTreeMap<T::Native, u64>, io::Error>
36where
37    T: ArrowPrimitiveType,
38    T::Native: Ord,
39{
40    let mut freqs = BTreeMap::new();
41    for item in arr.iter().flatten() {
42        *freqs.entry(item).or_insert(0) += 1;
43    }
44    Ok(freqs)
45}
46
47/// Converts a `BTreeMap` of frequencies into an Arrow `RecordBatch`.
48///
49/// # Arguments
50///
51/// * `m`: A `BTreeMap` where keys are the values and values are their frequencies.
52/// * `sch`: The `SchemaRef` for the output `RecordBatch`.
53///
54/// # Returns
55///
56/// A `Result` containing a `RecordBatch` with two columns, whose names are defined by the provided `SchemaRef`,
57/// or an `io::Error` if an error occurs.
58///
59/// # Example
60///
61/// ```
62/// use std::collections::BTreeMap;
63/// use std::sync::Arc;
64/// use arrow::datatypes::{DataType, Field, Schema};
65/// use rs_arrow_array2freq2rbat::map2batch;
66///
67/// let mut freqs = BTreeMap::new();
68/// freqs.insert(1, 1);
69/// freqs.insert(2, 2);
70/// freqs.insert(3, 3);
71///
72/// let schema = Arc::new(Schema::new(vec![
73///     Field::new("value", DataType::Int32, false),
74///     Field::new("frequency", DataType::UInt64, false),
75/// ]));
76///
77/// let batch = map2batch::<arrow::datatypes::Int32Type>(freqs, &schema).unwrap();
78///
79/// assert_eq!(batch.num_columns(), 2);
80/// assert_eq!(batch.num_rows(), 3);
81/// ```
82pub fn map2batch<T>(m: BTreeMap<T::Native, u64>, sch: &SchemaRef) -> Result<RecordBatch, io::Error>
83where
84    T: ArrowPrimitiveType,
85{
86    let keys: Vec<T::Native> = m.keys().cloned().collect();
87    let values: Vec<u64> = m.values().cloned().collect();
88
89    let key_array = PrimitiveArray::<T>::from_iter_values(keys);
90    let val_array = PrimitiveArray::<arrow::datatypes::UInt64Type>::from_iter(values);
91
92    let batch = RecordBatch::try_new(
93        sch.clone(),
94        vec![
95            std::sync::Arc::new(key_array),
96            std::sync::Arc::new(val_array),
97        ],
98    )
99    .map_err(|e| io::Error::other(e.to_string()))?;
100
101    Ok(batch)
102}
103
104/// Computes the frequencies of each value in a [`PrimitiveArray`] and converts the result into an Arrow [`RecordBatch`].
105///
106/// # Arguments
107///
108/// * `sch`: The `SchemaRef` for the output `RecordBatch`. This allows the caller to define custom column names and metadata for the resulting frequency and value columns. The schema is expected to have two fields, where the first field corresponds to the values from the `PrimitiveArray` (with `T::DATA_TYPE`) and the second field corresponds to the frequencies (with `DataType::UInt64`).
109///
110/// # Returns
111///
112/// A [`Result`] containing a [`RecordBatch`] with two columns, whose names are defined by the provided `SchemaRef`,
113/// or an [`io::Error`] if an error occurs.
114///
115/// # Example
116///
117/// ```
118/// use std::sync::Arc;
119/// use arrow::array::Int32Array;
120/// use arrow::datatypes::{DataType, Field, Schema};
121/// use rs_arrow_array2freq2rbat::array2frequency2batch;
122///
123/// let arr = Int32Array::from(vec![Some(2), Some(3), Some(3), Some(5), Some(5), Some(5)]);
124/// let schema = Arc::new(Schema::new(vec![
125///     Field::new("value", DataType::Int32, false),
126///     Field::new("frequency", DataType::UInt64, false),
127/// ]));
128/// let batch = array2frequency2batch(arr, schema).unwrap();
129///
130/// assert_eq!(batch.num_columns(), 2);
131/// assert_eq!(batch.num_rows(), 3);
132/// ```
133pub fn array2frequency2batch<T>(
134    arr: PrimitiveArray<T>,
135    sch: SchemaRef,
136) -> Result<RecordBatch, io::Error>
137where
138    T: ArrowPrimitiveType,
139    T::Native: Ord,
140{
141    let freqs = frequencies(arr)?;
142    map2batch::<T>(freqs, &sch)
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use arrow::array::Int32Array;
149
150    #[test]
151    fn test_frequencies() {
152        let arr = Int32Array::from(vec![1, 2, 2, 3, 3, 3]);
153        let freqs = frequencies(arr).unwrap();
154        assert_eq!(freqs.get(&1), Some(&1));
155        assert_eq!(freqs.get(&2), Some(&2));
156        assert_eq!(freqs.get(&3), Some(&3));
157    }
158
159    #[test]
160    fn test_map2batch() {
161        use arrow::datatypes::{DataType, Field, Schema};
162        use std::sync::Arc;
163
164        let mut freqs = BTreeMap::new();
165        freqs.insert(1, 1);
166        freqs.insert(2, 2);
167        freqs.insert(3, 3);
168
169        let schema = Arc::new(Schema::new(vec![
170            Field::new("value", DataType::Int32, false),
171            Field::new("frequency", DataType::UInt64, false),
172        ]));
173
174        let batch = map2batch::<arrow::datatypes::Int32Type>(freqs, &schema).unwrap();
175
176        assert_eq!(batch.num_columns(), 2);
177        assert_eq!(batch.num_rows(), 3);
178
179        let value_array = batch
180            .column(0)
181            .as_any()
182            .downcast_ref::<Int32Array>()
183            .unwrap();
184        let freq_array = batch
185            .column(1)
186            .as_any()
187            .downcast_ref::<arrow::array::UInt64Array>()
188            .unwrap();
189
190        assert_eq!(value_array.value(0), 1);
191        assert_eq!(value_array.value(1), 2);
192        assert_eq!(value_array.value(2), 3);
193
194        assert_eq!(freq_array.value(0), 1);
195        assert_eq!(freq_array.value(1), 2);
196        assert_eq!(freq_array.value(2), 3);
197    }
198
199    #[test]
200    fn test_array2frequency2batch() {
201        use arrow::datatypes::{DataType, Field, Schema};
202        use std::sync::Arc;
203
204        let arr = PrimitiveArray::<arrow::datatypes::Int32Type>::from(vec![1, 2, 2, 3, 3, 3]);
205        let schema = Arc::new(Schema::new(vec![
206            Field::new("value", DataType::Int32, false),
207            Field::new("frequency", DataType::UInt64, false),
208        ]));
209        let batch = array2frequency2batch(arr, schema).unwrap();
210
211        assert_eq!(batch.num_columns(), 2);
212        assert_eq!(batch.num_rows(), 3);
213
214        let value_array = batch
215            .column(0)
216            .as_any()
217            .downcast_ref::<Int32Array>()
218            .unwrap();
219        let freq_array = batch
220            .column(1)
221            .as_any()
222            .downcast_ref::<arrow::array::UInt64Array>()
223            .unwrap();
224
225        assert_eq!(value_array.value(0), 1);
226        assert_eq!(value_array.value(1), 2);
227        assert_eq!(value_array.value(2), 3);
228
229        assert_eq!(freq_array.value(0), 1);
230        assert_eq!(freq_array.value(1), 2);
231        assert_eq!(freq_array.value(2), 3);
232    }
233}