Skip to main content

cwind-tableau

A Python Tableau-style query DSL — build aggregate queries with a chainable API, generate and execute SQL.

English | 简体中文


Installation

pip install cwind-tableau

Requires Python >= 3.10.

Quick Start

import cwind_tableau as tab

# Reference columns and apply aggregations
total_sales = tab.col("amount").sum()
avg_sales = tab.col("amount").avg().alias("avg_amount")

# Create a view with dimensions and aggregations
view = tab.view(
    table="sales_table",
    dims=["product", "region"],
    aggs=[total_sales, avg_sales],
)

# Generate SQL
print(view.to_sql())
# SELECT product, region, SUM(amount) AS "SUM(amount)", AVG(amount) AS "avg_amount"
# FROM sales_table
# GROUP BY product, region

# Execute with DuckDB
result = view.to_query()

Features

Column Reference

tab.col("amount")  # Refer to a column
tab.col("order_date")  # Refer to a date column

Built-in Aggregations

tab.col("amount").sum()  # Sum
tab.col("amount").avg()  # Average
tab.col("amount").count()  # Count
tab.col("amount").count_distinct()  # Distinct count
tab.col("amount").min()  # Minimum
tab.col("amount").max()  # Maximum
tab.col("amount").median()  # Median
tab.col("amount").std()  # Standard deviation
tab.col("x").custom("MY_FUNC")  # Custom aggregation function (simple mode)
tab.col("x").custom("MY_FUNC({col}, {b}, {c})", b=1, c='xxx')  # Template mode: MY_FUNC(x, 1, 'xxx')

Custom Aliases

tab.col("amount").sum().alias("total_sales")  # Aggregation alias
tab.col("order_date").dt.year().alias("year")  # Dimension alias

Date Extraction (.dt accessor)

tab.col("order_date").dt.year()  # Year
tab.col("order_date").dt.month()  # Month
tab.col("order_date").dt.day()  # Day
tab.col("order_date").dt.quarter()  # Quarter
tab.col("order_date").dt.week()  # Week number
tab.col("order_date").dt.month_name()  # Month name (e.g. "January")

Window Dimensions (.fixed())

Turn an aggregation into a window function dimension:

# First order date per customer — used as a dimension, not an aggregation
tab.col("order_date").min().fixed("customer_id").alias("first_order_date")

# Multi-column partition
tab.col("order_date").min().fixed(["customer_id", "product"])

INCLUDE / EXCLUDE LOD Dimensions

Create dimensions based on Level of Detail (LOD) expressions:

# INCLUDE: partition by view_dimensions ∪ product
tab.col("amount").sum().include("product").alias("amount_by_product")

# EXCLUDE: partition by view_dimensions − region
tab.col("amount").sum().exclude("region").alias("amount_excl_region")

# Multi-column
# tab.col("amount").sum().include(["product", "city"])
# tab.col("amount").sum().exclude(["region", "category"])
Method LOD Type Partition Columns
.fixed(cols) FIXED cols only, ignoring view dimensions
.include(cols) INCLUDE view_dimensions ∪ cols
.exclude(cols) EXCLUDE view_dimensions − cols

LOD as Measure

Apply aggregations on LOD dimensions to create LOD measures (e.g. SUM({FIXED ...})):

# FIXED as measure — SUM({FIXED [product] : SUM([amount])})
tab.col("amount").sum().fixed("product").sum().alias("product_total")

# INCLUDE as measure — AVG({INCLUDE [product] : SUM([amount])})
tab.col("amount").sum().include("product").avg().alias("avg_by_product")

# EXCLUDE as measure — MAX({EXCLUDE [region] : SUM([amount])})
tab.col("amount").sum().exclude("region").max().alias("max_excl_region")

# Use in a View
view = tab.view(
    table="sales",
    dims=["region"],
    aggs=[
        tab.col("amount").sum().alias("region_sales"),
        tab.col("amount").sum().fixed("product").sum().alias("product_total"),
    ],
)

Arithmetic Expressions

# Calculate amount = price × quantity, then sum
(tab.col("price") * tab.col("quantity")).sum()

CASE WHEN Conditional Expressions

Build CASE WHEN conditional expressions via tab.if_(), usable as dimensions:

# Simple classification
c = tab.if_(tab.col("amount") > 500, "big").else_("small").alias("order_size")

# Multi-branch classification
c = (
    tab.if_(tab.col("amount") > 500, "high value")
    .else_if_(tab.col("amount") > 200, "medium value")
    .else_("low value")
    .alias("value_category")
)

# Use in a View
view = tab.view(
    table="sales",
    dims=["region", c],
    aggs=[tab.col("amount").sum().alias("total")],
)

# Combined with window dimensions (FixedDim)
daily_profit = tab.col("profit").sum().fixed("order_date").alias("daily_profit")
c2 = (
    tab.if_(daily_profit > 2000, "highly profitable")
    .else_if_(daily_profit < 0, "unprofitable")
    .else_("profitable")
    .alias("daily_category")
)

Supported operators: >, <, >=, <=. Values support int, float, str, Column.

API Reference

tab.col(name: str) -> Column

Create a column reference. Returns a Column object with aggregation and date extraction methods.

tab.view(table: str, dims: list, aggs: list) -> View

Create a pivot view.

Parameter Type Description
table str Source table name
dims list[str | Dimension] Dimension columns for GROUP BY
aggs list[Aggregation] Aggregation expressions for SELECT

View.to_sql() -> str

Generate the SQL string (DuckDB dialect).

View.to_query(con=None) -> duckdb.DuckDBPyResult

Execute the query via DuckDB.

  • Without con: uses duckdb.query() on the default connection
  • With con: uses con.query() on the provided connection

Architecture

cwind-tableau/
├── src/cwind_tableau/
│   ├── api/          # Facade: tab.col, tab.view
│   ├── core/         # Orchestration: View, SQL generation
│   └── models/       # Entities: Column, Aggregation, Dimension
├── tests/
├── docs/
└── examples/

Design Principles

  • Reusable aggregations: Aggregation expressions can be defined as variables and reused across queries
  • Default aliases: Auto-generated aliases (e.g. "SUM(amount)") when .alias() is not used
  • Chainable API: All operations via method chaining — consistent and readable
  • Expressions are objects: Any intermediate result (column, aggregation, dimension) is a Python object that can be stored, passed, and composed

Dependencies

  • DuckDB — SQL execution engine
  • sqlglot — SQL generation and future dialect transpilation

License

MIT

Metadata

Release files for cwind-tableau 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cwind-tableau 0.2.0
File Size Uploaded Provenance Actions
cwind_tableau-0.2.0.tar.gz 18.0 kB Without provenance Details for cwind_tableau-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for cwind-tableau 0.2.0
File Interpreter ABI Platform Actions
cwind_tableau-0.2.0-py3-none-any.whl Python 3 none any Details for cwind_tableau-0.2.0-py3-none-any.whl

Total release size: 48.5 kB

Release files / cwind_tableau-0.2.0.tar.gz

Download URL cwind_tableau-0.2.0.tar.gz
Size 18.0 kB
Tags Source
SHA-256 checksum
How to use checksums
f161fb337203587f8c3077b0523e313f5d03f9dcc02fce46356f4b494b3d66d8
BLAKE2b-256 checksum
How to use checksums
e5be52c7ee75d6f7e6480cd3ac0036ee82d8eb4f4b884123845d1be4313b3ffd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / cwind_tableau-0.2.0-py3-none-any.whl

Download URL cwind_tableau-0.2.0-py3-none-any.whl
Size 30.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5ba759baf053a7fb49a71c9a9304e41c69cb700b670e91eb41649dbfb813cdd5
BLAKE2b-256 checksum
How to use checksums
e68e34c4f3004c3b1fe33f9b9e0a7181869e512725a9cf38d436735f433184bd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for cwind-tableau 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page