def test_set_obsm(adata):
    init_hash = joblib.hash(adata)

    dim0_size = np.random.randint(2, adata.shape[0] - 1)
    dim1_size = np.random.randint(1, 99)
    orig_obsm_val = adata.obsm["o"].copy()
    subset_idx = np.random.choice(adata.obs_names, dim0_size, replace=False)

    subset = adata[subset_idx, :]
    assert subset.is_view
    subset.obsm = dict(o=np.ones((dim0_size, dim1_size)))
    assert not subset.is_view
    assert np.all(orig_obsm_val == adata.obsm["o"])  # Checking for mutation
    assert np.all(subset.obsm["o"] == np.ones((dim0_size, dim1_size)))

    subset = adata[subset_idx, :]
    subset_hash = joblib.hash(subset)
    with pytest.raises(ValueError):
        subset.obsm = dict(o=np.ones((dim0_size + 1, dim1_size)))
    with pytest.raises(ValueError):
        subset.varm = dict(o=np.ones((dim0_size - 1, dim1_size)))
    assert subset_hash == joblib.hash(subset)

    # Only modification have been made to a view
    assert init_hash == joblib.hash(adata)
def read_example_file(
        netcdf_file_name, read_all_target_vars, target_name=None,
        metadata_only=False, targets_only=False, include_soundings=True,
        radar_field_names_to_keep=None, radar_heights_to_keep_m_agl=None,
        sounding_field_names_to_keep=None, sounding_heights_to_keep_m_agl=None,
        first_time_to_keep_unix_sec=None, last_time_to_keep_unix_sec=None,
        num_rows_to_keep=None, num_columns_to_keep=None,
        downsampling_dict=None):
    """Reads examples from NetCDF file.

    If `metadata_only == True`, later input args are ignored.
    If `targets_only == True`, later input args are ignored.

    :param netcdf_file_name: Path to input file.
    :param read_all_target_vars: Boolean flag.  If True, will read all target
        variables.  If False, will read only `target_name`.  Either way, if
        downsampling is done, it will be based only on `target_name`.
    :param target_name: Will read this target variable.  If
        `read_all_target_vars == True` and `downsampling_dict is None`, you can
        leave this alone.
    :param metadata_only: Boolean flag.  If False, this method will read
        everything.  If True, will read everything except predictor and target
        variables.
    :param targets_only: Boolean flag.  If False, this method will read
        everything.  If True, will read everything except predictors.
    :param include_soundings: Boolean flag.  If True and the file contains
        soundings, this method will return soundings.  Otherwise, no soundings.
    :param radar_field_names_to_keep: See doc for `_subset_radar_data`.
    :param radar_heights_to_keep_m_agl: Same.
    :param sounding_field_names_to_keep: See doc for `_subset_sounding_data`.
    :param sounding_heights_to_keep_m_agl: Same.
    :param first_time_to_keep_unix_sec: First time to keep.  If
        `first_time_to_keep_unix_sec is None`, all storm objects will be kept.
    :param last_time_to_keep_unix_sec: Last time to keep.  If
        `last_time_to_keep_unix_sec is None`, all storm objects will be kept.
    :param num_rows_to_keep: See doc for `_subset_radar_data`.
    :param num_columns_to_keep: Same.
    :param downsampling_dict: See doc for `_filter_examples_by_class`.
    :return: example_dict: If `read_all_target_vars == True`, dictionary will
        have all keys listed in doc for `write_example_file`.  If
        `read_all_target_vars == False`, key "target_names" will be replaced by
        "target_name" and "target_matrix" will be replaced by "target_values".

    example_dict['target_name']: Name of target variable.
    example_dict['target_values']: length-E list of target values (integer
        class labels), where E = number of examples.
    """

    # TODO(thunderhoser): Allow this method to read only soundings without radar
    # data.

    if (
            target_name ==
            'tornado_lead-time=0000-3600sec_distance=00000-10000m'
    ):
        target_name = (
            'tornado_lead-time=0000-3600sec_distance=00000-30000m_min-fujita=0'
        )

    error_checking.assert_is_boolean(read_all_target_vars)
    error_checking.assert_is_boolean(include_soundings)
    error_checking.assert_is_boolean(metadata_only)
    error_checking.assert_is_boolean(targets_only)

    example_dict, netcdf_dataset = _read_metadata_from_example_file(
        netcdf_file_name=netcdf_file_name, include_soundings=include_soundings)

    need_main_target_values = (
        not read_all_target_vars
        or downsampling_dict is not None
    )

    if need_main_target_values:
        target_index = example_dict[TARGET_NAMES_KEY].index(target_name)
    else:
        target_index = -1

    if not read_all_target_vars:
        example_dict[TARGET_NAME_KEY] = target_name
        example_dict.pop(TARGET_NAMES_KEY)

    if metadata_only:
        netcdf_dataset.close()
        return example_dict

    if need_main_target_values:
        main_target_values = numpy.array(
            netcdf_dataset.variables[TARGET_MATRIX_KEY][:, target_index],
            dtype=int
        )
    else:
        main_target_values = None

    if read_all_target_vars:
        example_dict[TARGET_MATRIX_KEY] = numpy.array(
            netcdf_dataset.variables[TARGET_MATRIX_KEY][:], dtype=int
        )
    else:
        example_dict[TARGET_VALUES_KEY] = main_target_values

    # Subset by time.
    if first_time_to_keep_unix_sec is None:
        first_time_to_keep_unix_sec = 0
    if last_time_to_keep_unix_sec is None:
        last_time_to_keep_unix_sec = int(1e12)

    error_checking.assert_is_integer(first_time_to_keep_unix_sec)
    error_checking.assert_is_integer(last_time_to_keep_unix_sec)
    error_checking.assert_is_geq(
        last_time_to_keep_unix_sec, first_time_to_keep_unix_sec)

    example_indices_to_keep = numpy.where(numpy.logical_and(
        example_dict[STORM_TIMES_KEY] >= first_time_to_keep_unix_sec,
        example_dict[STORM_TIMES_KEY] <= last_time_to_keep_unix_sec
    ))[0]

    if downsampling_dict is not None:
        subindices_to_keep = _filter_examples_by_class(
            target_values=main_target_values[example_indices_to_keep],
            downsampling_dict=downsampling_dict
        )
    elif not read_all_target_vars:
        subindices_to_keep = numpy.where(
            main_target_values[example_indices_to_keep] !=
            target_val_utils.INVALID_STORM_INTEGER
        )[0]
    else:
        subindices_to_keep = numpy.linspace(
            0, len(example_indices_to_keep) - 1,
            num=len(example_indices_to_keep), dtype=int
        )

    example_indices_to_keep = example_indices_to_keep[subindices_to_keep]
    if len(example_indices_to_keep) == 0:
        return None

    example_dict[FULL_IDS_KEY] = [
        example_dict[FULL_IDS_KEY][k] for k in example_indices_to_keep
    ]
    example_dict[STORM_TIMES_KEY] = (
        example_dict[STORM_TIMES_KEY][example_indices_to_keep]
    )

    if read_all_target_vars:
        example_dict[TARGET_MATRIX_KEY] = (
            example_dict[TARGET_MATRIX_KEY][example_indices_to_keep, :]
        )
    else:
        example_dict[TARGET_VALUES_KEY] = (
            example_dict[TARGET_VALUES_KEY][example_indices_to_keep]
        )

    if targets_only:
        netcdf_dataset.close()
        return example_dict

    example_dict = _subset_radar_data(
        example_dict=example_dict, netcdf_dataset_object=netcdf_dataset,
        example_indices_to_keep=example_indices_to_keep,
        field_names_to_keep=radar_field_names_to_keep,
        heights_to_keep_m_agl=radar_heights_to_keep_m_agl,
        num_rows_to_keep=num_rows_to_keep,
        num_columns_to_keep=num_columns_to_keep)

    if not include_soundings:
        netcdf_dataset.close()
        return example_dict

    example_dict = _subset_sounding_data(
        example_dict=example_dict, netcdf_dataset_object=netcdf_dataset,
        example_indices_to_keep=example_indices_to_keep,
        field_names_to_keep=sounding_field_names_to_keep,
        heights_to_keep_m_agl=sounding_heights_to_keep_m_agl)

    netcdf_dataset.close()
    return example_dict