Skip to content

Commit

Permalink
native exp expression (#3629)
Browse files Browse the repository at this point in the history
  • Loading branch information
ritchie46 committed Jun 8, 2022
1 parent f114082 commit 37f4a46
Show file tree
Hide file tree
Showing 14 changed files with 91 additions and 18 deletions.
2 changes: 1 addition & 1 deletion polars/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ dot_diagram = ["polars-lazy/dot_diagram"]
dataframe_arithmetic = ["polars-core/dataframe_arithmetic"]
product = ["polars-core/product"]
unique_counts = ["polars-core/unique_counts", "polars-lazy/unique_counts"]
log = ["polars-core/log", "polars-lazy/log"]
log = ["polars-ops/log", "polars-lazy/log"]
partition_by = ["polars-core/partition_by"]
semi_anti_join = ["polars-core/semi_anti_join"]
list_eval = ["polars-lazy/list_eval"]
Expand Down
2 changes: 0 additions & 2 deletions polars/polars-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ ewma = []
dataframe_arithmetic = []
product = []
unique_counts = []
log = []
partition_by = []
semi_anti_join = []
chunked_ids = []
Expand Down Expand Up @@ -139,7 +138,6 @@ docs-selection = [
"string_encoding",
"product",
"unique_counts",
"log",
"describe",
"chunked_ids",
"semi_anti_join",
Expand Down
3 changes: 0 additions & 3 deletions polars/polars-core/src/series/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ pub mod diff;
#[cfg_attr(docsrs, doc(cfg(feature = "ewma")))]
mod ewm;
mod extend;
#[cfg(feature = "log")]
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
mod log;
#[cfg(feature = "moment")]
#[cfg_attr(docsrs, doc(cfg(feature = "moment")))]
pub mod moment;
Expand Down
2 changes: 1 addition & 1 deletion polars/polars-lazy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ dynamic_groupby = ["polars-core/dynamic_groupby"]
ewma = ["polars-core/ewma"]
dot_diagram = []
unique_counts = ["polars-core/unique_counts"]
log = ["polars-core/log"]
log = ["polars-ops/log"]
list_eval = []
cumulative_eval = []
chunked_ids = []
Expand Down
17 changes: 17 additions & 0 deletions polars/polars-lazy/src/dsl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1967,6 +1967,23 @@ impl Expr {
.with_fmt("log")
}

#[cfg(feature = "log")]
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
/// Calculate the exponential of all elements in the input array
pub fn exp(self) -> Self {
self.map(
move |s| Ok(s.exp()),
GetOutput::map_dtype(|dt| {
if matches!(dt, DataType::Float32) {
DataType::Float32
} else {
DataType::Float64
}
}),
)
.with_fmt("log")
}

#[cfg(feature = "log")]
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
/// Compute the entropy as `-sum(pk * log(pk)`.
Expand Down
2 changes: 2 additions & 0 deletions polars/polars-lazy/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub struct RollingGroupOptions {
pub index_column: String,
}

pub(crate) use polars_ops::prelude::*;

pub use crate::{
dsl::*,
frame::*,
Expand Down
3 changes: 3 additions & 0 deletions polars/polars-ops/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ dtype-duration = ["polars-core/dtype-duration", "polars-core/temporal"]
dtype-struct = ["polars-core/dtype-struct", "polars-core/temporal"]
dtype-u8 = ["polars-core/dtype-u8"]
object = ["polars-core/object"]

# ops
to_dummies = []
list_to_struct = ["polars-core/dtype-struct", "list"]
list = []
diff = []
strings = ["polars-core/strings"]
string_justify = ["polars-core/strings"]
log = []
3 changes: 3 additions & 0 deletions polars/polars-ops/src/series/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod _trait;
mod implementations;
mod ops;

pub use self::_trait::*;
use polars_core::prelude::*;
use polars_core::utils::Wrap;
Expand All @@ -10,6 +12,7 @@ type SeriesOpsRef = Arc<dyn SeriesOps>;
pub trait IntoSeriesOps {
fn to_ops(&self) -> SeriesOpsRef;
}
pub use ops::*;

impl IntoSeriesOps for Series {
fn to_ops(&self) -> SeriesOpsRef {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use crate::prelude::*;
use crate::series::ops::SeriesSealed;
use polars_core::prelude::*;

fn log<T: PolarsNumericType>(ca: &ChunkedArray<T>, base: f64) -> Float64Chunked {
ca.cast_and_apply_in_place(|v: f64| v.log(base))
}

impl Series {
fn exp<T: PolarsNumericType>(ca: &ChunkedArray<T>) -> Float64Chunked {
ca.cast_and_apply_in_place(|v: f64| v.exp())
}

pub trait LogSeries: SeriesSealed {
/// Compute the logarithm to a given base
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
pub fn log(&self, base: f64) -> Series {
let s = self.to_physical_repr();
fn log(&self, base: f64) -> Series {
let s = self.as_series().to_physical_repr();
let s = s.as_ref();

use DataType::*;
Expand All @@ -19,17 +24,36 @@ impl Series {
UInt64 => log(s.u64().unwrap(), base).into_series(),
Float32 => s.f32().unwrap().apply(|v| v.log(base as f32)).into_series(),
Float64 => s.f64().unwrap().apply(|v| v.log(base)).into_series(),
_ => unimplemented!(),
_ => s.cast(&DataType::Float64).unwrap().log(base),
}
}

#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
/// Calculate the exponential of all elements in the input array.
fn exp(&self) -> Series {
let s = self.as_series().to_physical_repr();
let s = s.as_ref();

use DataType::*;
match s.dtype() {
Int32 => exp(s.i32().unwrap()).into_series(),
Int64 => exp(s.i64().unwrap()).into_series(),
UInt32 => exp(s.u32().unwrap()).into_series(),
UInt64 => exp(s.u64().unwrap()).into_series(),
Float32 => s.f32().unwrap().apply(|v| v.exp()).into_series(),
Float64 => s.f64().unwrap().apply(|v| v.exp()).into_series(),
_ => s.cast(&DataType::Float64).unwrap().exp(),
}
}

/// Compute the entropy as `-sum(pk * log(pk)`.
/// where `pk` are discrete probabilities.
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
pub fn entropy(&self, base: f64, normalize: bool) -> Option<f64> {
match self.dtype() {
fn entropy(&self, base: f64, normalize: bool) -> Option<f64> {
let s = self.as_series().to_physical_repr();
match s.dtype() {
DataType::Float32 | DataType::Float64 => {
let pk = self;
let pk = s.as_ref();

let pk = if normalize {
let sum = pk.sum_as_series();
Expand All @@ -46,10 +70,12 @@ impl Series {
let log_pk = pk.log(base);
(&pk * &log_pk).sum::<f64>().map(|v| -v)
}
_ => self
_ => s
.cast(&DataType::Float64)
.ok()
.and_then(|s| s.entropy(base, normalize)),
}
}
}

impl LogSeries for Series {}
15 changes: 15 additions & 0 deletions polars/polars-ops/src/series/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#[cfg(feature = "log")]
mod log;
#[cfg(feature = "log")]
pub use log::*;
use polars_core::prelude::*;

pub trait SeriesSealed {
fn as_series(&self) -> &Series;
}

impl SeriesSealed for Series {
fn as_series(&self) -> &Series {
self
}
}
2 changes: 1 addition & 1 deletion py-polars/polars/internals/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def exp(self) -> "Expr":
"""
Return the exponential element-wise
"""
return np.exp(self) # type: ignore
return wrap_expr(self._pyexpr.exp())

def alias(self, name: str) -> "Expr":
"""
Expand Down
2 changes: 1 addition & 1 deletion py-polars/polars/internals/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ def exp(self) -> "Series":
"""
Return the exponential element-wise
"""
return np.exp(self) # type: ignore
return self.to_frame().select(pli.col(self.name).exp()).to_series()

def drop_nulls(self) -> "Series":
"""
Expand Down
4 changes: 4 additions & 0 deletions py-polars/src/lazy/dsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,10 @@ impl PyExpr {
self.inner.clone().log(base).into()
}

pub fn exp(&self) -> Self {
self.inner.clone().exp().into()
}

pub fn entropy(&self, base: f64, normalize: bool) -> Self {
self.inner.clone().entropy(base, normalize).into()
}
Expand Down
8 changes: 8 additions & 0 deletions py-polars/tests/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,14 @@ def test_sign() -> None:
verify_series_and_expr_api(a, expected, "sign")


def test_exp() -> None:
a = pl.Series("a", [0.1, 0.01, None])
expected = pl.Series("a", [1.1051709180756477, 1.010050167084168, None])
verify_series_and_expr_api(a, expected, "exp")
# test if we can run on empty series as well.
assert a[:0].exp().to_list() == []


def test_cumulative_eval() -> None:
s = pl.Series("values", [1, 2, 3, 4, 5])
expr = pl.element().first() - pl.element().last() ** 2
Expand Down

0 comments on commit 37f4a46

Please sign in to comment.