packages feed

dataframe-viz 1.0.2.0 → 1.0.3.0

raw patch · 6 files changed

+119/−21 lines, 6 filesdep ~dataframe-coredep ~textPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: dataframe-core, text

API changes (from Hackage documentation)

+ DataFrame.Display.Web.Chart: includeZero :: Channel -> Bool -> Chart -> Chart
+ DataFrame.Display.Web.Chart.Typed: includeZero :: forall (cols :: [Type]). Channel -> Bool -> Chart cols -> Chart cols
+ DataFrame.Display.Web.Plot: [includeZero] :: Line -> Bool
- DataFrame.Display.Web.Plot: Line :: Text -> [Text] -> Maybe Text -> Size -> Line
+ DataFrame.Display.Web.Plot: Line :: Text -> [Text] -> Maybe Text -> Size -> Bool -> Line
- DataFrame.Display.Web.Plot: Scatter :: Text -> Text -> Maybe Text -> Maybe Text -> Size -> Scatter
+ DataFrame.Display.Web.Plot: Scatter :: Text -> Text -> Maybe Text -> Maybe Text -> Size -> Bool -> Scatter

Files

dataframe-viz.cabal view
@@ -1,7 +1,6 @@ cabal-version:      2.4 name:               dataframe-viz-version:            1.0.2.0-+version:            1.0.3.0 synopsis:           Visualisation/plotting helpers for the dataframe ecosystem. description:     Display harness plus terminal and web plotters. Built on top of@@ -38,7 +37,7 @@     build-depends:      base >= 4 && < 5,                         aeson >= 0.11.0.0 && < 3,                         containers >= 0.6.7 && < 0.9,-                        dataframe-core ^>= 1.0,+                        dataframe-core ^>= 1.1,                         directory >= 1.3.0.0 && < 2,                         granite >= 0.6 && < 1,                         process ^>= 1.6,
src/DataFrame/Display/Internal/Common.hs view
@@ -47,7 +47,12 @@ import GHC.Stack (HasCallStack) import Type.Reflection (TypeRep, typeRep) -import DataFrame.Internal.Column (Column (..), Columnable, isNumeric)+import DataFrame.Internal.Column (+    Column (..),+    Columnable,+    isNumeric,+    materializePacked,+ ) import DataFrame.Internal.DataFrame (DataFrame (..), getColumn) import DataFrame.Internal.Types @@ -164,12 +169,14 @@             Nothing -> V.toList $ V.map (T.pack . show) vec     UnboxedColumn _ vec ->         V.toList $ VG.map (T.pack . show) (VG.convert vec)+    PackedText _ _ -> columnToStrings (materializePacked col)  -- | Coerce a numeric column to @[Double]@; errors if the element type is not numeric. columnToDoubles :: (HasCallStack) => Column -> [Double] columnToDoubles col = case col of     BoxedColumn _ vec -> vectorToDoubles vec     UnboxedColumn _ vec -> unboxedVectorToDoubles vec+    PackedText _ _ -> columnToDoubles (materializePacked col)  vectorToDoubles :: forall a. (Columnable a, Show a) => V.Vector a -> [Double] vectorToDoubles vec =@@ -206,6 +213,9 @@                         Just (countBoxed (typeRep @a) vec)                     UnboxedColumn _ (vec :: VU.Vector a) ->                         Just (countUnboxed (typeRep @a) vec)+                    PackedText _ _ -> case materializePacked col of+                        BoxedColumn _ (vec :: V.Vector a) -> Just (countBoxed (typeRep @a) vec)+                        _ -> Nothing   where     countBoxed ::         forall a. (Show a) => TypeRep a -> V.Vector a -> [(T.Text, Double)]
src/DataFrame/Display/Internal/VegaLite.hs view
@@ -21,6 +21,9 @@     Channel (..),     FieldType (..),     ChannelEnc (..),+    ScaleSpec (..),+    ScaleType (..),+    defaultScale,     Transform (..),     VLSpec (..),     emptySpec,@@ -125,13 +128,29 @@     , ceType :: FieldType     , ceAggregate :: Maybe T.Text     , ceBin :: Bool-    , ceLogScale :: Bool+    , ceScale :: ScaleSpec     } --- | A bare channel encoding with no aggregation, binning, or log scale.+-- | A bare channel encoding with no aggregation, binning, or scale options. chanEnc :: Channel -> T.Text -> FieldType -> ChannelEnc-chanEnc ch fld ft = ChannelEnc ch fld ft Nothing False False+chanEnc ch fld ft = ChannelEnc ch fld ft Nothing False defaultScale +{- | Per-channel scale configuration, mirroring Vega-Lite's @scale@ object.+'Nothing' fields leave the Vega-Lite default in place; grow this record+(domain, nice, clamp, scheme, ...) as the public vocabulary grows.+-}+data ScaleSpec = ScaleSpec+    { scaleType :: Maybe ScaleType+    , scaleZero :: Maybe Bool+    }+    deriving (Eq, Show)++data ScaleType = LogS+    deriving (Eq, Show)++defaultScale :: ScaleSpec+defaultScale = ScaleSpec Nothing Nothing+ data Transform     = -- | Fit @regression(yField) on xField@ (used inside a layer).       RegressionT T.Text T.Text@@ -294,10 +313,23 @@             , Just ("type" .= fieldTypeName (ceType e))             , fmap ("aggregate" .=) (ceAggregate e)             , if ceBin e then Just ("bin" .= True) else Nothing-            , if ceLogScale e-                then Just ("scale" .= object ["type" .= ("log" :: T.Text)])-                else Nothing+            , fmap ("scale" .=) (scaleValue (ceScale e))             ]++-- | Render a scale spec, or 'Nothing' when every field is at its default.+scaleValue :: ScaleSpec -> Maybe Value+scaleValue s+    | null pairs = Nothing+    | otherwise = Just (object pairs)+  where+    pairs =+        catMaybes+            [ fmap (("type" .=) . scaleTypeName) (scaleType s)+            , fmap ("zero" .=) (scaleZero s)+            ]++scaleTypeName :: ScaleType -> T.Text+scaleTypeName LogS = "log"  transformValue :: Transform -> Value transformValue (RegressionT yField xField) =
src/DataFrame/Display/Web/Chart.hs view
@@ -50,6 +50,7 @@     regression,     density,     logScale,+    includeZero,     title,     size, @@ -78,6 +79,8 @@     FieldType (..),     Mark (..),     ResolvedField (..),+    ScaleSpec (..),+    ScaleType (..),     Transform (DensityT, RegressionT),     VLSpec (..),     chanEnc,@@ -155,10 +158,21 @@ binX = withChannel X (\e -> e{ceBin = True}) binY = withChannel Y (\e -> e{ceBin = True}) --- | Put a channel on a log scale.+{- | Put a channel on a log scale. Like all channel modifiers, apply it+after 'enc' on the same channel ('enc' replaces the whole encoding).+-} logScale :: Channel -> Chart -> Chart-logScale ch = withChannel ch (\e -> e{ceLogScale = True})+logScale ch = withScale ch (\s -> s{scaleType = Just LogS}) +{- | Anchor (@True@) or release (@False@) a channel's scale at zero.+Vega-Lite includes zero on quantitative scales by default, which squashes+data far from the origin (e.g. geographic coordinates) against the chart+edge; @includeZero X False@ lets the axis fit the data instead. Apply it+after 'enc' on the same channel ('enc' replaces the whole encoding).+-}+includeZero :: Channel -> Bool -> Chart -> Chart+includeZero ch b = withScale ch (\s -> s{scaleZero = Just b})+ -- | Facet into small multiples by a column (alias for 'column'). facet :: (Columnable a) => Expr a -> Chart -> Chart facet = column@@ -246,10 +260,18 @@ -- One-shot convenience plots (the simple single-line path) -- --------------------------------------------------------------------------- --- | Scatter plot of two expressions.+-- | Scatter plot of two expressions. Axes fit the data (no zero anchor). scatter ::     (Columnable a, Columnable b) => Expr a -> Expr b -> DataFrame -> IO ()-scatter xE yE df = showChart (chart df & mark Point & enc X xE & enc Y yE)+scatter xE yE df =+    showChart+        ( chart df+            & mark Point+            & enc X xE+            & enc Y yE+            & includeZero X False+            & includeZero Y False+        )  -- | Count of rows per category, as bars. bar :: (Columnable a) => Expr a -> DataFrame -> IO ()@@ -260,9 +282,17 @@ histogram xE df =     showChart (chart df & mark Bar & enc X xE & binX & aggregateOn Y Count) --- | Line chart of @y@ over @x@.+-- | Line chart of @y@ over @x@. Axes fit the data (no zero anchor). line :: (Columnable a, Columnable b) => Expr a -> Expr b -> DataFrame -> IO ()-line xE yE df = showChart (chart df & mark Line & enc X xE & enc Y yE)+line xE yE df =+    showChart+        ( chart df+            & mark Line+            & enc X xE+            & enc Y yE+            & includeZero X False+            & includeZero Y False+        )  -- | Pie chart counting rows per category. pie :: (Columnable a) => Expr a -> DataFrame -> IO ()@@ -287,6 +317,10 @@      in if any ((== ch) . ceChannel) encs             then c{chEncs = map (\e -> if ceChannel e == ch then f e else e) encs}             else c{chEncs = encs ++ [f (chanEnc ch "" Quantitative)]}++-- | Modify a channel's scale spec (creating the encoding if absent).+withScale :: Channel -> (ScaleSpec -> ScaleSpec) -> Chart -> Chart+withScale ch f = withChannel ch (\e -> e{ceScale = f (ceScale e)})  aggVegaName :: Agg -> T.Text aggVegaName a = case a of
src/DataFrame/Display/Web/Chart/Typed.hs view
@@ -55,6 +55,7 @@     regression,     density,     logScale,+    includeZero,     title,     size, @@ -119,6 +120,10 @@ -- | Put a channel on a log scale. logScale :: Channel -> Chart cols -> Chart cols logScale ch (Chart c) = Chart (C.logScale ch c)++-- | Anchor (@True@) or release (@False@) a channel's scale at zero.+includeZero :: Channel -> Bool -> Chart cols -> Chart cols+includeZero ch b (Chart c) = Chart (C.includeZero ch b c)  -- | Facet into small multiples by a column (alias for 'column'). facet :: (Columnable a) => TExpr cols a -> Chart cols -> Chart cols
src/DataFrame/Display/Web/Plot.hs view
@@ -136,6 +136,10 @@     let spec' = spec{vlWidth = sz.width, vlHeight = sz.height}     return $ T.unpack $ specHtml chartId fields spec' +-- | Pin a channel's zero anchor explicitly, on or off.+anchorZero :: Bool -> VL.ChannelEnc -> VL.ChannelEnc+anchorZero b e = e{VL.ceScale = VL.defaultScale{VL.scaleZero = Just b}}+ -- --------------------------------------------------------------------------- -- Bar -- ---------------------------------------------------------------------------@@ -226,16 +230,22 @@ -- Scatter -- --------------------------------------------------------------------------- +{- | 'includeZero' anchors both axes at zero (Vega-Lite's own default).+It is off by default so the axes fit the data, as expected of a scatter —+data far from the origin (e.g. geographic coordinates) would otherwise be+squashed against the chart edge.+-} data Scatter = Scatter     { x :: T.Text     , y :: T.Text     , color :: Maybe T.Text     , title :: Maybe T.Text     , size :: Size+    , includeZero :: Bool     }  mkScatter :: T.Text -> T.Text -> Scatter-mkScatter xc yc = Scatter xc yc Nothing Nothing defaultSize+mkScatter xc yc = Scatter xc yc Nothing Nothing defaultSize False  scatter :: (HasCallStack) => Scatter -> DataFrame -> IO String scatter spec df = do@@ -245,7 +255,10 @@         xVals = extractNumericColumn spec.x df         yVals = extractNumericColumn spec.y df         baseFields = [numField spec.x xVals, numField spec.y yVals]-        baseEncs = [chanEnc X spec.x Quantitative, chanEnc Y spec.y Quantitative]+        baseEncs =+            map+                (anchorZero spec.includeZero)+                [chanEnc X spec.x Quantitative, chanEnc Y spec.y Quantitative]         (fields, encs) = case spec.color of             Nothing -> (baseFields, baseEncs)             Just grp ->@@ -263,15 +276,20 @@ -- Line -- --------------------------------------------------------------------------- +{- | 'includeZero' anchors the axes at zero (Vega-Lite's own default). Off+by default so the axes fit the data — a line is position-encoded, so a+forced zero baseline only squashes series far from the origin.+-} data Line = Line     { x :: T.Text     , y :: [T.Text]     , title :: Maybe T.Text     , size :: Size+    , includeZero :: Bool     }  mkLine :: T.Text -> [T.Text] -> Line-mkLine xc ys = Line xc ys Nothing defaultSize+mkLine xc ys = Line xc ys Nothing defaultSize False  line :: (HasCallStack) => Line -> DataFrame -> IO String line spec df = do@@ -297,8 +315,8 @@         vlSpec =             (emptySpec VL.Line)                 { vlEncodings =-                    [ chanEnc X spec.x Quantitative-                    , chanEnc Y "value" Quantitative+                    [ anchorZero spec.includeZero (chanEnc X spec.x Quantitative)+                    , anchorZero spec.includeZero (chanEnc Y "value" Quantitative)                     , chanEnc Color "series" Nominal                     ]                 , vlTitle = Just chartTitle