dataframe grammer
This commit is contained in:
parent
a315260faf
commit
4124f6dd88
|
@ -6,7 +6,7 @@ use std::{
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rhai::EvalAltResult;
|
use rhai::{Dynamic, EvalAltResult, EvalContext, Expression, Position};
|
||||||
|
|
||||||
pub fn setup_engine(engine: &mut rhai::Engine) {
|
pub fn setup_engine(engine: &mut rhai::Engine) {
|
||||||
//polar data frame
|
//polar data frame
|
||||||
|
@ -52,6 +52,18 @@ pub fn setup_engine(engine: &mut rhai::Engine) {
|
||||||
engine.register_fn("min", script_functions::min);
|
engine.register_fn("min", script_functions::min);
|
||||||
engine.register_fn("max", script_functions::max);
|
engine.register_fn("max", script_functions::max);
|
||||||
engine.register_fn("first", script_functions::first);
|
engine.register_fn("first", script_functions::first);
|
||||||
|
let _ = engine.register_custom_operator("gt", 200);
|
||||||
|
let _ = engine.register_custom_operator("gte", 200);
|
||||||
|
engine.register_fn("gt", script_functions::gt_op);
|
||||||
|
engine.register_fn("gte", script_functions::gte_op);
|
||||||
|
|
||||||
|
engine
|
||||||
|
.register_custom_syntax(
|
||||||
|
["from", "$ident$", "$expr$", ":", "$expr$"], // the custom syntax
|
||||||
|
true, // variables declared within this custom syntax
|
||||||
|
implementation_df_select,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -339,6 +351,82 @@ mod script_functions {
|
||||||
Ok(Series(s))
|
Ok(Series(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn gt_op(a: &str, b: rhai::Dynamic) -> DataFrameExpression {
|
||||||
|
DataFrameExpression(polars::prelude::col(a).gt(DataFrameExpression::from(b)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gte_op(a: &str, b: rhai::Dynamic) -> DataFrameExpression {
|
||||||
|
DataFrameExpression(polars::prelude::col(a).gt_eq(DataFrameExpression::from(b)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn implementation_df_select(
|
||||||
|
context: &mut EvalContext,
|
||||||
|
inputs: &[Expression],
|
||||||
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
|
let df_name = inputs[0].get_string_value().ok_or_else(|| {
|
||||||
|
Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
"variable not found".to_string(),
|
||||||
|
Position::default(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let df = context
|
||||||
|
.scope()
|
||||||
|
.get(df_name)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
format!("{} not found", df_name),
|
||||||
|
Position::default(),
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
.clone()
|
||||||
|
.try_cast::<DataFrame>()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
format!("{} not found", df_name),
|
||||||
|
Position::default(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let raw_filter_array = context.eval_expression_tree(&inputs[2])?;
|
||||||
|
let filter_array = raw_filter_array
|
||||||
|
.into_array()
|
||||||
|
.map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
format!("{} value not an array", e),
|
||||||
|
Position::default(),
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
.into_iter()
|
||||||
|
.map(|i| i.cast::<DataFrameExpression>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let raw_select_array = context.eval_expression_tree(&inputs[1])?;
|
||||||
|
let select_array = raw_select_array.into_array().map_err(|e| {
|
||||||
|
Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
format!("{} value not an array", e),
|
||||||
|
Position::default(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let select_expressions = select_array
|
||||||
|
.iter()
|
||||||
|
.map(|s| {
|
||||||
|
filter_array
|
||||||
|
.iter()
|
||||||
|
.fold(polars::prelude::col(&s.to_string()), |acc, i| {
|
||||||
|
acc.filter(i.0.clone())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Ok(Dynamic::from(DataFrame(
|
||||||
|
df.0.lazy()
|
||||||
|
.select(&select_expressions)
|
||||||
|
.collect()
|
||||||
|
.map_err(|e| e.to_string())?,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -494,4 +582,26 @@ s1 + s2
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert_eq!(s, vec![Some(18)]);
|
assert_eq!(s, vec![Some(18)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_dataframe_select_syntax() {
|
||||||
|
let res = process(
|
||||||
|
r#"
|
||||||
|
let data = load_csv("test/data.csv");
|
||||||
|
from data ["age"] : ["age" gt 18];
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
dbg!(&res);
|
||||||
|
|
||||||
|
let s = res
|
||||||
|
.into_frame()
|
||||||
|
.column("age")
|
||||||
|
.unwrap()
|
||||||
|
.i64()
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert_eq!(s, vec![Some(22), Some(32)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
name,age,
|
||||||
|
alice,18
|
||||||
|
sasha,22
|
||||||
|
lacey,32
|
|
|
@ -63,7 +63,7 @@ impl AppDelegate<AppData> for Delegate {
|
||||||
|
|
||||||
if cmd.is(commands::DELETE_BLOCK) {
|
if cmd.is(commands::DELETE_BLOCK) {
|
||||||
if let Some(index) = cmd.get(commands::DELETE_BLOCK) {
|
if let Some(index) = cmd.get(commands::DELETE_BLOCK) {
|
||||||
data.blocks.remove(*index);
|
data.remove_block(*index);
|
||||||
return druid::Handled::Yes;
|
return druid::Handled::Yes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,15 @@ pub struct AppData {
|
||||||
pub modals: Modals,
|
pub modals: Modals,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AppData {
|
||||||
|
pub fn remove_block(&mut self, idx: usize) {
|
||||||
|
self.blocks.remove(idx);
|
||||||
|
for (idx, block) in self.blocks.iter_mut().enumerate() {
|
||||||
|
block.index = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for AppData {
|
impl Default for AppData {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -41,10 +41,10 @@ impl AbacusEditor {
|
||||||
let rects = layout.rects_for_range(0..1);
|
let rects = layout.rects_for_range(0..1);
|
||||||
let rect = rects.first().unwrap();
|
let rect = rects.first().unwrap();
|
||||||
let rect = Rect::new(
|
let rect = Rect::new(
|
||||||
|
rect.min_x() - 1.0,
|
||||||
|
rect.min_y(),
|
||||||
rect.min_x() + 1.0,
|
rect.min_x() + 1.0,
|
||||||
rect.min_y() + 2.0,
|
rect.max_y(),
|
||||||
rect.min_x() + 3.0,
|
|
||||||
rect.max_y() - 2.0,
|
|
||||||
);
|
);
|
||||||
ctx.fill(
|
ctx.fill(
|
||||||
rect,
|
rect,
|
||||||
|
@ -55,10 +55,10 @@ impl AbacusEditor {
|
||||||
.last()
|
.last()
|
||||||
{
|
{
|
||||||
let cursor_rect = Rect::new(
|
let cursor_rect = Rect::new(
|
||||||
char_rect.max_x() + 3.0,
|
char_rect.max_x() - 1.0,
|
||||||
char_rect.min_y() + 2.0,
|
char_rect.min_y(),
|
||||||
char_rect.max_x() + 5.0,
|
char_rect.max_x() + 1.0,
|
||||||
char_rect.max_y() - 2.0,
|
char_rect.max_y(),
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.fill(
|
ctx.fill(
|
||||||
|
@ -204,6 +204,10 @@ impl Widget<EditorData> for AbacusEditor {
|
||||||
data.mode = EditMode::Insert;
|
data.mode = EditMode::Insert;
|
||||||
data.cursor_to_end_of_line();
|
data.cursor_to_end_of_line();
|
||||||
}
|
}
|
||||||
|
"I" => {
|
||||||
|
data.cursor_to_start_of_line();
|
||||||
|
data.mode = EditMode::Insert;
|
||||||
|
}
|
||||||
"a" => {
|
"a" => {
|
||||||
if e.mods.ctrl() {
|
if e.mods.ctrl() {
|
||||||
data.select_all();
|
data.select_all();
|
||||||
|
|
|
@ -61,6 +61,47 @@ pub fn output_block() -> impl Widget<Block> {
|
||||||
.expand_width(),
|
.expand_width(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Output::DataFrame(frame) => {
|
||||||
|
let mut flex = Flex::row();
|
||||||
|
for series in frame.iter() {
|
||||||
|
let mut col = Flex::column();
|
||||||
|
col.add_child(
|
||||||
|
Label::new(series.name())
|
||||||
|
.with_font(
|
||||||
|
FontDescriptor::new(FontFamily::MONOSPACE)
|
||||||
|
.with_weight(FontWeight::BLACK),
|
||||||
|
)
|
||||||
|
.with_text_size(OUTPUT_FONT_SIZE)
|
||||||
|
.expand_width()
|
||||||
|
.padding(3.0)
|
||||||
|
.border(Color::rgb8(60, 60, 60), 1.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
for v in series.iter() {
|
||||||
|
col.add_child(
|
||||||
|
Label::new(v.to_string())
|
||||||
|
.with_font(
|
||||||
|
FontDescriptor::new(FontFamily::MONOSPACE)
|
||||||
|
.with_weight(FontWeight::MEDIUM),
|
||||||
|
)
|
||||||
|
.with_text_size(OUTPUT_FONT_SIZE)
|
||||||
|
.expand_width()
|
||||||
|
.padding(3.0)
|
||||||
|
.border(Color::rgb8(60, 60, 60), 1.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
flex.add_flex_child(col, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Box::new(Padding::new(
|
||||||
|
25.0,
|
||||||
|
flex.padding(10.0)
|
||||||
|
.background(Color::rgb8(30, 30, 30))
|
||||||
|
.rounded(4.0)
|
||||||
|
.expand_width(),
|
||||||
|
))
|
||||||
|
}
|
||||||
_ => Box::new(Padding::new(
|
_ => Box::new(Padding::new(
|
||||||
0.0,
|
0.0,
|
||||||
Label::new("")
|
Label::new("")
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"name": "Block #1",
|
||||||
|
"content": "dataframe(#{ names: [\"Alice\", \"Bob\", \"Charles\"], ages: [18,21,35] })"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"name": "Things",
|
||||||
|
"content": "let df = dataframe(#{\n names: [\"Alice\", \"Charles\", \"Bob\"], \n ages: [18,25,31],\n id: [1,2,3],\n amount: [10,0,12]\n});\n\n//df.select([column(\"ages\").filter(column(\"ages\") gt 20)]);\n\nlet df2 = from df [\"names\", \"ages\"] : [\"ages\" gt 20];\ndf2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"name": "Things",
|
||||||
|
"content": "let x = 1;\nx+1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue