<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def bootstrap_isc(iscs, pairwise=False, summary_statistic='median', n_bootstraps=1000, ci_percentile=95, random_state=None):
 """One-sample group-level bootstrap hypothesis test for ISCs For ISCs from one more voxels or ROIs, resample subjects with replacement to construct a bootstrap distribution. Input is a list or ndarray of ISCs for a single voxel/ROI, or an ISCs-by-voxels ndarray. ISC values should be either N ISC values for N subjects in the leave-one-out appraoch (pairwise=False), N(N-1)/2 ISC values for N subjects in the pairwise approach (pairwise=True). In the pairwise approach, ISC values should correspond to the vectorized upper triangle of a square corrlation matrix (see scipy.stats.distance.squareform). Shifts bootstrap distribution by actual summary statistic (effectively to zero) for two-tailed null hypothesis test (Hall & Wilson, 1991). Uses subject-wise (not pair-wise) resampling in the pairwise approach. Returns the observed ISC, the confidence interval, and a p-value for the bootstrap hypothesis test, as well as the bootstrap distribution of summary statistics. According to Chen et al., 2016, this is the preferred nonparametric approach for controlling false positive rates (FPR) for one-sample tests in the pairwise approach. The implementation is based on the work in [Chen2016]_ and [HallWilson1991]_. .. [HallWilson1991] "Two guidelines for bootstrap hypothesis testing.", P. Hall, S. R., Wilson, 1991, Biometrics, 757-762. https://doi.org/10.2307/2532163 Parameters iscs : list or ndarray, ISCs by voxels array ISC values for one or more voxels pairwise : bool, default: False Indicator of pairwise or leave-one-out, should match ISCs structure summary_statistic : str, default: 'median' Summary statistic, either 'median' (default) or 'mean' n_bootstraps : int, default: 1000 Number of bootstrap samples (subject-level with replacement) ci_percentile : int, default: 95 Percentile for computing confidence intervals random_state = int or None, default: None Initial random seed Returns ------- observed : float, median (or mean) ISC value Summary statistic for actual ISCs ci : tuple, bootstrap confidence intervals Confidence intervals generated from bootstrap distribution p : float, p-value p-value based on bootstrap hypothesis test distribution : ndarray, bootstraps by voxels (optional) Bootstrap distribution if return_bootstrap=True """


    # Standardize structure of input data
    iscs, n_subjects, n_voxels = _check_isc_input(iscs, pairwise=pairwise)

    # Check for valid summary statistic
    if summary_statistic not in ('mean', 'median'):
        raise ValueError("Summary statistic must be 'mean' or 'median'")

    # Compute summary statistic for observed ISCs
    observed = compute_summary_statistic(iscs,
                                         summary_statistic=summary_statistic,
                                         axis=0)

    # Set up an empty list to build our bootstrap distribution
    distribution = []

    # Loop through n bootstrap iterations and populate distribution
    for i in np.arange(n_bootstraps):

        # Random seed to be deterministically re-randomized at each iteration
        if isinstance(random_state, np.random.RandomState):
            prng = random_state
        else:
            prng = np.random.RandomState(random_state)

        # Randomly sample subject IDs with replacement
        subject_sample = sorted(prng.choice(np.arange(n_subjects),
                                            size=n_subjects))

        # Squareform and shuffle rows/columns of pairwise ISC matrix to
        # to retain correlation structure among ISCs, then get triangle
        if pairwise:

            # Loop through voxels
            isc_sample = []
            for voxel_iscs in iscs.T:

                # Square the triangle and fill diagonal
                voxel_iscs = squareform(voxel_iscs)
                np.fill_diagonal(voxel_iscs, 1)

                # Check that pairwise ISC matrix is square and symmetric
                assert voxel_iscs.shape[0] == voxel_iscs.shape[1]
                assert np.allclose(voxel_iscs, voxel_iscs.T)

                # Shuffle square correlation matrix and get triangle
                voxel_sample = voxel_iscs[subject_sample, :][:, subject_sample]
                voxel_sample = squareform(voxel_sample, checks=False)

                # Censor off-diagonal 1s for same-subject pairs
                voxel_sample[voxel_sample == 1.] = np.NaN

                isc_sample.append(voxel_sample)

            isc_sample = np.column_stack(isc_sample)

        # Get simple bootstrap sample if not pairwise
        elif not pairwise:
            isc_sample = iscs[subject_sample, :]

        # Compute summary statistic for bootstrap ISCs per voxel
        # (alternatively could construct distribution for all voxels
        # then compute statistics, but larger memory footprint)
        distribution.append(compute_summary_statistic(
                                isc_sample,
                                summary_statistic=summary_statistic,
                                axis=0))

        # Update random state for next iteration
        random_state = np.random.RandomState(prng.randint(0, MAX_RANDOM_SEED))

    # Convert distribution to numpy array
    distribution = np.array(distribution)

    # Compute CIs of median from bootstrap distribution (default: 95%)
    ci = (np.percentile(distribution, (100 - ci_percentile)/2, axis=0),
          np.percentile(distribution, ci_percentile + (100 - ci_percentile)/2,
                        axis=0))

    # Shift bootstrap distribution to 0 for hypothesis test
    shifted = distribution - observed

    # Get p-value for actual median from shifted distribution
    p = p_from_null(observed, shifted,
                    side='two-sided', exact=False,
                    axis=0)

    return observed, ci, p, distribution