nu_command/math/
stddev.rs1use super::variance::compute_variance as variance;
2use crate::math::utils::run_with_function;
3use nu_engine::command_prelude::*;
4
5#[derive(Clone)]
6pub struct MathStddev;
7
8impl Command for MathStddev {
9 fn name(&self) -> &str {
10 "math stddev"
11 }
12
13 fn signature(&self) -> Signature {
14 Signature::build("math stddev")
15 .input_output_types(vec![
16 (Type::List(Box::new(Type::Number)), Type::Number),
17 (Type::Range, Type::Number),
18 (Type::table(), Type::record()),
19 (Type::record(), Type::record()),
20 ])
21 .switch(
22 "sample",
23 "calculate sample standard deviation (i.e. using N-1 as the denominator)",
24 Some('s'),
25 )
26 .allow_variants_without_examples(true)
27 .category(Category::Math)
28 }
29
30 fn description(&self) -> &str {
31 "Returns the standard deviation of a list of numbers, or of each column in a table."
32 }
33
34 fn search_terms(&self) -> Vec<&str> {
35 vec![
36 "SD",
37 "standard",
38 "deviation",
39 "dispersion",
40 "variation",
41 "statistics",
42 ]
43 }
44
45 fn is_const(&self) -> bool {
46 true
47 }
48
49 fn run(
50 &self,
51 engine_state: &EngineState,
52 stack: &mut Stack,
53 call: &Call,
54 input: PipelineData,
55 ) -> Result<PipelineData, ShellError> {
56 let sample = call.has_flag(engine_state, stack, "sample")?;
57 let name = call.head;
58 let span = input.span().unwrap_or(name);
59 let input: PipelineData = match input.try_expand_range() {
60 Err(_) => {
61 return Err(ShellError::IncorrectValue {
62 msg: "Range must be bounded".to_string(),
63 val_span: span,
64 call_span: name,
65 });
66 }
67 Ok(val) => val,
68 };
69 run_with_function(call, input, compute_stddev(sample))
70 }
71
72 fn run_const(
73 &self,
74 working_set: &StateWorkingSet,
75 call: &Call,
76 input: PipelineData,
77 ) -> Result<PipelineData, ShellError> {
78 let sample = call.has_flag_const(working_set, "sample")?;
79 let name = call.head;
80 let span = input.span().unwrap_or(name);
81 let input: PipelineData = match input.try_expand_range() {
82 Err(_) => {
83 return Err(ShellError::IncorrectValue {
84 msg: "Range must be bounded".to_string(),
85 val_span: span,
86 call_span: name,
87 });
88 }
89 Ok(val) => val,
90 };
91 run_with_function(call, input, compute_stddev(sample))
92 }
93
94 fn examples(&self) -> Vec<Example> {
95 vec![
96 Example {
97 description: "Compute the standard deviation of a list of numbers",
98 example: "[1 2 3 4 5] | math stddev",
99 result: Some(Value::test_float(std::f64::consts::SQRT_2)),
100 },
101 Example {
102 description: "Compute the sample standard deviation of a list of numbers",
103 example: "[1 2 3 4 5] | math stddev --sample",
104 result: Some(Value::test_float(1.5811388300841898)),
105 },
106 Example {
107 description: "Compute the standard deviation of each column in a table",
108 example: "[[a b]; [1 2] [3 4]] | math stddev",
109 result: Some(Value::test_record(record! {
110 "a" => Value::test_int(1),
111 "b" => Value::test_int(1),
112 })),
113 },
114 ]
115 }
116}
117
118pub fn compute_stddev(sample: bool) -> impl Fn(&[Value], Span, Span) -> Result<Value, ShellError> {
119 move |values: &[Value], span: Span, head: Span| {
120 let variance = variance(sample)(values, span, head)?;
122 let val_span = variance.span();
123 match variance {
124 Value::Float { val, .. } => Ok(Value::float(val.sqrt(), val_span)),
125 Value::Int { val, .. } => Ok(Value::float((val as f64).sqrt(), val_span)),
126 other => Ok(other),
127 }
128 }
129}
130
131#[cfg(test)]
132mod test {
133 use super::*;
134
135 #[test]
136 fn test_examples() {
137 use crate::test_examples;
138
139 test_examples(MathStddev {})
140 }
141}