## Surv


A validated time-to-event response for survival analysis.


Usage

``` python
Surv(
    type,
    stop,
    status,
    start=None,
    lower=None,
    states=None,
    weights=None,
)
```


[Surv](Surv.md#greenwood.Surv) represents the outcome in survival models: a time at which each subject either experienced an event (observed) or was censored (did not experience the event during follow-up). [Surv](Surv.md#greenwood.Surv) supports multiple censoring types:

- **Right-censored** (most common): The event time is at or after the recorded time. Use `Surv.right(time, event)`.
- **Left-censored**: The event time is before the recorded time. Use `Surv.left(time, event)`.
- **Counting-process** (left truncation, time-varying): Each subject enters the risk set at `start` and exits at `stop`. Use `Surv.counting(start, stop, event)`.
- **Interval-censored**: The event occurred within a time interval `[lower, upper)`. Use `Surv.interval(lower, upper)`.
- **Multi-state / competing risks**: Multiple mutually exclusive events. Use `Surv.multistate(time, event, states)`.

**Use the class methods** ([right](Surv.md#greenwood.Surv.right), [left](Surv.md#greenwood.Surv.left), [counting](Surv.md#greenwood.Surv.counting), [interval](Surv.md#greenwood.Surv.interval), [multistate](Surv.md#greenwood.Surv.multistate)) to construct [Surv](Surv.md#greenwood.Surv) objects. They validate your input and set the censoring type appropriately. As such, direct instantiation is not recommended.


## Attributes


`type: CensoringType`  
The [CensoringType](CensoringType.md#greenwood.CensoringType) enum indicating the censoring mechanism.

`stop: Array`  
Exit time (for interval censoring, the upper bound).

`status: Array`  
Integer event code per observation: 0 = censored, 1+ = event code (for multi-state, codes \>= 1 index into `states`).

`start: Array | None`  
Entry time for the counting-process form (left truncation); `None` otherwise.

`lower: Array | None`  
Lower bound for interval censoring; `None` otherwise.

`states: tuple[str, …] | None`  
Event-state labels for multi-state/competing-risks endpoints; `None` for the single-event case.

`weights: Array | None`  
Optional case weights (strictly positive); `None` if no weights provided.


## Examples

Here's an example of direct instantiation of [Surv](Surv.md#greenwood.Surv):


``` python
import greenwood as gw
import numpy as np

y = gw.Surv(
    type=gw.CensoringType.RIGHT,
    stop=np.array([5, 6, 4, 9]),
    status=np.array([1, 0, 1, 0])
)
y
```


    Surv(type=right, n=4, events=2)


While this is fine, the preferred approach is to use the class method constructors for each censoring type. They handle validation and conversion automatically.

Right-censored (the most common case): each subject has an exit time and an event indicator.


``` python
y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y
```


    Surv(type=right, n=4, events=2)


Counting-process form with left truncation (late entry):


``` python
y = gw.Surv.counting(start=[0, 2, 1], stop=[5, 6, 4], event=[1, 0, 1])
y
```


    Surv(type=counting, n=3, events=2, truncated)


Interval-censored (event known to occur in a time window):


``` python
y = gw.Surv.interval(lower=[1, 3], upper=[3, 8])
y
```


    Surv(type=interval, n=2, events=2)


Multi-state (competing risks, multiple mutually exclusive events):


``` python
y = gw.Surv.multistate(time=[5, 6, 4], event=[1, 0, 2], states=("pcm", "death"))
y
```


    Surv(type=right, n=3, events=2, states=('pcm', 'death'))


## Attributes

| Name | Description |
|----|----|
| [entry](#entry) | Entry times for each observation (or \\-\infty\\ if no left truncation). |
| [event](#event) | Boolean event indicator: True if any event occurred, False if censored. |
| [is_multistate](#is_multistate) | Whether the response has multiple competing event states. |
| [is_truncated](#is_truncated) | Whether the response has left truncation (late entry). |
| [n](#n) | Number of observations in the response. |
| [n_censored](#n_censored) | Count of censored observations. |
| [n_events](#n_events) | Count of observations where an event occurred (any state in multi-state data). |

------------------------------------------------------------------------


#### entry


Entry times for each observation (or \\-\infty\\ if no left truncation).


`entry: Array`


For counting-process data (late entry), this returns the `start` time when each subject became at risk. For standard right-censored data with no left truncation, all values are \\-\infty\\, indicating subjects entered at the beginning of follow-up.


##### Examples

Right-censored data (no left truncation) has all \\-\infty\\ entry times:


``` python
import greenwood as gw

y_right = gw.Surv.right(time=[5, 6, 4], event=[1, 0, 1])
y_right.entry
```


    array([-inf, -inf, -inf])


Counting-process data shows each subject's entry time:


``` python
import greenwood as gw

y_counting = gw.Surv.counting(start=[0, 2, 1], stop=[5, 6, 4], event=[1, 0, 1])
y_counting.entry
```


    array([0., 2., 1.])


The [entry](Surv.md#greenwood.Surv.entry) property is primarily used internally by survival estimators to correctly compute risk sets. You rarely need it directly, but it's available for custom analyses.

------------------------------------------------------------------------


#### event


Boolean event indicator: True if any event occurred, False if censored.


`event: Array`


Converts the integer `status` codes to a simple boolean: 1 or more -\> True (event), 0 -\> False (censored). This is a convenient summary when you only care about event occurrence, not which specific state occurred in multi-state data.


##### Examples


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y.event
```


    array([ True, False,  True, False])


The `True`/`False` values indicate which subjects experienced any event. This is useful for filtering, counting events, or checking data quality. For multi-state data, this collapses all states into a single "any event" indicator:


``` python
import greenwood as gw

y_multi = gw.Surv.multistate(
    time=[5, 6, 7, 8],
    event=[1, 2, 0, 1],
    states=("relapse", "death")
)

y_multi.event
```


    array([ True,  True, False,  True])


------------------------------------------------------------------------


#### is_multistate


Whether the response has multiple competing event states.


`is_multistate: bool`


Multi-state responses track which of several competing outcomes occurred (e.g., "relapse" vs. "death"). When False, there is only one event type (censored or not). When True, the `states` property contains the outcome labels.


##### Examples

Right-censored data has a single outcome:


``` python
import greenwood as gw

y_right = gw.Surv.right(time=[5, 6, 4], event=[1, 0, 1])
y_right.is_multistate
```


    False


Multi-state data with competing risks:


``` python
y_multi = gw.Surv.multistate(
    time=[5, 6, 7, 8],
    event=[1, 2, 0, 1],
    states=("relapse", "death")
)

y_multi.is_multistate
```


    True


This property is useful for determining how to interpret the event codes and what kind of survival estimation is needed.

------------------------------------------------------------------------


#### is_truncated


Whether the response has left truncation (late entry).


`is_truncated: bool`


Left truncation occurs in counting-process data when subjects enter the risk set at different times (late entry). This is common in studies with age-based entry or complex follow-up patterns. When True, the [entry()](Surv.md#greenwood.Surv.entry) property contains the actual start times; when False, all subjects implicitly start at time 0.


##### Examples

Right-censored data has no left truncation:


``` python
import greenwood as gw

y_right = gw.Surv.right(time=[5, 6, 4], event=[1, 0, 1])
y_right.is_truncated
```


    False


Counting-process data with late entry is truncated:


``` python
y_counting = gw.Surv.counting(start=[0, 2, 1], stop=[5, 6, 4], event=[1, 0, 1])
y_counting.is_truncated
```


    True


This property is useful for understanding data structure and for conditional logic that handles truncated vs. non-truncated data differently.

------------------------------------------------------------------------


#### n


Number of observations in the response.


`n: int`


Returns the total count of subjects/observations, regardless of event status. Equivalent to `len(surv_object)`.


##### Examples


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y.n
```


    4


This is useful for loops, validation, or allocating arrays. Often used to determine sample size or for sanity checks on data shape.

------------------------------------------------------------------------


#### n_censored


Count of censored observations.


`n_censored: int`


Counts all observations where the event was not observed (status == 0). These are subjects whose true event time is unknown but exceeds their observation time.


##### Examples


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y.n_censored
```


    2


Often used for descriptive summary: "We observed 2 events and 2 censored subjects out of 4 total." Can validate data quality:


``` python
assert y.n_events + y.n_censored == y.n
```


Higher censoring rates reduce the information available for estimation and may require larger sample sizes for stable inference.

------------------------------------------------------------------------


#### n_events


Count of observations where an event occurred (any state in multi-state data).


`n_events: int`


Counts all observations with `status >= 1`. For multi-state responses, this counts all events regardless of which specific state occurred. For single-event data, this is the count of subjects who experienced the event.


##### Examples


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y.n_events
```


    2


This is useful for descriptive statistics, event rate calculations, or validating data: `assert y.n_events + y.n_censored == y.n`.

For multi-state data, this gives the total event count across all states:


``` python
y_multi = gw.Surv.multistate(
    time=[5, 6, 7, 8],
    event=[1, 2, 0, 1],
    states=("relapse", "death")
)
y_multi.n_events
```


    3


## Methods

| Name | Description |
|----|----|
| [counting()](#counting) | Counting-process response: track subjects entering and exiting the risk set at |
| [from_dict()](#from_dict) | Rebuild a response from [to_dict](Surv.md#greenwood.Surv.to_dict) output. |
| [from_json()](#from_json) | Deserialize from [to_json](Surv.md#greenwood.Surv.to_json) output. |
| [interval()](#interval) | Interval-censored response: event time is known to lie within a range. |
| [left()](#left) | Left-censored response: event occurred before the observation time. |
| [multistate()](#multistate) | Multi-state or competing-risks response: track which of multiple outcomes occurs. |
| [right()](#right) | Right-censored response: the standard and most common form of survival data. |
| [to_dict()](#to_dict) | Return a JSON-ready mapping fully describing the response. |
| [to_frame()](#to_frame) | Return the response as a DataFrame (one row per observation). |
| [to_json()](#to_json) | Serialize to a deterministic JSON string. |

------------------------------------------------------------------------


#### counting()


Counting-process response: track subjects entering and exiting the risk set at


Usage

``` python
counting(start, stop, event=None, *, weights=None)
```


different times.

The counting-process form handles two important real-world complexities:

1.  **Late entry (left truncation)**: Not all subjects start being at risk at time 0. For example, a study might enroll subjects at different ages, or you might analyze a subset of follow-up time after some subjects are already older. The `start` time marks when each subject becomes eligible to experience the event.

2.  **Time-varying covariates**: The counting-process form naturally accommodates covariates that change over time. Each row represents one interval of time for a subject, allowing you to track how covariate values change.

Each subject contributes one or more (start, stop\] intervals. The subject is at risk only during their interval(s) and cannot experience the event before entering at `start`.


##### Parameters


`start: Any`  
Entry times (when each subject becomes at risk). Must be finite and non-negative. Represents when the subject enters the risk set. In standard studies, this is 0; in studies with late entry, it's the age/time at enrollment.

`stop: Any`  
Exit times (when follow-up ends). Must be finite, non-negative, and strictly greater than the corresponding `start`. Represents when the subject leaves follow-up (event, censoring, or end of study).

`event: Any = None`  
Event indicators (1 = event occurred, 0 = censored at `stop` time). If `None`, all subjects are treated as having experienced the event.

`weights: Any = None`  
Case weights (strictly positive, one per subject). Used to weight subjects differently in survival analysis. Default is `None` (all weights = 1).


##### Returns


`Surv`  
A counting-process [Surv](Surv.md#greenwood.Surv) response object with potential left truncation.


##### Examples

Here we have 3 subjects with different entry times:


``` python
import greenwood as gw

y = gw.Surv.counting(start=[0, 2, 1], stop=[5, 6, 4], event=[1, 0, 1])
y
```


    Surv(type=counting, n=3, events=2, truncated)


The display shows:

- Subject 1: Entered at time 0, exited with an event at time 5
- Subject 2: Entered at time 2 (late entry), exited censored at time 6
- Subject 3: Entered at time 1, experienced an event at time 4

Only subjects 2 and 3 benefit from the late entry handling, but the counting-process form elegantly handles all cases uniformly. This representation is also essential for studies with time-varying covariates, where you create multiple rows per subject as their covariate values change.

------------------------------------------------------------------------


#### from_dict()


Rebuild a response from [to_dict](Surv.md#greenwood.Surv.to_dict) output.


Usage

``` python
from_dict(data)
```


This is the inverse of [to_dict()](Surv.md#greenwood.Surv.to_dict): it reconstructs an equivalent [Surv](Surv.md#greenwood.Surv) object from a dictionary previously created by [to_dict()](Surv.md#greenwood.Surv.to_dict). Useful for deserializing stored or transmitted data, or for round-tripping through storage formats.


##### Parameters


`data: dict[str, Any]`  
A dictionary produced by [to_dict()](Surv.md#greenwood.Surv.to_dict) containing keys `type`, `stop`, `status`, and optional keys for `start`, `lower`, `states`, `weights`.


##### Returns


`Surv`  
A new [Surv](Surv.md#greenwood.Surv) object with the same data and structure as the input dictionary.


##### Examples

Rebuild an equivalent response from its dictionary representation. Here we serialize a response and immediately deserialize it:


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
reconstructed = gw.Surv.from_dict(y.to_dict())
print("Objects equal:", y.to_dict() == reconstructed.to_dict())
```


    Objects equal: True


The reconstructed object is equivalent to the original in every way.

------------------------------------------------------------------------


#### from_json()


Deserialize from [to_json](Surv.md#greenwood.Surv.to_json) output.


Usage

``` python
from_json(text)
```


This is the inverse of [to_json()](Surv.md#greenwood.Surv.to_json): it reconstructs a [Surv](Surv.md#greenwood.Surv) object from a JSON string previously created by [to_json()](Surv.md#greenwood.Surv.to_json). Useful for loading data from stored files, API responses, or any other JSON source. The reconstructed object is guaranteed to be equivalent to the original.


##### Parameters


`text: str`  
A JSON string produced by [to_json()](Surv.md#greenwood.Surv.to_json) containing the serialized [Surv](Surv.md#greenwood.Surv) data.


##### Returns


<a href="Surv.html#greenwood.Surv" class="gdls-link gdls-code"><code>Surv</code></a>  
A new [Surv](Surv.md#greenwood.Surv) object restored from the JSON representation.


##### Examples

Deserialize from JSON. Build a response, then round-trip through [to_json()](Surv.md#greenwood.Surv.to_json) and back:


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
json_text = y.to_json()
restored = gw.Surv.from_json(json_text)
print("Round-trip successful:", y.to_json() == restored.to_json())
```


    Round-trip successful: True


The restored object is an exact copy of the original [Surv](Surv.md#greenwood.Surv) object.

------------------------------------------------------------------------


#### interval()


Interval-censored response: event time is known to lie within a range.


Usage

``` python
interval(lower, upper, *, weights=None)
```


Interval censoring occurs when you know the event happened sometime between two observation times, but not exactly when. Common in:

- **Medical follow-up**: Disease detection between clinic visits. You might know a patient's disease status at two checkups, but not the exact time of onset.
- **Equipment reliability**: Failure detected between inspections. You know failure happened between the last working inspection and the current failed one.
- **Longitudinal surveys**: Event reported between survey waves but exact timing unknown.

The interval-censored form captures this uncertainty. The event happened somewhere in the interval (lower, upper\]. If lower == upper, it's an exact (uncensored) event. Use infinity for upper to represent right-censoring, and 0 for lower to represent left-censoring.


##### Parameters


`lower: Any`  
Interval lower bounds (one per subject). Must be finite and non-negative. Event happened *after* this time (possibly at this time). Set to 0 to mark left-censored subjects (event happened before first observation).

`upper: Any`  
Interval upper bounds (one per subject). Must be finite, non-negative, and \>= `lower`. Event happened *by* this time. Set to `numpy.inf` to mark right-censored subjects (no event observed by end of study).

`weights: Any = None`  
Case weights (strictly positive, one per subject). Used to weight subjects differently in survival analysis. Default is `None` (all weights = 1).


##### Returns


`Surv`  
An interval-censored [Surv](Surv.md#greenwood.Surv) response object.


##### Examples

Here we have 3 subjects with different levels of observation precision:


``` python
import greenwood as gw
import numpy as np

y = gw.Surv.interval(lower=[1, 2, 3], upper=[2, np.inf, 5])
y
```


    Surv(type=interval, n=3, events=2)


The display shows: - Subject 1: Exact event at time 2 (lower == upper) - Subject 2: Right-censored at time 2 (upper = infinity means event never observed) - Subject 3: Interval-censored between times 3 and 5 (event happened somewhere in that window)

Interval censoring gives you more information than right censoring alone. Rather than just knowing "no event by time X," you may know "event was definitely before time Y but after time X," which allows for more precise estimation when multiple observations bracket the event.


##### See Also

[left()](Surv.md#greenwood.Surv.left)  
Event occurred before the observation time.

[right()](Surv.md#greenwood.Surv.right)  
Event occurred after the observation time.

[counting()](Surv.md#greenwood.Surv.counting)  
Track subjects entering and exiting at different times.

------------------------------------------------------------------------


#### left()


Left-censored response: event occurred before the observation time.


Usage

``` python
left(time, event=None, *, weights=None)
```


Left censoring occurs when all you know is that an event happened *before* you observed the subject. For example, an infection that must have occurred before a patient was tested, or a failure that was known to have happened sometime before equipment was inspected. The exact event time is unknown, but you know it was no later than the recorded `time`.

This is less common than right censoring, but important in scenarios where you cannot pinpoint when something happened, only that it already had.


##### Parameters


`time: Any`  
Observation times (the upper bound on when the event occurred). Must be finite and non-negative. Each value represents "the event happened by this time".

`event: Any = None`  
Event indicators:

- 1 = event occurred before `time` (left-censored)
- 0 = subject was event-free at `time` (not censored)

If `None`, all subjects are treated as having experienced the event.

`weights: Any = None`  
Case weights (strictly positive, one per subject). Used to weight subjects differently in survival analysis. Default is `None` (all weights = 1).


##### Returns


`Surv`  
A left-censored [Surv](Surv.md#greenwood.Surv) response object.


##### Examples

Here we have 3 subjects. Two experienced the event before the recorded time (event=1), and one was event-free at observation (event=0):


``` python
import greenwood as gw

y = gw.Surv.left(time=[5, 6, 4], event=[1, 0, 1])
y
```


    Surv(type=left, n=3, events=2)


The display shows the data structure:

- The `<` symbol indicates left-censored observations (event occurred before time)
- The `+` symbol indicates subjects who were still event-free at the observation time
- The left-censoring type `"left"` is displayed at the top


##### See Also

[right()](Surv.md#greenwood.Surv.right)  
Right-censored response (event after the observation time).

[counting()](Surv.md#greenwood.Surv.counting)  
Time intervals with late entry.

[interval()](Surv.md#greenwood.Surv.interval)  
Event lies in a known interval.

------------------------------------------------------------------------


#### multistate()


Multi-state or competing-risks response: track which of multiple outcomes occurs.


Usage

``` python
multistate(time, event, states, *, start=None, weights=None)
```


Real-world studies often involve multiple competing outcomes. A patient in a cancer study might relapse, die from cancer, or die from other causes. Each subject can only experience one outcome, and once it happens, no other outcome is possible.

The multi-state framework elegantly handles this by:

1.  **Defining possible states**: You specify the labeled outcomes (e.g., "relapse", "death from cancer", "death from other causes") that are mutually exclusive.
2.  **Recording which state occurred**: Rather than a simple 0/1 event, you record which specific state the subject transitioned to (or 0 if censored).
3.  **Separate risk estimation**: You can estimate the risk of each state independently, accounting for the fact that other states prevent each outcome.

This is essential for realistic survival modeling: accounting for competing risks often substantially changes the estimated risk curves compared to treating all non-events identically.


##### Parameters


`time: Any`  
Event or censoring times (one per subject). Must be finite and non-negative. Represents when the subject experienced an outcome (or was censored).

`event: Any`  
Event codes indicating which state occurred:

- 0 = censored (no event observed)
- 1 = transitioned to states\[0\] (first outcome)
- 2 = transitioned to states\[1\] (second outcome)
- … and so on for each defined state

Must be in range \[0, len(states)\].

`states: tuple[str, …]`  
Labels for the possible outcomes. Event codes index into this tuple. Example: states=("relapse", "death") means:

- event code 1 → relapse occurred
- event code 2 → death occurred

Labels are arbitrary strings describing what the transition represents.

`start: array - like = None`  
Optional entry times (for late entry / left truncation). If provided, each subject is only at risk from `start` until `time`. Default is `None` (all subjects enter at time 0).

`weights: array - like = None`  
Case weights (strictly positive, one per subject). Used to weight subjects differently in survival analysis. Default is `None` (all weights = 1).


##### Returns


`Surv`  
A multi-state / competing-risks [Surv](Surv.md#greenwood.Surv) response object.


##### Examples

Here we have 4 subjects with 2 competing outcomes (relapse and death):


``` python
import greenwood as gw

y = gw.Surv.multistate(
    time=[5, 6, 7, 8],
    event=[1, 2, 0, 1],
    states=("relapse", "death")
)

y
```


    Surv(type=right, n=4, events=3, states=('relapse', 'death'))


The display shows:

- Subject 1: Transitioned to "relapse" (event code 1) at time 5
- Subject 2: Transitioned to "death" (event code 2) at time 6
- Subject 3: Censored (event code 0) at time 7
- Subject 4: Transitioned to "relapse" (event code 1) at time 8

You can then estimate the probability of each outcome separately, capturing the full picture: not just "will something happen?" but "which specific outcome is most likely?" This avoids the bias of artificially grouping competing outcomes together.


##### See Also

[right()](Surv.md#greenwood.Surv.right)  
Simple right-censored [Surv](Surv.md#greenwood.Surv) response object (only one possible outcome).

[counting()](Surv.md#greenwood.Surv.counting)  
Time intervals with late entry.

[left()](Surv.md#greenwood.Surv.left)  
Event occurred before the observation time.

------------------------------------------------------------------------


#### right()


Right-censored response: the standard and most common form of survival data.


Usage

``` python
right(time, event=None, *, weights=None)
```


Right censoring is the default in survival analysis. It occurs when follow-up ends before the event happens (a subject is still event-free when we last observed them). This is the most common censoring mechanism in practice:

- **Study ends**: A clinical trial concludes while some patients are still healthy
- **Loss to follow-up**: A subject drops out, moves away, or stops visiting the clinic
- **Administrative censoring**: Follow-up ends at a fixed time regardless of status

Right censoring is called "censoring from the right" because we know the event happened *after* the censoring time. We record that a subject was event-free at their last observation but don't know how much longer they could have gone.

This simple form assumes all subjects enter follow-up at the same reference time (typically time 0). If subjects enter at different times or follow-up is complex, use [counting()](Surv.md#greenwood.Surv.counting) or [interval()](Surv.md#greenwood.Surv.interval) instead.


##### Parameters


`time: Any`  
Exit times when follow-up ends (one per subject). Must be finite and non-negative. This is the time of either the event or censoring, whichever came first.

`event: Any = None`  
Event indicators:

- 1 = event occurred (fully observed)
- 0 = censored (event time unknown but \> time)

If `None`, all subjects are treated as having experienced the event (useful for testing or descriptive purposes).

`weights: Any = None`  
Case weights (strictly positive, one per subject). Used to weight subjects differently in survival analysis (e.g., inverse probability weighting). Default is `None` (all weights = 1).


##### Returns


`Surv`  
A right-censored [Surv](Surv.md#greenwood.Surv) response object (the most common type).


##### Examples

The most common case: subjects have an exit `time` and an [event](Surv.md#greenwood.Surv.event) indicator (1 if event occurred, 0 if censored):


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y
```


    Surv(type=right, n=4, events=2)


The display shows 4 observations with 2 events and 2 censored observations:

- Subjects 1 and 3: Event observed (no marker or `*` depending on visualization)
- Subjects 2 and 4: Censored (indicated by `+` marker; still event-free at times 6 and 9)

This is the default input format for nearly all survival analysis methods. Right-censored data is so ubiquitous that "survival data" often refers specifically to right-censored observations.


##### See Also

[left()](Surv.md#greenwood.Surv.left)  
Event occurred before the observation time.

[interval()](Surv.md#greenwood.Surv.interval)  
Event time is known to lie in a range.

[counting()](Surv.md#greenwood.Surv.counting)  
Late entry and time-varying covariates.

[multistate()](Surv.md#greenwood.Surv.multistate)  
Track multiple competing outcomes.

------------------------------------------------------------------------


#### to_dict()


Return a JSON-ready mapping fully describing the response.


Usage

``` python
to_dict()
```


This method serializes the entire [Surv](Surv.md#greenwood.Surv) object into a plain Python dictionary, making it suitable for JSON serialization, storage, or transmission. All array data is converted to plain Python lists. The dictionary captures the censoring type and every array (time, status, optional fields), with `None` for fields that a given censoring flavor does not use.


##### Returns


`dict[str, Any]`  
A dictionary with keys: `type` (CensoringType as string), `stop`, `status`, and optional keys `start`, `lower`, `states`, `weights` (as lists or None).


##### Examples

The mapping structure varies by censoring type, but always includes `type`, `stop`, and `status`. Unused fields are `None`. Here we build a response and convert it to a dictionary:


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y.to_dict()
```


    {'type': 'right',
     'stop': [5.0, 6.0, 4.0, 9.0],
     'status': [1, 0, 1, 0],
     'start': None,
     'lower': None,
     'states': None,
     'weights': None}


This is the serialized form that underpins [to_json()](Surv.md#greenwood.Surv.to_json) and enables round-tripping via [from_dict()](Surv.md#greenwood.Surv.from_dict).

------------------------------------------------------------------------


#### to_frame()


Return the response as a DataFrame (one row per observation).


Usage

``` python
to_frame(*, format=None)
```


Exports the [Surv](Surv.md#greenwood.Surv) object to a tidy table where each row represents one observation. The table includes the `stop` and `status` columns, plus optional columns for `start` (entry time in counting-process form), `lower` (lower bound for interval censoring), and `weight` (case weights). This is convenient for inspection, export to CSV, or integration with other DataFrame workflows.


##### Parameters


`format: str | None = None`  
Output format: `None` (default), `"pandas"`, `"polars"`, or `"pyarrow"`. When `None`, a backend is auto-detected (Polars, then Pandas, then PyArrow).


##### Returns


`pandas.DataFrame, polars.DataFrame, or pyarrow.Table`  
A tidy table with one row per observation, including columns for `stop`, `status`, and optional `start`, `lower`, `weight` columns.


##### Raises


`ImportError`  
If the requested (or, when auto-detecting, any) DataFrame library is not installed.


##### Examples

Build a right-censored response and export it as a Polars frame. Each row represents one observation with its event time and status:


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
y.to_frame(format="polars")
```


shape: (4, 2)

| stop | status |
|------|--------|
| f64  | i64    |
| 5.0  | 1      |
| 6.0  | 0      |
| 4.0  | 1      |
| 9.0  | 0      |


Request a different backend with `format=`:


``` python
y.to_frame(format="pandas")
```


|     | stop | status |
|-----|------|--------|
| 0   | 5.0  | 1      |
| 1   | 6.0  | 0      |
| 2   | 4.0  | 1      |
| 3   | 9.0  | 0      |


------------------------------------------------------------------------


#### to_json()


Serialize to a deterministic JSON string.


Usage

``` python
to_json(*, indent=2)
```


This method converts the entire [Surv](Surv.md#greenwood.Surv) object to a compact, JSON-formatted string suitable for storage in files, databases, or transmission over APIs. By default, the output is human-readable with indentation; pass `indent=None` for a compact form. The serialization is deterministic: the same [Surv](Surv.md#greenwood.Surv) object always produces the identical JSON string.


##### Parameters


`indent: int | None = ``2`  
Number of spaces to use for indentation. If `None`, produces compact JSON without whitespace. Default is 2 (human-readable).


##### Returns


`str`  
A JSON string representing the [Surv](Surv.md#greenwood.Surv) object, including censoring type and all arrays.


##### Examples

Serialize to JSON. By default, output is indented for readability. Here we build a response and show just the first 120 characters of compact JSON:


``` python
import greenwood as gw

y = gw.Surv.right(time=[5, 6, 4, 9], event=[1, 0, 1, 0])
json_compact = y.to_json(indent=None)
print(json_compact[:120])
```


    {"type": "right", "stop": [5.0, 6.0, 4.0, 9.0], "status": [1, 0, 1, 0], "start": null, "lower": null, "states": null, "w


The full JSON includes all data in a structured format that can be parsed back with [from_json()](Surv.md#greenwood.Surv.from_json).


#### See Also

- [right()](right.md): Simple right-censored response (all subjects start at time 0).
- [interval()](interval.md): Event lies in a known interval.
- [multistate()](multistate.md): Track transitions to multiple competing states.
