From 48e2adc69963f090bcfbfbf7dd89fd2c1b849d45 Mon Sep 17 00:00:00 2001 From: skymanbp <57272723+skymanbp@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:33:14 -0400 Subject: [PATCH] fix: pow and daysBetween are not commutative --- .../src/DataFrame/Functions.hs | 4 +-- docs/base/haskell_for_data_analysis.md | 2 +- docs/haskell_for_data_analysis.md | 2 +- tests/Functions.hs | 33 +++++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/dataframe-operations/src/DataFrame/Functions.hs b/dataframe-operations/src/DataFrame/Functions.hs index e9f0803e..09b52318 100644 --- a/dataframe-operations/src/DataFrame/Functions.hs +++ b/dataframe-operations/src/DataFrame/Functions.hs @@ -460,7 +460,7 @@ zScore :: Expr Double -> Expr Double zScore c = (c - mean c) / stddev c pow :: (Columnable a, Num a) => Expr a -> Int -> Expr a -pow expr i = lift2Decorated (^) "pow" (Just "^") True 8 expr (Lit i) +pow expr i = lift2Decorated (^) "pow" (Just "^") False 8 expr (Lit i) {-# SPECIALIZE pow :: Expr Double -> Int -> Expr Double #-} {-# SPECIALIZE pow :: Expr Float -> Int -> Expr Float #-} {-# SPECIALIZE pow :: Expr Int -> Int -> Expr Int #-} @@ -645,7 +645,7 @@ daysBetween = (\d1 d2 -> fromIntegral (diffDays d1 d2)) "daysBetween" Nothing - True + False 2 bind :: diff --git a/docs/base/haskell_for_data_analysis.md b/docs/base/haskell_for_data_analysis.md index 6e463905..987f8462 100644 --- a/docs/base/haskell_for_data_analysis.md +++ b/docs/base/haskell_for_data_analysis.md @@ -661,7 +661,7 @@ TIO.putStrLn $ D.toMarkdown withDates ```haskell -- This requires two Day columns; here we illustrate the pattern: --- D.derive "days_until" (F.daysBetween today_col deadline_col) df +-- D.derive "days_until" (F.daysBetween deadline_col today_col) df ``` ### Opting into stronger type safety diff --git a/docs/haskell_for_data_analysis.md b/docs/haskell_for_data_analysis.md index 6f6f180d..fc9a66ff 100644 --- a/docs/haskell_for_data_analysis.md +++ b/docs/haskell_for_data_analysis.md @@ -1039,7 +1039,7 @@ TIO.putStrLn $ D.toMarkdown withDates ```haskell -- This requires two Day columns; here we illustrate the pattern: --- D.derive "days_until" (F.daysBetween today_col deadline_col) df +-- D.derive "days_until" (F.daysBetween deadline_col today_col) df ``` diff --git a/tests/Functions.hs b/tests/Functions.hs index 24457065..a225bee5 100644 --- a/tests/Functions.hs +++ b/tests/Functions.hs @@ -3,6 +3,7 @@ module Functions where +import Data.Time.Calendar (Day, fromGregorian) import qualified DataFrame as D import DataFrame.Functions ( sanitize, @@ -79,8 +80,40 @@ testSum = (D.derive "sum" (F.sum (F.col @Int "A")) df) ) +testPow :: Test +testPow = + TestCase + ( assertEqual + "pow of a compound base" + [4, 9, 16, 25, 36, 49, 64, 81, 100, 121] + ( D.columnAsList @Int + (F.col "sq") + (D.derive "sq" (F.pow (F.col @Int "A" + F.lit 1) 2) df) + ) + ) + +testDaysBetween :: Test +testDaysBetween = + TestCase + ( assertEqual + "daysBetween d1 d2 is d1 minus d2 in either argument order" + ([-9], [9]) + ( days (F.col @Day "start") (F.col @Day "end") + , days (F.col @Day "end") (F.col @Day "start") + ) + ) + where + dates = + D.fromNamedColumns + [ ("start", DI.fromList [fromGregorian 2024 3 1]) + , ("end", DI.fromList [fromGregorian 2024 3 10]) + ] + days a b = D.columnAsList @Int (F.col "d") (D.derive "d" (F.daysBetween a b) dates) + tests :: [Test] tests = [ TestLabel "sanitizeIdentifiers" sanitizeIdentifiers , TestLabel "testSum" testSum + , TestLabel "testPow" testPow + , TestLabel "testDaysBetween" testDaysBetween ]