def merge_sample(sample, sample_subann,<EOL>data_sources=None, derived_attributes=None):
merged_attrs = {}<EOL>if sample_subann is None:<EOL><INDENT>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>")<EOL>return merged_attrs<EOL><DEDENT>if SAMPLE_NAME_COLNAME not in sample_subann.columns:<EOL><INDENT>raise KeyError(<EOL>"<STR_LIT>".<EOL>format(SAMPLE_NAME_COLNAME))<EOL><DEDENT>_LOGGER.debug("<STR_LIT>".<EOL>format(data_sources))<EOL>derived_attributes = set(derived_attributes or [])<EOL>_LOGGER.debug("<STR_LIT>".<EOL>format(derived_attributes))<EOL>sample_name = getattr(sample, SAMPLE_NAME_COLNAME)<EOL>sample_indexer = sample_subann[SAMPLE_NAME_COLNAME] == sample_name<EOL>this_sample_rows = sample_subann[sample_indexer]<EOL>if len(this_sample_rows) == <NUM_LIT:0>:<EOL><INDENT>_LOGGER.debug("<STR_LIT>", sample.name)<EOL>return merged_attrs<EOL><DEDENT>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>", len(this_sample_rows))<EOL>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>".format(this_sample_rows.to_dict()))<EOL>merged_attrs = {key: "<STR_LIT>" for key in this_sample_rows.columns}<EOL>subsamples = []<EOL>_LOGGER.debug(this_sample_rows)<EOL>for subsample_row_id, row in this_sample_rows.iterrows():<EOL><INDENT>try:<EOL><INDENT>row['<STR_LIT>']<EOL><DEDENT>except KeyError:<EOL><INDENT>row['<STR_LIT>'] = str(subsample_row_id)<EOL><DEDENT>subann_unit = Subsample(row)<EOL>subsamples.append(subann_unit)<EOL>_LOGGER.debug(subsamples)<EOL>rowdata = row.to_dict()<EOL>for attr_name in this_sample_rows.columns:<EOL><INDENT>if attr_name == SAMPLE_NAME_COLNAME orattr_name not in derived_attributes:<EOL><INDENT>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>", attr_name)<EOL>continue<EOL><DEDENT>attr_value = rowdata[attr_name]<EOL>col_key = attr_name + COL_KEY_SUFFIX<EOL>merged_attrs[col_key] = "<STR_LIT>"<EOL>rowdata[col_key] = attr_value<EOL>data_src_path = sample.locate_data_source(<EOL>data_sources, attr_name, source_key=rowdata[attr_name],<EOL>extra_vars=rowdata)  <EOL>rowdata[attr_name] = data_src_path<EOL><DEDENT>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>")<EOL>for attr in derived_attributes:<EOL><INDENT>if not hasattr(sample, attr) or attr in rowdata:<EOL><INDENT>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>", attr)<EOL>continue<EOL><DEDENT>col_key = attr + COL_KEY_SUFFIX<EOL>rowdata[col_key] = getattr(sample, attr)<EOL>rowdata[attr] = sample.locate_data_source(<EOL>data_sources, attr, source_key=getattr(sample, attr),<EOL>extra_vars=rowdata)<EOL><DEDENT>for attname, attval in rowdata.items():<EOL><INDENT>if attname == SAMPLE_NAME_COLNAME or not attval:<EOL><INDENT>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>".format(attname, attval))<EOL>continue<EOL><DEDENT>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>",<EOL>str(sample.name), str(attname), str(attval))<EOL>if attname not in merged_attrs:<EOL><INDENT>new_attval = str(attval).rstrip()<EOL><DEDENT>else:<EOL><INDENT>new_attval = "<STR_LIT>".format(merged_attrs[attname],<EOL>str(attval)).strip()<EOL><DEDENT>merged_attrs[attname] = new_attval  <EOL>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>",<EOL>new_attval, attname)<EOL><DEDENT><DEDENT>merged_attrs.pop(SAMPLE_NAME_COLNAME, None)<EOL>_LOGGER.log(<NUM_LIT:5>, "<STR_LIT>".format(sample.name, merged_attrs))<EOL>sample.update(merged_attrs)  <EOL>sample.merged_cols = merged_attrs<EOL>sample.merged = True<EOL>sample.subsamples = subsamples<EOL>return sample<EOL>
Use merge table (subannotation) data to augment/modify Sample.

:param Sample sample: sample to modify via merge table data
:param sample_subann: data with which to alter Sample
:param Mapping data_sources: collection of named paths to data locations,
    optional
:param Iterable[str] derived_attributes: names of attributes for which
    corresponding Sample attribute's value is data-derived, optional
:return Set[str]: names of columns/attributes that were merged