Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add $meta$show_graph() #64

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ Suggests:
cli,
clock,
data.table,
DiagrammeR,
DiagrammeRsvg,
hms,
jsonlite,
nanoarrow,
patrick,
rsvg,
testthat (>= 3.0.0),
tibble,
vctrs,
Expand Down
14 changes: 14 additions & 0 deletions R/000-wrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -1976,6 +1976,18 @@ class(`PlRDataType`) <- c("PlRDataType__bundle", "savvy_neopolars__sealed")
}
}

`PlRExpr_meta_show_graph` <- function(self) {
function() {
.Call(savvy_PlRExpr_meta_show_graph__impl, `self`)
}
}

`PlRExpr_meta_tree_format` <- function(self) {
function() {
.Call(savvy_PlRExpr_meta_tree_format__impl, `self`)
}
}

`PlRExpr_meta_undo_aliases` <- function(self) {
function() {
.savvy_wrap_PlRExpr(.Call(savvy_PlRExpr_meta_undo_aliases__impl, `self`))
Expand Down Expand Up @@ -3116,6 +3128,8 @@ class(`PlRDataType`) <- c("PlRDataType__bundle", "savvy_neopolars__sealed")
e$`meta_output_name` <- `PlRExpr_meta_output_name`(ptr)
e$`meta_pop` <- `PlRExpr_meta_pop`(ptr)
e$`meta_root_names` <- `PlRExpr_meta_root_names`(ptr)
e$`meta_show_graph` <- `PlRExpr_meta_show_graph`(ptr)
e$`meta_tree_format` <- `PlRExpr_meta_tree_format`(ptr)
e$`meta_undo_aliases` <- `PlRExpr_meta_undo_aliases`(ptr)
e$`min` <- `PlRExpr_min`(ptr)
e$`mode` <- `PlRExpr_mode`(ptr)
Expand Down
47 changes: 45 additions & 2 deletions R/expr-meta.R
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ expr_meta_root_names <- function() {
self$`_rexpr`$meta_root_names()
}

# TODO: add equivalent of meta.show_graph of Python Polars
#' Format the expression as a tree
#'
#' @return A character vector
Expand All @@ -195,10 +194,54 @@ expr_meta_root_names <- function() {
#' my_expr$meta$tree_format() |>
#' cat()
expr_meta_tree_format <- function() {
self$`_rexpr`$compute_tree_format(FALSE) |>
self$`_rexpr`$meta_tree_format() |>
wrap()
}

#' Format the expression as a Graphviz graph
#'
#' Note that Graphviz must be installed to render the visualization (if not
#' already present, you can download it here:
#' `<https://graphviz.org/download>`_).
Comment on lines +203 to +205
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this explanation is incorrect; Graphviz is not required.

#'
#' @inheritParams rlang::args_dots_empty
#' @param show Show the figure.
#' @param output_path Write the figure to disk.
#' @param raw_output Return dot syntax. This cannot be combined with `show` and
#' / or `output_path`.
#'
#' @return
#' If `show = TRUE`, the same output as `DiagrammeR::grViz()`.
#' If `raw_output = TRUE`, a string containing the GraphViz code for the graph.
#'
#' @examplesIf requireNamespace("DiagrammeR", quietly = TRUE)
#' my_expr <- (pl$col("foo") * pl$col("bar"))$sum()$over(pl$col("ham")) / 2
#' my_expr$meta$show_graph()
expr_meta_show_graph <- function(
...,
show = TRUE,
output_path = NULL,
raw_output = FALSE) {
wrap({
rlang::check_dots_empty0(...)
dot <- self$`_rexpr`$meta_show_graph() |>
wrap()
if (isTRUE(raw_output)) {
return(dot)
}
rlang::check_installed("DiagrammeR")
graph <- DiagrammeR::grViz(dot)
if (!is.null(output_path)) {
rlang::check_installed(c("DiagrammeRsvg", "rsvg"))
graph |>
DiagrammeRsvg::export_svg() |>
charToRaw() |>
rsvg::rsvg_svg(output_path)
}
graph
})
}

#' Indicate if this expression only selects columns (optionally with aliasing)
#'
#' This can include bare columns, column matches by regex or dtype, selectors
Expand Down
33 changes: 33 additions & 0 deletions man/expr_meta_show_graph.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,16 @@ SEXP savvy_PlRExpr_meta_root_names__impl(SEXP self__) {
return handle_result(res);
}

SEXP savvy_PlRExpr_meta_show_graph__impl(SEXP self__) {
SEXP res = savvy_PlRExpr_meta_show_graph__ffi(self__);
return handle_result(res);
}

SEXP savvy_PlRExpr_meta_tree_format__impl(SEXP self__) {
SEXP res = savvy_PlRExpr_meta_tree_format__ffi(self__);
return handle_result(res);
}

SEXP savvy_PlRExpr_meta_undo_aliases__impl(SEXP self__) {
SEXP res = savvy_PlRExpr_meta_undo_aliases__ffi(self__);
return handle_result(res);
Expand Down Expand Up @@ -2847,6 +2857,8 @@ static const R_CallMethodDef CallEntries[] = {
{"savvy_PlRExpr_meta_output_name__impl", (DL_FUNC) &savvy_PlRExpr_meta_output_name__impl, 1},
{"savvy_PlRExpr_meta_pop__impl", (DL_FUNC) &savvy_PlRExpr_meta_pop__impl, 1},
{"savvy_PlRExpr_meta_root_names__impl", (DL_FUNC) &savvy_PlRExpr_meta_root_names__impl, 1},
{"savvy_PlRExpr_meta_show_graph__impl", (DL_FUNC) &savvy_PlRExpr_meta_show_graph__impl, 1},
{"savvy_PlRExpr_meta_tree_format__impl", (DL_FUNC) &savvy_PlRExpr_meta_tree_format__impl, 1},
{"savvy_PlRExpr_meta_undo_aliases__impl", (DL_FUNC) &savvy_PlRExpr_meta_undo_aliases__impl, 1},
{"savvy_PlRExpr_min__impl", (DL_FUNC) &savvy_PlRExpr_min__impl, 1},
{"savvy_PlRExpr_mode__impl", (DL_FUNC) &savvy_PlRExpr_mode__impl, 1},
Expand Down
2 changes: 2 additions & 0 deletions src/rust/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ SEXP savvy_PlRExpr_meta_is_regex_projection__ffi(SEXP self__);
SEXP savvy_PlRExpr_meta_output_name__ffi(SEXP self__);
SEXP savvy_PlRExpr_meta_pop__ffi(SEXP self__);
SEXP savvy_PlRExpr_meta_root_names__ffi(SEXP self__);
SEXP savvy_PlRExpr_meta_show_graph__ffi(SEXP self__);
SEXP savvy_PlRExpr_meta_tree_format__ffi(SEXP self__);
SEXP savvy_PlRExpr_meta_undo_aliases__ffi(SEXP self__);
SEXP savvy_PlRExpr_min__ffi(SEXP self__);
SEXP savvy_PlRExpr_mode__ffi(SEXP self__);
Expand Down
8 changes: 8 additions & 0 deletions src/rust/src/expr/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,12 @@ impl PlRExpr {
fn meta_is_column(&self) -> Result<Sexp> {
self.inner.clone().meta().is_column().try_into()
}

fn meta_tree_format(&self) -> Result<Sexp> {
self.compute_tree_format(false)
}

fn meta_show_graph(&self) -> Result<Sexp> {
self.compute_tree_format(true)
}
Comment on lines +122 to +129
Copy link
Owner

@eitsupi eitsupi Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need to add these functions; just call <expr>$compute_tree_format(<bool>) from the R side.

}
23 changes: 23 additions & 0 deletions tests/testthat/_snaps/expr-meta.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,26 @@
}

# meta$show_graph

Code
cat(my_expr$meta$show_graph(raw_output = TRUE))
Output
graph {
n00 [label="binary: /", ordering="out"]
n00 -- n11
n00 -- n10
n10 [label="lit(dyn float: 2.0)", ordering="out"]
n11 [label="window", ordering="out"]
n11 -- n22
n11 -- n21
n21 [label="col(ham)", ordering="out"]
n22 [label="sum", ordering="out"]
n22 -- n32
n32 [label="binary: *", ordering="out"]
n32 -- n43
n32 -- n42
n42 [label="col(bar)", ordering="out"]
n43 [label="col(foo)", ordering="out"]
}

24 changes: 24 additions & 0 deletions tests/testthat/test-expr-meta.R
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,27 @@ test_that("meta$serialize", {
jsonlite::prettify()
)
})

test_that("meta$show_graph", {
skip_if_not_installed("DiagrammeR")
skip_if_not_installed("DiagrammeRsvg")
skip_if_not_installed("rsvg")

my_expr <- (pl$col("foo") * pl$col("bar"))$sum()$over(pl$col("ham")) / 2

# default
expect_equal(class(my_expr$meta$show_graph()), c("grViz", "htmlwidget"))

# raw_output
expect_snapshot(cat(my_expr$meta$show_graph(raw_output = TRUE)))

# output_path
temp <- withr::local_tempfile(fileext = ".svg")
expect_silent(my_expr$meta$show_graph(output_path = temp))
expect_true(file.exists(temp))

expect_error(
my_expr$meta$show_graph(TRUE),
"Did you forget to"
)
})
Loading