XplainClient Enhancements

The enhanced XplainClient improves usability for relative time dimensions and sequence analysis operations through better parameter handling, convenience methods, and client-side validation.

Note

The enhanced client is available as xplain.session_client_enhanced.XplainClient and is fully backward compatible with the standard XplainClient.

Quick Start

Import the enhanced client:

from xplain import Xsession
from xplain.session_client_enhanced import XplainClient
from xplain.relative_time_helpers import TimeUnit, RelativeTimeType

s = Xsession(...)
client = XplainClient(s)

Key Features

1. Tuple and String Dimension Paths

Use concise tuple or string format instead of verbose dicts:

# Old (verbose)
client.add_relative_time_dimensions(
    time_dimensions=[{"object": "Prescriptions", "dimension": "Date"}],
    reference_time_dimension={"object": "Diagnoses", "dimension": "DiagDate"},
)

# New (concise)
client.add_relative_time_dimensions(
    time_dimensions=[("Prescriptions", "Date")],
    reference_time_dimension=("Diagnoses", "DiagDate"),
)

# Also works with strings
client.add_relative_time_dimensions(
    time_dimensions=["Prescriptions.Date"],
    reference_time_dimension="Diagnoses.DiagDate",
)

2. Enum Constants with IDE Autocomplete

Use constants that provide IDE guidance:

from xplain.relative_time_helpers import TimeUnit, RelativeTimeType

# IDE autocomplete guides valid values
client.calculate_time_since(
    event_dimension=("Prescriptions", "Date"),
    reference=("Diagnoses", "DiagDate"),
    name="DaysSinceDiag",
    since=RelativeTimeType.TO_FIRST,  # or use "first"
    unit=TimeUnit.DAY,
)

3. Convenience Method: calculate_time_since()

Simplified relative time dimension creation with intuitive parameters:

# Creates days since first diagnosis for prescriptions
client.calculate_time_since(
    event_dimension=("Prescriptions", "Date"),
    reference=("Diagnoses", "DiagDate"),
    name="DaysSinceDiag",
    since="first",      # or "last", "nearest", "next", "previous"
    unit=TimeUnit.DAY,
    base_object="Patients",
)

4. Client-Side Validation

Fail fast with clear error messages before server round-trip:

# Invalid enum value → immediate error
try:
    client.add_relative_time_dimensions(
        time_dimensions=[("Prescriptions", "Date")],
        reference_time_dimension=("Diagnoses", "DiagDate"),
        time_unit="INVALID",  # Not valid!
    )
except ValueError as e:
    print(e)  # "Invalid time_unit: 'INVALID'. Must be one of {...}"

# Mismatched parameters → caught immediately
try:
    client.add_relative_time_dimensions(
        time_dimensions=[dim1, dim2],
        names=["OnlyOne"],  # Should have 2 names!
    )
except ValueError as e:
    print(e)  # "names cardinality mismatch..."

Relative Time Dimensions

Enhanced add_relative_time_dimensions() with tuple/string support:

from xplain.relative_time_helpers import TimeUnit, RelativeTimeType

# Measure time of prescriptions relative to first diagnosis
client.add_relative_time_dimensions(
    time_dimensions=[("Prescriptions", "Date")],
    reference_time_dimension=("Diagnoses", "DiagDate"),
    base_object="Patients",
    relative_time_type=RelativeTimeType.TO_FIRST,
    names=["DaysSinceFirstDiag"],
    time_unit=TimeUnit.DAY,
)

Reference Types

The relative_time_type parameter controls which event becomes the zero point:

  • TO_FIRST — Time relative to the first matching reference event

  • TO_LAST — Time relative to the last matching reference event

  • TO_NEAREST — Time relative to the nearest reference event (either direction)

  • TO_NEXT — Time relative to the next (future) reference event

  • TO_PREVIOUS — Time relative to the previous (past) reference event

Time Units

TimeUnit

Available time units for relative time calculations:

  • NANOSECOND, MILLISECOND, SECOND

  • MINUTE, HOUR, DAY, WEEK

  • MONTH, QUARTER, HALFYEAR, YEAR

  • DECADE, CENTURY

Examples

Days since first hospitalization:

client.calculate_time_since(
    event_dimension=("Hospitalizations", "DateFrom"),
    reference=("Hospitalizations", "DateFrom"),
    name="DaysSinceFirstHosp",
    since="first",
    unit=TimeUnit.DAY,
    base_object="Patients",
)

Months until next visit (calendar-based):

client.calculate_time_since(
    event_dimension=("Visits", "ScheduledDate"),
    reference=("Visits", "ActualDate"),
    name="MonthsUntilNextVisit",
    since="next",
    unit=TimeUnit.MONTH,
    calendar_based=True,
)

Hours to nearest lab result:

client.calculate_time_since(
    event_dimension=("LabResults", "Time"),
    reference=("Treatments", "StartTime"),
    name="HoursToNearestLab",
    since="nearest",
    unit=TimeUnit.HOUR,
)

Sequence Operations

Enhanced build_sorted_sequence() with tuple/string support:

# Rank prescriptions by date
client.build_sorted_sequence(
    base_object="Patients",
    target_object="Patients",
    sort_dimension=("Prescriptions", "Date"),        # tuple format
    dimensions_to_replicate=[("Prescriptions", "PZN")],
    ranks=[1, 2, 3],
    names=[["1st Rx", "2nd Rx", "3rd Rx"]],
)

# Or open directly for analysis
client.open_sequence(
    sequence_names=["RxJourney"],
    base_object="Patients",
    sort_dimension=("Prescriptions", "Date"),
    dimensions_to_replicate=[("Prescriptions", "PZN")],
    ranks=[1, 2, 3],
    names=[["1st Rx", "2nd Rx", "3rd Rx"]],
    open_marginal_queries=True,
)

Helper Modules

Constants and validation utilities for relative time operations.

The Enhanced XplainClient Class

class xplain.session_client_enhanced.XplainClient(xsession)

Bases: xplain.session_client.XplainClient

Enhanced XplainClient with improved UX for longitudinal analysis.

Extends the auto-generated XplainClient with:
  • Tuple/string support for dimension paths (via normalize_element_path)

  • Convenience methods for common workflows

  • Client-side validation with better error messages

  • Enum constants for valid string values

Parameters

xsession (Xsession) –

Return type

None

Relative Time Methods

XplainClient.add_relative_time_dimensions(reference_time_dimension, time_dimensions=None, interval_end_time_dimension=None, base_object=None, relative_time_type=None, names=None, reference_event_selections=None, fix_explicit_selections=None, time_unit=None, calendar_based=False, cache_reference_time=False, selection_set=None, floating_semantics=None)

Construct relative time dimension(s) based on reference events.

Enhanced version with:
  • Tuple and string support for dimension paths

  • Client-side validation

  • Better error messages

Parameters
  • reference_time_dimension (Union[Dict, Tuple, str]) – XElementPath (dict, tuple, or “object.dimension”)

  • time_dimensions (Optional[Union[List[Dict], List[Tuple], List[str]]]) – List of XElementPath (dict, tuple, or string format)

  • interval_end_time_dimension (Optional[Union[Dict, Tuple, str]]) – XElementPath for interval coding

  • base_object (str) – Base object name for the relative time axis

  • relative_time_type (str) – TO_FIRST|TO_LAST|TO_NEAREST|TO_NEXT|TO_PREVIOUS

  • names (List[str]) – Names for new relative time dimensions

  • reference_event_selections (List) – Selections defining the reference event

  • fix_explicit_selections (str) – none|all|only_propagate_upward

  • time_unit (str) – DAY|WEEK|MONTH|YEAR|HOUR|… (see TimeUnit)

  • calendar_based (bool) – Use calendar-based time differences (default False)

  • cache_reference_time (bool) – Enable caching (default False)

  • selection_set (str) – Selection set for floating semantics

  • floating_semantics (bool) – DEPRECATED (use selection_set instead)

Returns

Session state after the operation

Raises
  • ValueError – If parameter combinations are invalid

  • TypeError – If dimension paths have invalid format

Return type

dict

XplainClient.calculate_time_since(event_dimension, reference, name, since='first', unit='DAY', base_object=None, reference_filter=None, calendar_based=False, **kwargs)

Calculate time of events relative to a reference event (convenience method).

Simplifies the common case of measuring time since/until a reference event. This is a high-level wrapper around add_relative_time_dimensions().

Parameters
  • event_dimension (Union[Dict, Tuple, str]) – Dimension to measure relative time for (tuple/dict/string)

  • reference (Union[Dict, Tuple, str]) – Reference event dimension (tuple/dict/string)

  • name (str) – Name for the new relative time dimension

  • since (str) –

    Which event to use as zero point. Options:

    • ”first” (default): first matching reference event

    • ”last”: last matching reference event

    • ”nearest”: nearest reference event (in either direction)

    • ”next”: next (future) reference event

    • ”previous”: previous (past) reference event

  • unit (str) – Time unit (default: DAY). See TimeUnit for all options.

  • base_object (str) – Base object for the relative time axis

  • reference_filter (List) – Selections defining which event is the reference

  • calendar_based (bool) – Use calendar-based time differences (default False)

  • **kwargs – Pass-through for advanced parameters

Returns

Session state after the operation

Return type

dict

Examples

Days since first hospitalization for all prescriptions:

client.calculate_time_since(
    event_dimension=("Prescriptions", "Date"),
    reference=("Hospitalizations", "DateFrom"),
    name="DaysSinceFirstHosp",
    since="first",
    unit=TimeUnit.DAY,
    base_object="Patients",
)
Raises

ValueError – If ‘since’ value is invalid

Parameters
  • event_dimension (Union[Dict, Tuple, str]) –

  • reference (Union[Dict, Tuple, str]) –

  • name (str) –

  • since (str) –

  • unit (str) –

  • base_object (str) –

  • reference_filter (List) –

  • calendar_based (bool) –

Return type

dict

XplainClient.remove_relative_time_dimensions(names, base_object=None)

Remove multiple relative time dimensions (batch cleanup).

Convenience for cleaning up temporary dimensions created by add_relative_time_dimensions() in bulk.

Parameters
  • names (List[str]) – List of dimension names to remove

  • base_object (str) – Base object containing the dimensions (tries all known objects if not specified)

Return type

None

Sequence Methods

XplainClient.build_sorted_sequence(target_object=None, ranks=None, reverse=False, names=None, name_postfixes=None, dimensions_to_replicate=None, sort_dimension=None, base_object=None, zero_point_dimension=None, selections=None, fix_explicit_selections=None, selection_set=None, selection_set_defining_rank=None, floating_semantics=None, attributes_to_copy=None, sequence_names=None, rank_dimension_name=None, rank_zero_is_first_instance_equal_or_greater_zero_point=False, transition_attribute=None, only_segment_starts=False, transition_level=1)

Build a sorted sequence from time-ordered events (enhanced).

Enhanced version with:
  • Tuple and string support for dimension paths

  • Client-side validation

  • Better error messages

All parameters as in the parent XplainClient class.

Parameters
  • target_object (str) –

  • ranks (list) –

  • reverse (bool) –

  • names (list) –

  • name_postfixes (list) –

  • dimensions_to_replicate (list) –

  • sort_dimension (Optional[Union[Dict, Tuple, str]]) –

  • base_object (str) –

  • zero_point_dimension (Optional[Union[Dict, Tuple, str]]) –

  • selections (list) –

  • fix_explicit_selections (str) –

  • selection_set (str) –

  • selection_set_defining_rank (str) –

  • floating_semantics (bool) –

  • attributes_to_copy (list) –

  • sequence_names (list) –

  • rank_dimension_name (str) –

  • rank_zero_is_first_instance_equal_or_greater_zero_point (bool) –

  • transition_attribute (Optional[Union[Dict, Tuple, str]]) –

  • only_segment_starts (bool) –

  • transition_level (int) –

Return type

dict

XplainClient.open_sequence(sequence_names, dimensions_to_replicate=None, sort_dimension=None, ranks=None, target_object=None, base_object=None, zero_point_dimension=None, reverse=False, names=None, name_postfixes=None, selections=None, fix_explicit_selections=None, selection_set=None, selection_set_defining_rank=None, floating_semantics=None, rank_dimension_name=None, rank_zero_is_first_instance_equal_or_greater_zero_point=False, attributes_to_copy=None, transition_attribute=None, only_segment_starts=False, transition_level=1, open_marginal_queries=True, open_transition_queries=False)

Open a sequence for interactive exploration

Open a previously built sequence in the session for interactive analysis. The opened sequence computes and returns aggregated sequence results. Accepts all parameters of buildSortedSequence combined with those of openSequenceQueries.

Parameters
  • sequence_names (list) – Names of the sequences to build and immediately open.

  • dimensions_to_replicate (list, optional) – The dimensions which should be replicated (“unfolded” in time). Those need to be from the same source object. XElementPath

  • sort_dimension (dict, optional) – The dimension which defines the sorting (typically a time dimension, but could be any numeric dimension). XElementPath

  • ranks (list, optional) – The ranks of the new dimension (position when sorted according to sortDimension).

  • target_object (str, optional) – Name of the target object (optional - if not given the root object is assumed, or guessed otherwise). In case of a RankDimension this parameter is irrelevant. (The object of the sortDimension is used.)

  • base_object (str, optional) – The embracing object within which events are sorted (if not given the target object is assumed if this not in a different object branch, otherwise the root object).

  • zero_point_dimension (dict, optional) – An optional dimension relative to which the sorting / ranking is done. XElementPath

  • reverse (bool, optional, default=False) – For the case of reverse sorting / counting.

  • names (list, optional) – Names of the new dimensions - if not given a name will be generated. There needs to be one array of names for each dimensionToReplicate, and each array needs to be of same size as the number of given ranks (or a null array).

  • name_postfixes (list, optional) – When given instead of names, the names will be generated based on those post-fixes.

  • selections (list, optional) – The set of selections that will be applied as the “where” clause to the to be sorted/ranked events. SelectionUIModel

  • fix_explicit_selections (str, optional) – This parameter may have one of the three values “none” (the default), “all” or “only_propagate_upward”. See buildSortedSequence for full description. One of: none, all, only_propagate_upward.

  • selection_set (str, optional) – If specified, the selections from the given set will be used as the selections, thereby implementing a “floating semantics” concept.

  • selection_set_defining_rank (str, optional) – An old deprecated parameter which may be used instead of selectionSet.

  • floating_semantics (bool, optional) – Deprecated. If false when a selectionSet is specified, the current selections are copied and kept fixed.

  • rank_dimension_name (str, optional) – If this parameter is given, an additional “rank dimension” is added to the source object (the object where the dimensionsToReplicate come from), which assigns a number (the rank) to each instance.

  • rank_zero_is_first_instance_equal_or_greater_zero_point (bool, optional, default=False) – If true this means that the first instance in the array of sorted instances which is >= zero point receives the rank 0.

  • attributes_to_copy (list, optional) – A list of attribute names amongst the attributes attached to the dimensionsToReplicate which are copied to the new dimensions. If not given all attributes will be copied.

  • transition_attribute (dict, optional) – If non-null, only the transitions will be coded in the sequence, i.e. only if the value of this attribute on the given transitionLevel changes a new entry in the ranked sequence is made. XElementPath

  • only_segment_starts (bool, optional, default=False) – If set to true, only the first occurrence of a segment is assigned a rank, i.e. only if a new value of the transitionAttribute appears, whereby new is defined by the transitionLevel.

  • transition_level (int, optional, default=1) – See the parameter transitionAttribute: the hierarchy level of the attribute which is used to detect transitions.

  • open_marginal_queries (bool, optional, default=True) – If true, queries for the marginal distributions for each sequence step will be opened (one-dimensional).

  • open_transition_queries (bool, optional, default=False) – If true, queries which describe the transitions between each step will be opened (two-dimensional).

Returns

The session state after the operation.

Return type

dict

Migration Guide

From Standard to Enhanced Client

The enhanced client is a drop-in replacement:

# Before
from xplain.session_client import XplainClient
client = XplainClient(session)

# After (with new features)
from xplain.session_client_enhanced import XplainClient
client = XplainClient(session)

All existing code continues to work. New code can use the convenience methods and shorter syntax.

From Old Xplainapi to Enhanced Client

The enhanced client maintains compatibility with the old Xplainapi style:

# Old Xplainapi (deprecated)
from xplainapi import Xplainapi
xapi = Xplainapi(...)
xapi.build_sorted_sequence(
    dimensions_to_replicate=[("Prescriptions", "Rx PCN")],
    sort_dimension=("Prescriptions", "Rx Order date"),
)

# New Enhanced Client (recommended)
from xplain import Xsession
from xplain.session_client_enhanced import XplainClient
s = Xsession(...)
client = XplainClient(s)
client.build_sorted_sequence(
    dimensions_to_replicate=[("Prescriptions", "Rx PCN")],
    sort_dimension=("Prescriptions", "Rx Order date"),
)

Backward Compatibility

✓ Old dict syntax still works:

# Dict format (always supported)
client.add_relative_time_dimensions(
    time_dimensions=[{"object": "Prescriptions", "dimension": "Date"}],
    reference_time_dimension={"object": "Diagnoses", "dimension": "DiagDate"},
)

✓ Can mix styles:

# Mix tuple and dict formats in same session
client.add_relative_time_dimensions(
    time_dimensions=[("Prescriptions", "Date")],  # tuple
    reference_time_dimension={"object": "Diagnoses", "dimension": "DiagDate"},  # dict
)

Performance

Client-side validation adds minimal overhead:

  • Enum validation: O(1) set membership checks

  • Cardinality checks: O(n) list length comparisons

  • All validation runs before sending to server

Validation prevents unnecessary server round-trips for obviously incorrect parameters.

Testing

The enhancements are thoroughly tested:

# Run all enhancement tests
pytest test/xoedevtest/openapi_client/test_session_client_10_enhanced_relatives.py -v

# Helper functions only (no server required)
pytest test/xoedevtest/openapi_client/test_session_client_10_enhanced_relatives.py::TestHelperFunctions -v

See IMPLEMENTATION_SUMMARY.md in the docs/ directory for detailed test results.

Additional Resources

For more detailed analysis and recommendations, see the following markdown documents in the docs/ directory:

  • relative_time_dimensions_analysis.md — Complete analysis of relative time API

  • build_sorted_sequence_analysis.md — Analysis of sequence operations

  • IMPLEMENTATION_SUMMARY.md — Implementation details and test results

  • ENHANCEMENTS_README.md — User guide with examples

Troubleshooting

ValueError: Invalid time_unit

The time unit is not recognized. Use constants from TimeUnit:

from xplain.relative_time_helpers import TimeUnit
client.calculate_time_since(..., unit=TimeUnit.DAY)

ValueError: cardinality mismatch

The number of names doesn’t match the number of dimensions. Each dimension needs a corresponding name:

# Wrong
client.add_relative_time_dimensions(
    time_dimensions=[dim1, dim2],
    names=["OnlyOne"],  # 2 dimensions, 1 name
)

# Correct
client.add_relative_time_dimensions(
    time_dimensions=[dim1, dim2],
    names=["Name1", "Name2"],  # 2 dimensions, 2 names
)

ValueError: Invalid relative_time_type

The reference type is not valid. Use one of:

RelativeTimeType.TO_FIRST
RelativeTimeType.TO_LAST
RelativeTimeType.TO_NEAREST
RelativeTimeType.TO_NEXT
RelativeTimeType.TO_PREVIOUS

Or use strings:

client.calculate_time_since(..., since="first")  # Instead of TO_FIRST