TemporalPropertyView {
unique_props.into_iter().collect()
}
+ /// Compute the sum of all property values, or `None` if the dtype is not additive or the
+ /// property is empty.
+ pub fn sum(&self) -> Option {
+ generalised_reduce(self.values(), |a, b| a.add(b), |p| p.dtype().has_add())
+ }
+
+ /// Find the minimum `(time, value)` pair, or `None` if the dtype is not comparable or the
+ /// property is empty.
+ pub fn min(&self) -> Option<(EventTime, Prop)> {
+ generalised_reduce(
+ self.iter(),
+ |a, b| {
+ if a.1.partial_cmp(&b.1)?.is_le() {
+ Some(a)
+ } else {
+ Some(b)
+ }
+ },
+ |(_, v)| v.dtype().has_cmp(),
+ )
+ }
+
+ /// Find the maximum `(time, value)` pair, or `None` if the dtype is not comparable or the
+ /// property is empty.
+ pub fn max(&self) -> Option<(EventTime, Prop)> {
+ generalised_reduce(
+ self.iter(),
+ |a, b| {
+ if a.1.partial_cmp(&b.1)?.is_ge() {
+ Some(a)
+ } else {
+ Some(b)
+ }
+ },
+ |(_, v)| v.dtype().has_cmp(),
+ )
+ }
+
+ /// Count the number of property updates.
+ pub fn count(&self) -> usize {
+ self.iter().count()
+ }
+
+ /// Compute the mean of all property values as an `F64` Prop, or `None` if any value cannot be
+ /// converted to `f64` or the property is empty.
+ pub fn mean(&self) -> Option {
+ let mut iter = self.values();
+ let mut sum = iter.next()?.as_f64()?;
+ let mut count = 1usize;
+ for value in iter {
+ sum += value.as_f64()?;
+ count += 1;
+ }
+ Some(Prop::F64(sum / count as f64))
+ }
+
+ /// Alias for `mean`.
+ pub fn average(&self) -> Option {
+ self.mean()
+ }
+
+ /// Compute the median `(time, value)` pair (lower median on even-length inputs), or `None` if
+ /// the dtype is not comparable or the property is empty.
+ pub fn median(&self) -> Option<(EventTime, Prop)> {
+ let mut sorted: Vec<(EventTime, Prop)> = self.iter().collect();
+ if !sorted.first()?.1.dtype().has_cmp() {
+ return None;
+ }
+ sorted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
+ let len = sorted.len();
+ Some(sorted.swap_remove((len - 1) / 2))
+ }
+
pub fn ordered_dedupe(&self, latest_time: bool) -> Vec<(EventTime, Prop)> {
let mut last_seen_value: Option = None;
let mut result: Vec<(EventTime, Prop)> = vec![];
@@ -310,3 +383,16 @@ impl PropArrayUnwrap for TemporalPropertyView<
self.latest().into_array()
}
}
+
+fn generalised_reduce(
+ data: impl IntoIterator- ,
+ op: impl Fn(V, V) -> Option,
+ check: impl Fn(&V) -> bool,
+) -> Option {
+ let mut iter = data.into_iter();
+ let first = iter.next()?;
+ if !check(&first) {
+ return None;
+ }
+ iter.try_fold(first, op)
+}
diff --git a/raphtory/src/python/graph/properties/temporal_props.rs b/raphtory/src/python/graph/properties/temporal_props.rs
index d304e99d55..1d2e4251bb 100644
--- a/raphtory/src/python/graph/properties/temporal_props.rs
+++ b/raphtory/src/python/graph/properties/temporal_props.rs
@@ -303,7 +303,7 @@ impl PyTemporalProp {
/// Returns:
/// PropValue: The sum of all property values.
pub fn sum(&self) -> Option {
- compute_generalised_sum(self.prop.values(), |a, b| a.add(b), |d| d.dtype().has_add())
+ self.prop.sum()
}
/// Find the minimum property value and its associated time.
@@ -311,17 +311,7 @@ impl PyTemporalProp {
/// Returns:
/// Tuple[EventTime, PropValue]: A tuple containing the time and the minimum property value.
pub fn min(&self) -> Option<(EventTime, Prop)> {
- compute_generalised_sum(
- self.prop.iter(),
- |a, b| {
- if a.1.partial_cmp(&b.1)?.is_le() {
- Some(a)
- } else {
- Some(b)
- }
- },
- |d| d.1.dtype().has_cmp(),
- )
+ self.prop.min()
}
/// Find the maximum property value and its associated time.
@@ -329,17 +319,7 @@ impl PyTemporalProp {
/// Returns:
/// Tuple[EventTime, PropValue]: A tuple containing the time and the maximum property value.
pub fn max(&self) -> Option<(EventTime, Prop)> {
- compute_generalised_sum(
- self.prop.iter(),
- |a, b| {
- if a.1.partial_cmp(&b.1)?.is_ge() {
- Some(a)
- } else {
- Some(b)
- }
- },
- |d| d.1.dtype().has_cmp(),
- )
+ self.prop.max()
}
/// Count the number of properties.
@@ -347,7 +327,7 @@ impl PyTemporalProp {
/// Returns:
/// int: The number of properties.
pub fn count(&self) -> usize {
- self.prop.iter().count()
+ self.prop.count()
}
/// Compute the average of all property values. Alias for mean().
@@ -355,7 +335,7 @@ impl PyTemporalProp {
/// Returns:
/// PropValue: The average of each property values, or None if count is zero.
pub fn average(&self) -> Option {
- self.mean()
+ self.prop.average()
}
/// Compute the mean of all property values. Alias for mean().
@@ -363,7 +343,7 @@ impl PyTemporalProp {
/// Returns:
/// PropValue: The mean of each property values, or None if count is zero.
pub fn mean(&self) -> Option {
- compute_mean(self.prop.values())
+ self.prop.mean()
}
/// Compute the median of all property values.
@@ -371,17 +351,7 @@ impl PyTemporalProp {
/// Returns:
/// Tuple[EventTime, PropValue]: A tuple containing the time and the median property value, or None if empty
pub fn median(&self) -> Option<(EventTime, Prop)> {
- let mut sorted: Vec<(EventTime, Prop)> = self.prop.iter().collect();
- if !sorted.first()?.1.dtype().has_cmp() {
- return None;
- }
- sorted.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
- let len = sorted.len();
- if len == 0 {
- None
- } else {
- Some(sorted[(len - 1) / 2].clone())
- }
+ self.prop.median()
}
pub fn __repr__(&self) -> String {