Skip to content

Commit

Permalink
feat: add a new option to export decimal as character
Browse files Browse the repository at this point in the history
  • Loading branch information
eitsupi committed Sep 13, 2024
1 parent aa36918 commit 28eae28
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 10 deletions.
4 changes: 2 additions & 2 deletions R/000-wrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -1406,9 +1406,9 @@ class(`PlRLazyGroupBy`) <- "PlRLazyGroupBy__bundle"
}

`PlRSeries_to_r_vector` <- function(self) {
function(`ensure_vector`, `int64`, `struct`, `as_clock_class`, `ambiguous`, `non_existent`, `local_time_zone`) {
function(`ensure_vector`, `int64`, `struct`, `decimal`, `as_clock_class`, `ambiguous`, `non_existent`, `local_time_zone`) {
`ambiguous` <- .savvy_extract_ptr(`ambiguous`, "PlRExpr")
.Call(savvy_PlRSeries_to_r_vector__impl, `self`, `ensure_vector`, `int64`, `struct`, `as_clock_class`, `ambiguous`, `non_existent`, `local_time_zone`)
.Call(savvy_PlRSeries_to_r_vector__impl, `self`, `ensure_vector`, `int64`, `struct`, `decimal`, `as_clock_class`, `ambiguous`, `non_existent`, `local_time_zone`)
}
}

Expand Down
4 changes: 4 additions & 0 deletions R/dataframe-s3-base.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ as.list.polars_data_frame <- function(
as_series = FALSE,
int64 = "double",
struct = "dataframe",
decimal = "double",
as_clock_class = FALSE,
ambiguous = "raise",
non_existent = "raise") {
Expand All @@ -47,6 +48,7 @@ as.list.polars_data_frame <- function(
x$to_r_list(
int64 = int64,
struct = struct,
decimal = decimal,
as_clock_class = as_clock_class,
ambiguous = ambiguous,
non_existent = non_existent
Expand All @@ -69,13 +71,15 @@ as.list.polars_data_frame <- function(
as.data.frame.polars_data_frame <- function(
x, ...,
int64 = "double",
decimal = "double",
as_clock_class = FALSE,
ambiguous = "raise",
non_existent = "raise") {
x$to_struct()$to_r_vector(
ensure_vector = FALSE,
int64 = int64,
struct = "dataframe",
decimal = decimal,
as_clock_class = as_clock_class,
ambiguous = ambiguous,
non_existent = non_existent
Expand Down
2 changes: 2 additions & 0 deletions R/dataframe-s3-tibble.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ as_tibble.polars_data_frame <- function(
x, ...,
.name_repair = "check_unique",
int64 = "double",
decimal = "double",
as_clock_class = FALSE,
ambiguous = "raise",
non_existent = "raise") {
x$to_struct()$to_r_vector(
ensure_vector = FALSE,
int64 = int64,
struct = "tibble",
decimal = decimal,
as_clock_class = as_clock_class,
ambiguous = ambiguous,
non_existent = non_existent
Expand Down
4 changes: 3 additions & 1 deletion R/dataframe-to_r_list.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dataframe__to_r_list <- function(
int64 = "double",
as_clock_class = FALSE,
struct = "dataframe",
decimal = "double",
ambiguous = "raise",
non_existent = "raise") {
wrap({
Expand All @@ -30,8 +31,9 @@ dataframe__to_r_list <- function(
self$to_struct()$to_r_vector(
ensure_vector = TRUE,
int64 = int64,
as_clock_class = as_clock_class,
struct = struct,
decimal = decimal,
as_clock_class = as_clock_class,
ambiguous = ambiguous,
non_existent = non_existent
)
Expand Down
6 changes: 6 additions & 0 deletions R/series-to_r_vector.R
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
#' - `"dataframe"` (default): Convert to the R's [data.frame] class.
#' - `"tibble"`: Convert to the [tibble][tibble::tbl_df] class.
#' If the [tibble][tibble::tibble-package] package is not installed, a warning will be shown.
#' @param decimal Determine how to convert Polars' Decimal type values to R type.
#' One of the followings:
#' - `"double"` (default): Convert to the R's [double] type.
#' - `"character"`: Convert to the R's [character] type.
#' @param as_clock_class A logical value indicating whether to export datetimes and duration as
#' the [clock][clock::clock] package's classes.
#' - `FALSE` (default): Duration values are exported as [difftime]
Expand Down Expand Up @@ -157,6 +161,7 @@ series__to_r_vector <- function(
ensure_vector = FALSE,
int64 = "double",
struct = "dataframe",
decimal = "double",
as_clock_class = FALSE,
ambiguous = "raise",
non_existent = "raise") {
Expand Down Expand Up @@ -185,6 +190,7 @@ series__to_r_vector <- function(
ensure_vector = ensure_vector,
int64 = int64,
struct = struct,
decimal = decimal,
as_clock_class = as_clock_class,
ambiguous = ambiguous,
non_existent = non_existent,
Expand Down
8 changes: 8 additions & 0 deletions man/dataframe__to_r_list.Rd

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

8 changes: 8 additions & 0 deletions man/s3_as.data.frame.Rd

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

8 changes: 8 additions & 0 deletions man/s3_as.list.Rd

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

8 changes: 8 additions & 0 deletions man/s3_as_tibble.Rd

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

8 changes: 8 additions & 0 deletions man/series__to_r_vector.Rd

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

6 changes: 3 additions & 3 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -874,8 +874,8 @@ SEXP savvy_PlRSeries_new_i64_from_clock_pair__impl(SEXP c_arg__name, SEXP c_arg_
return handle_result(res);
}

SEXP savvy_PlRSeries_to_r_vector__impl(SEXP self__, SEXP c_arg__ensure_vector, SEXP c_arg__int64, SEXP c_arg__struct, SEXP c_arg__as_clock_class, SEXP c_arg__ambiguous, SEXP c_arg__non_existent, SEXP c_arg__local_time_zone) {
SEXP res = savvy_PlRSeries_to_r_vector__ffi(self__, c_arg__ensure_vector, c_arg__int64, c_arg__struct, c_arg__as_clock_class, c_arg__ambiguous, c_arg__non_existent, c_arg__local_time_zone);
SEXP savvy_PlRSeries_to_r_vector__impl(SEXP self__, SEXP c_arg__ensure_vector, SEXP c_arg__int64, SEXP c_arg__struct, SEXP c_arg__decimal, SEXP c_arg__as_clock_class, SEXP c_arg__ambiguous, SEXP c_arg__non_existent, SEXP c_arg__local_time_zone) {
SEXP res = savvy_PlRSeries_to_r_vector__ffi(self__, c_arg__ensure_vector, c_arg__int64, c_arg__struct, c_arg__decimal, c_arg__as_clock_class, c_arg__ambiguous, c_arg__non_existent, c_arg__local_time_zone);
return handle_result(res);
}

Expand Down Expand Up @@ -1139,7 +1139,7 @@ static const R_CallMethodDef CallEntries[] = {
{"savvy_PlRSeries_new_binary__impl", (DL_FUNC) &savvy_PlRSeries_new_binary__impl, 2},
{"savvy_PlRSeries_new_series_list__impl", (DL_FUNC) &savvy_PlRSeries_new_series_list__impl, 3},
{"savvy_PlRSeries_new_i64_from_clock_pair__impl", (DL_FUNC) &savvy_PlRSeries_new_i64_from_clock_pair__impl, 4},
{"savvy_PlRSeries_to_r_vector__impl", (DL_FUNC) &savvy_PlRSeries_to_r_vector__impl, 8},
{"savvy_PlRSeries_to_r_vector__impl", (DL_FUNC) &savvy_PlRSeries_to_r_vector__impl, 9},
{"savvy_PlRSeries_print__impl", (DL_FUNC) &savvy_PlRSeries_print__impl, 1},
{"savvy_PlRSeries_struct_unnest__impl", (DL_FUNC) &savvy_PlRSeries_struct_unnest__impl, 1},
{"savvy_PlRSeries_struct_fields__impl", (DL_FUNC) &savvy_PlRSeries_struct_fields__impl, 1},
Expand Down
2 changes: 1 addition & 1 deletion src/rust/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ SEXP savvy_PlRSeries_new_single_binary__ffi(SEXP c_arg__name, SEXP c_arg__values
SEXP savvy_PlRSeries_new_binary__ffi(SEXP c_arg__name, SEXP c_arg__values);
SEXP savvy_PlRSeries_new_series_list__ffi(SEXP c_arg__name, SEXP c_arg__values, SEXP c_arg__strict);
SEXP savvy_PlRSeries_new_i64_from_clock_pair__ffi(SEXP c_arg__name, SEXP c_arg__left, SEXP c_arg__right, SEXP c_arg__precision);
SEXP savvy_PlRSeries_to_r_vector__ffi(SEXP self__, SEXP c_arg__ensure_vector, SEXP c_arg__int64, SEXP c_arg__struct, SEXP c_arg__as_clock_class, SEXP c_arg__ambiguous, SEXP c_arg__non_existent, SEXP c_arg__local_time_zone);
SEXP savvy_PlRSeries_to_r_vector__ffi(SEXP self__, SEXP c_arg__ensure_vector, SEXP c_arg__int64, SEXP c_arg__struct, SEXP c_arg__decimal, SEXP c_arg__as_clock_class, SEXP c_arg__ambiguous, SEXP c_arg__non_existent, SEXP c_arg__local_time_zone);
SEXP savvy_PlRSeries_print__ffi(SEXP self__);
SEXP savvy_PlRSeries_struct_unnest__ffi(SEXP self__);
SEXP savvy_PlRSeries_struct_fields__ffi(SEXP self__);
Expand Down
31 changes: 28 additions & 3 deletions src/rust/src/series/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ enum StructConversion {
Tibble,
}

#[derive(Debug, Clone, EnumString)]
#[strum(serialize_all = "lowercase")]
enum DecimalConversion {
Character,
Double,
}

// `vctrs::unspecified` like function
fn vctrs_unspecified_sexp(n: usize) -> Sexp {
let mut sexp = OwnedLogicalSexp::new(n).unwrap();
Expand Down Expand Up @@ -51,6 +58,7 @@ impl PlRSeries {
ensure_vector: bool,
int64: &str,
r#struct: &str,
decimal: &str,
as_clock_class: bool,
ambiguous: &PlRExpr,
non_existent: &str,
Expand All @@ -69,6 +77,11 @@ impl PlRSeries {
"Argument `struct` must be one of ('dataframe', 'tibble')".to_string(),
)
})?;
let decimal = DecimalConversion::try_from(decimal).map_err(|_| {
savvy::Error::from(
"Argument `decimal` must be one of ('character', 'double')".to_string(),
)
})?;
let ambiguous = ambiguous.inner.clone();
let non_existent = <Wrap<NonExistent>>::try_from(non_existent)?.0;

Expand All @@ -77,6 +90,7 @@ impl PlRSeries {
ensure_vector: bool,
int64: Int64Conversion,
r#struct: StructConversion,
decimal: DecimalConversion,
as_clock_class: bool,
ambiguous: Expr,
non_existent: NonExistent,
Expand Down Expand Up @@ -128,6 +142,7 @@ impl PlRSeries {
false,
int64.clone(),
r#struct.clone(),
decimal.clone(),
as_clock_class,
ambiguous.clone(),
non_existent,
Expand All @@ -145,6 +160,7 @@ impl PlRSeries {
false,
int64.clone(),
r#struct.clone(),
decimal.clone(),
as_clock_class,
ambiguous.clone(),
non_existent,
Expand All @@ -168,6 +184,7 @@ impl PlRSeries {
false,
int64.clone(),
r#struct.clone(),
decimal.clone(),
as_clock_class,
ambiguous.clone(),
non_existent,
Expand All @@ -185,6 +202,7 @@ impl PlRSeries {
false,
int64.clone(),
r#struct.clone(),
decimal.clone(),
as_clock_class,
ambiguous.clone(),
non_existent,
Expand Down Expand Up @@ -237,9 +255,14 @@ impl PlRSeries {
}
}
}
DataType::Decimal(_, _) => Ok(<Sexp>::from(Wrap(
series.cast(&DataType::Float64).unwrap().f64().unwrap(),
))),
DataType::Decimal(_, _) => match decimal {
DecimalConversion::Character => Ok(<Sexp>::from(Wrap(
series.cast(&DataType::String).unwrap().str().unwrap(),
))),
DecimalConversion::Double => Ok(<Sexp>::from(Wrap(
series.cast(&DataType::Float64).unwrap().f64().unwrap(),
))),
},
DataType::String => Ok(<Sexp>::from(Wrap(series.str().unwrap()))),
DataType::Struct(_) => {
let df = series
Expand Down Expand Up @@ -269,6 +292,7 @@ impl PlRSeries {
false,
int64.clone(),
r#struct.clone(),
decimal.clone(),
as_clock_class,
ambiguous.clone(),
non_existent,
Expand Down Expand Up @@ -297,6 +321,7 @@ impl PlRSeries {
ensure_vector,
int64,
r#struct,
decimal,
as_clock_class,
ambiguous,
non_existent,
Expand Down
28 changes: 28 additions & 0 deletions tests/testthat/test-series-to_r_vector.R
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,34 @@ test_that("struct argument warning and error", {
)
})

patrick::with_parameters_test_that(
"decimal conversion",
.cases = {
tibble::tribble(
~.test_name, ~type, ~expected_out,
"double", "double", c(NA, 1, 0.1),
"character", "character", c(NA, "1.000", "0.100"),
)
},
code = {
series_decimal <- as_polars_series(c(NA, 1, 0.1))$cast(pl$Decimal(5, 3))

out <- series_decimal$to_r_vector(decimal = type)
expect_identical(out, expected_out)
}
)

test_that("decimal argument error", {
expect_error(
as_polars_series(1)$to_r_vector(decimal = TRUE),
r"(Argument `decimal` must be character)"
)
expect_error(
as_polars_series(1)$to_r_vector(decimal = "foo"),
r"(must be one of \('character', 'double'\))"
)
})

patrick::with_parameters_test_that("datetime conversion to clock classes",
.cases = {
skip_if_not_installed("clock")
Expand Down

0 comments on commit 28eae28

Please sign in to comment.