///|
fn parse_cell(text : StringView) -> Result[Double, String] {
let value = @json.parse(text.trim()) catch {
_ => return Err("invalid numeric cell")
}
json_number(value)
}
///|
/// Numeric TSV, not general CSV. Exact x/y/weight header; no quoting or missing values.
pub fn read_samples(
text : String,
max_rows? : Int = 100000,
) -> Result[Array[Sample], String] {
if max_rows <= 0 || text.length() > 16000000 {
return Err("table budget exceeded")
}
let lines = text.split("\n").to_array()
if lines.length() == 0 || lines[0].trim() != "x\ty\tweight" {
return Err("expected x/y/weight TSV header")
}
let samples = []
for i = 1; i < lines.length(); i = i + 1 {
let line = lines[i].trim()
if line.length() == 0 {
if i == lines.length() - 1 {
continue
}
return Err("empty table row")
}
if samples.length() >= max_rows {
return Err("row budget exceeded")
}
let cells = line.split("\t").to_array()
if cells.length() != 3 {
return Err("expected three numeric cells")
}
let x = match parse_cell(cells[0]) {
Err(e) => return Err(e)
Ok(v) => v
}
let y = match parse_cell(cells[1]) {
Err(e) => return Err(e)
Ok(v) => v
}
let weight = match parse_cell(cells[2]) {
Err(e) => return Err(e)
Ok(v) => v
}
samples.push(sample(x, y, weight~))
}
match validate_samples(samples) {
Err(e) => Err(e)
Ok(_) => Ok(samples)
}
}
///|
pub fn write_samples(samples : Array[Sample]) -> Result[String, String] {
match validate_samples(samples) {
Err(e) => return Err(e)
Ok(_) => ()
}
let lines = ["x\ty\tweight"]
for s in samples {
lines.push(
s.x.to_string() + "\t" + s.y.to_string() + "\t" + s.weight.to_string(),
)
}
Ok(lines.join("\n") + "\n")
}
///|
pub fn write_predictions(
samples : Array[Sample],
model : Model,
) -> Result[String, String] {
match validate_samples(samples) {
Err(e) => return Err(e)
Ok(_) => ()
}
let predictions = match predict_many(model, samples.map(fn(s) { s.x })) {
Err(e) => return Err(e)
Ok(v) => v
}
let lines = ["x\ty\tweight\tprediction\tresidual"]
for i = 0; i < samples.length(); i = i + 1 {
let s = samples[i]
let residual = s.y - predictions[i]
if !finite(residual) {
return Err("residual overflow")
}
lines.push(
s.x.to_string() +
"\t" +
s.y.to_string() +
"\t" +
s.weight.to_string() +
"\t" +
predictions[i].to_string() +
"\t" +
residual.to_string(),
)
}
Ok(lines.join("\n") + "\n")
}