def add_loom(self, other_file: str, key: str = None, fill_values: Dict[str, np.ndarray] = None, batch_size: int = <NUM_LIT:1000>, convert_attrs: bool = False, include_graphs: bool = False) -> None:
if self._file.mode != "<STR_LIT>":<EOL><INDENT>raise IOError("<STR_LIT>")<EOL><DEDENT>with loompy.connect(other_file) as other:<EOL><INDENT>ordering = None<EOL>if key is not None:<EOL><INDENT>def ixs_thatsort_a2b(a: np.ndarray, b: np.ndarray, check_content: bool = True) -> np.ndarray:<EOL><INDENT>"<STR_LIT>"<EOL>if check_content:<EOL><INDENT>assert len(np.intersect1d(a, b)) == len(a), "<STR_LIT>"<EOL><DEDENT>return np.argsort(a)[np.argsort(np.argsort(b))]<EOL><DEDENT>ordering = ixs_thatsort_a2b(a=other.row_attrs[key], b=self.row_attrs[key])<EOL>pk1 = sorted(other.row_attrs[key])<EOL>pk2 = sorted(self.row_attrs[key])<EOL>for ix, val in enumerate(pk1):<EOL><INDENT>if pk2[ix] != val:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT><DEDENT><DEDENT>diff_layers = set(self.layers.keys()) - set(other.layers.keys())<EOL>if len(diff_layers) > <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>" % (other_file, diff_layers))<EOL><DEDENT>if convert_attrs:<EOL><INDENT>globalkeys = set(self.attrs)<EOL>globalkeys.update(other.attrs)<EOL>for globalkey in globalkeys:<EOL><INDENT>if globalkey in self.attrs and globalkey in other.attrs and self.attrs[globalkey] == other.attrs[globalkey]:<EOL><INDENT>continue<EOL><DEDENT>if globalkey not in self.col_attrs:<EOL><INDENT>self_value = self.attrs[globalkey] if globalkey in self.attrs else np.zeros(<NUM_LIT:1>, dtype=other.attrs[globalkey].dtype)[<NUM_LIT:0>]<EOL>self.col_attrs[globalkey] = np.array([self_value] * self.shape[<NUM_LIT:1>])<EOL><DEDENT>if globalkey not in other.col_attrs:<EOL><INDENT>other_value = other.attrs[globalkey] if globalkey in other.attrs else np.zeros(<NUM_LIT:1>, dtype=self.attrs[globalkey].dtype)[<NUM_LIT:0>]<EOL>other.col_attrs[globalkey] = np.array([other_value] * other.shape[<NUM_LIT:1>])<EOL><DEDENT>if globalkey in self.attrs:<EOL><INDENT>delattr(self.attrs, globalkey)<EOL><DEDENT><DEDENT><DEDENT>for (ix, selection, vals) in other.batch_scan_layers(axis=<NUM_LIT:1>, layers=self.layers.keys(), batch_size=batch_size):<EOL><INDENT>ca = {key: v[selection] for key, v in other.col_attrs.items()}<EOL>if ordering is not None:<EOL><INDENT>vals = {key: val[ordering, :] for key, val in vals.items()}<EOL><DEDENT>self.add_columns(vals, ca, fill_values=fill_values)<EOL><DEDENT>if include_graphs:<EOL><INDENT>for gname in self.col_graphs.keys():<EOL><INDENT>if gname in other.col_graphs:<EOL><INDENT>g1 = self.col_graphs[gname]<EOL>g2 = other.col_graphs[gname]<EOL>n = self.shape[<NUM_LIT:1>]<EOL>m = other.shape[<NUM_LIT:1>]<EOL>g3 = scipy.sparse.coo_matrix((np.concatenate([g1.data, g2.data]), (np.concatenate([g1.row, g2.row + n]), np.concatenate([g1.col, g2.col + n]))), shape=(n + m, n + m))<EOL>self.col_graphs[gname] = g3<EOL><DEDENT><DEDENT><DEDENT><DEDENT>
Add the content of another loom file

Args:
        other_file:       filename of the loom file to append
        key:                    Primary key to use to align rows in the other file with this file
        fill_values:     default values to use for missing attributes (or None to drop missing attrs, or 'auto' to fill with sensible defaults)
        batch_size:       the batch size used by batchscan (limits the number of rows/columns read in memory)
        convert_attrs:   convert file attributes that differ between files into column attributes
        include_graphs:  if true, include all the column graphs from other_file that are also present in this file

Returns:
        Nothing, but adds the loom file. Note that the other loom file must have exactly the same
        number of rows, and must have exactly the same column attributes.
        Adds all the contents including layers but ignores layers in `other_file` that are not already present in self
        Note that graphs are normally not added, unless include_graphs == True, in which case column graphs are added