#![cfg(all(feature = "numpy", feature = "pandas", feature = "scipy", feature = "sklearn"))]
use quarb::library::CommandLibrary;
use quarb::{QueryResult, Value};
use std::sync::Arc;
fn doc() -> quarb_json::JsonAdapter {
quarb_json::JsonAdapter::parse(
r#"{"rows":[
{"book":"a","x":1,"y":2.0},
{"book":"b","x":3,"y":1.0},
{"book":"c","x":5,"y":0.0},
{"book":"d","x":6,"y":0.5}],
"v":[1,2,3,4,10],
"kv":[{"k":"p","v":1},{"k":"q","v":2},{"k":"p","v":4}],
"csv":[{"sex":"F","tip":"1.5"},{"sex":"M","tip":"2.5"},{"sex":"F","tip":"3.5"}],
"series":["1","2","3","4","5","6"]}"#,
)
.unwrap()
}
fn libs() -> quarb::Run {
quarb::Run {
libraries: vec![
Arc::new(quarb_lib_numpy::Numpy),
Arc::new(quarb_lib_pandas::Pandas),
Arc::new(quarb_lib_scipy::Scipy),
Arc::new(quarb_lib_sklearn::Sklearn),
],
..quarb::Run::ambient()
}
}
fn run(q: &str) -> quarb::Result<Vec<String>> {
let out = quarb::Query::parse(q, &doc())?.run(&doc(), &libs())?;
Ok(match out.result {
QueryResult::Values(vs) => vs.iter().map(Value::display_form).collect(),
QueryResult::Nodes(ns) => ns.iter().map(|n| format!("{n:?}")).collect(),
})
}
fn refuse(q: &str) -> String {
run(q).unwrap_err().to_string()
}
fn installed() -> bool {
quarb_lib_numpy::Numpy.available().is_ok()
&& quarb_lib_pandas::Pandas.available().is_ok()
&& quarb_lib_scipy::Scipy.available().is_ok()
&& quarb_lib_sklearn::Sklearn.available().is_ok()
}
#[test]
fn the_gate_reads_the_signature() {
if !installed() {
eprintln!("python packages not importable; skipping");
return;
}
let e = refuse("/v/* | :: @| np:meen");
assert!(e.contains("have no 'meen'") && e.contains("nearest: mean"), "{e}");
let e = refuse("/v/* | :: @| np:mean(--foo=1)");
assert!(e.contains("has no option '--foo'") && e.contains("--axis="), "{e}");
let e = refuse(r#"/rows/* | %(::book; ::x) @| pd:groupby.nothing(book)"#);
assert!(e.contains("nothing"), "{e}");
for q in [
r#"/v/* | :: @| pd:read_csv("x.csv")"#,
r#"/v/* | :: @| np:save("x.npy")"#,
r#"/v/* | :: @| sp:io.loadmat("x.mat")"#,
r#"/v/* | :: @| sklearn:datasets.load_iris"#,
r#"/rows/* | %(::book; ::x) @| pd:to_csv("x.csv")"#,
r#"/rows/* | %(::book; ::x) @| pd:to_pickle("x.pkl")"#,
] {
let e = refuse(q);
assert!(e.contains("is withheld") && e.contains("outside the query"), "{q}: {e}");
}
assert!(run(r#"/rows/* | %(::book; ::x) @| pd:to_dict"#).is_ok());
let model = r#"/rows/* | %(::book; ::x; ::y) @| sklearn:naive_bayes.GaussianNB.fit((^/rows/* | ::book))"#;
let e = refuse(&format!("{model} | [$_] | count"));
assert!(e.contains("library value"), "{e}");
let e = refuse(r#"/rows/* | %(::book; ::x; ::y) @| sklearn:naive_bayes.GaussianNB.fit((^/rows/* | ::book)) | [$_ > 1] | count"#);
assert!(e.contains("a comparison") && e.contains("library value"), "{e}");
}
#[test]
fn numpy_functions_over_the_stream() {
if !installed() {
return;
}
assert_eq!(run("/v/* | :: @| np:mean").unwrap(), vec!["4"]);
assert_eq!(run("/v/* | :: @| np:percentile(50)").unwrap(), vec!["3"]);
assert_eq!(
run(r#"/rows/* | %(::book; ::x; ::y) @| np:transpose | @"#).unwrap(),
vec!["@(@(1; 3; 5; 6); @(2; 1; 0; 0.5))"]
);
assert_eq!(
run(r#"/rows/* | %(::book; ::x; ::y) @| np:transpose @| @"#).unwrap(),
vec!["@(1; 3; 5; 6)", "@(2; 1; 0; 0.5)"]
);
assert_eq!(
run("/v/* | :: @| np:cumsum").unwrap(),
vec!["#(%(1);\n %(3);\n %(6);\n %(10);\n %(20))"]
);
assert_eq!(run("/v/* | :: @| np:cumsum @| @").unwrap(), vec!["1", "3", "6", "10", "20"]);
assert_eq!(run("/v/* | :: @| np:cumsum | [2]").unwrap(), vec!["3"]);
assert_eq!(run("= @(-1; 1) @| np:log @| @").unwrap(), vec!["nan", "0"]);
assert_eq!(
run("/rows/* | %(::book; ::x; ::y) @| np:transpose").unwrap(),
vec!["#(%(1; 3; 5; 6);\n %(2; 1; 0; 0.5))"]
);
assert_eq!(run("/v/* | :: @| np:cumsum @| np:max").unwrap(), vec!["20"]);
assert_eq!(run("/v/* | :: @| np:cumsum | np:max").unwrap(), vec!["20"]);
let e = refuse(r#"/rows/* | %(::book; ::x; ::y) @| sklearn:naive_bayes.GaussianNB.fit((^/rows/* | ::book)) | upper"#);
assert!(e.contains("is a library value") && e.contains("'| @'"), "{e}");
}
#[test]
fn numeric_text_enters_arrays_and_frames_as_numbers() {
if !installed() {
return;
}
assert_eq!(run("/csv/* | ::tip @| np:mean").unwrap(), vec!["2.5"]);
assert_eq!(run("/csv/* | ::tip @| np:percentile(50)").unwrap(), vec!["2.5"]);
let out = run(r#"/csv/* | %(::sex; ::tip) @| pd:groupby.mean(sex) @| @"#).unwrap();
assert_eq!(out, vec![r#"%(sex = "F"; tip = 2.5)"#, r#"%(sex = "M"; tip = 2.5)"#]);
assert_eq!(run(r#"/csv/* | %(::sex; ::tip) @| pd:tip.dtype"#).unwrap(), vec!["float64"]);
let a = run("/series/* | :: @| pd:Series.rolling.mean(3) @| @ @| [3]").unwrap();
let b = run("/series/* | :: @| pd:rolling.mean(3) @| @ @| [3]").unwrap();
assert_eq!(a, vec!["2"]);
assert_eq!(a, b);
}
#[test]
fn pandas_frames_chain_and_export() {
if !installed() {
return;
}
let out = run(r#"/rows/* | %(::book; ::x; ::y) @| pd:describe | pd:round(2) | [2] | :x"#)
.unwrap();
assert_eq!(out, vec!["3.75"]);
let out = run(r#"/rows/* | %(::book; ::x; ::y) @| pd:describe | pd:round(2) | header"#).unwrap();
assert_eq!(out, vec![r#"@("index"; "x"; "y")"#]);
let out = run(r#"/rows/* | %(::book; ::x; ::y) @| pd:corr(--method=spearman) @| @ @| [1]"#)
.unwrap();
assert_eq!(out, vec![r#"%(index = "x"; x = 1; y = -0.8)"#]);
let out = run(r#"/rows/* | %(::book; ::x; ::y) @| pd:sort_values(x; --ascending=false) @| @ @| [1]"#)
.unwrap();
assert_eq!(out, vec![r#"%(book = "d"; x = 6; y = 0.5)"#]);
assert_eq!(run(r#"/rows/* | %(::book; ::x; ::y) @| pd:x.sum"#).unwrap(), vec!["15"]);
let out = run(r#"/kv/* | %(::k; ::v) @| pd:groupby.sum(k) | @"#).unwrap();
assert_eq!(out, vec![r#"@(%(k = "p"; v = 5); %(k = "q"; v = 2))"#]);
let out = run(r#"/rows/*[::book = "a"] | %(::book; ::x) @| pd:round(0) | json"#).unwrap();
assert_eq!(out, vec![r#"[{"book": "a", "x": 1}]"#]);
}
#[test]
fn scipy_and_sklearn_by_reflection_beside_the_declared_commands() {
if !installed() {
return;
}
let declared = run(r#"/rows/* | %(::x) @| sp:shapiro(--digits=3) | :pvalue"#).unwrap();
let raw = run(r#"/rows/* | ::x @| sp:stats.shapiro | :pvalue | round(3)"#).unwrap();
assert_eq!(declared, raw);
let out = run(r#"= @(1; 2; 3; 4) | sp:stats.describe | :mean"#).unwrap();
assert_eq!(out, vec!["2.5"]);
assert_eq!(run("= 0 | sp:stats.norm.cdf(--loc=0 --scale=1)").unwrap(), vec!["0.5"]);
let out = run(r#"/rows/* | %(::book; ::x; ::y) @| sklearn:cluster.KMeans.fit_predict(2; --random-state=0 --n-init=1) @| @ @| count"#)
.unwrap();
assert_eq!(out, vec!["4"]);
let out = run(r#"/rows/* | %(::book; ::x; ::y) @| sklearn:decomposition.PCA.fit.explained_variance_ratio_(--n-components=2) @| @ @| count"#)
.unwrap();
assert_eq!(run("= 0 | sp:stats.norm(0; 1).cdf").is_err(), true);
assert_eq!(run("= 1.96 | sp:stats.norm.cdf | round(3)").unwrap(), vec!["0.975"]);
assert_eq!(out, vec!["2"]);
}
#[test]
fn operands_keep_their_kind_and_the_input_fills_the_data_slot() {
if !installed() {
return;
}
let x = "/rows/* | %(::book; ::x; ::y)";
let xr = "(^/rows/* | %(::book; ::x; ::y))";
let y = "(^/rows/* | ::book)";
let r = run(&format!("{x} @| sklearn:naive_bayes.GaussianNB.fit({y})")).unwrap();
assert_eq!(r, vec!["GaussianNB()"]);
let r = run(&format!(
"{x} @| sklearn:naive_bayes.GaussianNB.fit({y}) @| sklearn:naive_bayes.GaussianNB.predict({xr}) @| @"
))
.unwrap();
assert_eq!(r, vec!["a", "b", "c", "d"]);
let r = run(&format!(
"{x} @| sklearn:naive_bayes.GaussianNB.fit({y}) @| sklearn:naive_bayes.GaussianNB.score({xr}; {y})"
))
.unwrap();
assert_eq!(r, vec!["1"]);
let r = run("= 1 @| sklearn:naive_bayes.GaussianNB(--var-smoothing=0.5)").unwrap();
assert_eq!(r, vec!["GaussianNB(var_smoothing=0.5)"]);
let r = run(&format!(
"{x} @| sklearn:model_selection.cross_val_score((1 | sklearn:naive_bayes.GaussianNB); (^/rows/* | (::x < 4 ? \"lo\" : \"hi\")); --cv=2) @| @ @| count"
))
.unwrap();
assert_eq!(r, vec!["2"]);
let e = refuse(&format!("{x} @| sklearn:model_selection.cross_val_score(--cv=2)"));
assert!(
e.contains("needs 1 operand") && e.contains("cross_val_score(estimator; [X = $_]"),
"{e}"
);
let r = run(&format!(
"/rows/*[::x < 4] | %(::book; ::x; ::y) @| sp:spatial.distance.cdist({xr}) @| @ @| count"
))
.unwrap();
assert_eq!(r, vec!["2"]);
let r = run("/rows/* | %(::book; ::x; big = (::x > 2)) @| np:transpose @| @ @| [2]").unwrap();
assert_eq!(r, vec!["@(0; 1; 1; 1)"]);
let r = run("/rows/* | %(::book; ::x; big = (::x > 2)) @| pd:DataFrame | % | :big").unwrap();
assert_eq!(r, vec!["@(0; 1; 1; 1)"]);
let r = run("/rows/* | %(::book; big = (::x > 2)) @| sp:describe(--digits=2) | :mean").unwrap();
assert_eq!(r, vec!["0.75"]);
let r = run("/csv/* | ::tip @| np:add((^/csv/* | ::tip)) @| @").unwrap();
assert_eq!(r, vec!["3", "5", "7"]);
let e = refuse(&format!(
"{x} @| sklearn:naive_bayes.GaussianNB.fit({y}) @| sklearn:naive_bayes.GaussianNB.predict((^/kv/* | %(::k; v = ::v; w = (::k = \"p\" ? ::v : null))))"
));
assert!(e.contains("row q: field 'w'") && e.contains("hole"), "{e}");
}
#[test]
fn a_table_crosses_the_boundary_as_its_rows() {
if !installed() {
return;
}
let r = run("= #(%(1; 2); %(3; 4)) @| np:add((#(%(10; 20); %(30; 40)))) | @").unwrap();
assert_eq!(r, vec!["@(@(11; 22); @(33; 44))"]);
let r = run("/rows/* | %(::book; ::x; ::y) @| # @| sklearn:naive_bayes.GaussianNB.fit((^/rows/* | ::book))").unwrap();
assert_eq!(r, vec!["GaussianNB()"]);
let r = run("/rows/* | %(::book; ::x; ::y) @| sklearn:naive_bayes.GaussianNB.fit((^/rows/* | ::book)) @| sklearn:naive_bayes.GaussianNB.predict(#(@(book; x; y); %(\"z\"; 1; 2))) @| @").unwrap();
assert_eq!(r, vec!["a"]);
let r = run("/rows/* | %(::book; ::x; ::y) @| sklearn:naive_bayes.GaussianNB.fit((^/rows/* | ::book)) @| sklearn:naive_bayes.GaussianNB.predict(%(x = 6; y = 0.5)) @| @").unwrap();
assert_eq!(r, vec!["d"]);
let r = run("/rows/* | %(::book; ::x; ::y) @| sp:spatial.distance.cdist(#(@(x; y); %(1; 2))) | dim").unwrap();
assert_eq!(r, vec!["@(4; 1)"]);
let r = run("= 1 @| np:append(@(2; 3)) @| @").unwrap();
assert_eq!(r, vec!["1", "2", "3"]);
let r = run("/rows/* | %(::book; ::x; ::y) @| # @| pd:DataFrame.mean(--numeric-only=true) | @").unwrap();
assert_eq!(r, vec![r#"@(%(index = "x"; value = 3.75); %(index = "y"; value = 0.875))"#]);
}