1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! Dynamic JSON ingestion: parse JSON with mixed and missing values into a clean
//! numeric tensor.
//!
//! The JSON counterpart to the CSV on-ramp examples (`dynamic_02_missing_values`,
//! `dynamic_05_dirty_csv_cleanup`). `from_json_dynamic` accepts heterogeneous JSON —
//! integers, floats, and `null` — and lands it in a dynamic tensor, which a
//! `NumericPolicy` then coerces to a clean `f64` tensor. The format differs from CSV;
//! the on-ramp does not.
//!
//! Run: cargo run --example dynamic_08_json_ingestion --features dynamic,json
#[cfg(not(feature = "dynamic"))]
fn main() {
println!("Requires --features dynamic");
}
#[cfg(feature = "dynamic")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg(not(feature = "json"))]
use matten::Element;
use matten::{NumericPolicy, Tensor};
// A small JSON table with mixed numeric kinds and a missing cell (`null`).
// `from_json_dynamic` preserves each cell's original kind (Int / Float / None)
// until you decide how to coerce — exactly like the CSV on-ramp.
#[cfg(feature = "json")]
let t = {
let json = "[[1, 2.5, null], [4.0, 5, 6]]";
Tensor::from_json_dynamic(json)?
};
// Without the `json` feature, build the same dynamic tensor directly so the rest
// of the example still runs and asserts identically.
#[cfg(not(feature = "json"))]
let t = Tensor::from_elements(
vec![
Element::Int(1),
Element::Float(2.5),
Element::None,
Element::Float(4.0),
Element::Int(5),
Element::Int(6),
],
&[2, 3],
);
assert_eq!(t.shape(), &[2, 3]);
assert!(t.is_dynamic());
assert_eq!(t.count_none(), 1);
println!(
"parsed: shape={:?}, none_count={}",
t.shape(),
t.count_none()
);
// The missing cell is visible via none_mask (1.0 where the JSON had `null`).
let mask = t.none_mask();
assert_eq!(mask.get(&[0, 2]), Some(1.0)); // null at [0,2]
assert_eq!(mask.get(&[0, 0]), Some(0.0)); // 1 is present
println!("none_mask OK");
// Strict conversion fails: a missing value has no numeric meaning on its own.
assert!(t.try_numeric().is_err());
println!("try_numeric (strict): Err (expected — missing value)");
// A policy makes the coercion explicit: treat missing as 0.0; Int and Float both
// become f64. The result is a plain numeric tensor, ready for computation.
let clean = t.try_numeric_with(NumericPolicy::default().none_as(0.0))?;
assert!(!clean.is_dynamic());
assert_eq!(clean.shape(), &[2, 3]);
assert_eq!(clean.as_slice(), &[1.0, 2.5, 0.0, 4.0, 5.0, 6.0]);
println!("clean f64 = {:?}", clean.as_slice());
println!("All assertions passed.");
Ok(())
}