Skip to content

Data

Package


data

psyphy.data

submodule for handling psychophysical experiment data.

Includes: - dataset: ResponseData, TrialBatch, loaders - transforms: color/model space conversions - io: save/load datasets

Classes:

Name Description
ResponseData

Container for psychophysical trial data.

TrialBatch

Container for a proposed batch of trials

ResponseData

ResponseData()

Container for psychophysical trial data.

Attributes:

Name Type Description
refs List[Any]

List of reference stimuli.

comparisons List[Any]

List of comparison stimuli.

responses List[int]

List of subject responses (e.g., 0/1 or categorical).

Methods:

Name Description
add_batch

Append responses for a batch of trials.

add_trial

append a single trial.

copy

Create a deep copy of this dataset.

from_arrays

Construct ResponseData from arrays.

merge

Merge another dataset into this one (in-place).

tail

Return last n trials as a new ResponseData.

to_numpy

Return refs, comparisons, responses as numpy arrays.

Source code in src/psyphy/data/dataset.py
def __init__(self) -> None:
    self.refs: list[Any] = []
    self.comparisons: list[Any] = []
    self.responses: list[int] = []

comparisons

comparisons: list[Any] = []

refs

refs: list[Any] = []

responses

responses: list[int] = []

trials

trials: list[tuple[Any, Any, int]]

Return list of (ref, comparison, response) tuples.

Returns:

Type Description
list[tuple]

Each element is (ref, comparison, resp)

add_batch

add_batch(
    responses: list[int], trial_batch: TrialBatch
) -> None

Append responses for a batch of trials.

Parameters:

Name Type Description Default
responses List[int]

Responses corresponding to each (ref, comparison) in the trial batch.

required
trial_batch TrialBatch

The batch of proposed trials.

required
Source code in src/psyphy/data/dataset.py
def add_batch(self, responses: list[int], trial_batch: TrialBatch) -> None:
    """
    Append responses for a batch of trials.

    Parameters
    ----------
    responses : List[int]
        Responses corresponding to each (ref, comparison) in the trial batch.
    trial_batch : TrialBatch
        The batch of proposed trials.
    """
    for (ref, comparison), resp in zip(trial_batch.stimuli, responses):
        self.add_trial(ref, comparison, resp)

add_trial

add_trial(ref: Any, comparison: Any, resp: int) -> None

append a single trial.

Parameters:

Name Type Description Default
ref Any

Reference stimulus (numpy array, list, etc.)

required
comparison Any

Probe stimulus

required
resp int

Subject response (binary or categorical)

required
Source code in src/psyphy/data/dataset.py
def add_trial(self, ref: Any, comparison: Any, resp: int) -> None:
    """
    append a single trial.

    Parameters
    ----------
    ref : Any
        Reference stimulus (numpy array, list, etc.)
    comparison : Any
        Probe stimulus
    resp : int
        Subject response (binary or categorical)
    """
    self.refs.append(ref)
    self.comparisons.append(comparison)
    self.responses.append(resp)

copy

copy() -> ResponseData

Create a deep copy of this dataset.

Returns:

Type Description
ResponseData

New dataset with copied data

Source code in src/psyphy/data/dataset.py
def copy(self) -> ResponseData:
    """
    Create a deep copy of this dataset.

    Returns
    -------
    ResponseData
        New dataset with copied data
    """
    new_data = ResponseData()
    new_data.refs = list(self.refs)
    new_data.comparisons = list(self.comparisons)
    new_data.responses = list(self.responses)
    return new_data

from_arrays

from_arrays(
    X: ndarray | ndarray,
    y: ndarray | ndarray,
    *,
    comparisons: ndarray | ndarray | None = None,
) -> ResponseData

Construct ResponseData from arrays.

Parameters:

Name Type Description Default
X (array, shape(n_trials, 2, input_dim) or (n_trials, input_dim))

Stimuli. If 3D, second axis is [reference, comparison]. If 2D, comparisons must be provided separately.

required
y (array, shape(n_trials))

Responses

required
comparisons (array, shape(n_trials, input_dim))

Probe stimuli. Only needed if X is 2D.

None

Returns:

Type Description
ResponseData

Data container

Examples:

1
2
3
4
>>> # From paired stimuli
>>> X = jnp.array([[[0, 0], [1, 0]], [[1, 1], [2, 1]]])
>>> y = jnp.array([1, 0])
>>> data = ResponseData.from_arrays(X, y)
1
2
3
4
>>> # From separate refs and comparisons
>>> refs = jnp.array([[0, 0], [1, 1]])
>>> comparisons = jnp.array([[1, 0], [2, 1]])
>>> data = ResponseData.from_arrays(refs, y, comparisons=comparisons)
Source code in src/psyphy/data/dataset.py
@classmethod
def from_arrays(
    cls,
    X: jnp.ndarray | np.ndarray,
    y: jnp.ndarray | np.ndarray,
    *,
    comparisons: jnp.ndarray | np.ndarray | None = None,
) -> ResponseData:
    """
    Construct ResponseData from arrays.

    Parameters
    ----------
    X : array, shape (n_trials, 2, input_dim) or (n_trials, input_dim)
        Stimuli. If 3D, second axis is [reference, comparison].
        If 2D, comparisons must be provided separately.
    y : array, shape (n_trials,)
        Responses
    comparisons : array, shape (n_trials, input_dim), optional
        Probe stimuli. Only needed if X is 2D.

    Returns
    -------
    ResponseData
        Data container

    Examples
    --------
    >>> # From paired stimuli
    >>> X = jnp.array([[[0, 0], [1, 0]], [[1, 1], [2, 1]]])
    >>> y = jnp.array([1, 0])
    >>> data = ResponseData.from_arrays(X, y)

    >>> # From separate refs and comparisons
    >>> refs = jnp.array([[0, 0], [1, 1]])
    >>> comparisons = jnp.array([[1, 0], [2, 1]])
    >>> data = ResponseData.from_arrays(refs, y, comparisons=comparisons)
    """
    data = cls()

    X = np.asarray(X)
    y = np.asarray(y)

    if X.ndim == 3:
        # X is (n_trials, 2, input_dim)
        refs = X[:, 0, :]
        comparisons_arr = X[:, 1, :]
    elif X.ndim == 2 and comparisons is not None:
        refs = X
        comparisons_arr = np.asarray(comparisons)
    else:
        raise ValueError(
            "X must be shape (n_trials, 2, input_dim) or "
            "(n_trials, input_dim) with comparisons argument"
        )

    for ref, comparison, response in zip(refs, comparisons_arr, y):
        data.add_trial(ref, comparison, int(response))

    return data

merge

merge(other: ResponseData) -> None

Merge another dataset into this one (in-place).

Parameters:

Name Type Description Default
other ResponseData

Dataset to merge

required
Source code in src/psyphy/data/dataset.py
def merge(self, other: ResponseData) -> None:
    """
    Merge another dataset into this one (in-place).

    Parameters
    ----------
    other : ResponseData
        Dataset to merge
    """
    self.refs.extend(other.refs)
    self.comparisons.extend(other.comparisons)
    self.responses.extend(other.responses)

tail

tail(n: int) -> ResponseData

Return last n trials as a new ResponseData.

Parameters:

Name Type Description Default
n int

Number of trials to keep

required

Returns:

Type Description
ResponseData

New dataset with last n trials

Source code in src/psyphy/data/dataset.py
def tail(self, n: int) -> ResponseData:
    """
    Return last n trials as a new ResponseData.

    Parameters
    ----------
    n : int
        Number of trials to keep

    Returns
    -------
    ResponseData
        New dataset with last n trials
    """
    new_data = ResponseData()
    new_data.refs = self.refs[-n:]
    new_data.comparisons = self.comparisons[-n:]
    new_data.responses = self.responses[-n:]
    return new_data

to_numpy

to_numpy() -> tuple[ndarray, ndarray, ndarray]

Return refs, comparisons, responses as numpy arrays.

Returns:

Name Type Description
refs ndarray
comparisons ndarray
responses ndarray
Source code in src/psyphy/data/dataset.py
def to_numpy(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    Return refs, comparisons, responses as numpy arrays.

    Returns
    -------
    refs : np.ndarray
    comparisons : np.ndarray
    responses : np.ndarray
    """
    return (
        np.array(self.refs),
        np.array(self.comparisons),
        np.array(self.responses),
    )

TrialBatch

TrialBatch(stimuli: list[tuple[Any, Any]])

Container for a proposed batch of trials

Attributes:

Name Type Description
stimuli List[Tuple[Any, Any]]

Each trial is a (reference, comparison) tuple.

Methods:

Name Description
from_stimuli

Construct a TrialBatch from a list of stimuli (ref, comparison) pairs.

Source code in src/psyphy/data/dataset.py
def __init__(self, stimuli: list[tuple[Any, Any]]) -> None:
    self.stimuli = list(stimuli)

stimuli

stimuli = list(stimuli)

from_stimuli

from_stimuli(pairs: list[tuple[Any, Any]]) -> TrialBatch

Construct a TrialBatch from a list of stimuli (ref, comparison) pairs.

Source code in src/psyphy/data/dataset.py
@classmethod
def from_stimuli(cls, pairs: list[tuple[Any, Any]]) -> TrialBatch:
    """
    Construct a TrialBatch from a list of stimuli (ref, comparison) pairs.
    """
    return cls(pairs)

Data Containers for Response Data and Proposed Next Trials


dataset

dataset.py

Core data containers for psyphy.

defines: - ResponseData: container for psychophysical trial data - TrialBatch: container for a proposed batch of trials

Notes
  • Data is stored in standard NumPy (mutable!) arrays or Python lists.
  • Use numpy for I/O and analysis.
  • Convert to jax.numpy (jnp) (immutable!) arrays only when passing into WPPM or inference engines that require JAX/Optax.

Classes:

Name Description
ResponseData

Container for psychophysical trial data.

TrialBatch

Container for a proposed batch of trials

ResponseData

ResponseData()

Container for psychophysical trial data.

Attributes:

Name Type Description
refs List[Any]

List of reference stimuli.

comparisons List[Any]

List of comparison stimuli.

responses List[int]

List of subject responses (e.g., 0/1 or categorical).

Methods:

Name Description
add_batch

Append responses for a batch of trials.

add_trial

append a single trial.

copy

Create a deep copy of this dataset.

from_arrays

Construct ResponseData from arrays.

merge

Merge another dataset into this one (in-place).

tail

Return last n trials as a new ResponseData.

to_numpy

Return refs, comparisons, responses as numpy arrays.

Source code in src/psyphy/data/dataset.py
def __init__(self) -> None:
    self.refs: list[Any] = []
    self.comparisons: list[Any] = []
    self.responses: list[int] = []

comparisons

comparisons: list[Any] = []

refs

refs: list[Any] = []

responses

responses: list[int] = []

trials

trials: list[tuple[Any, Any, int]]

Return list of (ref, comparison, response) tuples.

Returns:

Type Description
list[tuple]

Each element is (ref, comparison, resp)

add_batch

add_batch(
    responses: list[int], trial_batch: TrialBatch
) -> None

Append responses for a batch of trials.

Parameters:

Name Type Description Default
responses List[int]

Responses corresponding to each (ref, comparison) in the trial batch.

required
trial_batch TrialBatch

The batch of proposed trials.

required
Source code in src/psyphy/data/dataset.py
def add_batch(self, responses: list[int], trial_batch: TrialBatch) -> None:
    """
    Append responses for a batch of trials.

    Parameters
    ----------
    responses : List[int]
        Responses corresponding to each (ref, comparison) in the trial batch.
    trial_batch : TrialBatch
        The batch of proposed trials.
    """
    for (ref, comparison), resp in zip(trial_batch.stimuli, responses):
        self.add_trial(ref, comparison, resp)

add_trial

add_trial(ref: Any, comparison: Any, resp: int) -> None

append a single trial.

Parameters:

Name Type Description Default
ref Any

Reference stimulus (numpy array, list, etc.)

required
comparison Any

Probe stimulus

required
resp int

Subject response (binary or categorical)

required
Source code in src/psyphy/data/dataset.py
def add_trial(self, ref: Any, comparison: Any, resp: int) -> None:
    """
    append a single trial.

    Parameters
    ----------
    ref : Any
        Reference stimulus (numpy array, list, etc.)
    comparison : Any
        Probe stimulus
    resp : int
        Subject response (binary or categorical)
    """
    self.refs.append(ref)
    self.comparisons.append(comparison)
    self.responses.append(resp)

copy

copy() -> ResponseData

Create a deep copy of this dataset.

Returns:

Type Description
ResponseData

New dataset with copied data

Source code in src/psyphy/data/dataset.py
def copy(self) -> ResponseData:
    """
    Create a deep copy of this dataset.

    Returns
    -------
    ResponseData
        New dataset with copied data
    """
    new_data = ResponseData()
    new_data.refs = list(self.refs)
    new_data.comparisons = list(self.comparisons)
    new_data.responses = list(self.responses)
    return new_data

from_arrays

from_arrays(
    X: ndarray | ndarray,
    y: ndarray | ndarray,
    *,
    comparisons: ndarray | ndarray | None = None,
) -> ResponseData

Construct ResponseData from arrays.

Parameters:

Name Type Description Default
X (array, shape(n_trials, 2, input_dim) or (n_trials, input_dim))

Stimuli. If 3D, second axis is [reference, comparison]. If 2D, comparisons must be provided separately.

required
y (array, shape(n_trials))

Responses

required
comparisons (array, shape(n_trials, input_dim))

Probe stimuli. Only needed if X is 2D.

None

Returns:

Type Description
ResponseData

Data container

Examples:

1
2
3
4
>>> # From paired stimuli
>>> X = jnp.array([[[0, 0], [1, 0]], [[1, 1], [2, 1]]])
>>> y = jnp.array([1, 0])
>>> data = ResponseData.from_arrays(X, y)
1
2
3
4
>>> # From separate refs and comparisons
>>> refs = jnp.array([[0, 0], [1, 1]])
>>> comparisons = jnp.array([[1, 0], [2, 1]])
>>> data = ResponseData.from_arrays(refs, y, comparisons=comparisons)
Source code in src/psyphy/data/dataset.py
@classmethod
def from_arrays(
    cls,
    X: jnp.ndarray | np.ndarray,
    y: jnp.ndarray | np.ndarray,
    *,
    comparisons: jnp.ndarray | np.ndarray | None = None,
) -> ResponseData:
    """
    Construct ResponseData from arrays.

    Parameters
    ----------
    X : array, shape (n_trials, 2, input_dim) or (n_trials, input_dim)
        Stimuli. If 3D, second axis is [reference, comparison].
        If 2D, comparisons must be provided separately.
    y : array, shape (n_trials,)
        Responses
    comparisons : array, shape (n_trials, input_dim), optional
        Probe stimuli. Only needed if X is 2D.

    Returns
    -------
    ResponseData
        Data container

    Examples
    --------
    >>> # From paired stimuli
    >>> X = jnp.array([[[0, 0], [1, 0]], [[1, 1], [2, 1]]])
    >>> y = jnp.array([1, 0])
    >>> data = ResponseData.from_arrays(X, y)

    >>> # From separate refs and comparisons
    >>> refs = jnp.array([[0, 0], [1, 1]])
    >>> comparisons = jnp.array([[1, 0], [2, 1]])
    >>> data = ResponseData.from_arrays(refs, y, comparisons=comparisons)
    """
    data = cls()

    X = np.asarray(X)
    y = np.asarray(y)

    if X.ndim == 3:
        # X is (n_trials, 2, input_dim)
        refs = X[:, 0, :]
        comparisons_arr = X[:, 1, :]
    elif X.ndim == 2 and comparisons is not None:
        refs = X
        comparisons_arr = np.asarray(comparisons)
    else:
        raise ValueError(
            "X must be shape (n_trials, 2, input_dim) or "
            "(n_trials, input_dim) with comparisons argument"
        )

    for ref, comparison, response in zip(refs, comparisons_arr, y):
        data.add_trial(ref, comparison, int(response))

    return data

merge

merge(other: ResponseData) -> None

Merge another dataset into this one (in-place).

Parameters:

Name Type Description Default
other ResponseData

Dataset to merge

required
Source code in src/psyphy/data/dataset.py
def merge(self, other: ResponseData) -> None:
    """
    Merge another dataset into this one (in-place).

    Parameters
    ----------
    other : ResponseData
        Dataset to merge
    """
    self.refs.extend(other.refs)
    self.comparisons.extend(other.comparisons)
    self.responses.extend(other.responses)

tail

tail(n: int) -> ResponseData

Return last n trials as a new ResponseData.

Parameters:

Name Type Description Default
n int

Number of trials to keep

required

Returns:

Type Description
ResponseData

New dataset with last n trials

Source code in src/psyphy/data/dataset.py
def tail(self, n: int) -> ResponseData:
    """
    Return last n trials as a new ResponseData.

    Parameters
    ----------
    n : int
        Number of trials to keep

    Returns
    -------
    ResponseData
        New dataset with last n trials
    """
    new_data = ResponseData()
    new_data.refs = self.refs[-n:]
    new_data.comparisons = self.comparisons[-n:]
    new_data.responses = self.responses[-n:]
    return new_data

to_numpy

to_numpy() -> tuple[ndarray, ndarray, ndarray]

Return refs, comparisons, responses as numpy arrays.

Returns:

Name Type Description
refs ndarray
comparisons ndarray
responses ndarray
Source code in src/psyphy/data/dataset.py
def to_numpy(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    Return refs, comparisons, responses as numpy arrays.

    Returns
    -------
    refs : np.ndarray
    comparisons : np.ndarray
    responses : np.ndarray
    """
    return (
        np.array(self.refs),
        np.array(self.comparisons),
        np.array(self.responses),
    )

TrialBatch

TrialBatch(stimuli: list[tuple[Any, Any]])

Container for a proposed batch of trials

Attributes:

Name Type Description
stimuli List[Tuple[Any, Any]]

Each trial is a (reference, comparison) tuple.

Methods:

Name Description
from_stimuli

Construct a TrialBatch from a list of stimuli (ref, comparison) pairs.

Source code in src/psyphy/data/dataset.py
def __init__(self, stimuli: list[tuple[Any, Any]]) -> None:
    self.stimuli = list(stimuli)

stimuli

stimuli = list(stimuli)

from_stimuli

from_stimuli(pairs: list[tuple[Any, Any]]) -> TrialBatch

Construct a TrialBatch from a list of stimuli (ref, comparison) pairs.

Source code in src/psyphy/data/dataset.py
@classmethod
def from_stimuli(cls, pairs: list[tuple[Any, Any]]) -> TrialBatch:
    """
    Construct a TrialBatch from a list of stimuli (ref, comparison) pairs.
    """
    return cls(pairs)

Transforms (e.g., from RGB to model space)


transforms

transforms.py

color space transformations.

functions (MVP stubs): - to_model_space(rgb): map RGB stimulus values into model space - to_rgb(model_coords): map from model space back to RGB

Future extensions: - other color spaces

Functions:

Name Description
model_to_stimuli

Map from model space coordinates to RGB values.

stimuli_to_model_space

Map RGB stimulus values to model space coordinates.

Attributes:

Name Type Description
ArrayLike

ArrayLike

ArrayLike = Union[Sequence[float], ndarray]

model_to_stimuli

model_to_stimuli(model_coords: ArrayLike) -> ndarray

Map from model space coordinates to RGB values.

Parameters:

Name Type Description Default
model_coords array - like

Model space coordinates.

required

Returns:

Type Description
ndarray

RGB values (MVP: identical to input).

Source code in src/psyphy/data/transforms.py
def model_to_stimuli(model_coords: ArrayLike) -> np.ndarray:
    """
    Map from model space coordinates to RGB values.

    Parameters
    ----------
    model_coords : array-like
        Model space coordinates.

    Returns
    -------
    np.ndarray
        RGB values (MVP: identical to input).
    """
    return np.array(model_coords)

stimuli_to_model_space

stimuli_to_model_space(rgb: ArrayLike) -> ndarray

Map RGB stimulus values to model space coordinates.

Parameters:

Name Type Description Default
rgb array - like

RGB values, shape (3,) or similar.

required

Returns:

Type Description
ndarray

Model-space coordinates (MVP: identical to input).

Source code in src/psyphy/data/transforms.py
def stimuli_to_model_space(rgb: ArrayLike) -> np.ndarray:
    """
    Map RGB stimulus values to model space coordinates.

    Parameters
    ----------
    rgb : array-like
        RGB values, shape (3,) or similar.

    Returns
    -------
    np.ndarray
        Model-space coordinates (MVP: identical to input).
    """
    return np.array(rgb)

I/O


io

io.py

I/O utilities for saving and loading psyphy data.

Supports: - CSV for human-readable trial logs - Pickle (.pkl) for Posterior and ResponseData checkpoints?

Notes
  • Data is stored in NumPy arrays (via ResponseData.to_numpy()).
  • Convert to jax.numpy when passing into models.

Functions:

Name Description
load_posterior

Load a Posterior object from pickle.

load_responses_csv

Load ResponseData from a CSV file.

save_posterior

Save a Posterior object to disk using pickle.

save_responses_csv

Save ResponseData to a CSV file.

Attributes:

Name Type Description
PathLike

PathLike

PathLike = Union[str, Path]

load_posterior

load_posterior(path: PathLike) -> object

Load a Posterior object from pickle.

Source code in src/psyphy/data/io.py
def load_posterior(path: PathLike) -> object:
    """
    Load a Posterior object from pickle.
    """
    with open(path, "rb") as f:
        return pickle.load(f)

load_responses_csv

load_responses_csv(path: PathLike) -> ResponseData

Load ResponseData from a CSV file.

Parameters:

Name Type Description Default
path str or Path
required

Returns:

Type Description
ResponseData
Source code in src/psyphy/data/io.py
def load_responses_csv(path: PathLike) -> ResponseData:
    """
    Load ResponseData from a CSV file.

    Parameters
    ----------
    path : str or Path

    Returns
    -------
    ResponseData
    """
    data = ResponseData()
    with open(path) as f:
        reader = csv.DictReader(f)
        for row in reader:
            ref = ast.literal_eval(row["ref"])
            probe = ast.literal_eval(row["probe"])
            resp = int(row["response"])
            data.add_trial(ref, probe, resp)
    return data

save_posterior

save_posterior(posterior: object, path: PathLike) -> None

Save a Posterior object to disk using pickle.

Source code in src/psyphy/data/io.py
def save_posterior(posterior: object, path: PathLike) -> None:
    """
    Save a Posterior object to disk using pickle.
    """
    with open(path, "wb") as f:
        pickle.dump(posterior, f)

save_responses_csv

save_responses_csv(
    data: ResponseData, path: PathLike
) -> None

Save ResponseData to a CSV file.

Parameters:

Name Type Description Default
data ResponseData
required
path str or Path
required
Source code in src/psyphy/data/io.py
def save_responses_csv(data: ResponseData, path: PathLike) -> None:
    """
    Save ResponseData to a CSV file.

    Parameters
    ----------
    data : ResponseData
    path : str or Path
    """
    refs, probes, resps = data.to_numpy()
    with open(path, "w", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(["ref", "probe", "response"])
        for r, p, y in zip(refs, probes, resps):
            writer.writerow([r.tolist(), p.tolist(), int(y)])