Skip to content

Background

fit_background(spot_colors, weight_shift=0)

This determines the coefficient of the background vectors for each spot. Coefficients determined using a weighted dot product as to avoid overfitting and accounting for the fact that background coefficients are not updated after this.

Note

background_vectors[i] is 1 in channel i for all rounds and 0 otherwise. It is then normalised to have L2 norm of 1 when summed over all rounds and channels.

Parameters:

Name Type Description Default
spot_colors np.ndarray

float [n_spots x n_rounds x n_channels]. Spot colors normalised to equalise intensities between channels (and rounds).

required
weight_shift float

shift to apply to weighting of each background vector to limit boost of weak spots.

0

Returns:

Type Description
np.ndarray
  • residual - float [n_spots x n_rounds x n_channels]. spot_colors after background removed.
np.ndarray
  • coef - float [n_spots, n_channels]. coefficient value for each background vector found for each spot.
np.ndarray
  • background_vectors float [n_channels x n_rounds x n_channels]. background_vectors[c] is the background vector for channel c.
Source code in coppafish/call_spots/background.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def fit_background(spot_colors: np.ndarray, weight_shift: float = 0) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    This determines the coefficient of the background vectors for each spot.
    Coefficients determined using a weighted dot product as to avoid overfitting
    and accounting for the fact that background coefficients are not updated after this.

    !!! note
        `background_vectors[i]` is 1 in channel `i` for all rounds and 0 otherwise.
        It is then normalised to have L2 norm of 1 when summed over all rounds and channels.

    Args:
        spot_colors: `float [n_spots x n_rounds x n_channels]`.
            Spot colors normalised to equalise intensities between channels (and rounds).
        weight_shift: shift to apply to weighting of each background vector to limit boost of weak spots.

    Returns:
        - residual - `float [n_spots x n_rounds x n_channels]`.
            `spot_colors` after background removed.
        - coef - `float [n_spots, n_channels]`.
            coefficient value for each background vector found for each spot.
        - background_vectors `float [n_channels x n_rounds x n_channels]`.
            background_vectors[c] is the background vector for channel c.

    """
    if weight_shift < 1e-20:
        warnings.warn(f'weight_shift value given, {weight_shift} is below 1e-20.'
                      f'Using weight_shift=1e-20 to stop blow up to infinity.')
    weight_shift = np.clip(weight_shift, 1e-20, np.inf)  # ensure weight_shift > 1e-20 to avoid blow up to infinity.

    n_rounds, n_channels = spot_colors[0].shape
    background_vectors = np.repeat(np.expand_dims(np.eye(n_channels), axis=1), n_rounds, axis=1)
    # give background_vectors an L2 norm of 1 so can compare coefficients with other genes.
    background_vectors = background_vectors / np.linalg.norm(background_vectors, axis=(1, 2), keepdims=True)

    weight_factor = 1 / (np.abs(spot_colors) + weight_shift)
    spot_weight = spot_colors * weight_factor
    background_weight = np.ones((1, n_rounds, n_channels)) * background_vectors[0, 0, 0] * weight_factor
    coef = np.sum(spot_weight * background_weight, axis=1) / np.sum(background_weight ** 2, axis=1)
    residual = spot_colors - np.expand_dims(coef, 1) * np.ones((1, n_rounds, n_channels)) * background_vectors[0, 0, 0]

    # # Old method, about 10x slower
    # n_spots = spot_colors.shape[0]
    # coef = np.zeros([n_spots, n_channels])
    # background_contribution = np.zeros_like(spot_colors)
    # background_vectors = np.zeros([n_channels, n_rounds, n_channels])
    # for c in range(n_channels):
    #     weight_factor = np.zeros([n_spots, n_rounds])
    #     for r in range(n_rounds):
    #         weight_factor[:, r] = 1 / (abs(spot_colors[:, r, c]) + weight_shift)
    #     weight_factor = np.expand_dims(weight_factor, 2)
    #
    #     background_vector = np.zeros([1, n_rounds, n_channels])
    #     background_vector[:, :, c] = 1
    #     # give background_vector an L2 norm of 1 so can compare coefficients with other genes.
    #     background_vector = background_vector / np.expand_dims(np.linalg.norm(background_vector, axis=(1, 2)), (1, 2))
    #     background_vectors[c] = background_vector
    #
    #     background_weight = background_vector * weight_factor
    #     spot_weight = spot_colors * weight_factor
    #
    #     coef[:, c] = np.sum(spot_weight * background_weight, axis=(1, 2)
    #     ) / np.sum(background_weight ** 2, axis=(1, 2))
    #     background_contribution[:, :, c] = np.expand_dims(coef[:, c], 1) * background_vector[0, 0, c]
    #
    # residual = spot_colors - background_contribution
    return residual, coef, background_vectors

Optimised

fit_background(spot_colors, weight_shift)

This determines the coefficient of the background vectors for each spot. Coefficients determined using a weighted dot product as to avoid overfitting and accounting for the fact that background coefficients are not updated after this.

Note

background_vectors[i] is 1 in channel i for all rounds and 0 otherwise. It is then normalised to have L2 norm of 1 when summed over all rounds and channels.

Parameters:

Name Type Description Default
spot_colors jnp.ndarray

float [n_spots x n_rounds x n_channels]. Spot colors normalised to equalise intensities between channels (and rounds).

required
weight_shift float

shift to apply to weighting of each background vector to limit boost of weak spots.

required

Returns:

Type Description
jnp.ndarray
  • residual - float [n_spots x n_rounds x n_channels]. spot_colors after background removed.
jnp.ndarray
  • coef - float [n_spots, n_channels]. coefficient value for each background vector found for each spot.
jnp.ndarray
  • background_vectors float [n_channels x n_rounds x n_channels]. background_vectors[c] is the background vector for channel c.
Source code in coppafish/call_spots/background_optimised.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@partial(jax.jit, static_argnums=1)
def fit_background(spot_colors: jnp.ndarray, weight_shift: float) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]:
    """
    This determines the coefficient of the background vectors for each spot.
    Coefficients determined using a weighted dot product as to avoid overfitting
    and accounting for the fact that background coefficients are not updated after this.

    !!! note
        `background_vectors[i]` is 1 in channel `i` for all rounds and 0 otherwise.
        It is then normalised to have L2 norm of 1 when summed over all rounds and channels.

    Args:
        spot_colors: `float [n_spots x n_rounds x n_channels]`.
            Spot colors normalised to equalise intensities between channels (and rounds).
        weight_shift: shift to apply to weighting of each background vector to limit boost of weak spots.

    Returns:
        - residual - `float [n_spots x n_rounds x n_channels]`.
            `spot_colors` after background removed.
        - coef - `float [n_spots, n_channels]`.
            coefficient value for each background vector found for each spot.
        - background_vectors `float [n_channels x n_rounds x n_channels]`.
            background_vectors[c] is the background vector for channel c.
    """
    return jax.vmap(fit_background_single, in_axes=(0, None), out_axes=(0, 0, None))(spot_colors, weight_shift)

fit_background_single(spot_color, weight_shift)

This determines the coefficient of the background vectors. Coefficients determined using a weighted dot product as to avoid over-fitting and accounting for the fact that background coefficients are not updated after this.

Note

background_vectors[i] is 1 in channel i for all rounds and 0 otherwise. It is then normalised to have L2 norm of 1 when summed over all rounds and channels.

Parameters:

Name Type Description Default
spot_color jnp.ndarray

float [n_rounds x n_channels]. Spot color normalised to equalise intensities between channels (and rounds).

required
weight_shift float

shift to apply to weighting of each background vector to limit boost of weak spots.

required

Returns:

Type Description
jnp.ndarray
  • residual - float [n_rounds x n_channels]. spot_color after background removed.
jnp.ndarray
  • coefs - float [n_channels]. coefficient value for each background vector.
jnp.ndarray
  • background_vectors float [n_channels x n_rounds x n_channels]. background_vectors[c] is the background vector for channel c.
Source code in coppafish/call_spots/background_optimised.py
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def fit_background_single(spot_color: jnp.ndarray, weight_shift: float) -> Tuple[jnp.ndarray, jnp.ndarray, jnp.ndarray]:
    """
    This determines the coefficient of the background vectors.
    Coefficients determined using a weighted dot product as to avoid over-fitting
    and accounting for the fact that background coefficients are not updated after this.

    !!! note
        `background_vectors[i]` is 1 in channel `i` for all rounds and 0 otherwise.
        It is then normalised to have L2 norm of 1 when summed over all rounds and channels.

    Args:
        spot_color: `float [n_rounds x n_channels]`.
            Spot color normalised to equalise intensities between channels (and rounds).
        weight_shift: shift to apply to weighting of each background vector to limit boost of weak spots.

    Returns:
        - residual - `float [n_rounds x n_channels]`.
            `spot_color` after background removed.
        - coefs - `float [n_channels]`.
            coefficient value for each background vector.
        - background_vectors `float [n_channels x n_rounds x n_channels]`.
            background_vectors[c] is the background vector for channel c.
    """
    n_rounds, n_channels = spot_color.shape
    background_vectors = jnp.repeat(jnp.expand_dims(jnp.eye(n_channels), axis=1), n_rounds, axis=1)
    # give background_vectors an L2 norm of 1 so can compare coefficients with other genes.
    background_vectors = background_vectors / jnp.linalg.norm(background_vectors, axis=(1, 2), keepdims=True)
    # array of correct shape containing the non-zero value of background_vectors everywhere.
    background_nz_value = jnp.full((n_rounds, n_channels), background_vectors[0, 0, 0])

    weight_factor = 1 / (jnp.abs(spot_color) + weight_shift)
    spot_weight = spot_color * weight_factor
    background_weight = background_nz_value * weight_factor
    coefs = jnp.sum(spot_weight * background_weight, axis=0) / jnp.sum(background_weight ** 2, axis=0)
    residual = spot_color - coefs * background_nz_value
    return residual, coefs, background_vectors