dataframe-3.5.0.0: A fast, safe, and intuitive DataFrame library.
Copyright(c) 2024 - 2026 Michael Chavinda
LicenseGPL-3.0
Maintainermschavinda@gmail.com
Stabilityexperimental
PortabilityPOSIX
Safe HaskellNone
LanguageHaskell2010

DataFrame

Description

Batteries-included entry point for dataframe: re-exports the most commonly used pieces for GHCi and scripts. Use the D. prefix for core table operations and F. for the expression DSL.

Synopsis

Core data structures

Operator symbols.

Display operations

Core dataframe operations

Types

data SchemaType where #

A runtime tag for a column’s element type.

Constructors

SType :: forall a. (Columnable a, Read a) => Proxy a -> SchemaType

Constructor carrying a Proxy of the element type.

Instances

Instances details
Show SchemaType

Show the underlying element type using typeRep.

Examples

Expand
>>> :set -XTypeApplications
>>> show (schemaType @Bool)
"Bool"
Instance details

Defined in DataFrame.Internal.Schema

Eq SchemaType

Two SchemaTypes are equal iff their element types are the same.

Examples

Expand
>>> :set -XTypeApplications
>>> schemaType @Int == schemaType @Int
True
>>> schemaType @Int == schemaType @Integer
False
Instance details

Defined in DataFrame.Internal.Schema

schemaType :: (Columnable a, Read a) => SchemaType #

Construct a SchemaType for the given a.

Examples

Expand
>>> :set -XTypeApplications
>>> schemaType @T.Text == schemaType @T.Text
True
>>> show (schemaType @Double)
"Double"

makeSchema :: [(Text, SchemaType)] -> Schema #

Construct a Schema from a list of (columnName, schemaType) pairs.

deriveSchemaValues :: Name -> DecsQ #

Derive an untyped Schema value plus per-column Expr accessors from a record type. The value-level companion to deriveSchemaFromType: $(deriveSchemaValues ''Order) generates orderSchema.

I/O

data ReadOptions #

CSV read parameters.

Constructors

ReadOptions 

Fields

data TypeSpec #

How a reader decides each column's type.

Example

Expand
ghci> D.readCsvWithOpts D.defaultReadOptions{D.typeSpec = D.InferFromSample 500} "wide.csv"

Constructors

InferFromSample Int

Infer each column's type from the first n rows.

SpecifyTypes [(Text, SchemaType)] TypeSpec

Pin the listed columns to their given types; fall back to the nested TypeSpec for the rest.

NoInference

Every column is read as Text, no inference at all.

data HeaderSpec #

Where a reader's column names come from.

Example

Expand
ghci> D.readCsvWithOpts D.defaultReadOptions{D.headerSpec = D.ProvideNames ["a", "b"]} "no_header.csv"

Constructors

NoHeader 
UseFirstRow

Take names from the first row (the default).

ProvideNames [Text]

Name the leading columns; any beyond the given list are numbered.

Instances

Instances details
Show HeaderSpec 
Instance details

Defined in DataFrame.IO.CSV.Internal.Options

Eq HeaderSpec 
Instance details

Defined in DataFrame.IO.CSV.Internal.Options

defaultReadOptions :: ReadOptions #

The default ReadOptions: infer types from a 100-row sample, treat the first row as a header, read every row and every column.

Example

Expand
ghci> D.readCsvWithOpts D.defaultReadOptions{D.columnSeparator = ';'} "data.csv"

readCsv :: FilePath -> IO DataFrame #

Read CSV file from path and load it into a dataframe.

Example

Expand
ghci> D.readCsv "./data/taxi.csv"

readCsvWithSchema :: Schema -> FilePath -> IO DataFrame #

Schema-driven CSV reader. Coerces each column to the type declared in Schema; columns absent from the schema fall back to inference and are still returned. To read only the schema's columns, pass schemaReadOptions to readCsvWithOpts.

import qualified DataFrame as D
df <- D.readCsvWithSchema schema "input.csv"

readCsvWithOpts :: ReadOptions -> FilePath -> IO DataFrame #

Read CSV file from path and load it into a dataframe.

Example

Expand
ghci> D.readCsvWithOpts "./data/taxi.csv" (D.defaultReadOptions { dateFormat = "%d%-m%-Y" })

readTsv :: FilePath -> IO DataFrame #

Read TSV (tab separated) file from path and load it into a dataframe.

Example

Expand
ghci> D.readTsv "./data/taxi.tsv"

readSeparated :: ReadOptions -> FilePath -> IO DataFrame #

Read text file with specified delimiter into a dataframe.

Example

Expand
ghci> D.readSeparated (D.defaultReadOptions { columnSeparator = ';' }) "./data/taxi.txt"

writeCsv :: FilePath -> DataFrame -> IO () #

Write a dataframe to a comma-separated file.

Example

Expand
ghci> D.writeCsv "./out.csv" df

writeSeparated #

Arguments

:: Char

Separator

-> FilePath

Path to write to

-> DataFrame 
-> IO () 

Write a dataframe to a file using the given field separator.

Example

Expand
ghci> D.writeSeparated ';' "./out.txt" df

fromCsv :: String -> IO (Either String DataFrame) #

Parse a CSV string into a DataFrame using default options.

Example

Expand
ghci> D.fromCsv "id,name\n1,Ada\n"
Right (DataFrame ...)

fromCsvBytes :: ByteString -> IO DataFrame #

Parse a lazy ByteString containing CSV data into a DataFrame using default options.

Example

Expand
ghci> D.fromCsvBytes "id,name\n1,Ada\n"

data ParquetReadOptions #

Options for reading Parquet data.

These options are applied in this order:

  1. predicate filtering
  2. column projection
  3. row range
  4. safe column promotion

Column selection for selectedColumns uses leaf column names only.

Constructors

ParquetReadOptions 

Fields

  • selectedColumns :: Maybe [Text]

    Columns to keep in the final dataframe. If set, only these columns are returned. Predicate-referenced columns are read automatically when needed and projected out after filtering.

  • predicate :: Maybe (Expr Bool)

    Optional row filter expression applied before projection.

  • rowRange :: Maybe (Int, Int)

    Optional row slice (start, end) with start-inclusive/end-exclusive semantics.

  • safeColumns :: Bool

    When True, every column is promoted to OptionalColumn after read, regardless of nullability in the schema.

Instances

Instances details
Show ParquetReadOptions 
Instance details

Defined in DataFrame.IO.Parquet

defaultParquetReadOptions :: ParquetReadOptions #

Default Parquet read options.

Equivalent to:

ParquetReadOptions
    { selectedColumns = Nothing
    , predicate = Nothing
    , rowRange = Nothing
    , safeColumns = False
    }

readParquet :: FilePath -> IO DataFrame #

Read a parquet file from path and load it into a dataframe.

Example

Expand
ghci> D.readParquet "./data/mtcars.parquet"

readParquetWithOpts :: ParquetReadOptions -> FilePath -> IO DataFrame #

Read a Parquet file using explicit read options.

Example

Expand
ghci> D.readParquetWithOpts
ghci|   (D.defaultParquetReadOptions{D.selectedColumns = Just ["id"], D.rowRange = Just (0, 10)})
ghci|   ".testsdata/alltypes_plain.parquet"

When selectedColumns is set and predicate references other columns, those predicate columns are auto-included for decoding, then projected back to the requested output columns.

readParquetFiles :: FilePath -> IO DataFrame #

Read Parquet files from a directory or glob path.

This is equivalent to calling readParquetFilesWithOpts with defaultParquetReadOptions.

readParquetFilesWithOpts :: ParquetReadOptions -> FilePath -> IO DataFrame #

Read multiple Parquet files (directory or glob) using explicit options.

If path is a directory, all non-directory entries are read. If path is a glob, matching files are read.

For multi-file reads, rowRange is applied once after concatenation (global range semantics).

Example

Expand
ghci> D.readParquetFilesWithOpts
ghci|   (D.defaultParquetReadOptions{D.selectedColumns = Just ["id"], D.rowRange = Just (0, 5)})
ghci|   ".testsdata/alltypes_plain*.parquet"

Lazy query engine

data LazyDataFrame #

A lazy query that has not been executed yet: a LogicalPlan tree whose execution is deferred until runDataFrame is called.

Instances

Instances details
Show LazyDataFrame 
Instance details

Defined in DataFrame.Lazy.Internal.DataFrame

runDataFrame :: LazyDataFrame -> IO DataFrame #

Execute the lazy query: optimise the logical plan, then stream-execute the resulting physical plan into a fully-materialised DataFrame.

fromDataFrame :: DataFrame -> LazyDataFrame #

Lift an already-loaded eager DataFrame into the lazy plan.

scanCsv :: Schema -> Text -> LazyDataFrame #

Scan a CSV file with the default comma separator and the in-tree strict reader. For the SIMD reader use scanCsvWith.

The Schema both types and selects: only the columns it names are read, matching scanParquet.

Example

Expand
ghci> schema = D.makeSchema [("id", D.schemaType @Int), ("name", D.schemaType @Text)]
ghci> L.runDataFrame (L.scanCsv schema "customers.csv")

scanCsvWith :: CsvReader -> Schema -> Text -> LazyDataFrame #

Like scanCsv but with an explicit CSV reader (e.g. the SIMD reader fastReadCsvWithOpts from dataframe-fastcsv). The scan derives the reader's ReadOptions from the schema and separator, so any CsvReader projects.

Example

Expand
ghci> import qualified DataFrame.IO.CSV.Fast as Fast
ghci> L.runDataFrame (L.scanCsvWith Fast.fastReadCsvWithOpts schema "customers.csv")

scanSeparated :: Char -> Schema -> Text -> LazyDataFrame #

Scan a character-separated file with the default strict reader.

Example

Expand
ghci> L.runDataFrame (L.scanSeparated ';' schema "customers.txt")

scanSeparatedWith :: CsvReader -> Char -> Schema -> Text -> LazyDataFrame #

Like scanSeparated but with an explicit CSV reader.

Example

Expand
ghci> L.runDataFrame (L.scanSeparatedWith Fast.fastReadCsvWithOpts ';' schema "customers.txt")

scanParquet :: Schema -> Text -> LazyDataFrame #

Scan a Parquet file, directory of files, or glob pattern.

Feature synthesis & decision trees

Type conversion

data SafeReadMode #

How parse failures are surfaced: NoSafeRead throws, MaybeRead yields Nothing (column wrapped Maybe a), EitherRead yields Left rawText (column wrapped Either Text a, preserving the original input).

data ParseOptions #

Options controlling how text columns are parsed into typed values.

Constructors

ParseOptions 

Fields

defaultParseOptions :: ParseOptions #

Sensible out-of-the-box parse options: infer from the first 100 rows, treat common nullish strings as missing, and expect ISO 8601 dates.

effectiveSafeRead :: SafeReadMode -> [(Text, SafeReadMode)] -> Text -> SafeReadMode #

Resolve a column's effective SafeReadMode: the override if present, otherwise the default.

Operations

filter #

Arguments

:: Columnable a 
=> Expr a

Column to filter by

-> (a -> Bool)

Filter condition

-> DataFrame

Dataframe to filter

-> DataFrame 

O(n * k) Filter rows by a given condition.

filter "x" even df

sample :: RandomGen g => g -> Double -> DataFrame -> DataFrame #

Sample a dataframe. The double parameter must be between 0 and 1 (inclusive).

Example

Expand
ghci> import System.Random
ghci> D.sample (mkStdGen 137) 0.1 df

range :: (Int, Int) -> DataFrame -> DataFrame #

O(k * n) Take a range of rows of a DataFrame.

take :: Int -> DataFrame -> DataFrame #

O(k * n) Take the first n rows of a DataFrame.

drop :: Int -> DataFrame -> DataFrame #

O(k * n) Drop the first n rows of a DataFrame.

select :: [Text] -> DataFrame -> DataFrame #

O(n) Selects a number of columns in a given dataframe.

select ["name", "age"] df

takeLast :: Int -> DataFrame -> DataFrame #

O(k * n) Take the last n rows of a DataFrame.

dropLast :: Int -> DataFrame -> DataFrame #

O(k * n) Drop the last n rows of a DataFrame.

filterBy :: Columnable a => (a -> Bool) -> Expr a -> DataFrame -> DataFrame #

O(k) a version of filter where the predicate comes first.

filterBy even "x" df

filterWhere :: Expr Bool -> DataFrame -> DataFrame #

O(k) filters the dataframe with a boolean expression.

filterWhere (F.col @Int x + F.col y F.> 5) df

filterJust :: Text -> DataFrame -> DataFrame #

O(k) removes all rows with Nothing in a given column from the dataframe.

filterJust "col" df

filterNothing :: Text -> DataFrame -> DataFrame #

O(k) returns all rows with Nothing in a give column.

filterNothing "col" df

filterAllJust :: DataFrame -> DataFrame #

O(n * k) removes all rows with Nothing from the dataframe.

filterAllJust df

filterAllNothing :: DataFrame -> DataFrame #

O(n * k) keeps any row with a null value.

filterAllNothing df

cube :: (Int, Int) -> DataFrame -> DataFrame #

O(k) cuts the dataframe in a cube of size (a, b) where a is the length and b is the width.

cube (10, 5) df

byName :: Text -> SelectionCriteria #

Criteria for selecting a column by name.

selectBy [byName "Age"] df

equivalent to:

select ["Age"] df

byProperty :: (Column -> Bool) -> SelectionCriteria #

Criteria for selecting columns whose property satisfies given predicate.

selectBy [byProperty isNumeric] df

byNameProperty :: (Text -> Bool) -> SelectionCriteria #

Criteria for selecting columns whose name satisfies given predicate.

selectBy [byNameProperty (T.isPrefixOf "weight")] df

byNameRange :: (Text, Text) -> SelectionCriteria #

Criteria for selecting columns whose names are in the given lexicographic range (inclusive).

selectBy [byNameRange ("a", "c")] df

byIndexRange :: (Int, Int) -> SelectionCriteria #

Criteria for selecting columns whose indices are in the given (inclusive) range.

selectBy [byIndexRange (0, 5)] df

selectBy :: [SelectionCriteria] -> DataFrame -> DataFrame #

O(n) select columns by column predicate name.

selectRows :: [Int] -> DataFrame -> DataFrame #

O(k * n) select rows by index

selectRows [0, 2, 4] df

exclude :: [Text] -> DataFrame -> DataFrame #

O(n) inverse of select

exclude ["Name"] df

randomSplit :: RandomGen g => g -> Double -> DataFrame -> (DataFrame, DataFrame) #

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

Expand
ghci> import System.Random
ghci> D.randomSplit (mkStdGen 137) 0.9 df

kFolds :: RandomGen g => g -> Int -> DataFrame -> [DataFrame] #

Creates n folds of a dataframe.

Example

Expand
ghci> import System.Random
ghci> D.kFolds (mkStdGen 137) 5 df

stratifiedSample :: forall a g. (SplittableGen g, Columnable a) => g -> Double -> Expr a -> DataFrame -> DataFrame #

Sample a dataframe, preserving per-stratum proportions.

Example

Expand
ghci> import System.Random
ghci> D.stratifiedSample (mkStdGen 42) 0.8 "label" df

stratifiedSplit :: forall a g. (SplittableGen g, Columnable a) => g -> Double -> Expr a -> DataFrame -> (DataFrame, DataFrame) #

Split a dataframe into two, preserving per-stratum proportions.

Example

Expand
ghci> import System.Random
ghci> D.stratifiedSplit (mkStdGen 42) 0.8 "label" df

apply #

Arguments

:: (Columnable b, Columnable c) 
=> (b -> c)

function to apply

-> Text

Column name

-> DataFrame

DataFrame to apply operation to

-> DataFrame 

O(k) Apply a function to a given column in a dataframe.

derive :: Columnable a => Text -> Expr a -> DataFrame -> DataFrame #

O(k) Apply a function to an expression in a dataframe and add the result into alias column.

safeApply #

Arguments

:: (Columnable b, Columnable c) 
=> (b -> c)

function to apply

-> Text

Column name

-> DataFrame

DataFrame to apply operation to

-> Either DataFrameException DataFrame 

O(k) Safe version of the apply function. Returns (instead of throwing) the error.

deriveWithExpr :: Columnable a => Text -> Expr a -> DataFrame -> (Expr a, DataFrame) #

O(k) Apply a function to an expression in a dataframe and add the result into alias column but

Examples

Expand
>>> (z, df') = deriveWithExpr "z" (F.col @Int "x" + F.col "y") df
>>> filterWhere (z .>= 50)

applyMany :: (Columnable b, Columnable c) => (b -> c) -> [Text] -> DataFrame -> DataFrame #

O(k * n) Apply a function to given column names in a dataframe.

applyInt #

Arguments

:: Columnable b 
=> (Int -> b)

function to apply

-> Text

Column name

-> DataFrame

DataFrame to apply operation to

-> DataFrame 

O(k) Convenience function that applies to an int column.

applyDouble #

Arguments

:: Columnable b 
=> (Double -> b)

function to apply

-> Text

Column name

-> DataFrame

DataFrame to apply operation to

-> DataFrame 

O(k) Convenience function that applies to an double column.

applyWhere #

Arguments

:: (Columnable a, Columnable b) 
=> (a -> Bool)

Filter condition

-> Text

Criterion Column

-> (b -> b)

function to apply

-> Text

Column name

-> DataFrame

DataFrame to apply operation to

-> DataFrame 

O(k * n) Apply a function to a column only if there is another column value that matches the given criterion.

applyWhere (<20) "Age" (const "Gen-Z") "Generation" df

applyAtIndex #

Arguments

:: Columnable a 
=> Int

Index

-> (a -> a)

function to apply

-> Text

Column name

-> DataFrame

DataFrame to apply operation to

-> DataFrame 

O(k) Apply a function to the column at a given index.

impute :: ImputeOp a => Expr a -> BaseType a -> DataFrame -> DataFrame #

Replace all instances of Nothing in a column with the given value. Throws when the column carries no nulls to replace.

groupBy :: [Text] -> DataFrame -> GroupedDataFrame #

O(k * n) group the dataframe by the given key columns, bucketing rows with an open-addressing hash table that re-verifies keys on each hash hit. Groups are numbered in first-appearance order; valueIndices/offsets follow by counting sort.

aggregate :: [NamedExpr] -> GroupedDataFrame -> DataFrame #

Aggregate a grouped dataframe using the expressions given. All ungrouped columns will be dropped.

distinct :: DataFrame -> DataFrame #

Filter out all non-unique values in a dataframe.

sortBy :: [SortOrder] -> DataFrame -> DataFrame #

O(k log n) Sorts the dataframe by a given row.

sortBy Ascending ["Age"] df

data SortOrder where #

Sort order taken as a parameter by the sortBy function.

Constructors

Asc :: forall a. (Columnable a, Ord a) => Expr a -> SortOrder 
Desc :: forall a. (Columnable a, Ord a) => Expr a -> SortOrder 

Instances

Instances details
Eq SortOrder 
Instance details

Defined in DataFrame.Operations.Permutation

join :: JoinType -> [Text] -> DataFrame -> DataFrame -> DataFrame #

Join two dataframes using SQL join semantics.

innerJoin :: [Text] -> DataFrame -> DataFrame -> DataFrame #

Performs an inner join on two dataframes using the specified key columns. Returns only rows where the key values exist in both dataframes.

Example

Expand
ghci> df = D.fromNamedColumns [("key", D.fromList [K0, K1, K2, K3]), (A, D.fromList [A0, A1, A2, A3])]
ghci> other = D.fromNamedColumns [("key", D.fromList [K0, K1, K2]), (B, D.fromList [B0, B1, B2])]
ghci> D.innerJoin ["key"] df other

-----------------
 key  |  A  |  B
------|-----|----
 Text | Text| Text
------|-----|----
 K0   | A0  | B0
 K1   | A1  | B1
 K2   | A2  | B2

leftJoin :: [Text] -> DataFrame -> DataFrame -> DataFrame #

Performs a left join on two dataframes using the specified key columns. Returns all rows from the left dataframe, with matching rows from the right dataframe. Non-matching rows will have Nothing/null values for columns from the right dataframe.

Example

Expand
ghci> df = D.fromNamedColumns [("key", D.fromList [K0, K1, K2, K3]), (A, D.fromList [A0, A1, A2, A3])]
ghci> other = D.fromNamedColumns [("key", D.fromList [K0, K1, K2]), (B, D.fromList [B0, B1, B2])]
ghci> D.leftJoin ["key"] df other

------------------------
 key  |  A  |     B
------|-----|----------
 Text | Text| Maybe Text
------|-----|----------
 K0   | A0  | Just B0
 K1   | A1  | Just B1
 K2   | A2  | Just B2
 K3   | A3  | Nothing

rightJoin :: [Text] -> DataFrame -> DataFrame -> DataFrame #

Performs a right join on two dataframes using the specified key columns. Returns all rows from the right dataframe, with matching rows from the left dataframe. Non-matching rows will have Nothing/null values for columns from the left dataframe.

Example

Expand
ghci> df = D.fromNamedColumns [("key", D.fromList [K0, K1, K2, K3]), (A, D.fromList [A0, A1, A2, A3])]
ghci> other = D.fromNamedColumns [("key", D.fromList [K0, K1]), (B, D.fromList [B0, B1])]
ghci> D.rightJoin ["key"] df other

-----------------
 key  |  A  |  B
------|-----|----
 Text | Text| Text
------|-----|----
 K0   | A0  | B0
 K1   | A1  | B1

data JoinType #

Equivalent to SQL join types.

Constructors

INNER 
LEFT 
RIGHT 
FULL_OUTER 

Instances

Instances details
Show JoinType 
Instance details

Defined in DataFrame.Operations.Join

Errors

Record bridge

class HasSchema a where #

Bridge a Haskell record type a to a typed-dataframe schema.

The schema is exposed as an associated type family Schema so that instances can pick it up from a Rep computation (see SchemaOf) or from an explicit list emitted by deriveSchemaFromType.

toColumns explodes a list of records into a list of named columns. fromColumns reconstructs the records from a DataFrame, returning Left err if a column is missing or has the wrong type.

Associated Types

type Schema a :: [(Symbol, Type)] #

Methods

toColumns :: [a] -> [(Text, Column)] #

fromColumns :: DataFrame -> Either Text [a] #

type family Schema a :: [(Symbol, Type)] #

fromRecords :: HasSchema a => [a] -> DataFrame #

Build an untyped DataFrame from a list of records.

data Order = Order { orderId :: Int64, region :: Text, amount :: Double }
$(deriveSchemaFromType ''Order)

xs :: [Order]
xs = [Order 1 "us" 10.0, Order 2 "eu" 20.0]

df :: DataFrame
df = fromRecords xs

toRecords :: HasSchema a => DataFrame -> Either Text [a] #

Parse a list of records out of an untyped DataFrame.

Returns Left err on schema mismatch (missing column, wrong type).

schemaColumnNames :: forall (cols :: [(Symbol, Type)]). KnownSchema cols => [Text] #

The column names a schema declares, in schema order. Pass it to a reader's options to fetch only those columns:

D.readCsvWithOpts
    D.defaultReadOptions{D.readColumns = Just (schemaColumnNames @(Schema Customer))}
    "customers.csv"

Template Haskell column-binding splices

declareColumnsFromCsvFile :: String -> DecsQ #

Splice a binding for every column of the DataFrame read from a CSV file. Each binding has type Expr T where T is the inferred column type.

declareColumnsFromParquetFile :: String -> DecsQ #

Splice a binding for every column of a parquet file (or directory of parquet files). The schema is read from each file's metadata and merged.

declareColumns :: DataFrame -> DecsQ #

Splice a binding for every column of df, named after the column. Column names that are not valid Haskell identifiers are sanitized (see sanitize).

declareColumnsWithPrefix :: Text -> DataFrame -> DecsQ #

Like declareColumns but prefixes every binding name with prefix_.

declareColumnsWithPrefix' :: Maybe Text -> DataFrame -> DecsQ #

Like declareColumnsWithPrefix but takes an optional prefix.

Plotting