use crate::{ dataframe::values::{Column, NuDataFrame, NuExpression}, values::{ cant_convert_err, to_pipeline_data, CustomValueSupport, PolarsPluginObject, PolarsPluginType, }, PolarsPlugin, }; use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand}; use nu_protocol::{ Category, Example, LabeledError, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value, }; #[derive(Clone)] pub struct LazyFillNA; impl PluginCommand for LazyFillNA { type Plugin = PolarsPlugin; fn name(&self) -> &str { "polars fill-nan" } fn usage(&self) -> &str { "Replaces NaN values with the given expression." } fn signature(&self) -> Signature { Signature::build(self.name()) .required( "fill", SyntaxShape::Any, "Expression to use to fill the NAN values", ) .input_output_type( Type::Custom("dataframe".into()), Type::Custom("dataframe".into()), ) .category(Category::Custom("lazyframe".into())) } fn examples(&self) -> Vec { vec![ Example { description: "Fills the NaN values with 0", example: "[1 2 NaN 3 NaN] | polars into-df | polars fill-nan 0", result: Some( NuDataFrame::try_from_columns( vec![Column::new( "0".to_string(), vec![ Value::test_int(1), Value::test_int(2), Value::test_int(0), Value::test_int(3), Value::test_int(0), ], )], None, ) .expect("Df for test should not fail") .into_value(Span::test_data()), ), }, Example { description: "Fills the NaN values of a whole dataframe", example: "[[a b]; [0.2 1] [0.1 NaN]] | polars into-df | polars fill-nan 0", result: Some( NuDataFrame::try_from_columns( vec![ Column::new( "a".to_string(), vec![Value::test_float(0.2), Value::test_float(0.1)], ), Column::new( "b".to_string(), vec![Value::test_int(1), Value::test_int(0)], ), ], None, ) .expect("Df for test should not fail") .into_value(Span::test_data()), ), }, ] } fn run( &self, plugin: &Self::Plugin, engine: &EngineInterface, call: &EvaluatedCall, input: PipelineData, ) -> Result { let fill: Value = call.req(0)?; let value = input.into_value(call.head); match PolarsPluginObject::try_from_value(plugin, &value)? { PolarsPluginObject::NuDataFrame(df) => { cmd_df(plugin, engine, call, df, fill, value.span()) } PolarsPluginObject::NuLazyFrame(lazy) => cmd_df( plugin, engine, call, lazy.collect(value.span())?, fill, value.span(), ), PolarsPluginObject::NuExpression(expr) => { Ok(cmd_expr(plugin, engine, call, expr, fill)?) } _ => Err(cant_convert_err( &value, &[ PolarsPluginType::NuDataFrame, PolarsPluginType::NuLazyFrame, PolarsPluginType::NuExpression, ], )), } .map_err(LabeledError::from) } } fn cmd_df( plugin: &PolarsPlugin, engine: &EngineInterface, call: &EvaluatedCall, frame: NuDataFrame, fill: Value, val_span: Span, ) -> Result { let columns = frame.columns(val_span)?; let dataframe = columns .into_iter() .map(|column| { let column_name = column.name().to_string(); let values = column .into_iter() .map(|value| { let span = value.span(); match value { Value::Float { val, .. } => { if val.is_nan() { fill.clone() } else { value } } Value::List { vals, .. } => { NuDataFrame::fill_list_nan(vals, span, fill.clone()) } _ => value, } }) .collect::>(); Column::new(column_name, values) }) .collect::>(); let df = NuDataFrame::try_from_columns(dataframe, None)?; to_pipeline_data(plugin, engine, call.head, df) } fn cmd_expr( plugin: &PolarsPlugin, engine: &EngineInterface, call: &EvaluatedCall, expr: NuExpression, fill: Value, ) -> Result { let fill = NuExpression::try_from_value(plugin, &fill)?.to_polars(); let expr: NuExpression = expr.to_polars().fill_nan(fill).into(); to_pipeline_data(plugin, engine, call.head, expr) } #[cfg(test)] mod test { use super::*; use crate::test::test_polars_plugin_command; #[test] fn test_examples() -> Result<(), ShellError> { test_polars_plugin_command(&LazyFillNA) } }