Skip to content

Transforms

"""
Unary transform operators example for aaiclick.

This example demonstrates Object-level transform operators that apply
ClickHouse functions element-wise and return new Objects. These are the
Object equivalents of View domain helpers (with_year, with_lower, etc.).
"""

import asyncio
from datetime import datetime, timezone

from aaiclick import create_object_from_value, literal
from aaiclick.data.data_context import data_context


async def example():
    """Run all transform examples."""
    # Example 1: Date/time transforms
    print("Example 1: Date/time transforms")
    print("-" * 50)

    dates = [
        datetime(2023, 3, 15, tzinfo=timezone.utc),
        datetime(2024, 7, 4, tzinfo=timezone.utc),
        datetime(2025, 12, 25, tzinfo=timezone.utc),
    ]
    obj = await create_object_from_value(dates)
    print(f"Dates: {dates}")

    years = await obj.year()
    print(f"year():        {await years.data()}")  # → [2023, 2024, 2025]

    months = await obj.month()
    print(f"month():       {await months.data()}")  # → [3, 7, 12]

    dows = await obj.day_of_week()
    print(f"day_of_week(): {await dows.data()}")  # → [3, 4, 4]

    # Example 2: String transforms
    print("\n" + "=" * 50)
    print("Example 2: String transforms")
    print("-" * 50)

    strings = ["  Hello World  ", " FOO ", "  bar  "]
    obj = await create_object_from_value(strings)
    print(f"Strings: {strings}")

    print(f"lower():  {await obj.lower().data()}")  # → ['  hello world  ', ' foo ', '  bar  ']
    print(f"upper():  {await obj.upper().data()}")  # → ['  HELLO WORLD  ', ' FOO ', '  BAR  ']
    print(f"trim():   {await obj.trim().data()}")  # → ['Hello World', 'FOO', 'bar']
    print(f"length(): {await obj.length().data()}")  # → [15, 5, 7]

    # Example 3: Math transforms
    print("\n" + "=" * 50)
    print("Example 3: Math transforms")
    print("-" * 50)

    numbers = [-9, -4, 0, 4, 16]
    obj = await create_object_from_value(numbers)
    print(f"Numbers: {numbers}")

    print(f"abs():  {await obj.abs().data()}")  # → [9.0, 4.0, 0.0, 4.0, 16.0]

    positives = await create_object_from_value([1, 2, 4, 8, 16])
    print(f"\nPositives: {await positives.data()}")
    print(f"log2(): {await positives.log2().data()}")  # → [0.0, 1.0, 2.0, 3.0, 4.0]
    print(f"sqrt(): {await positives.sqrt().data()}")  # → [1.0, 1.414..., 2.0, 2.828..., 4.0]

    # Example 4: Chaining transforms with other operators
    print("\n" + "=" * 50)
    print("Example 4: Chaining transforms with aggregations")
    print("-" * 50)

    dates = [
        datetime(2024, 1, 1, tzinfo=timezone.utc),
        datetime(2024, 6, 15, tzinfo=timezone.utc),
        datetime(2025, 3, 10, tzinfo=timezone.utc),
    ]
    obj = await create_object_from_value(dates)
    years = await obj.year()
    unique_years = await years.unique()
    print(f"Unique years: {sorted(await unique_years.data())}")  # → [2024, 2025]

    year_sum = await years.sum()
    print(f"Sum of years: {await year_sum.data()}")  # → 6073

    words = ["apple", "banana", "cherry", "date"]
    obj = await create_object_from_value(words)
    lengths = await obj.length()
    max_len = await lengths.max()
    print(f"Longest word length: {await max_len.data()}")  # → 6

    # Example 5: literal() — constant computed columns
    print("\n" + "=" * 50)
    print("Example 5: literal() helper")
    print("-" * 50)

    obj = await create_object_from_value([{"city": "NYC"}, {"city": "London"}])
    view = obj.with_columns(
        {
            "source": literal("survey_2024", "String"),
            "active": literal(True, "UInt8"),
            "weight": literal(1.0, "Float64"),
        }
    )
    result = await view.data()
    print(f"source: {result['source']}")  # → ['survey_2024', 'survey_2024']
    print(f"active: {result['active']}")  # → [1, 1]
    print(f"weight: {result['weight']}")  # → [1.0, 1.0]


async def amain():
    async with data_context():
        await example()


if __name__ == "__main__":
    asyncio.run(amain())