packages feed

dataframe 0.3.2.0 → 0.3.3.0

raw patch · 16 files changed

+1345/−462 lines, 16 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- DataFrame.Errors: typeAnnotationSuggestion :: String -> String
- DataFrame.Internal.Column: toVectorSafe :: forall a v. (Vector v a, Columnable a) => Column -> Either DataFrameException (v a)
+ DataFrame: kFolds :: RandomGen g => g -> Int -> DataFrame -> [DataFrame]
+ DataFrame: randomSplit :: RandomGen g => g -> Double -> DataFrame -> (DataFrame, DataFrame)
+ DataFrame: sample :: RandomGen g => g -> Double -> DataFrame -> DataFrame
+ DataFrame.Errors: [AggregatedAndNonAggregatedException] :: Text -> Text -> DataFrameException
+ DataFrame.Errors: [InternalException] :: Text -> DataFrameException
+ DataFrame.Errors: [NonColumnReferenceException] :: Text -> DataFrameException
+ DataFrame.Errors: [UnaggregatedException] :: Text -> DataFrameException
+ DataFrame.Internal.Expression: Aggregated :: TypedColumn a -> AggregationResult a
+ DataFrame.Internal.Expression: UnAggregated :: Column -> AggregationResult a
+ DataFrame.Internal.Expression: data AggregationResult a
+ DataFrame.Internal.Expression: mkUnaggregatedColumn :: (Vector v a, Columnable a) => v a -> Vector Int -> Vector Int -> Vector (v a)
+ DataFrame.Internal.Expression: nestedTypeException :: (Typeable a, Typeable b) => String -> DataFrameException
+ DataFrame.Operations.Subset: generateRandomVector :: RandomGen g => g -> Int -> Vector Double
+ DataFrame.Operations.Subset: kFolds :: RandomGen g => g -> Int -> DataFrame -> [DataFrame]
+ DataFrame.Operations.Subset: randomSplit :: RandomGen g => g -> Double -> DataFrame -> (DataFrame, DataFrame)
+ DataFrame.Operations.Subset: sample :: RandomGen g => g -> Double -> DataFrame -> DataFrame
- DataFrame: toMatrix :: DataFrame -> Vector (Vector Float)
+ DataFrame: toMatrix :: DataFrame -> Either DataFrameException (Vector (Vector Float))
- DataFrame: toVector :: Columnable a => Column -> Vector a
+ DataFrame: toVector :: forall a v. (Vector v a, Columnable a) => Column -> Either DataFrameException (v a)
- DataFrame.Internal.Column: concatColumns :: Column -> Column -> Maybe Column
+ DataFrame.Internal.Column: concatColumns :: Column -> Column -> Either DataFrameException Column
- DataFrame.Internal.Column: findIndices :: Columnable a => (a -> Bool) -> Column -> Maybe (Vector Int)
+ DataFrame.Internal.Column: findIndices :: Columnable a => (a -> Bool) -> Column -> Either DataFrameException (Vector Int)
- DataFrame.Internal.Column: headColumn :: Columnable a => Column -> Maybe a
+ DataFrame.Internal.Column: headColumn :: Columnable a => Column -> Either DataFrameException a
- DataFrame.Internal.Column: ifilterColumn :: Columnable a => (Int -> a -> Bool) -> Column -> Maybe Column
+ DataFrame.Internal.Column: ifilterColumn :: Columnable a => (Int -> a -> Bool) -> Column -> Either DataFrameException Column
- DataFrame.Internal.Column: ifoldlColumn :: (Columnable a, Columnable b) => (b -> Int -> a -> b) -> b -> Column -> Maybe b
+ DataFrame.Internal.Column: ifoldlColumn :: (Columnable a, Columnable b) => (b -> Int -> a -> b) -> b -> Column -> Either DataFrameException b
- DataFrame.Internal.Column: ifoldrColumn :: (Columnable a, Columnable b) => (Int -> a -> b -> b) -> b -> Column -> Maybe b
+ DataFrame.Internal.Column: ifoldrColumn :: (Columnable a, Columnable b) => (Int -> a -> b -> b) -> b -> Column -> Either DataFrameException b
- DataFrame.Internal.Column: imapColumn :: (Columnable b, Columnable c) => (Int -> b -> c) -> Column -> Maybe Column
+ DataFrame.Internal.Column: imapColumn :: (Columnable b, Columnable c) => (Int -> b -> c) -> Column -> Either DataFrameException Column
- DataFrame.Internal.Column: mapColumn :: (Columnable b, Columnable c, UnboxIf c) => (b -> c) -> Column -> Maybe Column
+ DataFrame.Internal.Column: mapColumn :: (Columnable b, Columnable c, UnboxIf c) => (b -> c) -> Column -> Either DataFrameException Column
- DataFrame.Internal.Column: reduceColumn :: Columnable a => (a -> b) -> Column -> Maybe b
+ DataFrame.Internal.Column: reduceColumn :: (Columnable a, Columnable b) => (a -> b) -> Column -> Either DataFrameException b
- DataFrame.Internal.Column: toVector :: Columnable a => Column -> Vector a
+ DataFrame.Internal.Column: toVector :: forall a v. (Vector v a, Columnable a) => Column -> Either DataFrameException (v a)
- DataFrame.Internal.Column: zipWithColumns :: (Columnable a, Columnable b, Columnable c) => (a -> b -> c) -> Column -> Column -> Maybe Column
+ DataFrame.Internal.Column: zipWithColumns :: (Columnable a, Columnable b, Columnable c) => (a -> b -> c) -> Column -> Column -> Either DataFrameException Column
- DataFrame.Internal.DataFrame: toMatrix :: DataFrame -> Vector (Vector Float)
+ DataFrame.Internal.DataFrame: toMatrix :: DataFrame -> Either DataFrameException (Vector (Vector Float))
- DataFrame.Internal.Expression: interpret :: Columnable a => DataFrame -> Expr a -> TypedColumn a
+ DataFrame.Internal.Expression: interpret :: Columnable a => DataFrame -> Expr a -> Either DataFrameException (TypedColumn a)
- DataFrame.Internal.Expression: interpretAggregation :: Columnable a => GroupedDataFrame -> Expr a -> Either Column (TypedColumn a)
+ DataFrame.Internal.Expression: interpretAggregation :: Columnable a => GroupedDataFrame -> Expr a -> Either DataFrameException (AggregationResult a)

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for dataframe +## 0.3.3.0+* Better error messaging for expression failures.+* Fix bug where exponentials were not being properly during CSV parsing.+* `toMatrix` now returns either an exception or the a vector of vector doubles.+* Add `sample`, `kFolds`, and `randomSplit` to sample dataframes.+ ## 0.3.2.0 * Fix dataframe semigroup instance. Appending two rows of the same name but different types now gives a row of `Either a b` (work by @jhrcek). * Fix left expansion of semigroup instance (work by @jhrcek). 
dataframe.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               dataframe-version:            0.3.2.0+version:            0.3.3.0  synopsis: A fast, safe, and intuitive DataFrame library. @@ -117,7 +117,6 @@                    dataframe ^>= 0.3     default-language: Haskell2010     ghc-options:-      -O2       -threaded       -rtsopts       -with-rtsopts=-N
src/DataFrame.hs view
@@ -278,7 +278,10 @@     filterJust,     filterNothing,     filterWhere,+    kFolds,+    randomSplit,     range,+    sample,     select,     selectBy,     take,
src/DataFrame/Errors.hs view
@@ -28,8 +28,12 @@         (Typeable a, Typeable b) =>         TypeErrorContext a b ->         DataFrameException+    AggregatedAndNonAggregatedException :: T.Text -> T.Text -> DataFrameException     ColumnNotFoundException :: T.Text -> T.Text -> [T.Text] -> DataFrameException     EmptyDataSetException :: T.Text -> DataFrameException+    InternalException :: T.Text -> DataFrameException+    NonColumnReferenceException :: T.Text -> DataFrameException+    UnaggregatedException :: T.Text -> DataFrameException     WrongQuantileNumberException :: Int -> DataFrameException     WrongQuantileIndexException :: VU.Vector Int -> Int -> DataFrameException     deriving (Exception)@@ -51,6 +55,14 @@     show (EmptyDataSetException callPoint) = emptyDataSetError callPoint     show (WrongQuantileNumberException q) = wrongQuantileNumberError q     show (WrongQuantileIndexException qs q) = wrongQuantileIndexError qs q+    show (InternalException msg) = "Internal error: " ++ T.unpack msg+    show (NonColumnReferenceException msg) = "Expression must be a column reference in: " ++ T.unpack msg+    show (UnaggregatedException expr) = "Expression is not fully aggregated: " ++ T.unpack expr+    show (AggregatedAndNonAggregatedException expr1 expr2) =+        "Cannot combine aggregated and non-aggregated expressions: \n"+            ++ T.unpack expr1+            ++ "\n"+            ++ T.unpack expr2  columnNotFound :: T.Text -> T.Text -> [T.Text] -> String columnNotFound name callPoint columns =@@ -102,31 +114,21 @@     err         ++ ( "\n\tThis happened when calling function "                 ++ brightGreen cp-                ++ " on the column "+                ++ " on "                 ++ brightGreen name-                ++ "\n\n"-                ++ typeAnnotationSuggestion cp            )-addCallPointInfo Nothing (Just cp) err = err ++ "\n" ++ typeAnnotationSuggestion cp+addCallPointInfo Nothing (Just cp) err =+    err+        ++ ( "\n\tThis happened when calling function "+                ++ brightGreen cp+           ) addCallPointInfo (Just name) Nothing err =     err-        ++ ( "\n\tOn the column "+        ++ ( "\n\tOn "                 ++ name                 ++ "\n\n"            ) addCallPointInfo Nothing Nothing err = err--typeAnnotationSuggestion :: String -> String-typeAnnotationSuggestion cp =-    "\n\n\tTry adding a type at the end of the function e.g "-        ++ "change\n\t\t"-        ++ red (cp ++ " ...")-        ++ " to \n\t\t"-        ++ green ("(" ++ cp ++ " ... :: <Type>)")-        ++ "\n\tor add "-        ++ "{-# LANGUAGE TypeApplications #-} to the top of your "-        ++ "file then change the call to \n\t\t"-        ++ brightGreen (cp ++ " @<Type> ....")  guessColumnName :: T.Text -> [T.Text] -> T.Text guessColumnName userInput columns = case map (\k -> (editDistance userInput k, k)) columns of
src/DataFrame/Functions.hs view
@@ -27,6 +27,7 @@ import qualified DataFrame.Operations.Statistics as Stats import DataFrame.Operations.Subset (exclude, select) +import Control.Exception (throw) import Control.Monad import qualified Data.Char as Char import Data.Function@@ -293,7 +294,9 @@         | S.member res seen = go seen xs         | otherwise = (x, res) : go (S.insert res seen) xs       where-        res = interpret df x+        res = case interpret df x of+            Left e -> throw e+            Right v -> v         hasInvalid = case res of             (TColumn (UnboxedColumn (col :: VU.Vector a))) -> case testEquality (typeRep @Double) (typeRep @a) of                 Just Refl -> VU.any (\n -> isNaN n || isInfinite n) col@@ -302,7 +305,9 @@  -- | Checks if two programs generate the same outputs given all the same inputs. equivalent :: DataFrame -> Expr Double -> Expr Double -> Bool-equivalent df p1 p2 = interpret df p1 Prelude.== interpret df p2+equivalent df p1 p2 = case (Prelude.==) <$> interpret df p1 <*> interpret df p2 of+    Left e -> throw e+    Right v -> v  synthesizeFeatureExpr ::     -- | Target expression@@ -316,11 +321,14 @@ synthesizeFeatureExpr target d b df =     let         df' = exclude [target] df+        t = case interpret df (Col target) of+            Left e -> throw e+            Right v -> v      in         case beamSearch             df'             (BeamConfig d b (\l r -> (^ 2) <$> correlation' l r))-            (interpret df (Col target))+            t             [] of             Nothing -> Left "No programs found"             Just p -> Right p@@ -338,6 +346,9 @@     let         df' = exclude [target] df         targetMean = fromMaybe 0 $ Stats.mean target df+        t = case interpret df (Col target) of+            Left e -> throw e+            Right v -> v      in         case beamSearch             df'@@ -351,7 +362,7 @@                         r                 )             )-            (interpret df (Col target))+            t             [] of             Nothing -> Left "No programs found"             Just p ->@@ -362,7 +373,7 @@                                 & select ["_generated_regression_feature_"]                             )                             (BeamConfig d b (\l r -> fmap negate (meanSquaredError l r)))-                            (interpret df (Col target))+                            t                             [Col "_generated_regression_feature_", lit targetMean, lit 10] of                             Nothing -> Left "Could not find coefficients"                             Just p' -> Right (replaceExpr p (Col @Double "_generated_regression_feature_") p')@@ -405,7 +416,9 @@ pickTopN _ _ _ [] = [] pickTopN df (TColumn col) cfg ps =     let-        l = VU.convert (toVector @Double col)+        l = case toVector @Double @VU.Vector col of+            Left e -> throw e+            Right v -> v         ordered =             Prelude.take                 (beamLength cfg)@@ -425,12 +438,18 @@             let                 (TColumn col') = c              in-                VU.convert (toVector @Double col')+                case toVector @Double @VU.Vector col' of+                    Left e -> throw e+                    Right v -> VU.convert v         interpretDoubleVector e =             let-                (TColumn col') = interpret df e+                (TColumn col') = case interpret df e of+                    Left e -> throw e+                    Right v -> v              in-                VU.convert (toVector @Double col')+                case toVector @Double @VU.Vector col' of+                    Left e -> throw e+                    Right v -> VU.convert v      in         trace             ( "Best loss: "@@ -444,7 +463,9 @@ satisfiesExamples :: DataFrame -> TypedColumn Double -> Expr Double -> Bool satisfiesExamples df col expr =     let-        result = interpret df expr+        result = case interpret df expr of+            Left e -> throw e+            Right v -> v      in         result Prelude.== col 
src/DataFrame/IO/Parquet.hs view
@@ -8,11 +8,11 @@ import Data.Bits import qualified Data.ByteString as BSO import Data.Char+import Data.Either import Data.IORef import Data.Int import qualified Data.List as L import qualified Data.Map as M-import Data.Maybe import qualified Data.Text as T import Data.Word import qualified DataFrame.Internal.Column as DI@@ -272,4 +272,4 @@         [] -> pure $ DI.fromList ([] :: [Maybe Int])         (c : cs) ->             pure $-                L.foldl' (\l r -> fromMaybe (error "concat failed") (DI.concatColumns l r)) c cs+                L.foldl' (\l r -> fromRight (error "concat failed") (DI.concatColumns l r)) c cs
src/DataFrame/Internal/Column.hs view
@@ -243,22 +243,49 @@     ) =>     (b -> c) ->     Column ->-    Maybe Column+    Either DataFrameException Column mapColumn f = \case     BoxedColumn (col :: VB.Vector a)         | Just Refl <- testEquality (typeRep @a) (typeRep @b) ->-            Just (fromVector @c (VB.map f col))-        | otherwise -> Nothing+            Right (fromVector @c (VB.map f col))+        | otherwise ->+            Left $+                TypeMismatchException+                    ( MkTypeErrorContext+                        { userType = Right (typeRep @b)+                        , expectedType = Right (typeRep @a)+                        , callingFunctionName = Just "mapColumn"+                        , errorColumnName = Nothing+                        }+                    )     OptionalColumn (col :: VB.Vector a)         | Just Refl <- testEquality (typeRep @a) (typeRep @b) ->-            Just (fromVector @c (VB.map f col))-        | otherwise -> Nothing+            Right (fromVector @c (VB.map f col))+        | otherwise ->+            Left $+                TypeMismatchException+                    ( MkTypeErrorContext+                        { userType = Right (typeRep @b)+                        , expectedType = Right (typeRep @a)+                        , callingFunctionName = Just "mapColumn"+                        , errorColumnName = Nothing+                        }+                    )     UnboxedColumn (col :: VU.Vector a)         | Just Refl <- testEquality (typeRep @a) (typeRep @b) ->-            Just $ case sUnbox @c of+            Right $ case sUnbox @c of                 STrue -> UnboxedColumn (VU.map f col)                 SFalse -> fromVector @c (VB.map f (VB.convert col))-        | otherwise -> Nothing+        | otherwise ->+            Left $+                TypeMismatchException+                    ( MkTypeErrorContext+                        { userType = Right (typeRep @b)+                        , expectedType = Right (typeRep @a)+                        , callingFunctionName = Just "mapColumn"+                        , errorColumnName = Nothing+                        }+                    )  -- | O(1) Gets the number of elements in the column. columnLength :: Column -> Int@@ -322,16 +349,43 @@     (Columnable a) =>     (a -> Bool) ->     Column ->-    Maybe (VU.Vector Int)-findIndices pred (BoxedColumn (column :: VB.Vector b)) = do-    Refl <- testEquality (typeRep @a) (typeRep @b)-    pure $ VG.convert (VG.findIndices pred column)-findIndices pred (UnboxedColumn (column :: VU.Vector b)) = do-    Refl <- testEquality (typeRep @a) (typeRep @b)-    pure $ VG.findIndices pred column-findIndices pred (OptionalColumn (column :: VB.Vector (Maybe b))) = do-    Refl <- testEquality (typeRep @a) (typeRep @(Maybe b))-    pure $ VG.convert (VG.findIndices pred column)+    Either DataFrameException (VU.Vector Int)+findIndices pred (BoxedColumn (column :: VB.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl -> pure $ VG.convert (VG.findIndices pred column)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "findIndices"+                    , errorColumnName = Nothing+                    }+                )+findIndices pred (UnboxedColumn (column :: VU.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl -> pure $ VG.findIndices pred column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "findIndices"+                    , errorColumnName = Nothing+                    }+                )+findIndices pred (OptionalColumn (column :: VB.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl -> pure $ VG.convert (VG.findIndices pred column)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "findIndices"+                    , errorColumnName = Nothing+                    }+                )  -- | An internal function that returns a vector of how indexes change after a column is sorted. sortedIndexes :: Bool -> Column -> VU.Vector Int@@ -362,89 +416,271 @@ imapColumn ::     forall b c.     (Columnable b, Columnable c) =>-    (Int -> b -> c) -> Column -> Maybe Column+    (Int -> b -> c) -> Column -> Either DataFrameException Column imapColumn f = \case     BoxedColumn (col :: VB.Vector a)         | Just Refl <- testEquality (typeRep @a) (typeRep @b) ->-            Just (fromVector @c (VB.imap f col))-        | otherwise -> Nothing+            pure (fromVector @c (VB.imap f col))+        | otherwise ->+            Left $+                TypeMismatchException+                    ( MkTypeErrorContext+                        { userType = Right (typeRep @b)+                        , expectedType = Right (typeRep @a)+                        , callingFunctionName = Just "imapColumn"+                        , errorColumnName = Nothing+                        }+                    )     UnboxedColumn (col :: VU.Vector a)         | Just Refl <- testEquality (typeRep @a) (typeRep @b) ->-            Just $+            pure $                 case sUnbox @c of                     STrue -> UnboxedColumn (VU.imap f col)                     SFalse -> fromVector @c (VB.imap f (VB.convert col))-        | otherwise -> Nothing+        | otherwise ->+            Left $+                TypeMismatchException+                    ( MkTypeErrorContext+                        { userType = Right (typeRep @b)+                        , expectedType = Right (typeRep @a)+                        , callingFunctionName = Just "imapColumn"+                        , errorColumnName = Nothing+                        }+                    )     OptionalColumn (col :: VB.Vector a)         | Just Refl <- testEquality (typeRep @a) (typeRep @b) ->-            Just (fromVector @c (VB.imap f col))-        | otherwise -> Nothing+            pure (fromVector @c (VB.imap f col))+        | otherwise ->+            Left $+                TypeMismatchException+                    ( MkTypeErrorContext+                        { userType = Right (typeRep @b)+                        , expectedType = Right (typeRep @a)+                        , callingFunctionName = Just "imapColumn"+                        , errorColumnName = Nothing+                        }+                    )  -- | Filter column with index. ifilterColumn ::-    forall a. (Columnable a) => (Int -> a -> Bool) -> Column -> Maybe Column-ifilterColumn f c@(BoxedColumn (column :: VB.Vector b)) = do-    Refl <- testEquality (typeRep @a) (typeRep @b)-    return $ BoxedColumn $ VG.ifilter f column-ifilterColumn f c@(UnboxedColumn (column :: VU.Vector b)) = do-    Refl <- testEquality (typeRep @a) (typeRep @b)-    return $ UnboxedColumn $ VG.ifilter f column-ifilterColumn _ _ = Nothing+    forall a.+    (Columnable a) =>+    (Int -> a -> Bool) -> Column -> Either DataFrameException Column+ifilterColumn f c@(BoxedColumn (column :: VB.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl -> pure $ BoxedColumn $ VG.ifilter f column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "ifilterColumn"+                    , errorColumnName = Nothing+                    }+                )+ifilterColumn f c@(UnboxedColumn (column :: VU.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl -> pure $ UnboxedColumn $ VG.ifilter f column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "ifilterColumn"+                    , errorColumnName = Nothing+                    }+                )+ifilterColumn f c@(OptionalColumn (column :: VB.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl -> pure $ OptionalColumn $ VG.ifilter f column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "ifilterColumn"+                    , errorColumnName = Nothing+                    }+                )  -- | Fold (right) column with index. ifoldrColumn ::     forall a b.-    (Columnable a, Columnable b) => (Int -> a -> b -> b) -> b -> Column -> Maybe b-ifoldrColumn f acc c@(BoxedColumn (column :: VB.Vector d)) = do-    Refl <- testEquality (typeRep @a) (typeRep @d)-    return $ VG.ifoldr f acc column-ifoldrColumn f acc c@(OptionalColumn (column :: VB.Vector d)) = do-    Refl <- testEquality (typeRep @a) (typeRep @d)-    return $ VG.ifoldr f acc column-ifoldrColumn f acc c@(UnboxedColumn (column :: VU.Vector d)) = do-    Refl <- testEquality (typeRep @a) (typeRep @d)-    return $ VG.ifoldr f acc column+    (Columnable a, Columnable b) =>+    (Int -> a -> b -> b) -> b -> Column -> Either DataFrameException b+ifoldrColumn f acc c@(BoxedColumn (column :: VB.Vector d)) = case testEquality (typeRep @a) (typeRep @d) of+    Just Refl -> pure $ VG.ifoldr f acc column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @d)+                    , callingFunctionName = Just "ifoldrColumn"+                    , errorColumnName = Nothing+                    }+                )+ifoldrColumn f acc c@(OptionalColumn (column :: VB.Vector d)) = case testEquality (typeRep @a) (typeRep @d) of+    Just Refl -> pure $ VG.ifoldr f acc column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @d)+                    , callingFunctionName = Just "ifoldrColumn"+                    , errorColumnName = Nothing+                    }+                )+ifoldrColumn f acc c@(UnboxedColumn (column :: VU.Vector d)) = case testEquality (typeRep @a) (typeRep @d) of+    Just Refl -> pure $ VG.ifoldr f acc column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @d)+                    , callingFunctionName = Just "ifoldrColumn"+                    , errorColumnName = Nothing+                    }+                )  -- | Fold (left) column with index. ifoldlColumn ::     forall a b.-    (Columnable a, Columnable b) => (b -> Int -> a -> b) -> b -> Column -> Maybe b-ifoldlColumn f acc c@(BoxedColumn (column :: VB.Vector d)) = do-    Refl <- testEquality (typeRep @a) (typeRep @d)-    return $ VG.ifoldl' f acc column-ifoldlColumn f acc c@(OptionalColumn (column :: VB.Vector d)) = do-    Refl <- testEquality (typeRep @a) (typeRep @d)-    return $ VG.ifoldl' f acc column-ifoldlColumn f acc c@(UnboxedColumn (column :: VU.Vector d)) = do-    Refl <- testEquality (typeRep @a) (typeRep @d)-    return $ VG.ifoldl' f acc column+    (Columnable a, Columnable b) =>+    (b -> Int -> a -> b) -> b -> Column -> Either DataFrameException b+ifoldlColumn f acc c@(BoxedColumn (column :: VB.Vector d)) = case testEquality (typeRep @a) (typeRep @d) of+    Just Refl -> pure $ VG.ifoldl' f acc column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @d)+                    , callingFunctionName = Just "ifoldlColumn"+                    , errorColumnName = Nothing+                    }+                )+ifoldlColumn f acc c@(OptionalColumn (column :: VB.Vector d)) = case testEquality (typeRep @a) (typeRep @d) of+    Just Refl -> pure $ VG.ifoldl' f acc column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @d)+                    , callingFunctionName = Just "ifoldlColumn"+                    , errorColumnName = Nothing+                    }+                )+ifoldlColumn f acc c@(UnboxedColumn (column :: VU.Vector d)) = case testEquality (typeRep @a) (typeRep @d) of+    Just Refl -> pure $ VG.ifoldl' f acc column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @d)+                    , callingFunctionName = Just "ifoldlColumn"+                    , errorColumnName = Nothing+                    }+                ) -headColumn :: forall a. (Columnable a) => Column -> Maybe a-headColumn (BoxedColumn (col :: VB.Vector b)) = do-    Refl <- testEquality (typeRep @a) (typeRep @b)-    pure (VG.head col)-headColumn (UnboxedColumn (col :: VU.Vector b)) = do-    Refl <- testEquality (typeRep @a) (typeRep @b)-    pure (VG.head col)-headColumn (OptionalColumn (col :: VB.Vector b)) = do-    Refl <- testEquality (typeRep @a) (typeRep @b)-    pure (VG.head col)+headColumn :: forall a. (Columnable a) => Column -> Either DataFrameException a+headColumn (BoxedColumn (col :: VB.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl ->+        if VG.null col+            then Left (EmptyDataSetException "headColumn")+            else pure (VG.head col)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "headColumn"+                    , errorColumnName = Nothing+                    }+                )+headColumn (UnboxedColumn (col :: VU.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl ->+        if VG.null col+            then Left (EmptyDataSetException "headColumn")+            else pure (VG.head col)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "headColumn"+                    , errorColumnName = Nothing+                    }+                )+headColumn (OptionalColumn (col :: VB.Vector b)) = case testEquality (typeRep @a) (typeRep @b) of+    Just Refl ->+        if VG.null col+            then Left (EmptyDataSetException "headColumn")+            else pure (VG.head col)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "headColumn"+                    , errorColumnName = Nothing+                    }+                )  -- | Generic reduce function for all Column types.-reduceColumn :: forall a b. (Columnable a) => (a -> b) -> Column -> Maybe b+reduceColumn ::+    forall a b.+    (Columnable a, Columnable b) =>+    (a -> b) -> Column -> Either DataFrameException b {-# SPECIALIZE reduceColumn ::-    (VU.Vector (Double, Double) -> Double) -> Column -> Maybe Double-    , (VU.Vector Double -> Double) -> Column -> Maybe Double+    (VU.Vector (Double, Double) -> Double) ->+    Column ->+    Either DataFrameException Double+    , (VU.Vector Double -> Double) -> Column -> Either DataFrameException Double     #-}-reduceColumn f (BoxedColumn (column :: c)) = do-    Refl <- testEquality (typeRep @c) (typeRep @a)-    pure $ f column-reduceColumn f (UnboxedColumn (column :: c)) = do-    Refl <- testEquality (typeRep @c) (typeRep @a)-    pure $ f column-reduceColumn f (OptionalColumn (column :: c)) = do-    Refl <- testEquality (typeRep @c) (typeRep @a)-    pure $ f column+reduceColumn f (BoxedColumn (column :: c)) = case testEquality (typeRep @c) (typeRep @a) of+    Just Refl -> pure $ f column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "reduceColumn"+                    , errorColumnName = Nothing+                    }+                )+reduceColumn f (UnboxedColumn (column :: c)) = case testEquality (typeRep @c) (typeRep @a) of+    Just Refl -> pure $ f column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "reduceColumn"+                    , errorColumnName = Nothing+                    }+                )+reduceColumn f (OptionalColumn (column :: c)) = case testEquality (typeRep @c) (typeRep @a) of+    Just Refl -> pure $ f column+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @b)+                    , callingFunctionName = Just "reduceColumn"+                    , errorColumnName = Nothing+                    }+                ) {-# INLINE reduceColumn #-}  -- | An internal, column version of zip.@@ -474,20 +710,43 @@ zipWithColumns ::     forall a b c.     (Columnable a, Columnable b, Columnable c) =>-    (a -> b -> c) -> Column -> Column -> Maybe Column+    (a -> b -> c) -> Column -> Column -> Either DataFrameException Column zipWithColumns f (UnboxedColumn (column :: VU.Vector d)) (UnboxedColumn (other :: VU.Vector e)) = case testEquality (typeRep @a) (typeRep @d) of     Just Refl -> case testEquality (typeRep @b) (typeRep @e) of         Just Refl -> pure $ case sUnbox @c of             STrue -> fromUnboxedVector (VU.zipWith f column other)             SFalse -> fromVector $ VB.zipWith f (VG.convert column) (VG.convert other)-        Nothing -> Nothing-    Nothing -> Nothing-zipWithColumns f left right =-    let-        left' = toVector @a left-        right' = toVector @b right-     in-        pure $ fromVector $ VB.zipWith f left' right'+        Nothing ->+            Left $+                TypeMismatchException+                    ( MkTypeErrorContext+                        { userType = Right (typeRep @b)+                        , expectedType = Right (typeRep @e)+                        , callingFunctionName = Just "zipWithColumns"+                        , errorColumnName = Nothing+                        }+                    )+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeRep @a)+                    , expectedType = Right (typeRep @d)+                    , callingFunctionName = Just "zipWithColumns"+                    , errorColumnName = Nothing+                    }+                )+zipWithColumns f left right = case toVector @a left of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException (context{callingFunctionName = Just "zipWithColumns"})+    Left e -> Left e+    Right left' -> case toVector @b right of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException (context{callingFunctionName = Just "zipWithColumns"})+        Left e -> Left e+        Right right' -> pure $ fromVector $ VB.zipWith f left' right' {-# INLINE zipWithColumns #-}  -- Functions for mutable columns (intended for IO).@@ -583,17 +842,53 @@ {- | Concatenates two columns. Returns Nothing if the columns are of different types. -}-concatColumns :: Column -> Column -> Maybe Column+concatColumns :: Column -> Column -> Either DataFrameException Column concatColumns (OptionalColumn left) (OptionalColumn right) = case testEquality (typeOf left) (typeOf right) of-    Nothing -> Nothing-    Just Refl -> Just (OptionalColumn $ left <> right)+    Just Refl -> pure (OptionalColumn $ left <> right)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeOf right)+                    , expectedType = Right (typeOf left)+                    , callingFunctionName = Just "concatColumns"+                    , errorColumnName = Nothing+                    }+                ) concatColumns (BoxedColumn left) (BoxedColumn right) = case testEquality (typeOf left) (typeOf right) of-    Nothing -> Nothing-    Just Refl -> Just (BoxedColumn $ left <> right)+    Just Refl -> pure (BoxedColumn $ left <> right)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeOf right)+                    , expectedType = Right (typeOf left)+                    , callingFunctionName = Just "concatColumns"+                    , errorColumnName = Nothing+                    }+                ) concatColumns (UnboxedColumn left) (UnboxedColumn right) = case testEquality (typeOf left) (typeOf right) of-    Nothing -> Nothing-    Just Refl -> Just (UnboxedColumn $ left <> right)-concatColumns _ _ = Nothing+    Just Refl -> pure (UnboxedColumn $ left <> right)+    Nothing ->+        Left $+            TypeMismatchException+                ( MkTypeErrorContext+                    { userType = Right (typeOf right)+                    , expectedType = Right (typeOf left)+                    , callingFunctionName = Just "concatColumns"+                    , errorColumnName = Nothing+                    }+                )+concatColumns left right =+    Left $+        TypeMismatchException+            ( MkTypeErrorContext+                { userType = Right (typeOf right)+                , expectedType = Right (typeOf left)+                , callingFunctionName = Just "concatColumns"+                , errorColumnName = Nothing+                }+            )  {- | Concatenates two columns. @@ -636,23 +931,6 @@     OptionalColumn $         fmap (Just . Left) (VG.convert left) <> fmap (fmap Right) right -{- | O(n) Converts a column to a boxed vector. Throws an exception if the wrong type is specified.--__Examples:__--@-> column = fromList [(1 :: Int), 2, 3, 4]-> toVector @Int column-[1,2,3,4]-> toVector @Double column-exception: ...-@--}-toVector :: forall a. (Columnable a) => Column -> VB.Vector a-toVector xs = case toVectorSafe xs of-    Left err -> throw err-    Right val -> val- {- | O(n) Converts a column to a list. Throws an exception if the wrong type is specified.  __Examples:__@@ -666,15 +944,15 @@ @ -} toList :: forall a. (Columnable a) => Column -> [a]-toList xs = case toVectorSafe @a xs of+toList xs = case toVector @a xs of     Left err -> throw err     Right val -> VB.toList val  -- | A safe version of toVector that returns an Either type.-toVectorSafe ::+toVector ::     forall a v.     (VG.Vector v a, Columnable a) => Column -> Either DataFrameException (v a)-toVectorSafe column@(OptionalColumn (col :: VB.Vector b)) =+toVector column@(OptionalColumn (col :: VB.Vector b)) =     case testEquality (typeRep @a) (typeRep @b) of         Just Refl -> Right $ VG.convert col         Nothing ->@@ -683,11 +961,11 @@                     ( MkTypeErrorContext                         { userType = Right (typeRep @a)                         , expectedType = Right (typeRep @b)-                        , callingFunctionName = Just "toVectorSafe"+                        , callingFunctionName = Just "toVector"                         , errorColumnName = Nothing                         }                     )-toVectorSafe (BoxedColumn (col :: VB.Vector b)) =+toVector (BoxedColumn (col :: VB.Vector b)) =     case testEquality (typeRep @a) (typeRep @b) of         Just Refl -> Right $ VG.convert col         Nothing ->@@ -696,11 +974,11 @@                     ( MkTypeErrorContext                         { userType = Right (typeRep @a)                         , expectedType = Right (typeRep @b)-                        , callingFunctionName = Just "toVectorSafe"+                        , callingFunctionName = Just "toVector"                         , errorColumnName = Nothing                         }                     )-toVectorSafe (UnboxedColumn (col :: VU.Vector b)) =+toVector (UnboxedColumn (col :: VU.Vector b)) =     case testEquality (typeRep @a) (typeRep @b) of         Just Refl -> Right $ VG.convert col         Nothing ->@@ -709,7 +987,7 @@                     ( MkTypeErrorContext                         { userType = Right (typeRep @a)                         , expectedType = Right (typeRep @b)-                        , callingFunctionName = Just "toVectorSafe"+                        , callingFunctionName = Just "toVector"                         , errorColumnName = Nothing                         }                     )
src/DataFrame/Internal/DataFrame.hs view
@@ -19,7 +19,7 @@ import Data.List (sortBy, transpose, (\\)) import Data.Type.Equality (TestEquality (testEquality), type (:~:) (Refl)) import DataFrame.Display.Terminal.PrettyPrint-import DataFrame.Errors (DataFrameException (..))+import DataFrame.Errors import DataFrame.Internal.Column import Text.Printf import Type.Reflection (typeRep)@@ -129,19 +129,22 @@ All entries in the dataframe must be doubles. This is useful for handing data over into ML systems. -}-toMatrix :: DataFrame -> V.Vector (VU.Vector Float)-toMatrix df =-    let-        m = V.map (toVector @Double) (columns df)-     in-        V.generate-            (fst (dataframeDimensions df))-            ( \i ->-                foldl-                    (\acc j -> acc `VU.snoc` realToFrac ((m V.! j) V.! i))-                    VU.empty-                    [0 .. (V.length m - 1)]-            )+toMatrix :: DataFrame -> Either DataFrameException (V.Vector (VU.Vector Float))+toMatrix df = case V.foldl'+    (\acc c -> V.snoc <$> acc <*> (toVector @Double c))+    (Right V.empty :: Either DataFrameException (V.Vector (V.Vector Double)))+    (columns df) of+    Left e -> Left e+    Right m ->+        pure $+            V.generate+                (fst (dataframeDimensions df))+                ( \i ->+                    foldl+                        (\acc j -> acc `VU.snoc` realToFrac ((m V.! j) V.! i))+                        VU.empty+                        [0 .. (V.length m - 1)]+                )  {- | Get a specific column as a vector. 
src/DataFrame/Internal/Expression.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE FlexibleContexts #-}@@ -6,6 +7,7 @@ {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}@@ -13,19 +15,18 @@  module DataFrame.Internal.Expression where -import Control.Exception (throw) import qualified Data.Map as M-import Data.Maybe (fromMaybe)+import Data.Maybe (fromMaybe, isJust) import qualified Data.Text as T import Data.Type.Equality (TestEquality (testEquality), type (:~:) (Refl)) import qualified Data.Vector as V import qualified Data.Vector.Generic as VG import qualified Data.Vector.Unboxed as VU-import DataFrame.Errors (DataFrameException (ColumnNotFoundException))+import DataFrame.Errors import DataFrame.Internal.Column import DataFrame.Internal.DataFrame import DataFrame.Internal.Types-import Type.Reflection (Typeable, typeOf, typeRep)+import Type.Reflection (TypeRep, Typeable, typeOf, typeRep, pattern App)  data Expr a where     Col :: (Columnable a) => T.Text -> Expr a@@ -94,287 +95,771 @@ data UExpr where     Wrap :: (Columnable a) => Expr a -> UExpr -interpret :: forall a. (Columnable a) => DataFrame -> Expr a -> TypedColumn a-interpret df (Lit value) = TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value+interpret ::+    forall a.+    (Columnable a) =>+    DataFrame -> Expr a -> Either DataFrameException (TypedColumn a)+interpret df (Lit value) =+    pure $ TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value interpret df (Col name) = case getColumn name df of-    Nothing -> throw $ ColumnNotFoundException name "" (M.keys $ columnIndices df)-    Just col -> TColumn col-interpret df (If cond l r) =-    let-        (TColumn conditions) = interpret @Bool df cond-        (TColumn left) = interpret @a df l-        (TColumn right) = interpret @a df r-     in-        TColumn $-            fromMaybe (error "zipWithColumns returned nothing") $-                zipWithColumns-                    (\(c :: Bool) (l' :: a, r' :: a) -> if c then l' else r')-                    conditions-                    (zipColumns left right)-interpret df (UnaryOp _ (f :: c -> d) value) =-    let-        (TColumn value') = interpret @c df value-     in-        -- TODO: Handle this gracefully.-        TColumn $ fromMaybe (error "mapColumn returned nothing") (mapColumn f value')-interpret df (BinaryOp _ (f :: c -> d -> e) (Lit left) (Lit right)) =-    TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) (f left right)-interpret df (BinaryOp _ (f :: c -> d -> e) (Lit left) right) =-    let-        (TColumn right') = interpret @d df right-     in-        TColumn $-            fromMaybe (error "mapColumn returned nothing") (mapColumn (f left) right')-interpret df (BinaryOp _ (f :: c -> d -> e) left (Lit right)) =-    let-        (TColumn left') = interpret @c df left-     in-        TColumn $-            fromMaybe (error "mapColumn returned nothing") (mapColumn (`f` right) left')-interpret df (BinaryOp _ (f :: c -> d -> e) left right) =-    let-        (TColumn left') = interpret @c df left-        (TColumn right') = interpret @d df right-     in+    Nothing -> Left $ ColumnNotFoundException name "" (M.keys $ columnIndices df)+    Just col -> pure $ TColumn col+interpret df expr@(If cond l r) = case interpret @Bool df cond of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show cond)+                    }+                )+    Left e -> Left e+    Right (TColumn conditions) -> case interpret @a df l of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    (context{callingFunctionName = Just "interpret", errorColumnName = Just (show l)})+        Left e -> Left e+        Right (TColumn left) -> case interpret @a df r of+            Left (TypeMismatchException context) ->+                Left $+                    TypeMismatchException+                        (context{callingFunctionName = Just "interpret", errorColumnName = Just (show r)})+            Left e -> Left e+            Right (TColumn right) -> case zipWithColumns+                (\(c :: Bool) (l' :: a, r' :: a) -> if c then l' else r')+                conditions+                (zipColumns left right) of+                Left (TypeMismatchException context) ->+                    Left $+                        TypeMismatchException+                            ( context+                                { callingFunctionName = Just "interpret"+                                , errorColumnName = Just (show expr)+                                }+                            )+                Left e -> Left e+                Right res -> pure $ TColumn res+interpret df expr@(UnaryOp _ (f :: c -> d) value) = case interpret @c df value of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show value)+                    }+                )+    Left e -> Left e+    Right (TColumn value') -> case mapColumn f value' of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpret"+                        , errorColumnName = Just (show expr)+                        }+                    )+        Left e -> Left e+        Right res -> pure $ TColumn res+interpret df expr@(BinaryOp _ (f :: c -> d -> e) (Lit left) (Lit right)) =+    pure $         TColumn $-            fromMaybe (error "mapColumn returned nothing") (zipWithColumns f left' right')-interpret df (AggVector expr op (f :: v b -> c)) =-    let-        (TColumn column) = interpret @b df expr-     in-        case column of-            (BoxedColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of-                Just Refl -> interpret @c df (Lit (f col))-                Nothing -> error "Type mismatch"-            (OptionalColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of-                Just Refl -> interpret @c df (Lit (f col))-                Nothing -> error "Type mismatch"-            (UnboxedColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of-                Just Refl -> interpret @c df (Lit (f col))-                Nothing -> error "Type mismatch"-interpret df (AggReduce expr op (f :: forall a. (Columnable a) => a -> a -> a)) =-    let-        (TColumn column) = interpret @a df expr-     in-        case headColumn @a column of-            Nothing -> error "Invalid operation"-            Just h -> case ifoldlColumn (\acc _ v -> f acc v) h column of-                Nothing -> error "Invalid operation"-                Just value -> TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value-interpret df (AggNumericVector expr op (f :: VU.Vector b -> c)) =-    let-        (TColumn column) = interpret @b df expr-     in-        case column of-            (UnboxedColumn (v :: VU.Vector d)) -> case testEquality (typeRep @d) (typeRep @b) of-                Just Refl -> TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) (f v)-                Nothing -> error "Invalid operation"-            (BoxedColumn (v :: V.Vector d)) -> case testEquality (typeRep @d) (typeRep @Integer) of-                Just Refl ->+            fromVector $+                V.replicate (fst $ dataframeDimensions df) (f left right)+interpret df expr@(BinaryOp _ (f :: c -> d -> e) (Lit left) right) = case interpret @d df right of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show right)+                    }+                )+    Left e -> Left e+    Right (TColumn right') -> case mapColumn (f left) right' of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpret"+                        , errorColumnName = Just (show expr)+                        }+                    )+        Left e -> Left e+        Right res -> pure $ TColumn res+interpret df expr@(BinaryOp _ (f :: c -> d -> e) left (Lit right)) = case interpret @c df left of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show left)+                    }+                )+    Left e -> Left e+    Right (TColumn left') -> case mapColumn (`f` right) left' of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpret"+                        , errorColumnName = Just (show expr)+                        }+                    )+        Left e -> Left e+        Right res -> pure $ TColumn res+interpret df expr@(BinaryOp _ (f :: c -> d -> e) left right) = case interpret @c df left of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show left)+                    }+                )+    Left e -> Left e+    Right (TColumn left') -> case interpret @d df right of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpret"+                        , errorColumnName = Just (show right)+                        }+                    )+        Left e -> Left e+        Right (TColumn right') -> case zipWithColumns f left' right' of+            Left (TypeMismatchException context) ->+                Left $+                    TypeMismatchException+                        ( context+                            { callingFunctionName = Just "interpret"+                            , errorColumnName = Just (show expr)+                            }+                        )+            Left e -> Left e+            Right res -> pure $ TColumn res+interpret df expression@(AggVector expr op (f :: v b -> c)) = case interpret @b df expr of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show expr)+                    }+                )+    Left e -> Left e+    Right (TColumn column) -> case column of+        (BoxedColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of+            Just Refl -> interpret @c df (Lit (f col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @(v b))+                            , expectedType = Right (typeOf col)+                            , callingFunctionName = Just "interpret"+                            , errorColumnName = Nothing+                            }+                        )+        (OptionalColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of+            Just Refl -> interpret @c df (Lit (f col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @(v b))+                            , expectedType = Right (typeOf col)+                            , callingFunctionName = Just "interpret"+                            , errorColumnName = Nothing+                            }+                        )+        (UnboxedColumn col) -> case testEquality (typeRep @(v b)) (typeOf col) of+            Just Refl -> interpret @c df (Lit (f col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @(v b))+                            , expectedType = Right (typeOf col)+                            , callingFunctionName = Just "interpret"+                            , errorColumnName = Nothing+                            }+                        )+interpret df expression@(AggReduce expr op (f :: forall a. (Columnable a) => a -> a -> a)) = case interpret @a df expr of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show expr)+                    }+                )+    Left e -> Left e+    Right (TColumn column) -> case headColumn @a column of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpret"+                        , errorColumnName = Just (show expr)+                        }+                    )+        Left (EmptyDataSetException loc) -> Left (EmptyDataSetException (T.pack $ show expr))+        Left e -> Left e+        Right h -> case ifoldlColumn (\acc _ v -> f acc v) h column of+            Left (TypeMismatchException context) ->+                Left $+                    TypeMismatchException+                        ( context+                            { callingFunctionName = Just "interpret"+                            , errorColumnName = Just (show expression)+                            }+                        )+            Left e -> Left e+            Right value ->+                pure $ TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value+interpret df expression@(AggNumericVector expr op (f :: VU.Vector b -> c)) = case interpret @b df expr of+    Left (TypeMismatchException context) ->+        Left $+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show expr)+                    }+                )+    Left e -> Left e+    Right (TColumn column) -> case column of+        (UnboxedColumn (v :: VU.Vector d)) -> case testEquality (typeRep @d) (typeRep @b) of+            Just Refl ->+                pure $ TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) (f v)+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @b)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpret"+                            , errorColumnName = Just (show expression)+                            }+                        )+        (BoxedColumn (v :: V.Vector d)) -> case testEquality (typeRep @d) (typeRep @Integer) of+            Just Refl ->+                Right $                     TColumn $                         fromVector $                             V.replicate                                 (fst $ dataframeDimensions df)                                 (f (VU.convert $ V.map fromInteger v))-                Nothing -> error "Invalid operation"-            _ -> error "Invalid operation"-interpret df (AggFold expr op start (f :: (a -> b -> a))) =-    let-        (TColumn column) = interpret @b df expr-     in-        case ifoldlColumn (\acc _ v -> f acc v) start column of-            Nothing -> error "Invalid operation"-            Just value -> TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value--interpretAggregation ::-    forall a.-    (Columnable a) => GroupedDataFrame -> Expr a -> Either Column (TypedColumn a)-interpretAggregation gdf (Lit value) =-    Right $ TColumn $ fromVector $ V.replicate (VG.length (offsets gdf) - 1) value-interpretAggregation gdf@(Grouped df names indices os) (Col name) = case getColumn name df of-    Nothing -> throw $ ColumnNotFoundException name "" (M.keys $ columnIndices df)-    Just (BoxedColumn col) ->-        Left $-            fromVector $-                V.generate-                    (VU.length os - 1)-                    ( \i ->-                        ( V.generate-                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                            ( \j ->-                                col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))-                            )+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @Integer)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpret"+                            , errorColumnName = Just (show expression)+                            }                         )-                    )-    Just (OptionalColumn col) ->-        Left $-            fromVector $-                V.generate-                    (VU.length os - 1)-                    ( \i ->-                        ( V.generate-                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                            ( \j ->-                                col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))+        (OptionalColumn (v :: V.Vector (Maybe d))) -> case sNumeric @d of+            STrue -> case testEquality (typeRep @d) (typeRep @b) of+                Nothing ->+                    Left $+                        TypeMismatchException+                            ( MkTypeErrorContext+                                { userType = Right (typeRep @b)+                                , expectedType = Right (typeRep @d)+                                , callingFunctionName = Just "interpret"+                                , errorColumnName = Just (show expression)+                                }                             )+                Just Refl ->+                    pure $+                        TColumn $+                            fromVector $+                                V.replicate+                                    (fst $ dataframeDimensions df)+                                    (f (VU.convert $ V.map (fromMaybe 0) $ V.filter isJust v))+            SFalse ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @d)+                            , expectedType = Right (typeRep @b)+                            , callingFunctionName = Just "interpret"+                            , errorColumnName = Just (show expression)+                            }                         )-                    )-    Just (UnboxedColumn col) ->+interpret df expression@(AggFold expr op start (f :: (a -> b -> a))) = case interpret @b df expr of+    Left (TypeMismatchException context) ->         Left $-            fromVector $-                V.generate-                    (VU.length os - 1)-                    ( \i ->-                        ( VU.generate-                            (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))-                            ( \j ->-                                col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))-                            )-                        )+            TypeMismatchException+                ( context+                    { callingFunctionName = Just "interpret"+                    , errorColumnName = Just (show expr)+                    }+                )+    Left e -> Left e+    Right (TColumn column) -> case ifoldlColumn (\acc _ v -> f acc v) start column of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpret"+                        , errorColumnName = Just (show expression)+                        }                     )-interpretAggregation gdf (UnaryOp _ (f :: c -> d) expr) =+        Left e -> Left e+        Right value ->+            pure $ TColumn $ fromVector $ V.replicate (fst $ dataframeDimensions df) value++data AggregationResult a+    = UnAggregated Column+    | Aggregated (TypedColumn a)++mkUnaggregatedColumn ::+    forall v a.+    (VG.Vector v a, Columnable a) =>+    v a -> VU.Vector Int -> VU.Vector Int -> V.Vector (v a)+mkUnaggregatedColumn col os indices =+    V.generate+        (VU.length os - 1)+        ( \i ->+            ( VG.generate+                (os `VG.unsafeIndex` (i + 1) - (os `VG.unsafeIndex` i))+                ( \j ->+                    col `VG.unsafeIndex` (indices `VG.unsafeIndex` (j + (os `VG.unsafeIndex` i)))+                )+            )+        )++nestedTypeException ::+    forall a b. (Typeable a, Typeable b) => String -> DataFrameException+nestedTypeException expression = case typeRep @a of+    App t1 t2 ->+        TypeMismatchException+            ( MkTypeErrorContext+                { userType = Left (show (typeRep @b)) :: Either String (TypeRep ())+                , expectedType = Left (show (typeRep @a)) :: Either String (TypeRep ())+                , callingFunctionName = Just "interpretAggregation"+                , errorColumnName = Just expression+                }+            )+    t ->+        TypeMismatchException+            ( MkTypeErrorContext+                { userType = Right (typeRep @(VU.Vector b))+                , expectedType = Right (typeRep @b)+                , callingFunctionName = Just "interpretAggregation"+                , errorColumnName = Just expression+                }+            )++interpretAggregation ::+    forall a.+    (Columnable a) =>+    GroupedDataFrame -> Expr a -> Either DataFrameException (AggregationResult a)+interpretAggregation gdf (Lit value) =+    Right $+        Aggregated $+            TColumn $+                fromVector $+                    V.replicate (VG.length (offsets gdf) - 1) value+interpretAggregation gdf@(Grouped df names indices os) (Col name) = case getColumn name df of+    Nothing -> Left $ ColumnNotFoundException name "" (M.keys $ columnIndices df)+    Just (BoxedColumn col) -> Right $ UnAggregated $ fromVector $ mkUnaggregatedColumn col os indices+    Just (OptionalColumn col) -> Right $ UnAggregated $ fromVector $ mkUnaggregatedColumn col os indices+    Just (UnboxedColumn col) -> Right $ UnAggregated $ fromVector $ mkUnaggregatedColumn col os indices+interpretAggregation gdf expression@(UnaryOp _ (f :: c -> d) expr) =     case interpretAggregation @c gdf expr of-        Left unaggregated -> case unaggregated of+        Left (TypeMismatchException context) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show expr)+                        }+                    )+        Left e -> Left e+        Right (UnAggregated unaggregated) -> case unaggregated of             BoxedColumn (col :: V.Vector b) -> case testEquality (typeRep @b) (typeRep @(V.Vector c)) of-                Just Refl -> Left $ fromVector $ V.map (V.map f) col+                Just Refl -> Right $ UnAggregated $ fromVector $ V.map (V.map f) col                 Nothing -> case testEquality (typeRep @b) (typeRep @(VU.Vector c)) of-                    Nothing -> error "Type mismatch"+                    Nothing -> Left $ nestedTypeException @b @c (show expression)                     Just Refl -> case (sUnbox @c, sUnbox @a) of-                        (STrue, STrue) -> Left $ fromVector $ V.map (VU.map f) col-                        (_, _) -> error ""-            _ -> error "Very unlikely: unaggregated column is boxed"-        Right (TColumn aggregated) -> case mapColumn f aggregated of-            Nothing -> error "Type error in interpretation"-            Just col -> Right $ TColumn col-interpretAggregation gdf (BinaryOp _ (f :: c -> d -> e) left right) =+                        (SFalse, _) -> Left $ InternalException "Boxed type inside an unboxed column"+                        (STrue, STrue) -> Right $ UnAggregated $ fromVector $ V.map (VU.map f) col+                        (STrue, _) -> Right $ UnAggregated $ fromVector $ V.map (V.map f . VU.convert) col+            _ -> Left $ InternalException "Aggregated into a non-boxed column"+        Right (Aggregated (TColumn aggregated)) -> case mapColumn f aggregated of+            Left e -> Left e+            Right col -> Right $ Aggregated $ TColumn col+interpretAggregation gdf expression@(BinaryOp _ (f :: c -> d -> e) left right) =     case (interpretAggregation @c gdf left, interpretAggregation @d gdf right) of-        (Right (TColumn left'), Right (TColumn right')) -> case zipWithColumns f left' right' of-            Nothing -> error "Type error in binary operation"-            Just col -> Right $ TColumn col-        (Left left', Left right') -> case (left', right') of+        (Right (Aggregated (TColumn left')), Right (Aggregated (TColumn right'))) -> case zipWithColumns f left' right' of+            Left e -> Left e+            Right col -> Right $ Aggregated $ TColumn col+        (Right (UnAggregated left'), Right (UnAggregated right')) -> case (left', right') of             (BoxedColumn (l :: V.Vector m), BoxedColumn (r :: V.Vector n)) -> case testEquality (typeRep @m) (typeRep @(VU.Vector c)) of                 Just Refl -> case testEquality (typeRep @n) (typeRep @(VU.Vector d)) of                     Just Refl -> case (sUnbox @c, sUnbox @d, sUnbox @e) of-                        (STrue, STrue, STrue) -> Left $ fromVector $ V.zipWith (\l' r' -> VU.zipWith f l' r') l r-                        (_, _, _) -> error "Weird error"+                        (STrue, STrue, STrue) ->+                            Right $ UnAggregated $ fromVector $ V.zipWith (\l' r' -> VU.zipWith f l' r') l r+                        (STrue, STrue, SFalse) ->+                            Right $+                                UnAggregated $+                                    fromVector $+                                        V.zipWith (\l' r' -> V.zipWith f (V.convert l') (V.convert r')) l r+                        (_, _, _) -> Left $ InternalException "Boxed vectors contain unboxed types"                     Nothing -> case testEquality (typeRep @n) (typeRep @(V.Vector d)) of                         Just Refl -> case sUnbox @c of-                            STrue -> Left $ fromVector $ V.zipWith (\l' r' -> V.zipWith f (V.convert l') r') l r-                            SFalse -> error "Very unlikely: boxed value in unboxed column."-                        Nothing -> error "Not a vector of any type???"-                Nothing -> error "Not a vector of any type???"-            _ -> error "Very unlikely: aggregation shouldn't be in a boxed column"-        (_, _) -> error "Cannot apply binary operation to unaggregated and aggregated column"-interpretAggregation gdf (If cond l r) =+                            STrue ->+                                Right $+                                    UnAggregated $+                                        fromVector $+                                            V.zipWith (\l' r' -> V.zipWith f (V.convert l') r') l r+                            SFalse -> Left $ InternalException "Unboxed vectors contain boxed types"+                        Nothing -> Left $ nestedTypeException @n @d (show right)+                Nothing -> case testEquality (typeRep @m) (typeRep @(V.Vector c)) of+                    Nothing -> Left $ nestedTypeException @m @c (show left)+                    Just Refl -> case testEquality (typeRep @n) (typeRep @(VU.Vector d)) of+                        Just Refl -> case (sUnbox @d, sUnbox @e) of+                            (STrue, STrue) ->+                                Right $+                                    UnAggregated $+                                        fromVector $+                                            V.zipWith+                                                (\l' r' -> V.convert @V.Vector @e @VU.Vector $ V.zipWith f l' (V.convert r'))+                                                l+                                                r+                            (STrue, SFalse) ->+                                Right $+                                    UnAggregated $+                                        fromVector $+                                            V.zipWith (\l' r' -> V.zipWith f l' (V.convert r')) l r+                            (_, _) -> Left $ InternalException "Unboxed vectors contain boxed types"+                        Nothing -> case testEquality (typeRep @n) (typeRep @(V.Vector d)) of+                            Just Refl -> case sUnbox @c of+                                STrue -> case sUnbox @e of+                                    SFalse ->+                                        Right $+                                            UnAggregated $+                                                fromVector $+                                                    V.zipWith (\l' r' -> V.zipWith f (V.convert l') r') l r+                                    STrue ->+                                        Right $+                                            UnAggregated $+                                                fromVector $+                                                    V.zipWith (\l' r' -> V.convert @V.Vector @e @VU.Vector $ V.zipWith f l' r') l r+                                SFalse -> Left $ InternalException "Unboxed vectors contain boxed types"+                            Nothing -> Left $ nestedTypeException @n @d (show right)+            _ -> Left $ InternalException "Aggregated into a non-boxed column"+        (Right _, Right _) ->+            Left $+                AggregatedAndNonAggregatedException (T.pack $ show left) (T.pack $ show right)+        (Left (TypeMismatchException context), _) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show left)+                        }+                    )+        (Left e, _) -> Left e+        (_, Left (TypeMismatchException context)) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show right)+                        }+                    )+        (_, Left e) -> Left e+interpretAggregation gdf expression@(If cond l r) =     case ( interpretAggregation @Bool gdf cond          , interpretAggregation @a gdf l          , interpretAggregation @a gdf r          ) of-        (Right (TColumn conditions), Right (TColumn left), Right (TColumn right)) ->-            Right $-                TColumn $-                    fromMaybe (error "zipWithColumns returned nothing") $-                        zipWithColumns-                            (\(c :: Bool) (l' :: a, r' :: a) -> if c then l' else r')-                            conditions-                            (zipColumns left right)-        ( Left conditions-            , Left left@(BoxedColumn left')-            , Left right@(BoxedColumn right')-            ) ->-                Left $-                    fromMaybe (error "zipWithColumns returned nothing") $-                        zipWithColumns-                            ( \(c :: VU.Vector Bool) (l' :: V.Vector a, r' :: V.Vector a) ->-                                V.zipWith-                                    (\c' (l'', r'') -> if c' then l'' else r'')-                                    (V.convert c)-                                    (V.zip l' r')-                            )-                            conditions-                            (zipColumns left right)-        ( Left conditions-            , Left left@(UnboxedColumn left')-            , Left right@(UnboxedColumn right')-            ) -> case sUnbox @a of-                STrue ->+        ( Right (Aggregated (TColumn conditions))+            , Right (Aggregated (TColumn left))+            , Right (Aggregated (TColumn right))+            ) -> case zipWithColumns+                (\(c :: Bool) (l' :: a, r' :: a) -> if c then l' else r')+                conditions+                (zipColumns left right) of+                Left e -> Left e+                Right v -> Right $ Aggregated (TColumn v)+        ( Right (UnAggregated conditions)+            , Right (UnAggregated left@(BoxedColumn (left' :: V.Vector b)))+            , Right (UnAggregated right@(BoxedColumn (right' :: V.Vector c)))+            ) -> case testEquality (typeRep @b) (typeRep @c) of+                Nothing ->                     Left $-                        fromMaybe (error "zipWithColumns returned nothing") $-                            zipWithColumns-                                ( \(c :: VU.Vector Bool) (l' :: VU.Vector a, r' :: VU.Vector a) -> VU.zipWith (\c' (l'', r'') -> if c' then l'' else r'') c (VU.zip l' r')+                        TypeMismatchException+                            ( MkTypeErrorContext+                                { userType = Right (typeRep @b)+                                , expectedType = Right (typeRep @c)+                                , callingFunctionName = Just "interpretAggregation"+                                , errorColumnName = Just (show expression)+                                }+                            )+                Just Refl -> case testEquality (typeRep @(V.Vector a)) (typeRep @b) of+                    Just Refl -> case zipWithColumns+                        ( \(c :: VU.Vector Bool) (l' :: V.Vector a, r' :: V.Vector a) ->+                            V.zipWith+                                (\c' (l'', r'') -> if c' then l'' else r'')+                                (V.convert c)+                                (V.zip l' r')+                        )+                        conditions+                        (zipColumns left right) of+                        Left (TypeMismatchException context) ->+                            Left $+                                TypeMismatchException+                                    ( context+                                        { callingFunctionName = Just "interpretAggregation"+                                        , errorColumnName = Just (show expression)+                                        }+                                    )+                        Left e -> Left e+                        Right v -> Right $ UnAggregated v+                    Nothing -> case testEquality (typeRep @(VU.Vector a)) (typeRep @b) of+                        Nothing -> Left $ nestedTypeException @b @a (show expression)+                        Just Refl -> case sUnbox @a of+                            SFalse -> Left $ InternalException "Boxed type in unboxed column"+                            STrue -> case zipWithColumns+                                ( \(c :: VU.Vector Bool) (l' :: VU.Vector a, r' :: VU.Vector a) ->+                                    VU.zipWith+                                        (\c' (l'', r'') -> if c' then l'' else r'')+                                        c+                                        (VU.zip l' r')                                 )                                 conditions-                                (zipColumns left right)-                SFalse -> error "Very unlikely: Unboxed type in boxed column"-        (_, _, _) -> error "Some columns in the conditional are unaggregated"-interpretAggregation gdf@(Grouped df names indices os) (AggVector expr op (f :: v b -> c)) =+                                (zipColumns left right) of+                                Left (TypeMismatchException context) ->+                                    Left $+                                        TypeMismatchException+                                            ( context+                                                { callingFunctionName = Just "interpretAggregation"+                                                , errorColumnName = Just (show expression)+                                                }+                                            )+                                Left e -> Left e+                                Right v -> Right $ UnAggregated v+        (Right _, Right _, Right _) ->+            Left $+                AggregatedAndNonAggregatedException (T.pack $ show l) (T.pack $ show r)+        (Left (TypeMismatchException context), _, _) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show cond)+                        }+                    )+        (Left e, _, _) -> Left e+        (_, Left (TypeMismatchException context), _) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show l)+                        }+                    )+        (_, Left e, _) -> Left e+        (_, _, Left (TypeMismatchException context)) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show r)+                        }+                    )+        (_, _, Left e) -> Left e+interpretAggregation gdf@(Grouped df names indices os) expression@(AggVector expr op (f :: v b -> c)) =     case interpretAggregation @b gdf expr of-        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(v b)) (typeRep @d) of-            Nothing -> error "Type mismatch"+        Right (UnAggregated (BoxedColumn (col :: V.Vector d))) -> case testEquality (typeRep @(v b)) (typeRep @d) of+            Nothing -> Left $ nestedTypeException @d @b (show expr)             Just Refl -> case testEquality (typeRep @v) (typeRep @V.Vector) of-                Nothing -> error "Container mismatch"-                Just Refl -> Right $ TColumn $ fromVector $ V.map f col-        Left _ -> error "Very unlikely: aggregated into unboxed column???"-        Right (TColumn (BoxedColumn col)) -> case testEquality (typeRep @(v b)) (typeOf col) of-            Just Refl -> interpretAggregation @c gdf (Lit (f col))-            Nothing -> error "Type mismatch"-        Right (TColumn (OptionalColumn col)) -> case testEquality (typeRep @(v b)) (typeOf col) of-            Just Refl -> interpretAggregation @c gdf (Lit (f col))-            Nothing -> error "Type mismatch"-        Right (TColumn (UnboxedColumn col)) -> case testEquality (typeRep @(v b)) (typeOf col) of+                Nothing -> Right $ Aggregated $ TColumn $ fromVector $ V.map (f . V.convert) col+                Just Refl -> Right $ Aggregated $ TColumn $ fromVector $ V.map f col+        Right (UnAggregated _) -> Left $ InternalException "Aggregated into non-boxed column"+        Right (Aggregated (TColumn (BoxedColumn (col :: V.Vector d)))) -> case testEquality (typeRep @b) (typeRep @d) of+            Just Refl -> case testEquality (typeRep @v) (typeRep @V.Vector) of+                Just Refl -> interpretAggregation @c gdf (Lit (f col))+                Nothing -> interpretAggregation @c gdf (Lit ((f . V.convert) col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @b)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpretAggregation"+                            , errorColumnName = Just (show expr)+                            }+                        )+        Right (Aggregated (TColumn (UnboxedColumn (col :: VU.Vector d)))) -> case testEquality (typeRep @b) (typeRep @d) of+            Just Refl -> case testEquality (typeRep @v) (typeRep @VU.Vector) of+                Just Refl -> interpretAggregation @c gdf (Lit (f col))+                Nothing -> interpretAggregation @c gdf (Lit ((f . VU.convert) col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @b)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpretAggregation"+                            , errorColumnName = Just (show expr)+                            }+                        )+        Right (Aggregated (TColumn (OptionalColumn (col :: V.Vector d)))) -> case testEquality (typeRep @b) (typeRep @d) of+            Just Refl -> case testEquality (typeRep @v) (typeRep @V.Vector) of+                Just Refl -> interpretAggregation @c gdf (Lit (f col))+                Nothing -> interpretAggregation @c gdf (Lit ((f . V.convert) col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @b)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpretAggregation"+                            , errorColumnName = Just (show expr)+                            }+                        )+        (Left (TypeMismatchException context)) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show expression)+                        }+                    )+        (Left e) -> Left e+interpretAggregation gdf@(Grouped df names indices os) expression@(AggNumericVector expr op (f :: VU.Vector b -> c)) =+    case interpretAggregation @b gdf expr of+        (Left (TypeMismatchException context)) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show expression)+                        }+                    )+        (Left e) -> Left e+        Right (UnAggregated (BoxedColumn (col :: V.Vector d))) -> case testEquality (typeRep @(VU.Vector b)) (typeRep @d) of+            Nothing -> case testEquality (typeRep @(VU.Vector Int)) (typeRep @d) of+                Nothing -> case testEquality (typeRep @(V.Vector Integer)) (typeRep @d) of+                    Nothing -> Left $ nestedTypeException @d @b (show expr)+                    Just Refl ->+                        Right $+                            Aggregated $+                                TColumn $+                                    fromVector $+                                        V.map (f . VU.convert . V.map fromIntegral) col+                Just Refl ->+                    Right $+                        Aggregated $+                            TColumn $+                                fromVector $+                                    V.map f (VG.map (VG.map fromIntegral) col)+            Just Refl -> Right $ Aggregated $ TColumn $ fromVector $ V.map f col+        Right (UnAggregated _) -> Left $ InternalException "Aggregated into non-boxed column"+        Right (Aggregated (TColumn (BoxedColumn (col :: V.Vector d)))) -> case testEquality (typeRep @Integer) (typeRep @d) of+            Just Refl -> interpretAggregation @c gdf (Lit ((f . V.convert . V.map fromIntegral) col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @b)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpretAggregation"+                            , errorColumnName = Just (show expr)+                            }+                        )+        Right (Aggregated (TColumn (UnboxedColumn (col :: VU.Vector d)))) -> case testEquality (typeRep @b) (typeRep @d) of             Just Refl -> interpretAggregation @c gdf (Lit (f col))-            Nothing -> error "Type mismatch"-interpretAggregation gdf@(Grouped df names indices os) (AggReduce expr op (f :: forall a. (Columnable a) => a -> a -> a)) =+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @b)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpretAggregation"+                            , errorColumnName = Just (show expr)+                            }+                        )+        Right (Aggregated (TColumn (OptionalColumn (col :: V.Vector (Maybe d))))) -> case testEquality (typeRep @b) (typeRep @d) of+            Just Refl ->+                interpretAggregation @c+                    gdf+                    (Lit ((f . V.convert . V.map (fromMaybe 0) . V.filter isJust) col))+            Nothing ->+                Left $+                    TypeMismatchException+                        ( MkTypeErrorContext+                            { userType = Right (typeRep @b)+                            , expectedType = Right (typeRep @d)+                            , callingFunctionName = Just "interpretAggregation"+                            , errorColumnName = Just (show expr)+                            }+                        )+interpretAggregation gdf@(Grouped df names indices os) expression@(AggReduce expr op (f :: forall a. (Columnable a) => a -> a -> a)) =     case interpretAggregation @a gdf expr of-        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(V.Vector a)) (typeRep @d) of+        (Left (TypeMismatchException context)) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show expression)+                        }+                    )+        (Left e) -> Left e+        Right (UnAggregated (BoxedColumn (col :: V.Vector d))) -> case testEquality (typeRep @(V.Vector a)) (typeRep @d) of             Nothing -> case testEquality (typeRep @(VU.Vector a)) (typeRep @d) of-                Nothing -> error "Type mismatch"+                Nothing -> Left $ nestedTypeException @d @a (show expr)                 Just Refl -> case sUnbox @a of                     STrue ->                         Right $-                            TColumn $-                                fromVector $-                                    V.map (\v -> VU.foldl' f (VG.head v) (VG.drop 1 v)) col-                    SFalse -> error "Very unlikely case: unboxed vector contains a boxed type"+                            Aggregated $+                                TColumn $+                                    fromVector $+                                        V.map (\v -> VU.foldl' f (VG.head v) (VG.drop 1 v)) col+                    SFalse -> Left $ InternalException "Boxed type inside an unboxed column"             Just Refl ->                 Right $-                    TColumn $-                        fromVector $-                            V.map (\v -> VG.foldl' f (VG.head v) (VG.drop 1 v)) col-        Left _ -> error "Impossible to aggregate into unaggregated column"-        Right (TColumn column) -> case headColumn @a column of-            Nothing -> error "Invalid operation"-            Just h -> case ifoldlColumn (\acc _ v -> f acc v) h column of-                Nothing -> error "Invalid operation"-                Just value -> interpretAggregation @a gdf (Lit value)-interpretAggregation gdf@(Grouped df names indices os) (AggFold expr op s (f :: (a -> b -> a))) =+                    Aggregated $+                        TColumn $+                            fromVector $+                                V.map (\v -> VG.foldl' f (VG.head v) (VG.drop 1 v)) col+        Right (UnAggregated _) -> Left $ InternalException "Aggregated into non-boxed column"+        Right (Aggregated (TColumn column)) -> case headColumn @a column of+            Left e -> Left e+            Right h -> case ifoldlColumn (\acc _ v -> f acc v) h column of+                Left e -> Left e+                Right value -> interpretAggregation @a gdf (Lit value)+interpretAggregation gdf@(Grouped df names indices os) expression@(AggFold expr op s (f :: (a -> b -> a))) =     case interpretAggregation @b gdf expr of-        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(V.Vector b)) (typeRep @d) of-            Just Refl -> Right $ TColumn $ fromVector $ V.map (\v -> V.foldl' f s v) col+        (Left (TypeMismatchException context)) ->+            Left $+                TypeMismatchException+                    ( context+                        { callingFunctionName = Just "interpretAggregation"+                        , errorColumnName = Just (show expression)+                        }+                    )+        (Left e) -> Left e+        Right (UnAggregated (BoxedColumn (col :: V.Vector d))) -> case testEquality (typeRep @(V.Vector b)) (typeRep @d) of+            Just Refl -> Right $ Aggregated $ TColumn $ fromVector $ V.map (\v -> V.foldl' f s v) col             Nothing -> case testEquality (typeRep @(VU.Vector b)) (typeRep @d) of                 Just Refl -> case sUnbox @b of-                    STrue -> Right $ TColumn $ fromVector $ V.map (\v -> VU.foldl' f s v) col-                    SFalse -> error "Very unlikely: boxed type in unboxed column"-                Nothing -> undefined-        Left _ -> error "Impossible to aggregate into unaggregated column"-        Right (TColumn column) -> case ifoldlColumn (\acc _ v -> f acc v) s column of-            Nothing -> error "Invalid operation"-            Just value -> interpretAggregation @a gdf (Lit value)-interpretAggregation gdf@(Grouped df names indices os) (AggNumericVector expr op (f :: VU.Vector b -> c)) =-    case interpretAggregation @b gdf expr of-        Left (BoxedColumn (col :: V.Vector d)) -> case testEquality (typeRep @(VU.Vector b)) (typeRep @d) of-            Nothing -> case testEquality (typeRep @(VU.Vector Int)) (typeRep @d) of-                Nothing -> error $ "Type mismatch - non numeric " ++ (show (typeRep @d))-                Just Refl -> Right $ TColumn $ fromVector $ V.map f (VG.map (VG.map fromIntegral) col)-            Just Refl -> Right $ TColumn $ fromVector $ V.map f col-        Left _ -> error "Impossible to aggregate into unaggregated column"-        Right (TColumn (UnboxedColumn col)) -> case testEquality (typeRep @(VU.Vector b)) (typeOf col) of-            Just Refl -> interpretAggregation @c gdf (Lit (f col))-            Nothing -> error "Type mismatch"-        Right (TColumn (BoxedColumn col)) -> case testEquality (typeRep @(V.Vector Integer)) (typeOf col) of-            Just Refl -> interpretAggregation @c gdf (Lit (f (VU.convert $ V.map fromInteger col)))-            Nothing -> error "Type mismatch"-        Right _ -> error "Cannot apply numeric aggregation to non-numeric column."+                    STrue ->+                        Right $ Aggregated $ TColumn $ fromVector $ V.map (\v -> VU.foldl' f s v) col+                    SFalse -> Left $ InternalException "Boxed type inside an unboxed column"+                Nothing -> Left $ nestedTypeException @d @b (show expr)+        Right (UnAggregated _) -> Left $ InternalException "Aggregated into non-boxed column"+        Right (Aggregated (TColumn column)) -> case ifoldlColumn (\acc _ v -> f acc v) s column of+            Left e -> Left e+            Right value -> interpretAggregation @a gdf (Lit value)  instance (Num a, Columnable a) => Num (Expr a) where     (+) :: Expr a -> Expr a -> Expr a
src/DataFrame/Internal/Parsing.hs view
@@ -41,10 +41,14 @@ {-# INLINE readByteStringInt #-}  readByteStringDouble :: (HasCallStack) => C.ByteString -> Maybe Double-readByteStringDouble s = case readSigned readDecimal (C.strip s) of-    Nothing -> Nothing-    Just (value, "") -> Just value-    Just (value, _) -> Nothing+readByteStringDouble s =+    let+        readFunc = if C.any (\c -> c == 'e' || c == 'E') s then readExponential else readDecimal+     in+        case readSigned readFunc (C.strip s) of+            Nothing -> Nothing+            Just (value, "") -> Just value+            Just (value, _) -> Nothing {-# INLINE readByteStringDouble #-}  readDouble :: (HasCallStack) => T.Text -> Maybe Double
src/DataFrame/Internal/Statistics.hs view
@@ -12,7 +12,9 @@ import DataFrame.Errors (DataFrameException (..))  mean' :: (Real a, VU.Unbox a) => VU.Vector a -> Double-mean' samp = VU.sum (VU.map realToFrac samp) / fromIntegral (VU.length samp)+mean' samp+    | VU.null samp = throw $ EmptyDataSetException "mean"+    | otherwise = VU.sum (VU.map realToFrac samp) / fromIntegral (VU.length samp) {-# INLINE mean' #-}  median' :: (Real a, VU.Unbox a) => VU.Vector a -> Double
src/DataFrame/Operations/Aggregation.hs view
@@ -18,17 +18,16 @@  import Control.Exception (throw) import Control.Monad.ST (runST)-import Data.Either (fromRight) import Data.Hashable import Data.Type.Equality (TestEquality (..), type (:~:) (Refl)) import DataFrame.Errors import DataFrame.Internal.Column (     Column (..),     Columnable,+    TypedColumn (..),     atIndicesStable,     getIndices,     getIndicesUnboxed,-    unwrapTypedColumn,  ) import DataFrame.Internal.DataFrame (DataFrame (..), GroupedDataFrame (..)) import DataFrame.Internal.Expression@@ -124,9 +123,12 @@          f (name, Wrap (expr :: Expr a)) d =             let-                value = fromRight (error "Not fully aggregated") (interpretAggregation @a gdf expr)+                value = case interpretAggregation @a gdf expr of+                    Left e -> throw e+                    Right (UnAggregated _) -> throw $ UnaggregatedException (T.pack $ show expr)+                    Right (Aggregated (TColumn col)) -> col              in-                insertColumn name (unwrapTypedColumn value) d+                insertColumn name value d      in         fold f aggs df' 
src/DataFrame/Operations/Subset.hs view
@@ -29,6 +29,7 @@ import DataFrame.Internal.Expression import DataFrame.Operations.Core import DataFrame.Operations.Transformations (apply)+import System.Random import Type.Reflection import Prelude hiding (filter, take) @@ -163,12 +164,12 @@ filterWhere :: Expr Bool -> DataFrame -> DataFrame filterWhere expr df =     let-        (TColumn col) = interpret @Bool df expr+        (TColumn col) = case interpret @Bool df expr of+            Left e -> throw e+            Right c -> c         indexes = case findIndices (== True) col of-            Just ixs -> ixs-            Nothing ->-                -- Shouldn't happen as we are filtering by Bool column-                error "filterWhere: type mismatch"+            Right ixs -> ixs+            Left e -> throw e         c' = snd $ dataframeDimensions df      in         df@@ -310,3 +311,98 @@ exclude cs df =     let keysToKeep = columnNames df L.\\ cs      in select keysToKeep df++{- | Sample a dataframe. The double parameter must be between 0 and 1 (inclusive).++==== __Example__+@+ghci> import System.Random+ghci> D.sample (mkStdGen 137) 0.1 df++@+-}+sample :: (RandomGen g) => g -> Double -> DataFrame -> DataFrame+sample pureGen p df =+    let+        rand = generateRandomVector pureGen (fst (dataframeDimensions df))+     in+        df+            & insertUnboxedVector "__rand__" rand+            & filterWhere (BinaryOp "geq" (>=) (Col @Double "__rand__") (Lit (1 - p)))+            & exclude ["__rand__"]++{- | Split a dataset into two. The first in the tuple gets a sample of p (0 <= p <= 1) and the second gets (1 - p). This is useful for creating test and train splits.++==== __Example__+@+ghci> import System.Random+ghci> D.randomSplit (mkStdGen 137) 0.9 df++@+-}+randomSplit ::+    (RandomGen g) => g -> Double -> DataFrame -> (DataFrame, DataFrame)+randomSplit pureGen p df =+    let+        rand = generateRandomVector pureGen (fst (dataframeDimensions df))+        withRand = df & insertUnboxedVector "__rand__" rand+     in+        ( withRand+            & filterWhere (BinaryOp "geq" (>=) (Col @Double "__rand__") (Lit (1 - p)))+            & exclude ["__rand__"]+        , withRand+            & filterWhere (BinaryOp "geq" (<) (Col @Double "__rand__") (Lit p))+            & exclude ["__rand__"]+        )++{- | Creates n folds of a dataframe.++==== __Example__+@+ghci> import System.Random+ghci> D.kFolds (mkStdGen 137) 5 df++@+-}+kFolds :: (RandomGen g) => g -> Int -> DataFrame -> [DataFrame]+kFolds pureGen folds df =+    let+        rand = generateRandomVector pureGen (fst (dataframeDimensions df))+        withRand = df & insertUnboxedVector "__rand__" rand+        partitionSize = 1 / (fromIntegral folds)+        singleFold n d =+            d+                & filterWhere+                    ( BinaryOp+                        "geq"+                        (>=)+                        (Col @Double "__rand__")+                        (Lit (fromIntegral n * partitionSize))+                    )+        go (-1) _ = []+        go n d =+            let+                d' = singleFold n d+                d'' =+                    d+                        & filterWhere+                            ( BinaryOp+                                "lt"+                                (<)+                                (Col @Double "__rand__")+                                (Lit (fromIntegral n * partitionSize))+                            )+             in+                d' : go (n - 1) d''+     in+        map (exclude ["__rand__"]) (go (folds - 1) withRand)++generateRandomVector :: (RandomGen g) => g -> Int -> VU.Vector Double+generateRandomVector pureGen k = VU.fromList $ go pureGen k+  where+    go g 0 = []+    go g n =+        let+            (v, g') = uniformR (0 :: Double, 1 :: Double) g+         in+            v : go g' (n - 1)
src/DataFrame/Operations/Transformations.hs view
@@ -17,16 +17,14 @@ import DataFrame.Internal.Column (     Column (..),     Columnable,-    columnTypeString,+    TypedColumn (..),     ifoldrColumn,     imapColumn,     mapColumn,-    unwrapTypedColumn,  ) import DataFrame.Internal.DataFrame (DataFrame (..), getColumn) import DataFrame.Internal.Expression import DataFrame.Operations.Core-import Type.Reflection (TypeRep, typeRep)  -- | O(k) Apply a function to a given column in a dataframe. apply ::@@ -40,6 +38,8 @@     DataFrame ->     DataFrame apply f columnName d = case safeApply f columnName d of+    Left (TypeMismatchException context) ->+        throw $ TypeMismatchException (context{callingFunctionName = Just "apply"})     Left exception -> throw exception     Right df -> df @@ -56,28 +56,17 @@     Either DataFrameException DataFrame safeApply f columnName d = case getColumn columnName d of     Nothing -> Left $ ColumnNotFoundException columnName "apply" (M.keys $ columnIndices d)-    Just column -> case mapColumn f column of-        Nothing ->-            Left $-                TypeMismatchException-                    ( MkTypeErrorContext-                        { userType = Right $ typeRep @b-                        , expectedType = Left (columnTypeString column) :: Either String (TypeRep ())-                        , errorColumnName = Just (T.unpack columnName)-                        , callingFunctionName = Just "apply"-                        }-                    )-        Just column' -> Right $ insertColumn columnName column' d+    Just column -> do+        column' <- mapColumn f column+        pure $ insertColumn columnName column' d  {- | O(k) Apply a function to a combination of columns in a dataframe and add the result into `alias` column. -} derive :: forall a. (Columnable a) => T.Text -> Expr a -> DataFrame -> DataFrame-derive name expr df =-    let-        value = interpret @a df expr-     in-        insertColumn name (unwrapTypedColumn value) df+derive name expr df = case interpret @a df expr of+    Left e -> throw e+    Right (TColumn value) -> insertColumn name value df  -- | O(k * n) Apply a function to given column names in a dataframe. applyMany ::@@ -142,17 +131,8 @@         (\i val acc -> if condition val then V.cons i acc else acc)         V.empty         column of-        Nothing ->-            throw $-                TypeMismatchException-                    ( MkTypeErrorContext-                        { userType = Right $ typeRep @a-                        , expectedType = Left (columnTypeString column) :: Either String (TypeRep ())-                        , errorColumnName = Just (T.unpack columnName)-                        , callingFunctionName = Just "applyWhere"-                        }-                    )-        Just indexes ->+        Left e -> throw e+        Right indexes ->             if V.null indexes                 then df                 else L.foldl' (\d i -> applyAtIndex i f columnName d) df indexes@@ -175,17 +155,8 @@         throw $             ColumnNotFoundException columnName "applyAtIndex" (M.keys $ columnIndices df)     Just column -> case imapColumn (\index value -> if index == i then f value else value) column of-        Nothing ->-            throw $-                TypeMismatchException-                    ( MkTypeErrorContext-                        { userType = Right $ typeRep @a-                        , expectedType = Left (columnTypeString column) :: Either String (TypeRep ())-                        , errorColumnName = Just (T.unpack columnName)-                        , callingFunctionName = Just "applyAtIndex"-                        }-                    )-        Just column' -> insertColumn columnName column' df+        Left e -> throw e+        Right column' -> insertColumn columnName column' df  -- | Replace all instances of `Nothing` in a column with the given value. impute ::
src/DataFrame/Operations/Typing.hs view
@@ -49,7 +49,9 @@                         hasNulls =                             V.foldl' (\acc v -> if isNothing v then acc || True else acc) False safeVector                      in-                        if safeRead && hasNulls then BoxedColumn safeVector else BoxedColumn c+                        if safeRead && hasNulls+                            then BoxedColumn safeVector+                            else BoxedColumn (V.map T.pack c)                 Nothing -> BoxedColumn c             Just Refl ->                 let example = T.strip (V.head c)
tests/Operations/Apply.hs view
@@ -55,7 +55,16 @@     TestCase         ( assertExpectException             "[Error Case]"-            (DE.typeMismatchError (show $ typeRep @Char) (show $ typeRep @[Char]))+            ( show $+                DE.TypeMismatchException+                    ( DE.MkTypeErrorContext+                        { DE.userType = Right (typeRep @Char)+                        , DE.expectedType = Right (typeRep @String)+                        , DE.callingFunctionName = Just "apply"+                        , DE.errorColumnName = Nothing+                        }+                    )+            )             (print $ DI.getColumn "test2" $ D.apply @Char (const (1 :: Int)) "test2" testData)         )