• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

int-brain-lab / ibllib / 29037613829

09 Jul 2026 05:36PM UTC coverage: 61.575% (-3.3%) from 64.888%
29037613829

push

github

web-flow
Merge 88653ad51 into 087557586

1822 of 3048 new or added lines in 81 files covered. (59.78%)

3091 existing lines in 75 files now uncovered.

12243 of 19883 relevant lines covered (61.58%)

0.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.8
/brainbox/task/closed_loop.py
1
"""
2
Computes task related output
3
"""
4

5
import numpy as np
1✔
6
from scipy.stats import ranksums, wilcoxon, ttest_ind, ttest_rel
1✔
7
from ._statsmodels import multipletests
1✔
8
from sklearn.metrics import roc_auc_score
1✔
9
import pandas as pd
1✔
10
from brainbox.population.decode import get_spike_counts_in_bins
1✔
11

12

13
def responsive_units(
1✔
14
    spike_times, spike_clusters, event_times, pre_time=[0.5, 0], post_time=[0, 0.5], alpha=0.05, fdr_corr=False, use_fr=False
15
):
16
    """
17
    Determine responsive neurons by doing a Wilcoxon Signed-Rank test between a baseline period
18
    before a certain task event (e.g. stimulus onset) and a period after the task event.
19

20
    Parameters
21
    ----------
22
    spike_times : 1D array
23
        spike times (in seconds)
24
    spike_clusters : 1D array
25
        cluster ids corresponding to each event in `spikes`
26
    event_times : 1D array
27
        times (in seconds) of the events from the two groups
28
    pre_time : two-element array
29
        time (in seconds) preceding the event to get the baseline (e.g. [0.5, 0.2] would be a
30
        window starting 0.5 seconds before the event and ending at 0.2 seconds before the event)
31
    post_time : two-element array
32
        time (in seconds) to follow the event times
33
    alpha : float
34
        alpha to use for statistical significance
35
    fdr_corr : boolean
36
        whether to use an FDR correction (Benjamin-Hochmann) to correct for multiple testing
37
    use_fr : bool
38
        whether to use the firing rate instead of total spike count
39

40
    Returns
41
    -------
42
    significant_units : ndarray
43
        an array with the indices of clusters that are significatly modulated
44
    stats : 1D array
45
        the statistic of the test that was performed
46
    p_values : ndarray
47
        the p-values of all the clusters
48
    cluster_ids : ndarray
49
        cluster ids of the p-values
50
    """
51

52
    # Get spike counts for baseline and event timewindow
53
    baseline_times = np.column_stack(((event_times - pre_time[0]), (event_times - pre_time[1])))
1✔
54
    baseline_counts, cluster_ids = get_spike_counts_in_bins(spike_times, spike_clusters, baseline_times)
1✔
55
    times = np.column_stack(((event_times + post_time[0]), (event_times + post_time[1])))
1✔
56
    spike_counts, cluster_ids = get_spike_counts_in_bins(spike_times, spike_clusters, times)
1✔
57

58
    if use_fr:
1✔
59
        baseline_counts = baseline_counts / (pre_time[0] - pre_time[1])
×
60
        spike_counts = spike_counts / (post_time[1] - post_time[0])
×
61

62
    # Do statistics
63
    sig_units, stats, p_values = compute_comparison_statistics(baseline_counts, spike_counts, test='signrank', alpha=alpha)
1✔
64
    significant_units = cluster_ids[sig_units]
1✔
65

66
    return significant_units, stats, p_values, cluster_ids
1✔
67

68

69
def differentiate_units(
1✔
70
    spike_times, spike_clusters, event_times, event_groups, pre_time=0, post_time=0.5, test='ranksums', alpha=0.05, fdr_corr=False
71
):
72
    """
73
    Determine units which significantly differentiate between two task events
74
    (e.g. stimulus left/right) by performing a statistical test between the spike rates
75
    elicited by the two events. Default is a Wilcoxon Rank Sum test.
76

77
    Parameters
78
    ----------
79
    spike_times : 1D array
80
        spike times (in seconds)
81
    spike_clusters : 1D array
82
        cluster ids corresponding to each event in `spikes`
83
    event_times : 1D array
84
        times (in seconds) of the events from the two groups
85
    event_groups : 1D array
86
        group identities of the events as either 0 or 1
87
    pre_time : float
88
        time (in seconds) to precede the event times to get the baseline
89
    post_time : float
90
        time (in seconds) to follow the event times
91
    test : string
92
        which statistical test to use, options are:
93
            'ranksums'      Wilcoxon Rank Sums test
94
            'signrank'      Wilcoxon Signed Rank test (for paired observations)
95
            'ttest'         independent samples t-test
96
            'paired_ttest'  paired t-test
97
    alpha : float
98
        alpha to use for statistical significance
99
    fdr_corr : boolean
100
        whether to use an FDR correction (Benjamin-Hochmann) to correct for multiple testing
101

102
    Returns
103
    -------
104
    significant_units : 1D array
105
        an array with the indices of clusters that are significatly modulated
106
    stats : 1D array
107
        the statistic of the test that was performed
108
    p_values : 1D array
109
        the p-values of all the clusters
110
    cluster_ids : ndarray
111
        cluster ids of the p-values
112
    """
113

114
    # Check input
115
    assert test in ['ranksums', 'signrank', 'ttest', 'paired_ttest']
1✔
116
    if (test == 'signrank') or (test == 'paired_ttest'):
1✔
NEW
117
        assert np.sum(event_groups == 0) == np.sum(event_groups == 1), (
×
118
            'For paired tests the number of events in both groups needs to be the same'
119
        )
120

121
    # Get spike counts for the two events
122
    times_1 = np.column_stack(((event_times[event_groups == 0] - pre_time), (event_times[event_groups == 0] + post_time)))
1✔
123
    counts_1, cluster_ids = get_spike_counts_in_bins(spike_times, spike_clusters, times_1)
1✔
124
    times_2 = np.column_stack(((event_times[event_groups == 1] - pre_time), (event_times[event_groups == 1] + post_time)))
1✔
125
    counts_2, cluster_ids = get_spike_counts_in_bins(spike_times, spike_clusters, times_2)
1✔
126

127
    # Do statistics
128
    sig_units, stats, p_values = compute_comparison_statistics(counts_1, counts_2, test=test, alpha=alpha)
1✔
129
    significant_units = cluster_ids[sig_units]
1✔
130

131
    return significant_units, stats, p_values, cluster_ids
1✔
132

133

134
def compute_comparison_statistics(value1, value2, test='ranksums', alpha=0.05, fdr_corr=False):
1✔
135
    """
136
    Compute statistical test between two arrays
137

138
    Parameters
139
    ----------
140
    value1 : 1D array
141
        first array of values to compare
142
    value2 : 1D array
143
        second array of values to compare
144
    test : string
145
        which statistical test to use, options are:
146
            'ranksums'      Wilcoxon Rank Sums test
147
            'signrank'      Wilcoxon Signed Rank test (for paired observations)
148
            'ttest'         independent samples t-test
149
            'paired_ttest'  paired t-test
150
    alpha : float
151
        alpha to use for statistical significance
152
    fdr_corr : boolean
153
        whether to use an FDR correction (Benjamin-Hochmann) to correct for multiple testing
154

155
    Returns
156
    -------
157
    significant_units : 1D array
158
        an array with the indices of values that are significatly modulated
159
    stats : 1D array
160
        the statistic of the test that was performed
161
    p_values : 1D array
162
        the p-values of all the values
163
    """
164

165
    p_values = np.empty(len(value1))
1✔
166
    stats = np.empty(len(value1))
1✔
167
    for i in range(len(value1)):
1✔
168
        if test == 'signrank':
1✔
169
            if np.sum(value1[i, :] - value2[i, :]) == 0:
1✔
170
                p_values[i] = 1
1✔
171
                stats[i] = 0
1✔
172
            else:
173
                stats[i], p_values[i] = wilcoxon(value1[i, :], value2[i, :])
1✔
174
        else:
175
            if (np.sum(value1[i, :]) == 0) and (np.sum(value2[i, :]) == 0):
1✔
176
                p_values[i] = 1
1✔
177
                stats[i] = 0
1✔
178
            else:
179
                if test == 'ranksums':
1✔
180
                    stats[i], p_values[i] = ranksums(value1[i, :], value2[i, :])
1✔
181
                elif test == 'ttest':
×
182
                    stats[i], p_values[i] = ttest_ind(value1[i, :], value2[i, :])
×
183
                elif test == 'paired_ttest':
×
184
                    stats[i], p_values[i] = ttest_rel(value1[i, :], value2[i, :])
×
185

186
    # Perform Benjamin-Hochmann FDR correction for multiple testing
187
    if fdr_corr:
1✔
188
        sig_units, p_values, _, _ = multipletests(p_values, alpha, method='fdr_bh')
×
189
    else:
190
        sig_units = p_values < alpha
1✔
191

192
    return sig_units, stats, p_values
1✔
193

194

195
def roc_single_event(spike_times, spike_clusters, event_times, pre_time=[0.5, 0], post_time=[0, 0.5]):
1✔
196
    """
197
    Determine how well neurons respond to a certain task event by calculating the area under the
198
    ROC curve between a baseline period before the event and a period after the event.
199
    Values of > 0.5 indicate the neuron respons positively to the event and < 0.5 indicate
200
    a negative response.
201

202
    Parameters
203
    ----------
204
    spike_times : 1D array
205
        spike times (in seconds)
206
    spike_clusters : 1D array
207
        cluster ids corresponding to each event in `spikes`
208
    event_times : 1D array
209
        times (in seconds) of the events from the two groups
210
    pre_time : two-element array
211
        time (in seconds) preceding the event to get the baseline (e.g. [0.5, 0.2] would be a
212
        window starting 0.5 seconds before the event and ending at 0.2 seconds before the event)
213
    post_time : two-element array
214
        time (in seconds) to follow the event times
215

216
    Returns
217
    -------
218
    auc_roc : 1D array
219
        the area under the ROC curve
220
    cluster_ids : 1D array
221
        cluster ids of the p-values
222
    """
223

224
    # Get spike counts for baseline and event timewindow
225
    baseline_times = np.column_stack(((event_times - pre_time[0]), (event_times - pre_time[1])))
1✔
226
    baseline_counts, cluster_ids = get_spike_counts_in_bins(spike_times, spike_clusters, baseline_times)
1✔
227
    times = np.column_stack(((event_times + post_time[0]), (event_times + post_time[1])))
1✔
228
    spike_counts, cluster_ids = get_spike_counts_in_bins(spike_times, spike_clusters, times)
1✔
229

230
    # Calculate area under the ROC curve per neuron
231
    auc_roc = np.empty(spike_counts.shape[0])
1✔
232
    for i in range(spike_counts.shape[0]):
1✔
233
        auc_roc[i] = roc_auc_score(
1✔
234
            np.concatenate((np.zeros(baseline_counts.shape[1]), np.ones(spike_counts.shape[1]))),
235
            np.concatenate((baseline_counts[i, :], spike_counts[i, :])),
236
        )
237

238
    return auc_roc, cluster_ids
1✔
239

240

241
def roc_between_two_events(spike_times, spike_clusters, event_times, event_groups, pre_time=0, post_time=0.25):
1✔
242
    """
243
    Calcluate area under the ROC curve that indicates how well the activity of the neuron
244
    distiguishes between two events (e.g. movement to the right vs left). A value of 0.5 indicates
245
    the neuron cannot distiguish between the two events. A value of 0 or 1 indicates maximum
246
    distinction. Significance is determined by bootstrapping the ROC curves. If 0.5 is not
247
    included in the 95th percentile of the bootstrapped distribution, the neuron is deemed
248
    to be significant.
249

250
    Parameters
251
    ----------
252
    spike_times : 1D array
253
        spike times (in seconds)
254
    spike_clusters : 1D array
255
        cluster ids corresponding to each event in `spikes`
256
    event_times : 1D array
257
        times (in seconds) of the events from the two groups
258
    event_groups : 1D array
259
        group identities of the events as either 0 or 1
260
    pre_time : float
261
        time (in seconds) to precede the event times
262
    post_time : float
263
        time (in seconds) to follow the event times
264

265
    Returns
266
    -------
267
    auc_roc : 1D array
268
        an array of the area under the ROC curve for every neuron
269
    cluster_ids : 1D array
270
        cluster ids of the AUC values
271
    """
272

273
    # Get spike counts
274
    times = np.column_stack(((event_times - pre_time), (event_times + post_time)))
1✔
275
    spike_counts, cluster_ids = get_spike_counts_in_bins(spike_times, spike_clusters, times)
1✔
276

277
    # Calculate area under the ROC curve per neuron
278
    auc_roc = np.empty(spike_counts.shape[0])
1✔
279
    for i in range(spike_counts.shape[0]):
1✔
280
        auc_roc[i] = roc_auc_score(event_groups, spike_counts[i, :])
1✔
281

282
    return auc_roc, cluster_ids
1✔
283

284

285
def _get_biased_probs(n: int, idx: int = -1, prob: float = 0.5) -> list:
1✔
286
    n_1 = n - 1
1✔
287
    z = n_1 + prob
1✔
288
    p = [1 / z] * (n_1 + 1)
1✔
289
    p[idx] *= prob
1✔
290
    return p
1✔
291

292

293
def _draw_contrast(contrast_set: list, prob_type: str = 'biased', idx: int = -1, idx_prob: float = 0.5) -> float:
1✔
294
    if prob_type in ['non-uniform', 'biased']:
1✔
295
        p = _get_biased_probs(len(contrast_set), idx=idx, prob=idx_prob)
1✔
296
        return np.random.choice(contrast_set, p=p)
1✔
297
    elif prob_type == 'uniform':
1✔
298
        return np.random.choice(contrast_set)
1✔
299

300

301
def _draw_position(position_set, stim_probability_left):
1✔
302
    return int(np.random.choice(position_set, p=[stim_probability_left, 1 - stim_probability_left]))
1✔
303

304

305
def generate_pseudo_blocks(n_trials, factor=60, min_=20, max_=100, first5050=90):
1✔
306
    """
307
    Generate a pseudo block structure
308

309
    Parameters
310
    ----------
311
    n_trials : int
312
        how many trials to generate
313
    factor : int
314
        factor of the exponential
315
    min_ : int
316
        minimum number of trials per block
317
    max_ : int
318
        maximum number of trials per block
319
    first5050 : int
320
        amount of trials with 50/50 left right probability at the beginning
321

322
    Returns
323
    ---------
324
    probabilityLeft : 1D array
325
        array with probability left per trial
326
    """
327

328
    block_ids = []
1✔
329
    while len(block_ids) < n_trials:
1✔
330
        x = np.random.exponential(factor)
1✔
331
        while (x <= min_) | (x >= max_):
1✔
332
            x = np.random.exponential(factor)
1✔
333
        if (len(block_ids) == 0) & (np.random.randint(2) == 0):
1✔
334
            block_ids += [0.2] * int(x)
1✔
335
        elif len(block_ids) == 0:
1✔
336
            block_ids += [0.8] * int(x)
1✔
337
        elif block_ids[-1] == 0.2:
1✔
338
            block_ids += [0.8] * int(x)
1✔
339
        elif block_ids[-1] == 0.8:
1✔
340
            block_ids += [0.2] * int(x)
1✔
341
    return np.array([0.5] * first5050 + block_ids[: n_trials - first5050])
1✔
342

343

344
def generate_pseudo_stimuli(n_trials, contrast_set=[0, 0.06, 0.12, 0.25, 1], first5050=90):
1✔
345
    """
346
    Generate a block structure with stimuli
347

348
    Parameters
349
    ----------
350
    n_trials : int
351
        number of trials to generate
352
    contrast_set : 1D array
353
        the contrasts that are presented. The default is [0.06, 0.12, 0.25, 1].
354
    first5050 : int
355
        Number of 50/50 trials at the beginning of the session. The default is 90.
356

357
    Returns
358
    -------
359
    p_left : 1D array
360
        probability of left stimulus
361
    contrast_left : 1D array
362
        contrast on the left
363
    contrast_right : 1D array
364
        contrast on the right
365

366
    """
367

368
    # Initialize vectors
369
    contrast_left = np.empty(n_trials)
1✔
370
    contrast_left[:] = np.nan
1✔
371
    contrast_right = np.empty(n_trials)
1✔
372
    contrast_right[:] = np.nan
1✔
373

374
    # Generate block structure
375
    p_left = generate_pseudo_blocks(n_trials, first5050=first5050)
1✔
376

377
    for i in range(n_trials):
1✔
378
        # Draw position and contrast for this trial
379
        position = _draw_position([-1, 1], p_left[i])
1✔
380
        contrast = _draw_contrast(contrast_set, 'uniform')
1✔
381

382
        # Add to trials
383
        if position == -1:
1✔
384
            contrast_left[i] = contrast
1✔
385
        elif position == 1:
1✔
386
            contrast_right[i] = contrast
1✔
387

388
    return p_left, contrast_left, contrast_right
1✔
389

390

391
def generate_pseudo_session(trials, generate_choices=True, contrast_distribution='non-uniform'):
1✔
392
    """
393
    Generate a complete pseudo session with biased blocks, all stimulus contrasts, choices and
394
    rewards and omissions. Biased blocks and stimulus contrasts are generated using the same
395
    statistics as used in the actual task. The choices of the animal are generated using the
396
    actual psychometrics of the animal in the session. For each synthetic trial the choice is
397
    determined by drawing from a Bernoulli distribution that is biased according to the proportion
398
    of times the animal chose left for the stimulus contrast, side, and block probability.
399
    No-go trials are ignored in the generating of the synthetic choices.
400

401
    Parameters
402
    ----------
403
    trials : DataFrame
404
        Pandas dataframe with columns as trial vectors loaded using ONE
405
    generate_choices : bool
406
        whether to generate the choices (runs faster without)
407
    contrast_distribution: str ['uniform', 'non-uniform']
408
        the absolute contrast distribution.
409
        If uniform, the zero contrast is as likely as other contrasts: BiasedChoiceWorld task
410
        If 'non-uniform', the zero contrast is half as likely to occur: EphysChoiceWorld task
411
        ('biased' is kept for compatibility, but is deprecated as it is confusing)
412

413
    Returns
414
    -------
415
    pseudo_trials : DataFrame
416
        a trials dataframe with synthetically generated trials
417
    """
418
    # Get contrast set presented to the animal
419
    contrast_set = np.unique(trials['contrastLeft'][~np.isnan(trials['contrastLeft'])])
1✔
420
    signed_contrast = trials['contrastRight'].copy()
1✔
421
    signed_contrast[np.isnan(signed_contrast)] = -trials['contrastLeft'][~np.isnan(trials['contrastLeft'])]
1✔
422

423
    # Generate synthetic session
424
    pseudo_trials = pd.DataFrame()
1✔
425
    pseudo_trials['probabilityLeft'] = generate_pseudo_blocks(trials.shape[0])
1✔
426

427
    # For each trial draw stimulus contrast and side and generate a synthetic choice
428
    for i in range(pseudo_trials.shape[0]):
1✔
429
        # Draw position and contrast for this trial
430
        position = _draw_position([-1, 1], pseudo_trials['probabilityLeft'][i])
1✔
431
        contrast = _draw_contrast(contrast_set, prob_type=contrast_distribution, idx=np.where(contrast_set == 0)[0][0])
1✔
432
        signed_stim = contrast * np.sign(position)
1✔
433

434
        if generate_choices:
1✔
435
            # Generate synthetic choice by drawing from Bernoulli distribution
NEW
436
            trial_select = (
×
437
                (signed_contrast == signed_stim)
438
                & (trials['choice'] != 0)
439
                & (trials['probabilityLeft'] == pseudo_trials['probabilityLeft'][i])
440
            )
NEW
441
            p_right = np.sum(trials['choice'][trial_select] == 1) / trials['choice'][trial_select].shape[0]
×
442
            this_choice = [-1, 1][np.random.binomial(1, p_right)]
×
443

444
            # Add to trials
445
            if position == -1:
×
446
                pseudo_trials.loc[i, 'contrastLeft'] = contrast
×
447
                if this_choice == -1:
×
448
                    pseudo_trials.loc[i, 'feedbackType'] = -1
×
UNCOV
449
                elif this_choice == 1:
×
UNCOV
450
                    pseudo_trials.loc[i, 'feedbackType'] = 1
×
UNCOV
451
            elif position == 1:
×
UNCOV
452
                pseudo_trials.loc[i, 'contrastRight'] = contrast
×
UNCOV
453
                if this_choice == -1:
×
UNCOV
454
                    pseudo_trials.loc[i, 'feedbackType'] = 1
×
UNCOV
455
                elif this_choice == 1:
×
UNCOV
456
                    pseudo_trials.loc[i, 'feedbackType'] = -1
×
UNCOV
457
            pseudo_trials.loc[i, 'choice'] = this_choice
×
458
        else:
459
            if position == -1:
1✔
460
                pseudo_trials.loc[i, 'contrastLeft'] = contrast
1✔
461
            elif position == 1:
1✔
462
                pseudo_trials.loc[i, 'contrastRight'] = contrast
1✔
463
        pseudo_trials.loc[i, 'stim_side'] = position
1✔
464
    pseudo_trials['signed_contrast'] = pseudo_trials['contrastRight']
1✔
465
    pseudo_trials.loc[pseudo_trials['signed_contrast'].isnull(), 'signed_contrast'] = -pseudo_trials['contrastLeft']
1✔
466
    return pseudo_trials
1✔
467

468

469
def get_impostor_target(targets, labels, current_label=None, seed_idx=None, verbose=False):
1✔
470
    """
471
    Generate impostor targets by selecting from a list of current targets of variable length.
472
    Targets are selected and stitched together to the length of the current labeled target,
473
    aka 'Frankenstein' targets, often used for evaluating a null distribution while decoding.
474

475
    Parameters
476
    ----------
477
    targets : list of all targets
478
            targets may be arrays of any dimension (a,b,...,z)
479
            but must have the same shape except for the last dimension, z.  All targets must
480
            have z > 0.
481
    labels : numpy array of strings
482
            labels corresponding to each target e.g. session eid.
483
            only targets with unique labels are used to create impostor target.  Typically,
484
            use eid as the label because each eid has a unique target.
485
    current_label : string
486
            targets with the current label are not used to create impostor
487
            target.  Size of corresponding target is used to determine size of impostor
488
            target.  If None, a random selection from the set of unique labels is used.
489

490
    Returns
491
    --------
492
    impostor_final : numpy array, same shape as all targets except last dimension
493

494
    """
495

496
    np.random.seed(seed_idx)
1✔
497

498
    unique_labels, unique_label_idxs = np.unique(labels, return_index=True)
1✔
499
    unique_targets = [targets[unique_label_idxs[i]] for i in range(len(unique_label_idxs))]
1✔
500
    if current_label is None:
1✔
501
        current_label = np.random.choice(unique_labels)
1✔
502
    avoid_same_label = ~(unique_labels == current_label)
1✔
503
    # current label must correspond to exactly one unique label
504
    assert len(np.nonzero(~avoid_same_label)[0]) == 1
1✔
505
    avoided_index = np.nonzero(~avoid_same_label)[0][0]
1✔
506
    nonavoided_indices = np.nonzero(avoid_same_label)[0]
1✔
507
    ntargets = len(nonavoided_indices)
1✔
508
    all_impostor_targets = [unique_targets[nonavoided_indices[i]] for i in range(ntargets)]
1✔
509
    all_impostor_sizes = np.array([all_impostor_targets[i].shape[-1] for i in range(ntargets)])
1✔
510
    current_target_size = unique_targets[avoided_index].shape[-1]
1✔
511
    if verbose:
1✔
UNCOV
512
        print('impostor target has length %s' % (current_target_size))
×
513
    assert np.min(all_impostor_sizes) > 0  # all targets must be nonzero in size
1✔
514
    max_needed_to_tile = int(np.max(all_impostor_sizes) / np.min(all_impostor_sizes)) + 1
1✔
515
    tile_indices = np.random.choice(np.arange(len(all_impostor_targets), dtype=int), size=max_needed_to_tile, replace=False)
1✔
516
    impostor_tiles = [all_impostor_targets[tile_indices[i]] for i in range(len(tile_indices))]
1✔
517
    impostor_tile_sizes = all_impostor_sizes[tile_indices]
1✔
518
    if verbose:
1✔
UNCOV
519
        print('Randomly chose %s targets to tile the impostor target' % (max_needed_to_tile))
×
UNCOV
520
        print('with the following sizes:', impostor_tile_sizes)
×
521

522
    number_of_tiles_needed = np.sum(np.cumsum(impostor_tile_sizes) < current_target_size) + 1
1✔
523
    impostor_tiles = impostor_tiles[:number_of_tiles_needed]
1✔
524
    if verbose:
1✔
NEW
525
        print('%s of %s needed to tile the entire impostor target' % (number_of_tiles_needed, max_needed_to_tile))
×
526

527
    impostor_stitch = np.concatenate(impostor_tiles, axis=-1)
1✔
528
    start_ind = np.random.randint((impostor_stitch.shape[-1] - current_target_size) + 1)
1✔
529
    impostor_final = impostor_stitch[..., start_ind : start_ind + current_target_size]
1✔
530
    if verbose:
1✔
NEW
531
        print('%s targets stitched together with shift of %s\n' % (number_of_tiles_needed, start_ind))
×
532

533
    np.random.seed(None)  # reset numpy seed to None
1✔
534

535
    return impostor_final
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc