dataframe-viz 1.3.2.0 → 1.3.2.1
raw patch · 9 files changed
+49/−319 lines, 9 filesdep ~dataframe-core
Dependency ranges changed: dataframe-core
Files
- README.md +3/−3
- dataframe-viz.cabal +2/−2
- src/DataFrame/Display.hs +3/−9
- src/DataFrame/Display/Internal/Common.hs +6/−19
- src/DataFrame/Display/Internal/VegaLite.hs +12/−58
- src/DataFrame/Display/Terminal/Plot.hs +7/−71
- src/DataFrame/Display/Web/Chart.hs +10/−68
- src/DataFrame/Display/Web/Chart/Typed.hs +2/−29
- src/DataFrame/Display/Web/Plot.hs +4/−60
README.md view
@@ -1,4 +1,4 @@-<!-- scripths: 0.5.3.0 -->+<!-- scripths: 0.5.5.0 --> <!-- This file is the runnable scripths source for dataframe-viz's README.@@ -42,7 +42,7 @@ -- cabal: packages: ../dataframe-operations, ../dataframe-viz -- cabal: default-extensions: OverloadedStrings, TypeApplications, OverloadedLabels -- cabal: default-extensions: DataKinds, TypeOperators, FlexibleContexts-import DataFrame.Internal.DataFrame (DataFrame, fromNamedColumns)+import DataFrame.Internal.DataFrame (DataFrame, fromColumns) import DataFrame.Internal.Column (fromList) import DataFrame.Expression.Operators ((|>)) import qualified DataFrame.Functions as F@@ -60,7 +60,7 @@ import qualified Data.Aeson.KeyMap as KM import qualified Data.Text.Lazy as TL -df = fromNamedColumns+df = fromColumns [ ("income", fromList [1.5, 2.0, 3.1, 4.2, 5.0, 2.2, 3.3, 1.1 :: Double]) , ("value", fromList [100, 150, 200, 250, 300, 180, 220, 90 :: Double]) , ("region", fromList (["INLAND","NEAR BAY","INLAND","NEAR OCEAN","ISLAND","INLAND","NEAR BAY","INLAND"] :: [Text]))
dataframe-viz.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: dataframe-viz-version: 1.3.2.0+version: 1.3.2.1 synopsis: Visualisation/plotting helpers for the dataframe ecosystem. description: Display harness plus terminal and web plotters. Built on top of@@ -37,7 +37,7 @@ build-depends: base >= 4 && < 5, aeson >= 0.11.0.0 && < 3, containers >= 0.6.7 && < 0.10,- dataframe-core >= 2.5 && < 2.6,+ dataframe-core >= 2.6 && < 2.7, directory >= 1.3.0.0 && < 2, granite >= 0.6 && < 1, process ^>= 1.6,
src/DataFrame/Display.hs view
@@ -8,17 +8,11 @@ import qualified DataFrame.Internal.DataFrame as D data DisplayOptions = DisplayOptions- { {-- | Controls truncation on all three axes (rows, columns, cell width).- --- -- * 'Nothing' renders every row and column at full width.- -- * 'Just cfg' caps each axis whose limit in @cfg@ is positive.- --}- displayTruncate :: Maybe D.TruncateConfig+ { displayTruncate :: Maybe D.TruncateConfig+ -- ^ 'Nothing' shows everything; 'Just cfg' caps rows, columns, widths. } -{- | Defaults aimed at terminal use: 10 rows, plus the standard column and cell-caps from 'D.defaultTruncateConfig'.--}+-- | Terminal defaults: 10 rows plus the standard column and cell caps. defaultDisplayOptions :: DisplayOptions defaultDisplayOptions = DisplayOptions
src/DataFrame/Display/Internal/Common.hs view
@@ -7,10 +7,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -{- |-Internal shared helpers used by both the terminal and web plot backends.-Not part of the public API.--}+-- | Internal helpers shared by the terminal and web plot backends. module DataFrame.Display.Internal.Common ( -- * Aggregation Agg (..),@@ -82,12 +79,7 @@ aggLabel Min = "min" aggLabel Max = "max" -{- | Apply an aggregation across rows grouped by the given category column.--For 'Count', the value column is ignored. For all other aggregations, the-value column is extracted as numeric and folded per group. Group order is-the order in which categories first appear.--}+-- | Aggregate values across rows grouped by a category column. aggregateByGroup :: (HasCallStack) => Agg ->@@ -121,10 +113,7 @@ reduce = numericReducer agg in [(g, reduce (M.findWithDefault [] g byGroup)) | g <- seen] -{- | The numeric reducers, broken out so the overlapping-Count case in- 'aggregateByGroup' can be dispatched at the head pattern without- producing a redundant inner match.--}+-- | The numeric reducers, split out from 'aggregateByGroup'. numericReducer :: Agg -> [Double] -> Double numericReducer Sum = sum numericReducer Mean = \vs -> if null vs then 0 else sum vs / fromIntegral (length vs)@@ -161,7 +150,7 @@ Nothing -> error $ "Column " ++ T.unpack colName ++ " not found" Just idx -> columnToDoubles (columns df V.! idx) --- | Render a column's values as strings (identity for @Text@, @show@ otherwise).+-- | Render a column's values as strings (@Text@ as is, @show@ otherwise). columnToStrings :: (HasCallStack) => Column -> [T.Text] columnToStrings col = case col of BoxedColumn _ (vec :: V.Vector a) ->@@ -173,7 +162,7 @@ PackedText _ _ -> columnToStrings (materializePacked col) MergedColumn _ _ -> columnToStrings (materializeMerged col) --- | Coerce a numeric column to @[Double]@; errors if the element type is not numeric.+-- | Coerce a numeric column to @[Double]@; errors on non-numeric elements. columnToDoubles :: (HasCallStack) => Column -> [Double] columnToDoubles col = case col of BoxedColumn _ vec -> vectorToDoubles vec@@ -270,9 +259,7 @@ M.toList $ L.foldl' (\acc x -> M.insertWith (+) (show x) (1 :: Int) acc) M.empty xs -{- | Keep the top-N entries by value; fold the rest into a single "Other"-bucket. Used to limit bar/pie counts to a readable number.--}+-- | Keep the top-N entries and fold the rest into an "Other" bucket. groupWithOther :: Int -> [(T.Text, Double)] -> [(T.Text, Double)] groupWithOther n items = let sorted = L.sortOn (negate . snd) items
src/DataFrame/Display/Internal/VegaLite.hs view
@@ -7,11 +7,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -{- |-Internal Vega-Lite spec model plus aeson encoding, shared by the web plot-backends. Not public. Channel encodings resolve to a 'ResolvedField' carrying-the field name, Vega-Lite type, and inlined column values.--}+-- | Internal Vega-Lite spec model and aeson encoding for the web backends. module DataFrame.Display.Internal.VegaLite ( -- * Spec model Mark (..),@@ -62,10 +58,6 @@ import DataFrame.Display.Internal.Common (columnToDoubles, columnToStrings) --- ------------------------------------------------------------------------------ Spec model--- ---------------------------------------------------------------------------- data Mark = Bar | Line | Point | Area | Boxplot | Arc | Rule | Tick deriving (Eq, Show) @@ -118,9 +110,7 @@ Ordinal -> "ordinal" Temporal -> "temporal" -{- | A single channel encoding. An empty 'ceField' with a 'ceAggregate' of-@count@ produces a fieldless count aggregation, as Vega-Lite expects.--}+-- | A single channel encoding. data ChannelEnc = ChannelEnc { ceChannel :: Channel , ceField :: T.Text@@ -131,21 +121,15 @@ , ceSort :: Maybe Sort } --- | A bare channel encoding with no aggregation, binning, scale, or sort options.+-- | A bare channel encoding: no aggregation, binning, scale, or sort options. chanEnc :: Channel -> T.Text -> FieldType -> ChannelEnc chanEnc ch fld ft = ChannelEnc ch fld ft Nothing False defaultScale Nothing -{- | Domain ordering for a discrete channel, mirroring Vega-Lite's @sort@.-'DataOrder' pins the axis to the order rows appear in the inlined data, so-pre-sorted values (e.g. histogram bins) display in that order.--}+-- | Domain ordering for a discrete channel, mirroring Vega-Lite's @sort@. data Sort = DataOrder deriving (Eq, Show) -{- | 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.--}+-- | Per-channel scale configuration, mirroring Vega-Lite's @scale@ object. data ScaleSpec = ScaleSpec { scaleType :: Maybe ScaleType , scaleZero :: Maybe Bool@@ -165,10 +149,7 @@ DensityT T.Text deriving (Eq, Show) -{- | A Vega-Lite spec. When 'vlLayers' is non-empty the spec is a layered-container: the top level carries the shared data and each layer carries its own-mark/encoding/transform.--}+-- | A Vega-Lite spec, layered when 'vlLayers' is non-empty. data VLSpec = VLSpec { vlMark :: Mark , vlEncodings :: [ChannelEnc]@@ -182,14 +163,7 @@ emptySpec :: Mark -> VLSpec emptySpec m = VLSpec m [] [] Nothing 600 400 [] --- ------------------------------------------------------------------------------ Field-type inference from the expression's element type--- -----------------------------------------------------------------------------{- | Derive the Vega-Lite field type from a Haskell type: numeric →-'Quantitative', date/time → 'Temporal', everything else → 'Nominal'. @Maybe a@-classifies as its inner type.--}+-- | Derive the Vega-Lite field type from a Haskell type. fieldTypeOf :: forall a. (Columnable a) => FieldType fieldTypeOf = classify (typeRep @a) @@ -223,20 +197,13 @@ temporalNames = ["Day", "UTCTime", "LocalTime", "ZonedTime", "TimeOfDay"] --- ------------------------------------------------------------------------------ Resolving an expression to a field + values--- ---------------------------------------------------------------------------- data ResolvedField = ResolvedField { rfName :: T.Text , rfType :: FieldType , rfValues :: [Value] } -{- | Resolve an expression against a frame. A bare @Col name@ reuses the named-column; any other expression is materialised with the core interpreter and-stored under the given fallback name.--}+-- | Resolve an expression against a frame, reusing a bare column directly. resolveField :: forall a. (Columnable a) => DataFrame -> T.Text -> Expr a -> ResolvedField resolveField df fallbackName expr =@@ -264,14 +231,10 @@ textField :: T.Text -> [T.Text] -> ResolvedField textField name vals = ResolvedField name Nominal (map (toJSON :: T.Text -> Value) vals) --- | A quantitative field built directly from numeric values (for pre-computed data).+-- | A quantitative field built directly from pre-computed numeric values. numField :: T.Text -> [Double] -> ResolvedField numField name vals = ResolvedField name Quantitative (map toJSON vals) --- ------------------------------------------------------------------------------ Encoding to JSON--- ---------------------------------------------------------------------------- schemaUrl :: T.Text schemaUrl = "https://vega.github.io/schema/vega-lite/v5.json" @@ -293,7 +256,7 @@ , Just ("data" .= object ["values" .= inlineRows fields]) ] --- | The mark/encoding/transform pairs of a unit spec (no data — that is shared).+-- | The mark/encoding/transform pairs of a unit spec (data is shared). unitPairs :: VLSpec -> [(K.Key, Value)] unitPairs spec = catMaybes@@ -357,14 +320,7 @@ row i = object [K.fromText nm .= fromMaybe Null (vs V.!? i) | (nm, vs) <- vecs] in toJSON [row i | i <- [0 .. n - 1]] --- ------------------------------------------------------------------------------ HTML embedding (vega-embed via CDN)--- -----------------------------------------------------------------------------{- | Render a spec to a self-contained HTML snippet that loads vega/vega-lite/-vega-embed from a CDN and embeds the chart. Data is inlined, so the snippet-renders correctly even from a @file://@ URL.--}+-- | Render a spec to a self-contained HTML snippet with inlined data. specHtml :: T.Text -> [ResolvedField] -> VLSpec -> T.Text specHtml chartId fields spec = let specJson = TL.toStrict (encodeToLazyText (specToValue fields spec))@@ -382,9 +338,7 @@ , ");</script>\n" ] -{- | Warn on stderr when a large number of rows is being inlined, which bloats-the spec and can slow the browser.--}+-- | Warn on stderr when a large number of rows is being inlined. rowCountWarning :: [ResolvedField] -> IO () rowCountWarning fields = do let n = maximum (0 : map (length . rfValues) fields)
src/DataFrame/Display/Terminal/Plot.hs view
@@ -3,18 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoFieldSelectors #-} -{- |-A plotly-express-style one-shot plotting API for terminal output.--Each chart kind has a small record spec (with sensible defaults) and a-single render function. Customise plots by record-update on the spec:--@-bar ((mkBar "Sex") { y = Just "Survived" }) titanic-bar ((mkBar "ocean_proximity")- { y = Just "median_house_value", agg = Mean }) housing-@--}+-- | A plotly-express-style one-shot plotting API for terminal output. module DataFrame.Display.Terminal.Plot ( -- * Aggregation Agg (..),@@ -55,11 +44,7 @@ -- * Deprecated legacy entry points - {- |- These shims delegate to the parametrized API. They are kept for- one release to ease migration; prefer the spec-based functions- above for new code.- -}+ -- Deprecated shims that delegate to the parametrized API. plotHistogram, plotScatter, plotBars,@@ -92,15 +77,7 @@ import Granite (Plot, defPlot) import qualified Granite --- ------------------------------------------------------------------------------ Bar--- -----------------------------------------------------------------------------{- | Bar-chart specification.--@x@ is required; everything else has a default. When @y@ is @Nothing@ the-chart counts rows per group; otherwise @y@ is aggregated using @agg@.--}+-- | Bar-chart specification; @x@ is required and everything else defaults. data Bar = Bar { x :: T.Text -- ^ Grouping (categorical) column.@@ -142,10 +119,6 @@ cfg = spec.plot{Granite.plotTitle = t} T.putStrLn $ Granite.bars rows' cfg --- ------------------------------------------------------------------------------ Histogram--- ---------------------------------------------------------------------------- -- | Histogram specification for a numeric column. data Histogram = Histogram { x :: T.Text@@ -175,10 +148,6 @@ cfg = spec.plot{Granite.plotTitle = t} T.putStrLn $ Granite.histogram (Granite.bins spec.bins lo hi) values cfg --- ------------------------------------------------------------------------------ Scatter--- ---------------------------------------------------------------------------- -- | Scatter-plot specification. data Scatter = Scatter { x :: T.Text@@ -220,10 +189,6 @@ ] T.putStrLn $ Granite.scatter series cfg --- ------------------------------------------------------------------------------ Line--- ---------------------------------------------------------------------------- -- | Line-chart specification: one x column, one or more y series. data Line = Line { x :: T.Text@@ -255,20 +220,12 @@ return (col, zip xVals values) T.putStrLn $ Granite.lineGraph seriesData cfg --- ------------------------------------------------------------------------------ Pie--- ---------------------------------------------------------------------------- -- | Pie-chart specification. data Pie = Pie { values :: T.Text- {- ^ Slice-defining column. For 'Count', categorical; for the- numeric aggs, the value column.- -}+ -- ^ Slice-defining column: categorical for 'Count', else the value column. , names :: Maybe T.Text- {- ^ Optional label column. If @Nothing@, slices are labelled by- the value column directly (for 'Count') or by row index.- -}+ -- ^ Optional label column; otherwise slices are labelled by value or index. , agg :: Agg , topN :: Maybe Int , title :: Maybe T.Text@@ -307,10 +264,6 @@ rows' = maybe rows (`groupWithOtherForPie` rows) spec.topN T.putStrLn $ Granite.pie rows' cfg --- ------------------------------------------------------------------------------ Box--- ---------------------------------------------------------------------------- -- | Box-plot specification across one or more numeric columns. data Box = Box { y :: [T.Text]@@ -335,13 +288,7 @@ boxData <- forM spec.y $ \col -> return (col, extractNumericColumn col df) T.putStrLn $ Granite.boxPlot boxData cfg --- ------------------------------------------------------------------------------ Whole-frame plots--- -----------------------------------------------------------------------------{- | Heatmap of all numeric columns in the dataframe; each numeric column-becomes a column of the matrix.--}+-- | Heatmap of all numeric columns in the dataframe. heatmap :: (HasCallStack) => DataFrame -> IO () heatmap df = do let numericCols = filter (isNumericColumn df) (columnNames df)@@ -370,19 +317,11 @@ stdY = sqrt $ sum [(b - meanY) ^ (2 :: Int) | b <- ys] / n in covXY / (stdX * stdY) --- ------------------------------------------------------------------------------ Title helper--- ---------------------------------------------------------------------------- autoTitle :: Agg -> Maybe T.Text -> T.Text -> T.Text autoTitle Count _ groupCol = "count by " <> groupCol autoTitle a (Just yCol) groupCol = aggLabel a <> "(" <> yCol <> ") by " <> groupCol autoTitle a Nothing groupCol = aggLabel a <> " by " <> groupCol --- ------------------------------------------------------------------------------ Deprecated legacy entry points--- ---------------------------------------------------------------------------- -- | Use 'histogram' with 'mkHistogram' instead. plotHistogram :: (HasCallStack) => T.Text -> DataFrame -> IO () plotHistogram c = histogram (mkHistogram c)@@ -415,10 +354,7 @@ plotBoxPlots ys = box (mkBox ys) {-# DEPRECATED plotBoxPlots "use 'box (mkBox cols)' instead" #-} -{- | Stacked-bar plot: one bar per category, segments from the listed value-columns. Kept as a direct passthrough to granite — has no spec record-because no aggregation is involved (segments are summed per category).--}+-- | Stacked-bar plot with one bar per category, passed straight to granite. plotStackedBars :: (HasCallStack) => T.Text -> [T.Text] -> DataFrame -> IO () plotStackedBars categoryCol valueColumns df = do let categories = extractStringColumn categoryCol df
src/DataFrame/Display/Web/Chart.hs view
@@ -1,30 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} -{- |-A composable, expression-based grammar of graphics over the untyped 'Expr'.-Charts are built by piping combinators and rendered to interactive Vega-Lite.--@-import qualified DataFrame.Display.Web.Chart as Plt-import DataFrame.Functions (col)--Plt.showChart- ( Plt.chart df- |> Plt.mark Plt.Point- |> Plt.enc Plt.X (col \@Double "age")- |> Plt.enc Plt.Y (col \@Double "fare")- |> Plt.enc Plt.Color (col \@Text "class")- )-@--The element type of each encoded expression determines the Vega-Lite field-type (numeric → quantitative, date/time → temporal, otherwise nominal). A bare-column expression encodes that column; any other expression is materialised-with the core interpreter and inlined under the channel's name.--For the schema-checked typed variant see "DataFrame.Display.Web.Chart.Typed".--}+-- | Composable expression-based grammar of graphics rendered to Vega-Lite. module DataFrame.Display.Web.Chart ( -- * Chart Chart,@@ -96,13 +73,7 @@ import DataFrame.Internal.DataFrame (DataFrame) import DataFrame.Internal.Expression (Expr) --- ------------------------------------------------------------------------------ Chart builder--- -----------------------------------------------------------------------------{- | An in-progress chart: the source frame, a mark, channel encodings, the-data fields to inline, transforms, and (for layered charts) sub-layers.--}+-- | An in-progress chart: frame, mark, encodings, transforms, and layers. data Chart = Chart { chDf :: DataFrame , chMark :: Mark@@ -123,9 +94,7 @@ mark :: Mark -> Chart -> Chart mark m c = c{chMark = m} -{- | Encode an expression on a channel. The field type is inferred from the-expression's element type; use 'encAs' to override.--}+-- | Encode an expression on a channel, inferring the field type. enc :: (Columnable a) => Channel -> Expr a -> Chart -> Chart enc ch e c = let rf = resolveField (chDf c) (channelName ch) e@@ -139,9 +108,7 @@ ce = chanEnc ch (rfName rf) ft in c{chEncs = setChannel ce (chEncs c), chFields = chFields c ++ [rf]} -{- | Apply a declarative aggregation to a channel (computed by Vega-Lite). For-'Count' the field is dropped, matching Vega-Lite's fieldless count.--}+-- | Apply a Vega-Lite aggregation to a channel. aggregateOn :: Channel -> Agg -> Chart -> Chart aggregateOn ch a = withChannel@@ -158,18 +125,11 @@ binX = withChannel X (\e -> e{ceBin = True}) binY = withChannel Y (\e -> e{ceBin = True}) -{- | Put a channel on a log scale. Like all channel modifiers, apply it-after 'enc' on the same channel ('enc' replaces the whole encoding).--}+-- | Put a channel on a log scale; apply it after 'enc'. logScale :: Channel -> Chart -> Chart 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).--}+-- | Anchor or release a channel's scale at zero; apply it after 'enc'. includeZero :: Channel -> Bool -> Chart -> Chart includeZero ch b = withScale ch (\s -> s{scaleZero = Just b}) @@ -190,9 +150,7 @@ size :: Int -> Int -> Chart -> Chart size w h c = c{chW = w, chH = h} -{- | Overlay several charts that share data into a single layered chart. The-title and size of the first layer are used for the container.--}+-- | Overlay several charts that share data into one layered chart. layer :: [Chart] -> Chart layer [] = error "DataFrame.Display.Web.Chart.layer: empty layer list" layer cs@(c0 : _) =@@ -201,9 +159,7 @@ , chFields = concatMap chFields cs } -{- | Add a regression (least-squares) line over the chart, fitting @y@ on @x@.-Produces a layered chart: the original marks plus a fitted line.--}+-- | Add a least-squares regression line fitting @y@ on @x@. regression :: (Columnable a, Columnable b) => Expr a -> Expr b -> Chart -> Chart regression xE yE c = let rfx = resolveField (chDf c) "x" xE@@ -222,9 +178,7 @@ } in withData{chLayers = [points, lineLayer]} -{- | Kernel-density estimate of an expression, drawn as an area. Replaces the-chart's mark and encodings with the density curve (Vega-Lite @density@).--}+-- | Kernel-density estimate of an expression, drawn as an area. density :: (Columnable a) => Expr a -> Chart -> Chart density e c = let rf = resolveField (chDf c) "value" e@@ -238,11 +192,7 @@ ] } --- ------------------------------------------------------------------------------ Rendering--- ------------------------------------------------------------------------------- | The Vega-Lite spec as an aeson 'Value' (escape hatch for advanced use / hvega).+-- | The Vega-Lite spec as an aeson 'Value' (escape hatch, e.g. for hvega). toVegaSpec :: Chart -> Value toVegaSpec c = specToValue (allFields c) (toVLSpec c) @@ -256,10 +206,6 @@ rowCountWarning (allFields c) showInDefaultBrowser (toHtml c) --- ------------------------------------------------------------------------------ One-shot convenience plots (the simple single-line path)--- ---------------------------------------------------------------------------- -- | Scatter plot of two expressions. Axes fit the data (no zero anchor). scatter :: (Columnable a, Columnable b) => Expr a -> Expr b -> DataFrame -> IO ()@@ -301,10 +247,6 @@ -- | Box-and-whisker plot of a numeric expression. box :: (Columnable a) => Expr a -> DataFrame -> IO () box yE df = showChart (chart df & mark Boxplot & enc Y yE)---- ------------------------------------------------------------------------------ Internals--- --------------------------------------------------------------------------- -- | Replace any existing encoding on the same channel (last write wins). setChannel :: ChannelEnc -> [ChannelEnc] -> [ChannelEnc]
src/DataFrame/Display/Web/Chart/Typed.hs view
@@ -2,34 +2,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE KindSignatures #-} -{- |-The schema-checked, typed-expression analogue of "DataFrame.Display.Web.Chart".-Charts are built over a 'TypedDataFrame' using typed expressions ('TExpr',-@#col@), so column references are checked against the schema at compile time-and the field type follows from the column's Haskell type.--@-{\-\# LANGUAGE OverloadedLabels \#-\}-import qualified DataFrame.Display.Web.Chart.Typed as Plt---- one-liner-Plt.scatter #age #fare tdf---- composable grammar-Plt.showChart- ( Plt.chart tdf- |> Plt.mark Plt.Boxplot- |> Plt.enc Plt.X #region- |> Plt.enc Plt.Y #value- |> Plt.enc Plt.Color #grp- |> Plt.facet #grp- )-@--Every combinator delegates to "DataFrame.Display.Web.Chart" by unwrapping the-typed expression ('unTExpr') and frame ('unTDF'); the @cols@ phantom is erased-at the boundary.--}+-- | Schema-checked typed-expression analogue of "DataFrame.Display.Web.Chart". module DataFrame.Display.Web.Chart.Typed ( -- * Chart Chart,@@ -157,7 +130,7 @@ density :: (Columnable a) => TExpr cols a -> Chart cols -> Chart cols density e (Chart c) = Chart (C.density (unTExpr e) c) --- | The Vega-Lite spec as an aeson 'Value' (escape hatch for advanced use / hvega).+-- | The Vega-Lite spec as an aeson 'Value' (escape hatch, e.g. for hvega). toVegaSpec :: Chart cols -> Value toVegaSpec (Chart c) = C.toVegaSpec c
src/DataFrame/Display/Web/Plot.hs view
@@ -3,11 +3,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoFieldSelectors #-} -{- |-A plotly-express-style one-shot plotting API for HTML output: the string-keyed-convenience tier emitting interactive Vega-Lite charts. For the composable-expression-based grammar see "DataFrame.Display.Web.Chart" and its @.Typed@.--}+-- | Plotly-express-style one-shot plotting API emitting Vega-Lite HTML. module DataFrame.Display.Web.Plot ( -- * Aggregation Agg (..),@@ -108,10 +104,6 @@ import qualified DataFrame.Display.Internal.VegaLite as VL import DataFrame.Internal.DataFrame (DataFrame, columnNames) --- ------------------------------------------------------------------------------ Layout--- ---------------------------------------------------------------------------- -- | Display dimensions for the chart, in pixels. data Size = Size {width :: Int, height :: Int} @@ -138,10 +130,6 @@ anchorZero :: Bool -> VL.ChannelEnc -> VL.ChannelEnc anchorZero b e = e{VL.ceScale = VL.defaultScale{VL.scaleZero = Just b}} --- ------------------------------------------------------------------------------ Bar--- ---------------------------------------------------------------------------- data Bar = Bar { x :: T.Text , y :: Maybe T.Text@@ -182,10 +170,6 @@ } renderSpec spec.size fields vlSpec --- ------------------------------------------------------------------------------ Histogram--- ---------------------------------------------------------------------------- data Histogram = Histogram { x :: T.Text , bins :: Int@@ -224,14 +208,7 @@ } renderSpec spec.size fields vlSpec --- ------------------------------------------------------------------------------ Scatter--- -----------------------------------------------------------------------------{- | 'includeZero' anchors both axes at zero. Off by default so the axes fit-the data — otherwise points far from the origin (e.g. geographic coordinates)-get squashed against the chart edge.--}+-- | Scatter spec; 'includeZero' anchors both axes at zero (off by default). data Scatter = Scatter { x :: T.Text , y :: T.Text@@ -269,14 +246,7 @@ } renderSpec spec.size fields vlSpec --- ------------------------------------------------------------------------------ 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.--}+-- | Line spec; 'includeZero' anchors the axes at zero and is off by default. data Line = Line { x :: T.Text , y :: [T.Text]@@ -319,10 +289,6 @@ } renderSpec spec.size fields vlSpec --- ------------------------------------------------------------------------------ Pie--- ---------------------------------------------------------------------------- data Pie = Pie { values :: T.Text , names :: Maybe T.Text@@ -369,10 +335,6 @@ } renderSpec spec.size fields vlSpec --- ------------------------------------------------------------------------------ Box--- ---------------------------------------------------------------------------- data Box = Box { y :: [T.Text] , title :: Maybe T.Text@@ -404,32 +366,18 @@ } renderSpec spec.size fields vlSpec --- ------------------------------------------------------------------------------ Whole-frame helpers--- -----------------------------------------------------------------------------{- | Concatenate a histogram for every numeric column in the frame. Useful-as a one-shot exploratory summary.--}+-- | Concatenate a histogram for every numeric column in the frame. allHistograms :: (HasCallStack) => DataFrame -> IO String allHistograms df = do let cols = filter (isNumericColumn df) (columnNames df) xs <- forM cols $ \c -> histogram (mkHistogram c) df return $ L.intercalate "\n" xs --- ------------------------------------------------------------------------------ Title helper--- ---------------------------------------------------------------------------- autoTitle :: Agg -> Maybe T.Text -> T.Text -> T.Text autoTitle Count _ groupCol = "count by " <> groupCol autoTitle a (Just yCol) groupCol = aggLabel a <> "(" <> yCol <> ") by " <> groupCol autoTitle a Nothing groupCol = aggLabel a <> " by " <> groupCol --- ------------------------------------------------------------------------------ Deprecated legacy entry points--- ---------------------------------------------------------------------------- plotHistogram :: (HasCallStack) => T.Text -> DataFrame -> IO String plotHistogram c = histogram (mkHistogram c) {-# DEPRECATED plotHistogram "use 'histogram (mkHistogram col)' instead" #-}@@ -455,10 +403,6 @@ plotBoxPlots :: (HasCallStack) => [T.Text] -> DataFrame -> IO String plotBoxPlots ys = box (mkBox ys) {-# DEPRECATED plotBoxPlots "use 'box (mkBox cols)' instead" #-}---- ------------------------------------------------------------------------------ Browser launcher--- --------------------------------------------------------------------------- -- | Windows launches via a shell: 'start' is a cmd builtin. showInDefaultBrowser :: String -> IO ()