Table Module
Cross-tabulation and summary tables from DataFrames.
Inspired by Stata's table command, with idiomatic Keel syntax (pipes, data-last, builder pattern).
Quick forms
import Table
import DataFrame
let df = DataFrame.readCsv "data.csv"
-- One-way frequency table
df |> Table.freq "sex"
-- Two-way cross-tabulation
df |> Table.cross "sex" "highbp"
Builder pattern
df
|> Table.create
|> Table.rows ["sex"]
|> Table.cols ["highbp"]
|> Table.count
|> Table.meanOf "age"
|> Table.show
Nesting vs adjacency
- Multiple vars in one call = nested hierarchy:
Table.rows ["sex", "race"] - Multiple calls = adjacent sections:
Table.cols ["sex"] |> Table.cols ["race"]
Statistics
| Function | Description |
|---|---|
count | Frequency count (default if none specified) |
percent | Percentage of total |
meanOf v | Mean of variable |
sdOf v | Standard deviation |
medianOf v | Median |
minOf v / maxOf v | Min / Max |
sumOf v | Sum |
Layout
Tables separate data from layout. Use Table.relayout to rearrange dimensions without recomputing statistics. Use Table.toDataFrame to convert to a flat DataFrame for further analysis.
Functions
Table.cols
[String] -> TableSpec -> TableSpec
Add column dimension variables.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", bp = "high" }, { sex = "F", bp = "low" }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.cols ["bp"]
|> Table.showTry itNotes: Multiple calls create adjacent column groups. Multiple vars in one call create nested hierarchy.
See also: Table.rows, Table.create
Table.count
TableSpec -> TableSpec
Add frequency count statistic.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M" }, { sex = "M" }, { sex = "F" }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.count
|> Table.showTry itNotes: If no stat functions are called before Table.show, count is the default.
See also: Table.percent, Table.meanOf
Table.create
DataFrame -> TableSpec
Initialize a table specification builder from a DataFrame.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "F", age = 25 }]
df
|> Table.createTry itNotes: Use Table.rows, Table.cols, and stat functions to configure the table, then Table.show to materialize it.
See also: Table.rows, Table.cols, Table.show
Table.cross
a -> b -> DataFrame -> Table
Create a two-way cross-tabulation. Accepts String or [String] for each dimension.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", bp = "high" }, { sex = "F", bp = "low" }, { sex = "M", bp = "low" }]
df
|> Table.cross "sex" "bp"Try itNotes: Shortcut for Table.create |> Table.rows rowVars |> Table.cols colVars |> Table.count |> Table.show.
See also: Table.freq, Table.create
Table.facetBy
String -> TableSpec -> TableSpec
Split into separate sub-tables for each level of the given variable.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", race = "W", age = 30 }, { sex = "F", race = "B", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.facetBy "race"
|> Table.showTry itSee also: Table.rows, Table.cols
Table.freq
a -> DataFrame -> Table
Create a one-way frequency table. Accepts a String or [String].
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M" }, { sex = "M" }, { sex = "F" }]
df
|> Table.freq "sex"Try itNotes: Shortcut for Table.create |> Table.rows vars |> Table.count |> Table.show.
See also: Table.cross, Table.create
Table.maxOf
String -> TableSpec -> TableSpec
Add maximum of a variable.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "M", age = 40 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.maxOf "age"
|> Table.showTry itSee also: Table.minOf
Table.meanOf
String -> TableSpec -> TableSpec
Add mean of a variable.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "M", age = 40 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.meanOf "age"
|> Table.showTry itSee also: Table.sdOf, Table.medianOf
Table.medianOf
String -> TableSpec -> TableSpec
Add median of a variable.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "M", age = 40 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.medianOf "age"
|> Table.showTry itSee also: Table.meanOf, Table.minOf, Table.maxOf
Table.minOf
String -> TableSpec -> TableSpec
Add minimum of a variable.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "M", age = 40 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.minOf "age"
|> Table.showTry itSee also: Table.maxOf
Table.noTotals
TableSpec -> TableSpec
Suppress row and column totals.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M" }, { sex = "M" }, { sex = "F" }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.noTotals
|> Table.showTry itTable.percent
TableSpec -> TableSpec
Add percentage of total statistic.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M" }, { sex = "M" }, { sex = "F" }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.percent
|> Table.showTry itSee also: Table.count
Table.relayout
[String] -> [String] -> Table -> Table
Re-layout an existing Table with new row and column dimensions.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", bp = "high" }, { sex = "F", bp = "low" }, { sex = "M", bp = "low" }]
let t =
df |> Table.cross "sex" "bp"
t
|> Table.relayout ["bp"] ["sex"]Try itNotes: First argument is new row variables, second is new column variables. No recomputation of statistics.
See also: Table.show
Table.rows
[String] -> TableSpec -> TableSpec
Add row dimension variables.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.showTry itNotes: Multiple calls create adjacent row sections. Multiple vars in one call create nested hierarchy.
See also: Table.cols, Table.create
Table.sdOf
String -> TableSpec -> TableSpec
Add standard deviation of a variable.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "M", age = 40 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.sdOf "age"
|> Table.showTry itSee also: Table.meanOf
Table.show
TableSpec -> Table
Materialize the table specification into a displayable Table.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.count
|> Table.showTry itNotes: Computes all statistics and creates the table with the current layout. Use Table.relayout to rearrange after.
See also: Table.relayout, Table.toDataFrame
Table.sumOf
String -> TableSpec -> TableSpec
Add sum of a variable.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "M", age = 40 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.sumOf "age"
|> Table.showTry itSee also: Table.count, Table.meanOf
Table.toDataFrame
Table -> DataFrame
Convert a Table to a flat DataFrame.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M" }, { sex = "F" }, { sex = "M" }]
let t =
df |> Table.freq "sex"
t
|> Table.toDataFrameTry itNotes: Returns the raw aggregated data as a DataFrame for further manipulation.
See also: Table.show
Table.withPValue
TableSpec -> TableSpec
Add p-value column with auto-selected test.
import Table
import DataFrame
let df =
DataFrame.fromRecords [{ sex = "M", age = 30 }, { sex = "M", age = 40 }, { sex = "F", age = 25 }]
df
|> Table.create
|> Table.rows ["sex"]
|> Table.count
|> Table.withPValue
|> Table.showTry itNotes: Selects t-test (Welch's) for continuous variables, chi-squared for categorical. ANOVA for >2 groups.